diff --git a/.efrocachemap b/.efrocachemap index 15bf8e53..a1bce6d4 100644 --- a/.efrocachemap +++ b/.efrocachemap @@ -420,8 +420,8 @@ "assets/build/ba_data/audio/zoeOw.ogg": "https://files.ballistica.net/cache/ba1/60/ad/38269b7f1c7dc20cb9a506cd0681", "assets/build/ba_data/audio/zoePickup01.ogg": "https://files.ballistica.net/cache/ba1/72/85/d6fc4d16b7081d91fba2850b5b10", "assets/build/ba_data/audio/zoeScream01.ogg": "https://files.ballistica.net/cache/ba1/e9/ae/1d674d0c086eaa0bd1c3b1db0505", - "assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/e8/42/a43c158be7fa45f2c0c3d4b84a1f", - "assets/build/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/5b/cf/4501b151257c3d8d6ee8d0497d14", + "assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/5a/10/dae249cdd589b795fd341ebbec42", + "assets/build/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/e2/24/5e7ea9ca5c9de4d3b7a28e53564d", "assets/build/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/61/03/89070ca765e06da3a419a579f503", "assets/build/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/f8/15/e1a2fa38697417bcf2cf19cd34ef", "assets/build/ba_data/data/languages/chinesetraditional.json": "https://files.ballistica.net/cache/ba1/44/aa/c12568afb4558dc7f9f2fa155467", @@ -431,17 +431,17 @@ "assets/build/ba_data/data/languages/dutch.json": "https://files.ballistica.net/cache/ba1/68/93/da8e9874f41a786edf52ba4ccaad", "assets/build/ba_data/data/languages/english.json": "https://files.ballistica.net/cache/ba1/72/80/d6395c8a168558750c0d79ce769b", "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/8f/73/093120ae2241d8f4b899ccda2d75", - "assets/build/ba_data/data/languages/french.json": "https://files.ballistica.net/cache/ba1/25/65/1cb03566e73811fc6e1b841d9072", - "assets/build/ba_data/data/languages/german.json": "https://files.ballistica.net/cache/ba1/ef/e6/d4909f571d7473fd04055728490e", + "assets/build/ba_data/data/languages/filipino.json": "https://files.ballistica.net/cache/ba1/e9/07/b2dc862601bcd70701b083d43279", + "assets/build/ba_data/data/languages/french.json": "https://files.ballistica.net/cache/ba1/2e/48/b0a8fafc5e5436e99d9a3d697d23", + "assets/build/ba_data/data/languages/german.json": "https://files.ballistica.net/cache/ba1/8a/09/3e0fa9e44913b53f4dab195d3fae", "assets/build/ba_data/data/languages/gibberish.json": "https://files.ballistica.net/cache/ba1/8a/2a/b2bc00eed0608b2199b2bc379b2e", - "assets/build/ba_data/data/languages/greek.json": "https://files.ballistica.net/cache/ba1/82/eb/37ff44af76812097f9c98f05c730", - "assets/build/ba_data/data/languages/hindi.json": "https://files.ballistica.net/cache/ba1/50/e8/837be1324c8128507b3df89b689f", + "assets/build/ba_data/data/languages/greek.json": "https://files.ballistica.net/cache/ba1/e0/04/6be14bff785255719756e0906ea9", + "assets/build/ba_data/data/languages/hindi.json": "https://files.ballistica.net/cache/ba1/91/98/42701cd595c2f70b7484614a8f49", "assets/build/ba_data/data/languages/hungarian.json": "https://files.ballistica.net/cache/ba1/d8/f2/aa16bc336bd7660cc86c3264bfc4", - "assets/build/ba_data/data/languages/indonesian.json": "https://files.ballistica.net/cache/ba1/e3/85/14e57e3f49505e5a190daf7fe276", + "assets/build/ba_data/data/languages/indonesian.json": "https://files.ballistica.net/cache/ba1/7f/a9/db86d4c8b70f06fd8a1fe0c0511b", "assets/build/ba_data/data/languages/italian.json": "https://files.ballistica.net/cache/ba1/1a/10/9563348e729d1e5c8ae8c9cbc1f2", "assets/build/ba_data/data/languages/korean.json": "https://files.ballistica.net/cache/ba1/a8/e9/171a904f1331fdb7b1918a0f2598", - "assets/build/ba_data/data/languages/persian.json": "https://files.ballistica.net/cache/ba1/83/4a/ec10142ac479bf8d80455b47a62b", + "assets/build/ba_data/data/languages/persian.json": "https://files.ballistica.net/cache/ba1/df/b1/b2c9ebaad5e873ebedd365726d3d", "assets/build/ba_data/data/languages/polish.json": "https://files.ballistica.net/cache/ba1/19/e9/59c891b1fb85f3ba9f19283c233d", "assets/build/ba_data/data/languages/portuguese.json": "https://files.ballistica.net/cache/ba1/da/95/36797ec53a697a04e55b225a701d", "assets/build/ba_data/data/languages/romanian.json": "https://files.ballistica.net/cache/ba1/d7/06/9d70642d0a4d1e3b1c2149d7a17c", @@ -452,7 +452,7 @@ "assets/build/ba_data/data/languages/swedish.json": "https://files.ballistica.net/cache/ba1/91/0a/35c4baf539d5951fc03a794c0e0b", "assets/build/ba_data/data/languages/tamil.json": "https://files.ballistica.net/cache/ba1/94/1a/533bc718e676191bafc25e2dc98f", "assets/build/ba_data/data/languages/thai.json": "https://files.ballistica.net/cache/ba1/f7/df/7ba5f99c5c2c4c86fc0503fcf0b7", - "assets/build/ba_data/data/languages/turkish.json": "https://files.ballistica.net/cache/ba1/14/a0/783cc6da2d122e9a7482c6a5ef8c", + "assets/build/ba_data/data/languages/turkish.json": "https://files.ballistica.net/cache/ba1/9a/90/8e2ed626def09f88c3b9ab5215a3", "assets/build/ba_data/data/languages/ukrainian.json": "https://files.ballistica.net/cache/ba1/ab/35/644e4239cfa62a597a905412b90c", "assets/build/ba_data/data/languages/venetian.json": "https://files.ballistica.net/cache/ba1/53/9e/068074156b38bab7f732977a4031", "assets/build/ba_data/data/languages/vietnamese.json": "https://files.ballistica.net/cache/ba1/25/13/b64b849fc9fedcc18d81f6e08c4d", @@ -944,10 +944,10 @@ "assets/build/ba_data/models/zoeUpperArm.bob": "https://files.ballistica.net/cache/ba1/99/38/b7694cae0804260eeb337aa1676a", "assets/build/ba_data/models/zoeUpperLeg.bob": "https://files.ballistica.net/cache/ba1/83/4f/28b2202d0109fa93272c0b09fa2d", "assets/build/ba_data/python-site-packages/_yaml/__init__.py": "https://files.ballistica.net/cache/ba1/0d/45/65ba92f51d411dcffac8835b6130", - "assets/build/ba_data/python-site-packages/certifi/__init__.py": "https://files.ballistica.net/cache/ba1/ca/90/4cf111df6bafc9735f6d2a05d6f2", + "assets/build/ba_data/python-site-packages/certifi/__init__.py": "https://files.ballistica.net/cache/ba1/f0/50/dbe7d0065006ac12adf2eaa239fc", "assets/build/ba_data/python-site-packages/certifi/__main__.py": "https://files.ballistica.net/cache/ba1/b2/bb/d7d8216212bcf66cdc3067700fb7", - "assets/build/ba_data/python-site-packages/certifi/cacert.pem": "https://files.ballistica.net/cache/ba1/c9/f9/e2d0dd61a7f4e36a3309a3981d07", - "assets/build/ba_data/python-site-packages/certifi/core.py": "https://files.ballistica.net/cache/ba1/8a/01/33e774b2ed89a56756f32d2f3bef", + "assets/build/ba_data/python-site-packages/certifi/cacert.pem": "https://files.ballistica.net/cache/ba1/eb/1c/18ef584961785d002a2550d389e0", + "assets/build/ba_data/python-site-packages/certifi/core.py": "https://files.ballistica.net/cache/ba1/ac/28/37f05b52df3806856bce2c0b9772", "assets/build/ba_data/python-site-packages/typing_extensions.py": "https://files.ballistica.net/cache/ba1/a5/c3/66c408bfad73af8644f507d8ee17", "assets/build/ba_data/python-site-packages/yaml/__init__.py": "https://files.ballistica.net/cache/ba1/e5/47/17715ca7620f3b9749558b9dcb2d", "assets/build/ba_data/python-site-packages/yaml/composer.py": "https://files.ballistica.net/cache/ba1/3e/aa/d7fcfc4707ad19a6964d72654b82", @@ -3995,50 +3995,51 @@ "assets/src/ba_data/python/ba/_generated/__init__.py": "https://files.ballistica.net/cache/ba1/ee/e8/cad05aa531c7faf7ff7b96db7f6e", "assets/src/ba_data/python/ba/_generated/enums.py": "https://files.ballistica.net/cache/ba1/b2/e5/0ee0561e16257a32830645239f34", "ballisticacore-windows/Generic/BallisticaCore.ico": "https://files.ballistica.net/cache/ba1/89/c0/e32c7d2a35dc9aef57cc73b0911a", - "build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/ad/15/3b9d4eb830dd3f65266b90544461", - "build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/3f/94/ce703438b67755c530ca90d7147e", - "build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/57/49/aef9dbd58c9638d2f57c61fa9dee", - "build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/53/73/7aee5551f9aa8c60bfa36ce86889", - "build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/8d/cd/61b3022a6a3620bb9a0d288a3a07", - "build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/67/d3/7e7b3147c119bcf9edc62f271755", - "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/41/81/b4225f4df31a6bb482eee912d1e7", - "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/8a/3e/9a284ee8d8cb75fef73f31c95a8f", - "build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/a5/a2/69829de7375f981ac01464d1271c", - "build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/8f/de/c40022e95c0d51e3ea7d68713ac8", - "build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/2a/e3/74cdea89e9606f525ae01f5fb860", - "build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/c2/b3/8f5c7076b293ce6a961b23321ebe", - "build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/45/4b/43950780d121290a40cb444e86c4", - "build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/74/6c/5c8a7cab586c813a07d5180e25c2", - "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ca/36/94c0d402ec38ffdd74cd165f51c3", - "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b7/a9/14ea5150ad61a43b5780975551ac", - "build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/23/72/2bdc1106cdf2aa163a591e7c47b8", - "build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/1e/d9/5a7fd190b86683bef223c1cdb0b6", - "build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/2e/f1/27b90831c9a25d2b9d06a266e931", - "build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/17/ce/d3e33370c016fc8110e6dddd120f", - "build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/32/b0/df61f7b69d796fbdf2aa0d11974b", - "build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/42/31/a45e87e70e5d5232956586620f35", - "build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/94/5e/6ac9534c08e600201b04d28a6069", - "build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/5f/5d/52e74182f459f5b7b55ae07b36c6", - "build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7b/9d/367338622b31482a8ef87621f5aa", - "build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a8/18/4dff9ab6b2529d258a2ee5607d0c", - "build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/79/b9/e5e9d0f92f82f6cf35abf0c049cf", - "build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/4c/b4/2c65b84265e0b2d576317ca86e77", - "build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d1/86/635b942e0837be18e6d9d859f2c4", - "build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b0/f5/3da5acc926ca5e6c02307b8a16a6", - "build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a3/51/d624ac150603304183ba23d369a2", - "build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e2/51/5eb2216e4cdc531c500244316f1d", - "build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b1/2b/c72179ed1d11ae7e11f429b90ae9", - "build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/52/78/cca5ff0fcc837467cd23239537c7", - "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e7/6e/eb7102dd939e85172d729ba471b8", - "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e7/92/b7c90b5dbf614aa056cf467f73e0", - "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/cf/d9/52c1c145efa1a24ebf54dd71f691", - "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/31/15/ef849aeaf5a9cc2169e6ec4998c8", - "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/86/a7/def29184cd6e947469726ae382e1", - "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/5f/30/42705d0b580994d1345c34651170", - "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/2f/b1/6a6e8cf45fc5afa4085bf90a526f", - "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/7e/fb/e7eaee33c6196f9204a2997fc65c", - "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/5a/39/e00cddb91e8eca83b117235c1332", - "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/07/dd/6e331996e6e6907f05a21abc20a6", - "src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/7d/3e/229a581cb2454ed856f1d8b564a7", - "src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/98/12/571b2160d69d42580e8f31fa6a8d" + "build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/2a/10/01cc7af239cfca6d9abeb6e6deb5", + "build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/fd/49/6da409797f98e3834cdd2ba9a1e5", + "build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/cd/ba/2e17df1379cd7f60708f1ec3aecb", + "build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/72/0d/d953d2cda66ea47552c6a3f5dfee", + "build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/12/44/fac079140811eaf6b3dd1f1d0fe0", + "build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/00/74/a671dd7047610d8d555c4b0a303c", + "build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/23/4a/40c6eb2af25a0a8f29530f8e63b0", + "build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b3/ff/ced34572d1502e3ce393338810a9", + "build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/cd/73/54f48f6cb05f5dab5e32532b831a", + "build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/26/ca/b667ad8ba524618e62ffc5a58c45", + "build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/c9/f1/f92e2fd877e32f2364eb1656052b", + "build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/14/dc/d61b2e43131283a6049f203943bc", + "build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/b3/8b/84937c08100c035641764b95fe1d", + "build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/d0/59/e84165efa3367582461128db885a", + "build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/26/3f/da203a1eb557318e9abc5f76aac1", + "build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/07/db/5d04f8f0736732cdba70bb85fb19", + "build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/ad/79/b6fcb72c6ff1ac3d507dd938ce14", + "build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/46/5a/582ca64edfc416e140f4b5b1827d", + "build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/5b/b5/ca8b600f49d4c28108f4e16c2f00", + "build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/48/3f/8327430b9c194c14974bed19ea16", + "build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/41/b3/5a5813294b9281ca6bbb1f5caebe", + "build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/28/05/b6cc01e7b6762a3c8d47851cd30c", + "build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f7/8c/36acf6d02d7c322aed086cc4ec3f", + "build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ca/65/de52706de6aa624f01c5907e5367", + "build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2d/3d/3e3e65846f42810c032cb5efd997", + "build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/da/bf/c4a1f6e6d2495723acbd93e1b22c", + "build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ac/34/6605f152207515d50174eca420e7", + "build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8c/b6/770c3d26d70edec763335c38de73", + "build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/33/10/a753a671240f243b2351b5730917", + "build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/1c/e4/05dc037f8669fc5ddc993dc8defc", + "build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7c/c9/879778a21f2403346adf8db17318", + "build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/86/36/6d77a933a8126df4fde064fe881f", + "build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/80/e8/1c9c69d9fd5e47d0fd6e3c9b1134", + "build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ac/e2/4208f2fc902ed0526bf4cb5bbc54", + "build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e2/95/5fd6cede6b0e88753bb50b437110", + "build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/87/0b/442839bc7a21c12ed6e173abf7ca", + "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/b6/aa/0666c00d1751f934cc1b0907fa40", + "build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/aa/22/3afc115395a7909eac78d98cf856", + "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/9f/e6/5097b01991ce22e1d3fd594808b2", + "build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/d9/4f/7f2a34294f53378990487655f4ab", + "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/18/d3/50bbe34de1862d8224dafa8792e7", + "build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/82/93/f80194878bfed43f58809a7e9823", + "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/73/54/88f193633990884b61719a481dec", + "build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/f4/23/2a4e21dc99e6135c1cb102f6ca37", + "src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/c0/32/b7907e3859a5c5013a3d97b6b523", + "src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/2d/4f/f4fe67827f36cd59cd5193333a02", + "src/ballistica/generated/python_embedded/bootstrap_monolithic.inc": "https://files.ballistica.net/cache/ba1/ef/c1/aa5f1aa10af89f5c0b1e616355fd" } \ No newline at end of file diff --git a/.idea/dictionaries/ericf.xml b/.idea/dictionaries/ericf.xml index 013a0841..de2b9cc5 100644 --- a/.idea/dictionaries/ericf.xml +++ b/.idea/dictionaries/ericf.xml @@ -64,9 +64,11 @@ aiomain alarmsound alibaba + allerrors allpaths allsettings allteams + allwarnings aman amazonaws aname @@ -111,6 +113,7 @@ appspot appstate appstore + apptime apputils archivepath archivepathbase @@ -234,6 +237,7 @@ benning bfiledir bfiles + bgdynamics bgmodel bgrn bgterrain @@ -415,6 +419,7 @@ cleanlist cleanupcheck cleanupchecks + clearsign clientid clientlist clienttobasn @@ -433,6 +438,7 @@ clrhdr clrnames clrred + cmakelist cmakelists cmakeserver cmath @@ -1341,6 +1347,7 @@ levelgametype levelmodule levelname + levelno lfull lfval libballisticacore @@ -1647,6 +1654,7 @@ nettest nettesting netutils + networkwrite nevermind newactivity newdamage @@ -2380,6 +2388,7 @@ startscan startsplits starttime + startupmsg statestr statictest statictestfiles @@ -2408,6 +2417,7 @@ stot strftime stringified + stringifying stringprep stringptr strippable diff --git a/CHANGELOG.md b/CHANGELOG.md index dd70dfde..ba74fb7a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,14 @@ -### 1.7.7 (build 20798, api 7, 2022-09-09) +### 1.7.9 (build 20877, api 7, 2022-09-21) +- Cleaned up the efro.message system to isolate response types that are used purely internally (via a new SysResponse type). +- Fixed bug with 'Disable Camera Shake' option. (GitHub #511) +- Fixed an issue where Co-op football would play no music. - Accept "fairydust" as an emit type in `ba.emitfx()`. + +### 1.7.8 (build 20871, api 7, 2022-09-21) +- Fixed tournament scores submits which were broken in 1.7.7 (oops). +- Added @clear command to stdin command reader. + +### 1.7.7 (build 20868, api 7, 2022-09-20) - Added `ba.app.meta.load_exported_classes()` for loading classes discovered by the meta subsystem cleanly in a background thread. - Improved logging of missing playlist game types. - Some ba.Lstr functionality can now be used in background threads. @@ -9,7 +18,7 @@ - Added support for the console tool in the new devices section on ballistica.net. - Increased timeouts in net-testing gui and a few other places to be able to better diagnose/handle places with very poor connectivity. - Removed `Platform::SetLastPyCall()` which was just for debugging and which has not been useful in a while. -- Moved some app bootstrapping from the C++ layer to the ba._bootstrap module. +- Moved some app bootstrapping from the C++ layer to the `ba._bootstrap` module. - The game will now properly return to the stress-test window after a stress test finishes (thanks vishal332008!) - Continue window will now pause the game to avoid running up times in the background (thanks vishal332008!) - Keepaway and KingOfTheHill now have epic options (thanks FAL-Guys!) @@ -17,14 +26,37 @@ - Starting to rename the 'game' thread to the 'logic' thread. This is the thread where most high level app logic happen, not only game logic. - `_ba.in_game_thread()` is now `_ba.in_logic_thread()`. - Misc C++ layer tidying/refactoring. -- Split out the `_ba` binary module into `_ba` and `_bainternal`. This will eventually allow running without the closed-source parts (_bainternal) present at all. +- Split out the `_ba` binary module into `_ba` and `_bainternal`. This will eventually allow running without the closed-source parts (`_bainternal`) present at all. - There is now a `_bainternal.py` dummy-module alongside the existing `_ba.py` one. Be sure to exclude it from any script collections used by the game (the same as `_ba.py`). -- Added checks to make sure _ba or _bainternal arent used outside of ba. Any 'internal' functionality needed outside of ba should be exposed through ba.internal. _ba and _bainternal are internal implementation details. +- Added checks to make sure `_ba` or `_bainternal` arent used outside of ba. Any 'internal' functionality needed outside of ba should be exposed through ba.internal. `_ba` and `_bainternal` are internal implementation details. - Removed C++ Module class and simplified Thread class. The Module class was an old relic of long ago before C++ had lambdas and its existence was pretty pointless and confusing these days. +- Renamed C++ App to AppFlavor and AppGlobals to App. +- Renamed C++ Media to Assets. +- Removed 'scores to beat' list in coop which was only ever functional in limited cases on the Mac version. Perhaps that feature can reappear in a cross-platform way sometime. +- Simplified C++ bootstrapping to allocate all globals in one place. +- Renamed C++ Game classes to Logic. +- The app now bootstraps Python in the main thread instead of the logic thread. This will keep things more consistent later when we are able to run under an already-existing Python interpreter. +- As a side-effect of initing Python in the main thread, it seems that Python now catches segfaults in our debug builds and prints Python stack traces. (see https://docs.python.org/3/library/faulthandler.html). We'll have to experiment and see if this is a net positive or something we want to disable or make optional. +- Python and `_ba` are now completely initialized in public source code. Now we just need to enable the app to survive without `_bainternal` and it'll be possible to build a 100% open source app. +- `Logging::Log()` in the C++ layer now takes a LogLevel arg (kDebug, kWarning, kError, etc.) and simply calls the equivalent Python logging.XXX call. This unifies our C++ and Python logging to go through the same place. +- `ba.log()` is no more. Instead just use standard Python logging functions (logging.info(), logging.error(), etc.). +- `_ba.getlog()` is now `_ba.get_v1_cloud_log()`. Note that this functionality will go away eventually so you should use `ba.app.log_handler` and/or standard Python logging functions to get at app logs. +- Along the same lines, `_ba.get_log_file_path()` is now `_ba.get_v1_cloud_log_file_path()`. +- Added `_ba.display_log()` function which ships a log message to the in-game terminal and platform-specific places like the Android log. The engine wires up standard Python logging output to go through this. +- Added `_ba.v1_cloud_log()` which ships a message to the old v1-cloud-log (the log which is gathered and sent to the v1 master server to help me identify problems people are seeing). This is presently wired up to a subset of Python logging output to approximate how it used to work. +- Note: Previously in the C++ layer some code would mix Python print calls (such as `PyErr_PrintEx()`) with ballistica::Log() calls. Previously these all wound up going to the same place (Python's sys.stderr) so it worked, but now they no longer do and so this sort of mixing should be avoided. So if you see a weird combination of colored log output lines with non-colored lines that seem to go together, please holler as it means something needs to be fixed. +- Builds for Apple devices now explicitly set a thread stack size of 1MB. The default there is 512k and I was seeing some stack overflows for heavy physics sims or very recursive Python stuff. +- If you want to grab recent logs, you can now use `ba.app.log_handler.get_cached()`. This will give you everything that has gone through Python logging, Python stdout/stderr, and the C++ Log() call (up to the max cache size that is). +- LogHandler output now ALWAYS goes to stderr. Previously it only would if an interactive terminal was detected. This should make the binary easier to debug if run from scripts/etc. We can add a `--quiet` option if needed or whatnot. +- (build 20859) Fixed an error setting up asyncio loops under Windows related to the fact that Python is now inited in the main thread. +- (build 20864) Fatal-error message/traceback now properly prints to stderr again (I think the recent logging rejiggering caused it to stop). +- (build 20864) Fixed an issue where the app could crash when connected to the cloud console while in a network game. +- Added a simplified help() command which behaves reasonably under the in-game console or cloud-console. + ### 1.7.6 (build 20687, api 7, 2022-08-11) -- Cleaned up da MetaSubsystem code. -- It is now possible to tell the meta system about arbitrary classes (ba_meta export foo.bar.Class) instead of just the preset types 'plugin', 'game', etc. +- Cleaned up the MetaSubsystem code. +- It is now possible to tell the meta system about arbitrary classes (ba\_meta export foo.bar.Class) instead of just the preset types 'plugin', 'game', etc. - Newly discovered plugins are now activated immediately instead of requiring a restart. ### 1.7.5 (build 20672, api 7, 2022-07-25) diff --git a/Makefile b/Makefile index 53284308..59935c80 100644 --- a/Makefile +++ b/Makefile @@ -20,6 +20,10 @@ help: # Set env-var BA_ENABLE_IRONY_BUILD_DB=1 to enable creating/updating a # cmake compile-commands database for use with irony for emacs (and possibly # other tools). +# FIXME - this can break if we move/rename files and then run 'make update' +# because it tries to use the previous cmakelist with the no-longer-valid +# filename *before* it updates the cmakelist. Need to rethink order of +# operations there. ifeq ($(BA_ENABLE_IRONY_BUILD_DB),1) PREREQ_IRONY = .cache/irony/compile_commands.json endif diff --git a/assets/src/ba_data/python/._ba_sources_hash b/assets/src/ba_data/python/._ba_sources_hash index b56206f3..4d3af060 100644 --- a/assets/src/ba_data/python/._ba_sources_hash +++ b/assets/src/ba_data/python/._ba_sources_hash @@ -1 +1 @@ -126683827977798484003262787310231621875 \ No newline at end of file +137071025041513581787580065580079045765 \ No newline at end of file diff --git a/assets/src/ba_data/python/._bainternal_sources_hash b/assets/src/ba_data/python/._bainternal_sources_hash index 1e1473b6..99a731ce 100644 --- a/assets/src/ba_data/python/._bainternal_sources_hash +++ b/assets/src/ba_data/python/._bainternal_sources_hash @@ -1 +1 @@ -27855823406376609779797608591198912121 \ No newline at end of file +139020022013133168311319486434408589898 diff --git a/assets/src/ba_data/python/_ba.py b/assets/src/ba_data/python/_ba.py index 0705868d..2f751fc3 100644 --- a/assets/src/ba_data/python/_ba.py +++ b/assets/src/ba_data/python/_ba.py @@ -1108,12 +1108,6 @@ def add_clean_frame_callback(call: Callable) -> None: return None -def add_transaction(transaction: dict, - callback: Callable | None = None) -> None: - """(internal)""" - return None - - def android_get_external_files_dir() -> str: """(internal) @@ -1137,6 +1131,11 @@ def android_show_wifi_settings() -> None: return None +def app_instance_uuid() -> str: + """(internal)""" + return str() + + def apply_config() -> None: """(internal)""" return None @@ -1472,6 +1471,16 @@ def disconnect_from_host() -> None: return None +def display_log(name: str, level: str, message: str) -> None: + """(internal) + + Sends a log message to the in-game console and any per-platform + log destinations (Android log, etc.). This generally is not called + directly and should instead be fed Python logging output. + """ + return None + + def do_once() -> bool: """Return whether this is the first time running a line of code. @@ -1561,15 +1570,6 @@ def focus_window() -> None: return None -def game_service_has_leaderboard(game: str, config: str) -> bool: - """(internal) - - Given a game and config string, returns whether there is a leaderboard - for it on the game service. - """ - return bool() - - def get_appconfig_builtin_keys() -> list[str]: """(internal)""" return ['blah', 'blah2'] @@ -1700,29 +1700,11 @@ def get_local_active_input_devices_count() -> int: return int() -def get_log_file_path() -> str: - """(internal) - - Return the path to the app log file. - """ - return str() - - def get_low_level_config_value(key: str, default_value: int) -> int: """(internal)""" return int() -def get_master_server_address(source: int = -1, - version: int = 1, - internal: bool = False) -> str: - """(internal) - - Return the address of the master server. - """ - return str() - - def get_max_graphics_quality() -> str: """(internal) @@ -1731,11 +1713,6 @@ def get_max_graphics_quality() -> str: return str() -def get_news_show() -> str: - """(internal)""" - return str() - - def get_package_collide_model(package: ba.AssetPackage, name: str) -> ba.CollideModel: """(internal)""" @@ -1767,16 +1744,6 @@ def get_package_texture(package: ba.AssetPackage, name: str) -> ba.Texture: return ba.Texture() -def get_price(item: str) -> str | None: - """(internal)""" - return '' - - -def get_public_login_id() -> str | None: - """(internal)""" - return '' - - def get_public_party_enabled() -> bool: """(internal)""" return bool() @@ -1787,16 +1754,6 @@ def get_public_party_max_size() -> int: return int() -def get_purchased(item: str) -> bool: - """(internal)""" - return bool() - - -def get_purchases_state() -> int: - """(internal)""" - return int() - - def get_qrcode_texture(url: str) -> ba.Texture: """(internal)""" import ba # pylint: disable=cyclic-import @@ -1824,11 +1781,6 @@ def get_replays_dir() -> str: return str() -def get_scores_to_beat(level: str, config: str, callback: Callable) -> None: - """(internal)""" - return None - - def get_special_widget(name: str) -> Widget: """(internal)""" return Widget() @@ -1872,56 +1824,16 @@ def get_ui_input_device() -> ba.InputDevice: return ba.InputDevice() -def get_v1_account_display_string(full: bool = True) -> str: +def get_v1_cloud_log() -> str: """(internal)""" return str() -def get_v1_account_misc_read_val(name: str, default_value: Any) -> Any: - """(internal)""" - return _uninferrable() - - -def get_v1_account_misc_read_val_2(name: str, default_value: Any) -> Any: - """(internal)""" - return _uninferrable() - - -def get_v1_account_misc_val(name: str, default_value: Any) -> Any: - """(internal)""" - return _uninferrable() - - -def get_v1_account_name() -> str: - """(internal)""" - return str() - - -def get_v1_account_state() -> str: - """(internal)""" - return str() - - -def get_v1_account_state_num() -> int: - """(internal)""" - return int() - - -def get_v1_account_ticket_count() -> int: +def get_v1_cloud_log_file_path() -> str: """(internal) - Returns the number of tickets for the current account. + Return the path to the app log file. """ - return int() - - -def get_v1_account_type() -> str: - """(internal)""" - return str() - - -def get_v2_fleet() -> str: - """(internal)""" return str() @@ -2015,11 +1927,6 @@ def getinputdevice(name: str, unique_id: str, doraise: bool = True) -> Any: return None -def getlog() -> str: - """(internal)""" - return str() - - def getmodel(name: str) -> ba.Model: """Return a model, loading it if necessary. @@ -2129,11 +2036,6 @@ def have_incentivized_ad() -> bool: return bool() -def have_outstanding_transactions() -> bool: - """(internal)""" - return bool() - - def have_permission(permission: ba.Permission) -> bool: """(internal)""" return bool() @@ -2210,15 +2112,10 @@ def imagewidget(edit: ba.Widget | None = None, return ba.Widget() -def in_game_purchase(item: str, price: int) -> None: - """(internal)""" - return None - - def in_logic_thread() -> bool: """(internal) - Returns whether or not the current thread is the game thread. + Returns whether or not the current thread is the logic thread. """ return bool() @@ -2240,11 +2137,6 @@ def increment_analytics_counts_raw(name: str, increment: int = 1) -> None: return None -def is_blessed() -> bool: - """(internal)""" - return bool() - - def is_in_replay() -> bool: """(internal)""" return bool() @@ -2301,22 +2193,6 @@ def lock_all_input() -> None: return None -def log(message: str, to_stdout: bool = True, to_server: bool = True) -> None: - """Category: **General Utility Functions** - - Log a message. This goes to the default logging mechanism depending - on the platform (stdout on mac, android log on android, etc). - - Log messages also go to the in-game console unless 'to_console' - is False. They are also sent to the master-server for use in analyzing - issues unless to_server is False. - - Python's standard print() is wired to call this (with default values) - so in most cases you can just use that. - """ - return None - - def mac_music_app_get_library_source() -> None: """(internal)""" return None @@ -2352,14 +2228,6 @@ def mac_music_app_stop() -> None: return None -def mark_config_dirty() -> None: - """(internal) - - Category: General Utility Functions - """ - return None - - def mark_log_sent() -> None: """(internal)""" return None @@ -2488,11 +2356,6 @@ def playsound(sound: Sound, return None -def power_ranking_query(callback: Callable, season: Any = None) -> None: - """(internal)""" - return None - - def print_context() -> None: """(internal) @@ -2509,23 +2372,6 @@ def print_load_info() -> None: return None -def print_stderr(message: str) -> None: - """(internal) - - Print to system stderr. - Also forwards to the internal console, etc. - """ - return None - - -def print_stdout(message: str) -> None: - """(internal) - Print to system stdout. - Also forwards to the internal console, etc. - """ - return None - - def printnodes() -> None: """Print various info about existing nodes; useful for debugging. @@ -2545,14 +2391,10 @@ def printobjects() -> None: return None -def purchase(item: str) -> None: - """(internal)""" - return None - - def pushcall(call: Callable, from_other_thread: bool = False, - suppress_other_thread_warning: bool = False) -> None: + suppress_other_thread_warning: bool = False, + other_thread_use_fg_context: bool = False) -> None: """Pushes a call onto the event loop to be run during the next cycle. Category: **General Utility Functions** @@ -2560,12 +2402,14 @@ def pushcall(call: Callable, This can be handy for calls that are disallowed from within other callbacks, etc. - This call expects to be used in the game thread, and will automatically + This call expects to be used in the logic thread, and will automatically save and restore the ba.Context to behave seamlessly. - If you want to push a call from outside of the game thread, + If you want to push a call from outside of the logic thread, however, you can pass 'from_other_thread' as True. In this case - the call will always run in the UI context on the game thread. + the call will always run in the UI context on the logic thread + or whichever context is in the foreground if + other_thread_use_fg_context is True. """ return None @@ -2616,21 +2460,11 @@ def reload_media() -> None: return None -def report_achievement(achievement: str, pass_to_account: bool = True) -> None: - """(internal)""" - return None - - def request_permission(permission: ba.Permission) -> None: """(internal)""" return None -def reset_achievements() -> None: - """(internal)""" - return None - - def reset_game_activity_tracking() -> None: """(internal)""" return None @@ -2646,11 +2480,6 @@ def resolve_appconfig_value(key: str) -> Any: return _uninferrable() -def restore_purchases() -> None: - """(internal)""" - return None - - def rowwidget(edit: ba.Widget | None = None, parent: ba.Widget | None = None, size: Sequence[float] | None = None, @@ -2673,11 +2502,6 @@ def rowwidget(edit: ba.Widget | None = None, return ba.Widget() -def run_transactions() -> None: - """(internal)""" - return None - - def safecolor(color: Sequence[float], target_intensity: float = 0.6) -> tuple[float, ...]: """Given a color tuple, return a color safe to display as text. @@ -2702,11 +2526,12 @@ def screenmessage(message: str | ba.Lstr, Category: **General Utility Functions** If 'top' is True, the message will go to the top message area. - For 'top' messages, 'image' can be a texture to display alongside the - message. - If 'log' is True, the message will also be printed to the output log - 'clients' can be a list of client-ids the message should be sent to, - or None to specify that everyone should receive it. + For 'top' messages, 'image' must be a dict containing 'texture' + and 'tint_texture' textures and 'tint_color' and 'tint2_color' + colors. This defines an icon to display alongside the message. + If 'log' is True, the message will also be submitted to the log. + 'clients' can be a list of client-ids the message should be sent + to, or None to specify that everyone should receive it. If 'transient' is True, the message will not be included in the game-stream and thus will not show up when viewing replays. Currently the 'clients' option only works for transient messages. @@ -2953,48 +2778,11 @@ def show_progress_bar() -> None: return None -def sign_in_v1(account_type: str) -> None: - """(internal) - - Category: General Utility Functions - """ - return None - - -def sign_out_v1(v2_embedded: bool = False) -> None: - """(internal) - - Category: General Utility Functions - """ - return None - - def submit_analytics_counts() -> None: """(internal)""" return None -def submit_score(game: str, - config: str, - name: Any, - score: int | None, - callback: Callable, - friend_callback: Callable | None, - order: str = 'increasing', - tournament_id: str | None = None, - score_type: str = 'points', - campaign: str | None = None, - level: str | None = None) -> None: - """(internal) - - Submit a score to the server; callback will be called with the results. - As a courtesy, please don't send fake scores to the server. I'd prefer - to devote my time to improving the game instead of trying to make the - score server more mischief-proof. - """ - return None - - def textwidget(edit: ba.Widget | None = None, parent: ba.Widget | None = None, size: Sequence[float] | None = None, @@ -3173,12 +2961,6 @@ def timer(time: float, return None -def tournament_query(callback: Callable[[dict | None], None], - args: dict) -> None: - """(internal)""" - return None - - def uibounds() -> tuple[float, float, float, float]: """(internal) @@ -3198,6 +2980,19 @@ def unlock_all_input() -> None: return None +def user_ran_commands() -> None: + """(internal)""" + return None + + +def v1_cloud_log(message: str) -> None: + """(internal) + + Push messages to the old v1 cloud log. + """ + return None + + def value_test(arg: str, change: float | None = None, absolute: float | None = None) -> float: diff --git a/assets/src/ba_data/python/ba/__init__.py b/assets/src/ba_data/python/ba/__init__.py index 4b06c0ed..43d98979 100644 --- a/assets/src/ba_data/python/ba/__init__.py +++ b/assets/src/ba_data/python/ba/__init__.py @@ -13,12 +13,11 @@ from _ba import ( Node, SessionPlayer, Sound, Texture, Timer, Vec3, Widget, buttonwidget, camerashake, checkboxwidget, columnwidget, containerwidget, do_once, emitfx, getactivity, getcollidemodel, getmodel, getnodes, getsession, - getsound, gettexture, hscrollwidget, imagewidget, log, newactivity, - newnode, playsound, printnodes, printobjects, pushcall, quit, rowwidget, - safecolor, screenmessage, scrollwidget, set_analytics_screen, charstr, - textwidget, time, timer, open_url, widget, clipboard_is_supported, - clipboard_has_text, clipboard_get_text, clipboard_set_text, getdata, - in_logic_thread) + getsound, gettexture, hscrollwidget, imagewidget, newactivity, newnode, + playsound, printnodes, printobjects, pushcall, quit, rowwidget, safecolor, + screenmessage, scrollwidget, set_analytics_screen, charstr, textwidget, + time, timer, open_url, widget, clipboard_is_supported, clipboard_has_text, + clipboard_get_text, clipboard_set_text, getdata, in_logic_thread) from ba._activity import Activity from ba._plugin import PotentialPlugin, Plugin, PluginSubsystem from ba._actor import Actor @@ -103,7 +102,7 @@ __all__ = [ 'ImpactDamageMessage', 'in_logic_thread', 'InputDevice', 'InputDeviceNotFoundError', 'InputType', 'IntChoiceSetting', 'IntSetting', 'is_browser_likely_available', 'is_point_in_box', 'Keyboard', - 'LanguageSubsystem', 'Level', 'Lobby', 'log', 'Lstr', 'Map', 'Material', + 'LanguageSubsystem', 'Level', 'Lobby', 'Lstr', 'Map', 'Material', 'MetadataSubsystem', 'Model', 'MultiTeamSession', 'MusicPlayer', 'MusicPlayMode', 'MusicSubsystem', 'MusicType', 'newactivity', 'newnode', 'Node', 'NodeActor', 'NodeNotFoundError', 'normalized_color', diff --git a/assets/src/ba_data/python/ba/_app.py b/assets/src/ba_data/python/ba/_app.py index b1c012c9..b976517b 100644 --- a/assets/src/ba_data/python/ba/_app.py +++ b/assets/src/ba_data/python/ba/_app.py @@ -26,6 +26,7 @@ if TYPE_CHECKING: import asyncio from typing import Any, Callable + import efro.log import ba from ba._cloud import CloudSubsystem from bastd.actor import spazappearance @@ -49,6 +50,7 @@ class App: # Implementations for these will be filled in by internal libs. accounts_v2: AccountV2Subsystem cloud: CloudSubsystem + log_handler: efro.log.LogHandler class State(Enum): """High level state the app can be in.""" @@ -384,7 +386,7 @@ class App: # If there's a leftover log file, attempt to upload it to the # master-server and/or get rid of it. - _apputils.handle_leftover_log_file() + _apputils.handle_leftover_v1_cloud_log_file() # Only do this stuff if our config file is healthy so we don't # overwrite a broken one or whatnot and wipe out data. diff --git a/assets/src/ba_data/python/ba/_appconfig.py b/assets/src/ba_data/python/ba/_appconfig.py index 13270319..4fd28deb 100644 --- a/assets/src/ba_data/python/ba/_appconfig.py +++ b/assets/src/ba_data/python/ba/_appconfig.py @@ -128,12 +128,6 @@ def read_config() -> tuple[AppConfig, bool]: shutil.copyfile(config_file_path, config_file_path + '.broken') except Exception as exc2: print('EXC copying broken config:', exc2) - try: - _ba.log('broken config contents:\n' + - config_contents.replace('\000', ''), - to_stdout=False) - except Exception as exc2: - print('EXC logging broken config contents:', exc2) config = AppConfig() # Now attempt to read one of our 'prev' backup copies. diff --git a/assets/src/ba_data/python/ba/_apputils.py b/assets/src/ba_data/python/ba/_apputils.py index e1d5d626..d7c2cfe9 100644 --- a/assets/src/ba_data/python/ba/_apputils.py +++ b/assets/src/ba_data/python/ba/_apputils.py @@ -50,7 +50,7 @@ def should_submit_debug_info() -> bool: return _ba.app.config.get('Submit Debug Info', True) -def handle_log() -> None: +def handle_v1_cloud_log() -> None: """Called on debug log prints. When this happens, we can upload our log to the server @@ -74,7 +74,7 @@ def handle_log() -> None: activityname = 'unavailable' info = { - 'log': _ba.getlog(), + 'log': _ba.get_v1_cloud_log(), 'version': app.version, 'build': app.build_number, 'userAgentString': app.user_agent_string, @@ -108,7 +108,7 @@ def handle_log() -> None: def _reset() -> None: app.log_upload_timer_started = False if app.log_have_new: - handle_log() + handle_v1_cloud_log() if not _ba.is_log_full(): with _ba.Context('ui'): @@ -118,14 +118,15 @@ def handle_log() -> None: suppress_format_warning=True) -def handle_leftover_log_file() -> None: - """Handle an un-uploaded log from a previous run.""" +def handle_leftover_v1_cloud_log_file() -> None: + """Handle an un-uploaded v1-cloud-log from a previous run.""" try: import json from ba._net import master_server_post - if os.path.exists(_ba.get_log_file_path()): - with open(_ba.get_log_file_path(), encoding='utf-8') as infile: + if os.path.exists(_ba.get_v1_cloud_log_file_path()): + with open(_ba.get_v1_cloud_log_file_path(), + encoding='utf-8') as infile: info = json.loads(infile.read()) infile.close() do_send = should_submit_debug_info() @@ -136,7 +137,7 @@ def handle_leftover_log_file() -> None: # lets kill it. if data is not None: try: - os.remove(_ba.get_log_file_path()) + os.remove(_ba.get_v1_cloud_log_file_path()) except FileNotFoundError: # Saw this in the wild. The file just existed # a moment ago but I suppose something could have @@ -146,7 +147,7 @@ def handle_leftover_log_file() -> None: master_server_post('bsLog', info, response) else: # If they don't want logs uploaded just kill it. - os.remove(_ba.get_log_file_path()) + os.remove(_ba.get_v1_cloud_log_file_path()) except Exception: from ba import _error _error.print_exception('Error handling leftover log file.') diff --git a/assets/src/ba_data/python/ba/_asyncio.py b/assets/src/ba_data/python/ba/_asyncio.py index afb3adc4..3639d2af 100644 --- a/assets/src/ba_data/python/ba/_asyncio.py +++ b/assets/src/ba_data/python/ba/_asyncio.py @@ -18,7 +18,7 @@ import os if TYPE_CHECKING: import ba -# Our timer and event loop for the ballistica game thread. +# Our timer and event loop for the ballistica logic thread. _asyncio_timer: ba.Timer | None = None _asyncio_event_loop: asyncio.AbstractEventLoop | None = None diff --git a/assets/src/ba_data/python/ba/_bootstrap.py b/assets/src/ba_data/python/ba/_bootstrap.py index 490f64f5..325e4b70 100644 --- a/assets/src/ba_data/python/ba/_bootstrap.py +++ b/assets/src/ba_data/python/ba/_bootstrap.py @@ -5,14 +5,14 @@ from __future__ import annotations import os import sys -import signal -import threading from typing import TYPE_CHECKING +from efro.log import setup_logging, LogLevel import _ba if TYPE_CHECKING: - from typing import Any, TextIO, Callable + from typing import Any + from efro.log import LogEntry _g_did_bootstrap = False # pylint: disable=invalid-name @@ -20,25 +20,32 @@ _g_did_bootstrap = False # pylint: disable=invalid-name def bootstrap() -> None: """Run bootstrapping logic. - This is the very first userland code that runs. + This is the very first ballistica code that runs (aside from imports). It sets up low level environment bits and creates the app instance. """ + global _g_did_bootstrap # pylint: disable=global-statement, invalid-name if _g_did_bootstrap: raise RuntimeError('Bootstrap has already been called.') _g_did_bootstrap = True - # The first thing we set up is capturing/redirecting Python - # stdout/stderr so we can at least debug problems on systems where - # native stdout/stderr is not easily accessible (looking at you, Android). - sys.stdout = _Redirect(sys.stdout, _ba.print_stdout) # type: ignore - sys.stderr = _Redirect(sys.stderr, _ba.print_stderr) # type: ignore + # The first thing we do is set up our logging system and feed + # Python's stdout/stderr into it. Then we can at least debug problems + # on systems where native stdout/stderr is not easily accessible + # such as Android. + log_handler = setup_logging(log_path=None, + level=LogLevel.DEBUG, + suppress_non_root_debug=True, + log_stdout_stderr=True, + cache_size_limit=1024 * 1024) + + log_handler.add_callback(_on_log) env = _ba.env() # Give a soft warning if we're being used with a different binary # version than we expect. - expected_build = 20798 + expected_build = 20877 running_build: int = env['build_number'] if running_build != expected_build: print( @@ -48,16 +55,11 @@ def bootstrap() -> None: f' This might cause the app to error or misbehave.', file=sys.stderr) - # Tell Python to not handle SIGINT itself (it normally generates - # KeyboardInterrupts which make a mess; we want to intercept them - # for simple clean exit). We capture interrupts per-platform in - # the C++ layer. - # Note: I tried creating a handler in Python but it seemed to often have - # a delay of up to a second before getting called. (not a huge deal - # but I'm picky). - signal.signal(signal.SIGINT, signal.SIG_DFL) # Do default handling. + # In bootstrap_monolithic.py we told Python not to handle SIGINT itself + # (because that must be done in the main thread). Now we finish the + # job by adding our own handler to replace it. - # ..though it turns out we need to set up our C signal handling AFTER + # Note: I've found we need to set up our C signal handling AFTER # we've told Python to disable its own; otherwise (on Mac at least) it # wipes out our existing C handler. _ba.setup_sigint() @@ -91,101 +93,83 @@ def bootstrap() -> None: os.environ['SSL_CERT_FILE'] = os.environ['REQUESTS_CA_BUNDLE'] = ( certifi.where()) - # FIXME: I think we should init Python in the main thread, which should - # also avoid these issues. (and also might help us play better with - # Python debuggers?) + # On Windows I'm seeing the following error creating asyncio loops in + # background threads with the default proactor setup: + # ValueError: set_wakeup_fd only works in main thread of the main + # interpreter + # So let's explicitly request selector loops. + # Interestingly this error only started showing up once I moved + # Python init to the main thread; previously the various asyncio + # bg thread loops were working fine (maybe something caused them + # to default to selector in that case?.. + if sys.platform == 'win32': + import asyncio + asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) - # Gloriously hacky workaround here: - # Our 'main' Python thread is the game thread (not the app's main - # thread) which means it has a small stack compared to the main - # thread (at least on apple). Sadly it turns out this causes the - # debug build of Python to blow its stack immediately when doing - # some big imports. - # Normally we'd just give the game thread the same stack size as - # the main thread and that'd be the end of it. However - # we're using std::threads which it turns out have no way to set - # the stack size (as of fall '19). Grumble. - # - # However python threads *can* take custom stack sizes. - # (and it appears they might use the main thread's by default?..) - # ...so as a workaround in the debug version, we can run problematic - # heavy imports here in another thread and all is well. - # If we ever see stack overflows in our release build we'll have - # to take more drastic measures like switching from std::threads - # to pthreads. - - if debug_build: - - # noinspection PyUnresolvedReferences - def _thread_func() -> None: - # pylint: disable=unused-import - import json - import urllib.request - - testthread = threading.Thread(target=_thread_func) - testthread.start() - testthread.join() - del testthread - - # Clear out the standard quit/exit messages since they don't work for us. # pylint: disable=c-extension-no-member if not TYPE_CHECKING: import __main__ + + # Clear out the standard quit/exit messages since they don't + # work for us. del __main__.__builtins__.quit del __main__.__builtins__.exit + # Also replace standard interactive help with our simplified + # one which is more friendly to cloud/in-game console situations. + __main__.__builtins__.help = _CustomHelper() + # Now spin up our App instance and store it on both _ba and ba. from ba._app import App import ba _ba.app = ba.app = App() + _ba.app.log_handler = log_handler -class _Redirect: +class _CustomHelper: + """Replacement 'help' that behaves better for our setup.""" - def __init__(self, original: TextIO, call: Callable[[str], None]) -> None: - self._lock = threading.Lock() - self._linebits: list[str] = [] - self._original = original - self._call = call - self._pending_ship = False + def __repr__(self) -> str: + return 'Type help(object) for help about object.' - def write(self, sval: Any) -> None: - """Override standard write call.""" + def __call__(self, *args: Any, **kwds: Any) -> Any: + # We get an ugly error importing pydoc on our embedded + # platforms due to _sysconfigdata_xxx.py not being present + # (but then things mostly work). Let's get the ugly error out + # of the way explicitly. + import sysconfig + try: + # This errors once but seems to run cleanly after, so let's + # get the error out of the way. + sysconfig.get_path('stdlib') + except ModuleNotFoundError: + pass - self._call(sval) + import pydoc + # Disable pager and interactive help since neither works well + # with our funky multi-threaded setup or in-game/cloud consoles. + # Let's just do simple text dumps. + pydoc.pager = pydoc.plainpager + if not args and not kwds: + print('Interactive help is not available in this environment.\n' + 'Type help(object) for help about object.') + return None + return pydoc.help(*args, **kwds) - # Now do logging: - # Add it to our accumulated line. - # If the message ends in a newline, we can ship it - # immediately as a log entry. Otherwise, schedule a ship - # next cycle (if it hasn't yet at that point) so that we - # can accumulate subsequent prints. - # (so stuff like print('foo', 123, 'bar') will ship as one entry) - with self._lock: - self._linebits.append(sval) - if sval.endswith('\n'): - self._shiplog() - else: - _ba.pushcall(self._shiplog, - from_other_thread=True, - suppress_other_thread_warning=True) - def _shiplog(self) -> None: - with self._lock: - line = ''.join(self._linebits) - if not line: - return - self._linebits = [] +def _on_log(entry: LogEntry) -> None: - # Log messages aren't expected to have trailing newlines. - if line.endswith('\n'): - line = line[:-1] - _ba.log(line, to_stdout=False) + # Just forward this along to the engine to display in the in-game console, + # in the Android log, etc. + _ba.display_log( + name=entry.name, + level=entry.level.name, + message=entry.message, + ) - def flush(self) -> None: - """Flush the file.""" - self._original.flush() - - def isatty(self) -> bool: - """Are we a terminal?""" - return self._original.isatty() + # We also want to feed some logs to the old V1-cloud-log system. + # Let's go with anything warning or higher as well as the stdout/stderr + # log messages that ba.app.log_handler creates for us. + if entry.level.value >= LogLevel.WARNING.value or entry.name in ('stdout', + 'stderr'): + _ba.v1_cloud_log(entry.message) diff --git a/assets/src/ba_data/python/ba/_coopgame.py b/assets/src/ba_data/python/ba/_coopgame.py index ecae427b..6e380d84 100644 --- a/assets/src/ba_data/python/ba/_coopgame.py +++ b/assets/src/ba_data/python/ba/_coopgame.py @@ -55,19 +55,6 @@ class CoopGameActivity(GameActivity[PlayerType, TeamType]): # Preload achievement images in case we get some. _ba.timer(2.0, WeakCall(self._preload_achievements)) - # Let's ask the server for a 'time-to-beat' value. - levelname = self._get_coop_level_name() - campaign = self.session.campaign - assert campaign is not None - config_str = (str(len(self.players)) + 'p' + campaign.getlevel( - self.settings_raw['name']).get_score_version_string().replace( - ' ', '_')) - _ba.get_scores_to_beat(levelname, config_str, - WeakCall(self._on_got_scores_to_beat)) - - def _on_got_scores_to_beat(self, scores: list[dict[str, Any]]) -> None: - pass - def _show_standard_scores_to_beat_ui(self, scores: list[dict[str, Any]]) -> None: from efro.util import asserttype diff --git a/assets/src/ba_data/python/ba/_error.py b/assets/src/ba_data/python/ba/_error.py index bb82a78a..5e6aba42 100644 --- a/assets/src/ba_data/python/ba/_error.py +++ b/assets/src/ba_data/python/ba/_error.py @@ -125,6 +125,11 @@ class WidgetNotFoundError(NotFoundError): """ +# TODO: Should integrate some sort of context printing into our +# log handling so we can just use logging.exception() and kill these +# two functions. + + def print_exception(*args: Any, **keywds: Any) -> None: """Print info about an exception along with pertinent context state. diff --git a/assets/src/ba_data/python/ba/_hooks.py b/assets/src/ba_data/python/ba/_hooks.py index 9d103449..ab7e36cd 100644 --- a/assets/src/ba_data/python/ba/_hooks.py +++ b/assets/src/ba_data/python/ba/_hooks.py @@ -28,7 +28,7 @@ def finish_bootstrapping() -> None: assert _ba.in_logic_thread() # Kick off our asyncio event handling, allowing us to use coroutines - # in our game thread alongside our internal event handling. + # in our logic thread alongside our internal event handling. # setup_asyncio() # Ok, bootstrapping is done; time to get the show started. diff --git a/assets/src/ba_data/python/ba/_meta.py b/assets/src/ba_data/python/ba/_meta.py index 40f75619..1faca382 100644 --- a/assets/src/ba_data/python/ba/_meta.py +++ b/assets/src/ba_data/python/ba/_meta.py @@ -192,13 +192,13 @@ class MetadataSubsystem: color=(1, 0, 0)) _ba.playsound(_ba.getsound('error')) if results.warnings: - _ba.log(textwrap.indent('\n'.join(results.warnings), - 'Warning (meta-scan): '), - to_server=False) + allwarnings = textwrap.indent('\n'.join(results.warnings), + 'Warning (meta-scan): ') + logging.warning(allwarnings) if results.errors: - _ba.log( - textwrap.indent('\n'.join(results.errors), - 'Error (meta-scan): ')) + allerrors = textwrap.indent('\n'.join(results.errors), + 'Error (meta-scan): ') + logging.error(allerrors) # Let the game know we're done. assert self._scan_complete_cb is not None diff --git a/assets/src/ba_data/python/ba/_playlist.py b/assets/src/ba_data/python/ba/_playlist.py index 1550934c..5518601a 100644 --- a/assets/src/ba_data/python/ba/_playlist.py +++ b/assets/src/ba_data/python/ba/_playlist.py @@ -5,6 +5,7 @@ from __future__ import annotations import copy +import logging from typing import Any, TYPE_CHECKING if TYPE_CHECKING: @@ -29,7 +30,6 @@ def filter_playlist(playlist: PlaylistType, # pylint: disable=too-many-locals # pylint: disable=too-many-branches # pylint: disable=too-many-statements - import _ba from ba._map import get_filtered_map_name from ba._store import get_unowned_maps, get_unowned_game_types from ba._general import getclass @@ -140,8 +140,8 @@ def filter_playlist(playlist: PlaylistType, entry['settings'][setting.name] = setting.default goodlist.append(entry) except ImportError as exc: - _ba.log(f'Import failed while scanning playlist \'{name}\': {exc}', - to_server=False) + logging.warning('Import failed while scanning playlist \'%s\': %s', + name, exc) except Exception: from ba import _error _error.print_exception() diff --git a/assets/src/ba_data/python/ba/_plugin.py b/assets/src/ba_data/python/ba/_plugin.py index c4702045..d05cdbe9 100644 --- a/assets/src/ba_data/python/ba/_plugin.py +++ b/assets/src/ba_data/python/ba/_plugin.py @@ -4,6 +4,7 @@ from __future__ import annotations +import logging from typing import TYPE_CHECKING from dataclasses import dataclass @@ -127,8 +128,7 @@ class PluginSubsystem: subs=[('${PLUGIN}', plugkey), ('${ERROR}', str(exc))]), color=(1, 0, 0)) - _ba.log(f"Error loading plugin class '{plugkey}': {exc}", - to_server=False) + logging.exception("Error loading plugin class '%s'", plugkey) continue try: plugin = cls() @@ -155,10 +155,8 @@ class PluginSubsystem: color=(1, 1, 0), ) plugnames = ', '.join(disappeared_plugs) - _ba.log( - f'{len(disappeared_plugs)} plugin(s) no longer found:' - f' {plugnames}.', - to_server=False) + logging.warning('%d plugin(s) no longer found: %s.', + len(disappeared_plugs), plugnames) for goneplug in disappeared_plugs: del _ba.app.config['Plugins'][goneplug] _ba.app.config.commit() diff --git a/assets/src/ba_data/python/ba/_servermode.py b/assets/src/ba_data/python/ba/_servermode.py index ebc7f20d..8c8ed981 100644 --- a/assets/src/ba_data/python/ba/_servermode.py +++ b/assets/src/ba_data/python/ba/_servermode.py @@ -5,6 +5,7 @@ from __future__ import annotations import sys import time +import logging from typing import TYPE_CHECKING from efro.terminal import Clr @@ -334,11 +335,11 @@ class ServerController: if self._first_run: curtimestr = time.strftime('%c') - _ba.log( + startupmsg = ( f'{Clr.BLD}{Clr.BLU}{_ba.appnameupper()} {app.version}' f' ({app.build_number})' - f' entering server-mode {curtimestr}{Clr.RST}', - to_server=False) + f' entering server-mode {curtimestr}{Clr.RST}') + logging.info(startupmsg) if sessiontype is FreeForAllSession: appcfg['Free-for-All Playlist Selection'] = self._playlist_name diff --git a/assets/src/ba_data/python/ba/internal.py b/assets/src/ba_data/python/ba/internal.py index d5b47820..fdf99803 100644 --- a/assets/src/ba_data/python/ba/internal.py +++ b/assets/src/ba_data/python/ba/internal.py @@ -9,16 +9,16 @@ defensively) in mods. from __future__ import annotations from _ba import ( - get_scores_to_beat, show_online_score_ui, set_ui_input_device, - is_party_icon_visible, getinputdevice, add_clean_frame_callback, - unlock_all_input, increment_analytics_count, set_debug_speed_exponent, - get_special_widget, get_qrcode_texture, get_string_height, - get_string_width, show_app_invite, appnameupper, lock_all_input, - open_file_externally, fade_screen, appname, have_incentivized_ad, - has_video_ads, workspaces_in_use, set_party_icon_always_visible, - connect_to_party, get_game_port, end_host_scanning, host_scan_cycle, - charstr, get_public_party_enabled, get_public_party_max_size, - set_public_party_name, set_public_party_max_size, set_authenticate_clients, + show_online_score_ui, set_ui_input_device, is_party_icon_visible, + getinputdevice, add_clean_frame_callback, unlock_all_input, + increment_analytics_count, set_debug_speed_exponent, get_special_widget, + get_qrcode_texture, get_string_height, get_string_width, show_app_invite, + appnameupper, lock_all_input, open_file_externally, fade_screen, appname, + have_incentivized_ad, has_video_ads, workspaces_in_use, + set_party_icon_always_visible, connect_to_party, get_game_port, + end_host_scanning, host_scan_cycle, charstr, get_public_party_enabled, + get_public_party_max_size, set_public_party_name, + set_public_party_max_size, set_authenticate_clients, set_public_party_enabled, reset_random_player_names, new_host_session, get_foreground_host_session, get_local_active_input_devices_count, get_ui_input_device, is_in_replay, set_replay_speed_exponent, @@ -78,7 +78,6 @@ from ba._internal import ( sign_out_v1, sign_in_v1, mark_config_dirty) __all__ = [ - 'get_scores_to_beat', 'show_online_score_ui', 'set_ui_input_device', 'is_party_icon_visible', diff --git a/assets/src/ba_data/python/bastd/activity/coopjoin.py b/assets/src/ba_data/python/bastd/activity/coopjoin.py index 3e60af76..b8cc8af5 100644 --- a/assets/src/ba_data/python/bastd/activity/coopjoin.py +++ b/assets/src/ba_data/python/bastd/activity/coopjoin.py @@ -10,7 +10,7 @@ import ba from ba.internal import JoinActivity if TYPE_CHECKING: - from typing import Any, Sequence + pass class CoopJoinActivity(JoinActivity): @@ -24,17 +24,6 @@ class CoopJoinActivity(JoinActivity): session = self.session assert isinstance(session, ba.CoopSession) - # Let's show a list of scores-to-beat for 1 player at least. - assert session.campaign is not None - level_name_full = (session.campaign.name + ':' + - session.campaign_level_name) - config_str = ('1p' + session.campaign.getlevel( - session.campaign_level_name).get_score_version_string().replace( - ' ', '_')) - ba.internal.get_scores_to_beat( - level_name_full, config_str, - ba.WeakCall(self._on_got_scores_to_beat)) - def on_transition_in(self) -> None: from bastd.actor.controlsguide import ControlsGuide from bastd.actor.text import Text @@ -53,143 +42,61 @@ class CoopJoinActivity(JoinActivity): position=(0, -95)).autoretain() ControlsGuide(delay=1.0).autoretain() - def _on_got_scores_to_beat(self, - scores: list[dict[str, Any]] | None) -> None: - # pylint: disable=too-many-locals - # pylint: disable=too-many-statements - from efro.util import asserttype - from bastd.actor.text import Text + ba.pushcall(self._show_remaining_achievements) - # Sort by originating date so that the most recent is first. - if scores is not None: - scores.sort(reverse=True, - key=lambda score: asserttype(score['time'], int)) + def _show_remaining_achievements(self) -> None: + from bastd.actor.text import Text # We only show achievements and challenges for CoopGameActivities. session = self.session assert isinstance(session, ba.CoopSession) gameinstance = session.get_current_game_instance() - if isinstance(gameinstance, ba.CoopGameActivity): - score_type = gameinstance.get_score_type() - if scores is not None: - achievement_challenges = [ - a for a in scores if a['type'] == 'achievement_challenge' - ] - score_challenges = [ - a for a in scores if a['type'] == 'score_challenge' - ] - else: - achievement_challenges = score_challenges = [] + if not isinstance(gameinstance, ba.CoopGameActivity): + return - delay = 1.0 - vpos = -140.0 - spacing = 25 - delay_inc = 0.1 + delay = 1.0 + vpos = -140.0 - def _add_t( - text: str | ba.Lstr, - h_offs: float = 0.0, - scale: float = 1.0, - color: Sequence[float] = (1.0, 1.0, 1.0, 0.46) - ) -> None: - Text(text, - scale=scale * 0.76, - h_align=Text.HAlign.LEFT, + # Now list our remaining achievements for this level. + assert self.session.campaign is not None + assert isinstance(self.session, ba.CoopSession) + levelname = (self.session.campaign.name + ':' + + self.session.campaign_level_name) + ts_h_offs = 60 + + if not (ba.app.demo_mode or ba.app.arcade_mode): + achievements = [ + a for a in ba.app.ach.achievements_for_coop_level(levelname) + if not a.complete + ] + have_achievements = bool(achievements) + achievements = [a for a in achievements if not a.complete] + vrmode = ba.app.vr_mode + if have_achievements: + Text(ba.Lstr(resource='achievementsRemainingText'), + host_only=True, + position=(ts_h_offs - 10, vpos), + transition=Text.Transition.FADE_IN, + scale=1.1 * 0.76, h_attach=Text.HAttach.LEFT, v_attach=Text.VAttach.TOP, - transition=Text.Transition.FADE_IN, - transition_delay=delay, - color=color, - position=(60 + h_offs, vpos)).autoretain() - - if score_challenges: - _add_t(ba.Lstr(value='${A}:', - subs=[('${A}', - ba.Lstr(resource='scoreChallengesText')) - ]), - scale=1.1) - delay += delay_inc - vpos -= spacing - for chal in score_challenges: - _add_t(str(chal['value'] if score_type == 'points' else ba. - timestring(int(chal['value']) * 10, - timeformat=ba.TimeFormat.MILLISECONDS - ).evaluate()) + ' (1 player)', - h_offs=30, - color=(0.9, 0.7, 1.0, 0.8)) - delay += delay_inc - vpos -= 0.6 * spacing - _add_t(chal['player'], - h_offs=40, - color=(0.8, 1, 0.8, 0.6), - scale=0.8) - delay += delay_inc - vpos -= 1.2 * spacing - vpos -= 0.5 * spacing - - if achievement_challenges: - _add_t(ba.Lstr( - value='${A}:', - subs=[('${A}', - ba.Lstr(resource='achievementChallengesText'))]), - scale=1.1) - delay += delay_inc - vpos -= spacing - for chal in achievement_challenges: - _add_t(str(chal['value']), - h_offs=30, - color=(0.9, 0.7, 1.0, 0.8)) - delay += delay_inc - vpos -= 0.6 * spacing - _add_t(chal['player'], - h_offs=40, - color=(0.8, 1, 0.8, 0.6), - scale=0.8) - delay += delay_inc - vpos -= 1.2 * spacing - vpos -= 0.5 * spacing - - # Now list our remaining achievements for this level. - assert self.session.campaign is not None - assert isinstance(self.session, ba.CoopSession) - levelname = (self.session.campaign.name + ':' + - self.session.campaign_level_name) - ts_h_offs = 60 - - if not (ba.app.demo_mode or ba.app.arcade_mode): - achievements = [ - a - for a in ba.app.ach.achievements_for_coop_level(levelname) - if not a.complete - ] - have_achievements = bool(achievements) - achievements = [a for a in achievements if not a.complete] - vrmode = ba.app.vr_mode - if have_achievements: - Text(ba.Lstr(resource='achievementsRemainingText'), + color=(1, 1, 1.2, 1) if vrmode else (0.8, 0.8, 1, 1), + shadow=1.0, + flatness=1.0 if vrmode else 0.6, + transition_delay=delay).autoretain() + hval = ts_h_offs + 50 + vpos -= 35 + for ach in achievements: + delay += 0.05 + ach.create_display(hval, vpos, delay, style='in_game') + vpos -= 55 + if not achievements: + Text(ba.Lstr(resource='noAchievementsRemainingText'), host_only=True, - position=(ts_h_offs - 10, vpos), + position=(ts_h_offs + 15, vpos + 10), transition=Text.Transition.FADE_IN, - scale=1.1 * 0.76, + scale=0.7, h_attach=Text.HAttach.LEFT, v_attach=Text.VAttach.TOP, - color=(1, 1, 1.2, 1) if vrmode else (0.8, 0.8, 1, 1), - shadow=1.0, - flatness=1.0 if vrmode else 0.6, - transition_delay=delay).autoretain() - hval = ts_h_offs + 50 - vpos -= 35 - for ach in achievements: - delay += 0.05 - ach.create_display(hval, vpos, delay, style='in_game') - vpos -= 55 - if not achievements: - Text(ba.Lstr(resource='noAchievementsRemainingText'), - host_only=True, - position=(ts_h_offs + 15, vpos + 10), - transition=Text.Transition.FADE_IN, - scale=0.7, - h_attach=Text.HAttach.LEFT, - v_attach=Text.VAttach.TOP, - color=(1, 1, 1, 0.5), - transition_delay=delay + 0.5).autoretain() + color=(1, 1, 1, 0.5), + transition_delay=delay + 0.5).autoretain() diff --git a/assets/src/ba_data/python/bastd/game/easteregghunt.py b/assets/src/ba_data/python/bastd/game/easteregghunt.py index 3372522d..1dc4de02 100644 --- a/assets/src/ba_data/python/bastd/game/easteregghunt.py +++ b/assets/src/ba_data/python/bastd/game/easteregghunt.py @@ -44,7 +44,10 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]): name = 'Easter Egg Hunt' description = 'Gather eggs!' - available_settings = [ba.BoolSetting('Pro Mode', default=False)] + available_settings = [ + ba.BoolSetting('Pro Mode', default=False), + ba.BoolSetting('Epic Mode', default=False), + ] scoreconfig = ba.ScoreConfig(label='Score', scoretype=ba.ScoreType.POINTS) # We're currently hard-coded for one map. @@ -70,6 +73,7 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]): self.egg_tex_3 = ba.gettexture('eggTex3') self._collect_sound = ba.getsound('powerup01') self._pro_mode = settings.get('Pro Mode', False) + self._epic_mode = settings.get('Epic Mode', False) self._max_eggs = 1.0 self.egg_material = ba.Material() self.egg_material.add_actions( @@ -81,7 +85,9 @@ class EasterEggHuntGame(ba.TeamGameActivity[Player, Team]): self._bots: SpazBotSet | None = None # Base class overrides - self.default_music = ba.MusicType.FORWARD_MARCH + self.slow_motion = self._epic_mode + self.default_music = (ba.MusicType.EPIC if self._epic_mode else + ba.MusicType.FORWARD_MARCH) def on_team_join(self, team: Team) -> None: if self.has_begun(): diff --git a/assets/src/ba_data/python/bastd/game/football.py b/assets/src/ba_data/python/bastd/game/football.py index 263f8e3e..2faed91f 100644 --- a/assets/src/ba_data/python/bastd/game/football.py +++ b/assets/src/ba_data/python/bastd/game/football.py @@ -335,6 +335,8 @@ class FootballCoopGame(ba.CoopGameActivity[Player, Team]): scoreconfig = ba.ScoreConfig(scoretype=ba.ScoreType.MILLISECONDS, version='B') + default_music = ba.MusicType.FOOTBALL + # FIXME: Need to update co-op games to use getscoreconfig. def get_score_type(self) -> str: return 'time' diff --git a/ballisticacore-cmake/.idea/dictionaries/ericf.xml b/ballisticacore-cmake/.idea/dictionaries/ericf.xml index 160953f7..21c6ae55 100644 --- a/ballisticacore-cmake/.idea/dictionaries/ericf.xml +++ b/ballisticacore-cmake/.idea/dictionaries/ericf.xml @@ -41,7 +41,9 @@ airborn alext alibaba + allerrors allocs + allwarnings alot alphaimg alphapixels @@ -67,6 +69,7 @@ appnameupper appspot appstate + apptime argsjoined argstr armcap @@ -119,6 +122,7 @@ bdea benning bezanson + bgdynamics bgra bigendian bilinear @@ -226,10 +230,12 @@ classdict classline cleanupcheck + clearsign clientid clientinfo clienttobasn clipcount + cmakelist cmath cmds cmdvals @@ -697,6 +703,7 @@ leaderboard leaderboards lenval + levelno lgui lhalf libbz @@ -848,6 +855,7 @@ netcode netplay nettest + networkwrite newactivity newchild newimg @@ -1239,6 +1247,7 @@ startpos startsplits starttime + startupmsg startx starty statestr @@ -1258,6 +1267,7 @@ strdup stringi stringified + stringifying strlen strs strtof diff --git a/ballisticacore-cmake/.idea/inspectionProfiles/Project_Default.xml b/ballisticacore-cmake/.idea/inspectionProfiles/Project_Default.xml index ffa57a65..bccdbc35 100644 --- a/ballisticacore-cmake/.idea/inspectionProfiles/Project_Default.xml +++ b/ballisticacore-cmake/.idea/inspectionProfiles/Project_Default.xml @@ -58,6 +58,7 @@ + diff --git a/ballisticacore-cmake/CMakeLists.txt b/ballisticacore-cmake/CMakeLists.txt index 0f58aa0a..77deaf38 100644 --- a/ballisticacore-cmake/CMakeLists.txt +++ b/ballisticacore-cmake/CMakeLists.txt @@ -210,6 +210,40 @@ add_executable(ballisticacore ${BA_SRC_ROOT}/ballistica/app/app_flavor_vr.h ${BA_SRC_ROOT}/ballistica/app/stress_test.cc ${BA_SRC_ROOT}/ballistica/app/stress_test.h + ${BA_SRC_ROOT}/ballistica/assets/assets.cc + ${BA_SRC_ROOT}/ballistica/assets/assets.h + ${BA_SRC_ROOT}/ballistica/assets/assets_server.cc + ${BA_SRC_ROOT}/ballistica/assets/assets_server.h + ${BA_SRC_ROOT}/ballistica/assets/component/asset_component.cc + ${BA_SRC_ROOT}/ballistica/assets/component/asset_component.h + ${BA_SRC_ROOT}/ballistica/assets/component/collide_model.cc + ${BA_SRC_ROOT}/ballistica/assets/component/collide_model.h + ${BA_SRC_ROOT}/ballistica/assets/component/cube_map_texture.cc + ${BA_SRC_ROOT}/ballistica/assets/component/cube_map_texture.h + ${BA_SRC_ROOT}/ballistica/assets/component/data.cc + ${BA_SRC_ROOT}/ballistica/assets/component/data.h + ${BA_SRC_ROOT}/ballistica/assets/component/model.cc + ${BA_SRC_ROOT}/ballistica/assets/component/model.h + ${BA_SRC_ROOT}/ballistica/assets/component/sound.cc + ${BA_SRC_ROOT}/ballistica/assets/component/sound.h + ${BA_SRC_ROOT}/ballistica/assets/component/texture.cc + ${BA_SRC_ROOT}/ballistica/assets/component/texture.h + ${BA_SRC_ROOT}/ballistica/assets/data/asset_component_data.cc + ${BA_SRC_ROOT}/ballistica/assets/data/asset_component_data.h + ${BA_SRC_ROOT}/ballistica/assets/data/collide_model_data.cc + ${BA_SRC_ROOT}/ballistica/assets/data/collide_model_data.h + ${BA_SRC_ROOT}/ballistica/assets/data/data_data.cc + ${BA_SRC_ROOT}/ballistica/assets/data/data_data.h + ${BA_SRC_ROOT}/ballistica/assets/data/model_data.cc + ${BA_SRC_ROOT}/ballistica/assets/data/model_data.h + ${BA_SRC_ROOT}/ballistica/assets/data/model_renderer_data.h + ${BA_SRC_ROOT}/ballistica/assets/data/sound_data.cc + ${BA_SRC_ROOT}/ballistica/assets/data/sound_data.h + ${BA_SRC_ROOT}/ballistica/assets/data/texture_data.cc + ${BA_SRC_ROOT}/ballistica/assets/data/texture_data.h + ${BA_SRC_ROOT}/ballistica/assets/data/texture_preload_data.cc + ${BA_SRC_ROOT}/ballistica/assets/data/texture_preload_data.h + ${BA_SRC_ROOT}/ballistica/assets/data/texture_renderer_data.h ${BA_SRC_ROOT}/ballistica/audio/al_sys.cc ${BA_SRC_ROOT}/ballistica/audio/al_sys.h ${BA_SRC_ROOT}/ballistica/audio/audio.cc @@ -298,43 +332,6 @@ add_executable(ballisticacore ${BA_SRC_ROOT}/ballistica/dynamics/part.h ${BA_SRC_ROOT}/ballistica/dynamics/rigid_body.cc ${BA_SRC_ROOT}/ballistica/dynamics/rigid_body.h - ${BA_SRC_ROOT}/ballistica/game/account.cc - ${BA_SRC_ROOT}/ballistica/game/account.h - ${BA_SRC_ROOT}/ballistica/game/client_controller_interface.h - ${BA_SRC_ROOT}/ballistica/game/connection/connection.cc - ${BA_SRC_ROOT}/ballistica/game/connection/connection.h - ${BA_SRC_ROOT}/ballistica/game/connection/connection_set.cc - ${BA_SRC_ROOT}/ballistica/game/connection/connection_set.h - ${BA_SRC_ROOT}/ballistica/game/connection/connection_to_client.cc - ${BA_SRC_ROOT}/ballistica/game/connection/connection_to_client.h - ${BA_SRC_ROOT}/ballistica/game/connection/connection_to_client_udp.cc - ${BA_SRC_ROOT}/ballistica/game/connection/connection_to_client_udp.h - ${BA_SRC_ROOT}/ballistica/game/connection/connection_to_host.cc - ${BA_SRC_ROOT}/ballistica/game/connection/connection_to_host.h - ${BA_SRC_ROOT}/ballistica/game/connection/connection_to_host_udp.cc - ${BA_SRC_ROOT}/ballistica/game/connection/connection_to_host_udp.h - ${BA_SRC_ROOT}/ballistica/game/friend_score_set.h - ${BA_SRC_ROOT}/ballistica/game/game.cc - ${BA_SRC_ROOT}/ballistica/game/game.h - ${BA_SRC_ROOT}/ballistica/game/game_stream.cc - ${BA_SRC_ROOT}/ballistica/game/game_stream.h - ${BA_SRC_ROOT}/ballistica/game/host_activity.cc - ${BA_SRC_ROOT}/ballistica/game/host_activity.h - ${BA_SRC_ROOT}/ballistica/game/player.cc - ${BA_SRC_ROOT}/ballistica/game/player.h - ${BA_SRC_ROOT}/ballistica/game/player_spec.cc - ${BA_SRC_ROOT}/ballistica/game/player_spec.h - ${BA_SRC_ROOT}/ballistica/game/score_to_beat.h - ${BA_SRC_ROOT}/ballistica/game/session/client_session.cc - ${BA_SRC_ROOT}/ballistica/game/session/client_session.h - ${BA_SRC_ROOT}/ballistica/game/session/host_session.cc - ${BA_SRC_ROOT}/ballistica/game/session/host_session.h - ${BA_SRC_ROOT}/ballistica/game/session/net_client_session.cc - ${BA_SRC_ROOT}/ballistica/game/session/net_client_session.h - ${BA_SRC_ROOT}/ballistica/game/session/replay_client_session.cc - ${BA_SRC_ROOT}/ballistica/game/session/replay_client_session.h - ${BA_SRC_ROOT}/ballistica/game/session/session.cc - ${BA_SRC_ROOT}/ballistica/game/session/session.h ${BA_SRC_ROOT}/ballistica/generic/base64.cc ${BA_SRC_ROOT}/ballistica/generic/base64.h ${BA_SRC_ROOT}/ballistica/generic/buffer.h @@ -453,9 +450,41 @@ add_executable(ballisticacore ${BA_SRC_ROOT}/ballistica/input/input.h ${BA_SRC_ROOT}/ballistica/input/remote_app.cc ${BA_SRC_ROOT}/ballistica/input/remote_app.h - ${BA_SRC_ROOT}/ballistica/input/std_input_module.cc - ${BA_SRC_ROOT}/ballistica/input/std_input_module.h ${BA_SRC_ROOT}/ballistica/internal/app_internal.h + ${BA_SRC_ROOT}/ballistica/logic/client_controller_interface.h + ${BA_SRC_ROOT}/ballistica/logic/connection/connection.cc + ${BA_SRC_ROOT}/ballistica/logic/connection/connection.h + ${BA_SRC_ROOT}/ballistica/logic/connection/connection_set.cc + ${BA_SRC_ROOT}/ballistica/logic/connection/connection_set.h + ${BA_SRC_ROOT}/ballistica/logic/connection/connection_to_client.cc + ${BA_SRC_ROOT}/ballistica/logic/connection/connection_to_client.h + ${BA_SRC_ROOT}/ballistica/logic/connection/connection_to_client_udp.cc + ${BA_SRC_ROOT}/ballistica/logic/connection/connection_to_client_udp.h + ${BA_SRC_ROOT}/ballistica/logic/connection/connection_to_host.cc + ${BA_SRC_ROOT}/ballistica/logic/connection/connection_to_host.h + ${BA_SRC_ROOT}/ballistica/logic/connection/connection_to_host_udp.cc + ${BA_SRC_ROOT}/ballistica/logic/connection/connection_to_host_udp.h + ${BA_SRC_ROOT}/ballistica/logic/friend_score_set.h + ${BA_SRC_ROOT}/ballistica/logic/host_activity.cc + ${BA_SRC_ROOT}/ballistica/logic/host_activity.h + ${BA_SRC_ROOT}/ballistica/logic/logic.cc + ${BA_SRC_ROOT}/ballistica/logic/logic.h + ${BA_SRC_ROOT}/ballistica/logic/player.cc + ${BA_SRC_ROOT}/ballistica/logic/player.h + ${BA_SRC_ROOT}/ballistica/logic/player_spec.cc + ${BA_SRC_ROOT}/ballistica/logic/player_spec.h + ${BA_SRC_ROOT}/ballistica/logic/session/client_session.cc + ${BA_SRC_ROOT}/ballistica/logic/session/client_session.h + ${BA_SRC_ROOT}/ballistica/logic/session/host_session.cc + ${BA_SRC_ROOT}/ballistica/logic/session/host_session.h + ${BA_SRC_ROOT}/ballistica/logic/session/net_client_session.cc + ${BA_SRC_ROOT}/ballistica/logic/session/net_client_session.h + ${BA_SRC_ROOT}/ballistica/logic/session/replay_client_session.cc + ${BA_SRC_ROOT}/ballistica/logic/session/replay_client_session.h + ${BA_SRC_ROOT}/ballistica/logic/session/session.cc + ${BA_SRC_ROOT}/ballistica/logic/session/session.h + ${BA_SRC_ROOT}/ballistica/logic/v1_account.cc + ${BA_SRC_ROOT}/ballistica/logic/v1_account.h ${BA_SRC_ROOT}/ballistica/math/matrix44f.cc ${BA_SRC_ROOT}/ballistica/math/matrix44f.h ${BA_SRC_ROOT}/ballistica/math/point2d.h @@ -466,44 +495,10 @@ add_executable(ballisticacore ${BA_SRC_ROOT}/ballistica/math/vector3f.cc ${BA_SRC_ROOT}/ballistica/math/vector3f.h ${BA_SRC_ROOT}/ballistica/math/vector4f.h - ${BA_SRC_ROOT}/ballistica/media/component/collide_model.cc - ${BA_SRC_ROOT}/ballistica/media/component/collide_model.h - ${BA_SRC_ROOT}/ballistica/media/component/cube_map_texture.cc - ${BA_SRC_ROOT}/ballistica/media/component/cube_map_texture.h - ${BA_SRC_ROOT}/ballistica/media/component/data.cc - ${BA_SRC_ROOT}/ballistica/media/component/data.h - ${BA_SRC_ROOT}/ballistica/media/component/media_component.cc - ${BA_SRC_ROOT}/ballistica/media/component/media_component.h - ${BA_SRC_ROOT}/ballistica/media/component/model.cc - ${BA_SRC_ROOT}/ballistica/media/component/model.h - ${BA_SRC_ROOT}/ballistica/media/component/sound.cc - ${BA_SRC_ROOT}/ballistica/media/component/sound.h - ${BA_SRC_ROOT}/ballistica/media/component/texture.cc - ${BA_SRC_ROOT}/ballistica/media/component/texture.h - ${BA_SRC_ROOT}/ballistica/media/data/collide_model_data.cc - ${BA_SRC_ROOT}/ballistica/media/data/collide_model_data.h - ${BA_SRC_ROOT}/ballistica/media/data/data_data.cc - ${BA_SRC_ROOT}/ballistica/media/data/data_data.h - ${BA_SRC_ROOT}/ballistica/media/data/media_component_data.cc - ${BA_SRC_ROOT}/ballistica/media/data/media_component_data.h - ${BA_SRC_ROOT}/ballistica/media/data/model_data.cc - ${BA_SRC_ROOT}/ballistica/media/data/model_data.h - ${BA_SRC_ROOT}/ballistica/media/data/model_renderer_data.h - ${BA_SRC_ROOT}/ballistica/media/data/sound_data.cc - ${BA_SRC_ROOT}/ballistica/media/data/sound_data.h - ${BA_SRC_ROOT}/ballistica/media/data/texture_data.cc - ${BA_SRC_ROOT}/ballistica/media/data/texture_data.h - ${BA_SRC_ROOT}/ballistica/media/data/texture_preload_data.cc - ${BA_SRC_ROOT}/ballistica/media/data/texture_preload_data.h - ${BA_SRC_ROOT}/ballistica/media/data/texture_renderer_data.h - ${BA_SRC_ROOT}/ballistica/media/media.cc - ${BA_SRC_ROOT}/ballistica/media/media.h - ${BA_SRC_ROOT}/ballistica/media/media_server.cc - ${BA_SRC_ROOT}/ballistica/media/media_server.h ${BA_SRC_ROOT}/ballistica/networking/network_reader.cc ${BA_SRC_ROOT}/ballistica/networking/network_reader.h - ${BA_SRC_ROOT}/ballistica/networking/network_write_module.cc - ${BA_SRC_ROOT}/ballistica/networking/network_write_module.h + ${BA_SRC_ROOT}/ballistica/networking/network_writer.cc + ${BA_SRC_ROOT}/ballistica/networking/network_writer.h ${BA_SRC_ROOT}/ballistica/networking/networking.cc ${BA_SRC_ROOT}/ballistica/networking/networking.h ${BA_SRC_ROOT}/ballistica/networking/networking_sys.h @@ -519,6 +514,8 @@ add_executable(ballisticacore ${BA_SRC_ROOT}/ballistica/platform/platform.h ${BA_SRC_ROOT}/ballistica/platform/sdl/sdl_app.cc ${BA_SRC_ROOT}/ballistica/platform/sdl/sdl_app.h + ${BA_SRC_ROOT}/ballistica/platform/stdio_console.cc + ${BA_SRC_ROOT}/ballistica/platform/stdio_console.h ${BA_SRC_ROOT}/ballistica/platform/windows/platform_windows.cc ${BA_SRC_ROOT}/ballistica/platform/windows/platform_windows.h ${BA_SRC_ROOT}/ballistica/python/class/python_class.cc @@ -557,14 +554,14 @@ add_executable(ballisticacore ${BA_SRC_ROOT}/ballistica/python/class/python_class_widget.h ${BA_SRC_ROOT}/ballistica/python/methods/python_methods_app.cc ${BA_SRC_ROOT}/ballistica/python/methods/python_methods_app.h + ${BA_SRC_ROOT}/ballistica/python/methods/python_methods_assets.cc + ${BA_SRC_ROOT}/ballistica/python/methods/python_methods_assets.h ${BA_SRC_ROOT}/ballistica/python/methods/python_methods_gameplay.cc ${BA_SRC_ROOT}/ballistica/python/methods/python_methods_gameplay.h ${BA_SRC_ROOT}/ballistica/python/methods/python_methods_graphics.cc ${BA_SRC_ROOT}/ballistica/python/methods/python_methods_graphics.h ${BA_SRC_ROOT}/ballistica/python/methods/python_methods_input.cc ${BA_SRC_ROOT}/ballistica/python/methods/python_methods_input.h - ${BA_SRC_ROOT}/ballistica/python/methods/python_methods_media.cc - ${BA_SRC_ROOT}/ballistica/python/methods/python_methods_media.h ${BA_SRC_ROOT}/ballistica/python/methods/python_methods_networking.cc ${BA_SRC_ROOT}/ballistica/python/methods/python_methods_networking.h ${BA_SRC_ROOT}/ballistica/python/methods/python_methods_system.cc @@ -638,6 +635,10 @@ add_executable(ballisticacore ${BA_SRC_ROOT}/ballistica/scene/node/time_display_node.h ${BA_SRC_ROOT}/ballistica/scene/scene.cc ${BA_SRC_ROOT}/ballistica/scene/scene.h + ${BA_SRC_ROOT}/ballistica/scene/scene_stream.cc + ${BA_SRC_ROOT}/ballistica/scene/scene_stream.h + ${BA_SRC_ROOT}/ballistica/scene/v1/scene_v1.cc + ${BA_SRC_ROOT}/ballistica/scene/v1/scene_v1.h ${BA_SRC_ROOT}/ballistica/ui/console.cc ${BA_SRC_ROOT}/ballistica/ui/console.h ${BA_SRC_ROOT}/ballistica/ui/root_ui.cc diff --git a/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj b/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj index 06cae3fd..8f4b7c5a 100644 --- a/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj +++ b/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj @@ -201,6 +201,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -289,43 +323,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -444,9 +441,41 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -457,44 +486,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -510,6 +505,8 @@ + + @@ -548,14 +545,14 @@ + + - - @@ -629,6 +626,10 @@ + + + + diff --git a/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj.filters b/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj.filters index 0db00d48..4430ba75 100644 --- a/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj.filters +++ b/ballisticacore-windows/Generic/BallisticaCoreGeneric.vcxproj.filters @@ -37,6 +37,108 @@ ballistica\app + + ballistica\assets + + + ballistica\assets + + + ballistica\assets + + + ballistica\assets + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + ballistica\audio @@ -301,117 +403,6 @@ ballistica\dynamics - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game\session - - - ballistica\game\session - - - ballistica\game\session - - - ballistica\game\session - - - ballistica\game\session - - - ballistica\game\session - - - ballistica\game\session - - - ballistica\game\session - - - ballistica\game\session - - - ballistica\game\session - ballistica\generic @@ -766,15 +757,111 @@ ballistica\input - - ballistica\input - - - ballistica\input - ballistica\internal + + ballistica\logic + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic + + + ballistica\logic + + + ballistica\logic + + + ballistica\logic + + + ballistica\logic + + + ballistica\logic + + + ballistica\logic + + + ballistica\logic + + + ballistica\logic + + + ballistica\logic\session + + + ballistica\logic\session + + + ballistica\logic\session + + + ballistica\logic\session + + + ballistica\logic\session + + + ballistica\logic\session + + + ballistica\logic\session + + + ballistica\logic\session + + + ballistica\logic\session + + + ballistica\logic\session + + + ballistica\logic + + + ballistica\logic + ballistica\math @@ -805,118 +892,16 @@ ballistica\math - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media - - - ballistica\media - - - ballistica\media - - - ballistica\media - ballistica\networking ballistica\networking - + ballistica\networking - + ballistica\networking @@ -964,6 +949,12 @@ ballistica\platform\sdl + + ballistica\platform + + + ballistica\platform + ballistica\platform\windows @@ -1078,6 +1069,12 @@ ballistica\python\methods + + ballistica\python\methods + + + ballistica\python\methods + ballistica\python\methods @@ -1096,12 +1093,6 @@ ballistica\python\methods - - ballistica\python\methods - - - ballistica\python\methods - ballistica\python\methods @@ -1321,6 +1312,18 @@ ballistica\scene + + ballistica\scene + + + ballistica\scene + + + ballistica\scene\v1 + + + ballistica\scene\v1 + ballistica\ui @@ -1646,15 +1649,15 @@ + + + - - - @@ -1665,10 +1668,10 @@ + + + - - - @@ -1680,6 +1683,7 @@ + diff --git a/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj b/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj index ccbacee1..f1654fcf 100644 --- a/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj +++ b/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj @@ -196,6 +196,40 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -284,43 +318,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -439,9 +436,41 @@ - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -452,44 +481,10 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + @@ -505,6 +500,8 @@ + + @@ -543,14 +540,14 @@ + + - - @@ -624,6 +621,10 @@ + + + + diff --git a/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj.filters b/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj.filters index 0db00d48..4430ba75 100644 --- a/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj.filters +++ b/ballisticacore-windows/Headless/BallisticaCoreHeadless.vcxproj.filters @@ -37,6 +37,108 @@ ballistica\app + + ballistica\assets + + + ballistica\assets + + + ballistica\assets + + + ballistica\assets + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\component + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + + + ballistica\assets\data + ballistica\audio @@ -301,117 +403,6 @@ ballistica\dynamics - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game\connection - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game - - - ballistica\game\session - - - ballistica\game\session - - - ballistica\game\session - - - ballistica\game\session - - - ballistica\game\session - - - ballistica\game\session - - - ballistica\game\session - - - ballistica\game\session - - - ballistica\game\session - - - ballistica\game\session - ballistica\generic @@ -766,15 +757,111 @@ ballistica\input - - ballistica\input - - - ballistica\input - ballistica\internal + + ballistica\logic + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic\connection + + + ballistica\logic + + + ballistica\logic + + + ballistica\logic + + + ballistica\logic + + + ballistica\logic + + + ballistica\logic + + + ballistica\logic + + + ballistica\logic + + + ballistica\logic + + + ballistica\logic\session + + + ballistica\logic\session + + + ballistica\logic\session + + + ballistica\logic\session + + + ballistica\logic\session + + + ballistica\logic\session + + + ballistica\logic\session + + + ballistica\logic\session + + + ballistica\logic\session + + + ballistica\logic\session + + + ballistica\logic + + + ballistica\logic + ballistica\math @@ -805,118 +892,16 @@ ballistica\math - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\component - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media\data - - - ballistica\media - - - ballistica\media - - - ballistica\media - - - ballistica\media - ballistica\networking ballistica\networking - + ballistica\networking - + ballistica\networking @@ -964,6 +949,12 @@ ballistica\platform\sdl + + ballistica\platform + + + ballistica\platform + ballistica\platform\windows @@ -1078,6 +1069,12 @@ ballistica\python\methods + + ballistica\python\methods + + + ballistica\python\methods + ballistica\python\methods @@ -1096,12 +1093,6 @@ ballistica\python\methods - - ballistica\python\methods - - - ballistica\python\methods - ballistica\python\methods @@ -1321,6 +1312,18 @@ ballistica\scene + + ballistica\scene + + + ballistica\scene + + + ballistica\scene\v1 + + + ballistica\scene\v1 + ballistica\ui @@ -1646,15 +1649,15 @@ + + + - - - @@ -1665,10 +1668,10 @@ + + + - - - @@ -1680,6 +1683,7 @@ + diff --git a/src/ballistica/app/app.h b/src/ballistica/app/app.h index c3d3546c..754d42e1 100644 --- a/src/ballistica/app/app.h +++ b/src/ballistica/app/app.h @@ -13,24 +13,15 @@ namespace ballistica { -// The first thing the engine does is allocate an instance of this as g_globals. -// As much as possible, previously static/global values should be moved to here, -// ideally as a temporary measure until they can be placed as non-static members -// in the proper classes. -// Any use of non-trivial global/static values such as class instances should be -// avoided since it can introduce ambiguities during init and teardown. -// For more explanation, see the 'Static and Global Variables' section in the -// Google C++ Style Guide. +// The first thing the engine does is allocate an instance of this as g_app. class App { public: App(int argc, char** argv); - /// Program argument count (on applicable platforms). + // The following are misc values that should be migrated to applicable + // subsystem classes. int argc{}; - - /// Program argument values (on applicable platforms). char** argv{}; - bool threads_paused{}; std::unordered_map node_types; std::unordered_map node_types_by_id; @@ -41,10 +32,10 @@ class App { std::vector pausable_threads; TouchInput* touch_input{}; std::string console_startup_messages; - std::mutex log_mutex; - std::string log; - bool put_log{}; - bool log_full{}; + std::mutex v1_cloud_log_mutex; + std::string v1_cloud_log; + bool did_put_v1_cloud_log{}; + bool v1_cloud_log_full{}; int master_server_source{0}; int session_count{}; bool shutting_down{}; diff --git a/src/ballistica/app/app_config.cc b/src/ballistica/app/app_config.cc index b529ec9f..cf6f0b23 100644 --- a/src/ballistica/app/app_config.cc +++ b/src/ballistica/app/app_config.cc @@ -8,8 +8,6 @@ namespace ballistica { -void AppConfig::Init() { new AppConfig(); } - auto AppConfig::Entry::FloatValue() const -> float { throw Exception("not a float entry"); } @@ -137,12 +135,7 @@ class AppConfig::BoolEntry : public AppConfig::Entry { bool default_value_{}; }; -AppConfig::AppConfig() { - // (We're a singleton). - assert(g_app_config == nullptr); - g_app_config = this; - SetupEntries(); -} +AppConfig::AppConfig() { SetupEntries(); } // Clion think all calls of this are unreachable. #pragma clang diagnostic push diff --git a/src/ballistica/app/app_config.h b/src/ballistica/app/app_config.h index 6e3303e7..9a970b4a 100644 --- a/src/ballistica/app/app_config.h +++ b/src/ballistica/app/app_config.h @@ -13,7 +13,7 @@ namespace ballistica { // This class wrangles user config values for the app. // The underlying config data currently lives in the Python layer, -// so at the moment these calls are only usable from the game thread, +// so at the moment these calls are only usable from the logic thread, // but that may change in the future. class AppConfig { public: @@ -94,7 +94,6 @@ class AppConfig { std::string name_; }; - static void Init(); AppConfig(); // Given specific ids, returns resolved values (fastest access). diff --git a/src/ballistica/app/app_flavor.cc b/src/ballistica/app/app_flavor.cc index 37d59fc7..70614020 100644 --- a/src/ballistica/app/app_flavor.cc +++ b/src/ballistica/app/app_flavor.cc @@ -4,11 +4,11 @@ #include "ballistica/app/stress_test.h" #include "ballistica/core/thread.h" -#include "ballistica/game/game.h" #include "ballistica/graphics/graphics_server.h" #include "ballistica/graphics/renderer.h" #include "ballistica/input/device/touch_input.h" #include "ballistica/input/input.h" +#include "ballistica/logic/logic.h" #include "ballistica/networking/network_reader.h" #include "ballistica/networking/networking.h" #include "ballistica/networking/telnet_server.h" @@ -18,20 +18,30 @@ namespace ballistica { AppFlavor::AppFlavor(Thread* thread) : thread_(thread), stress_test_(std::make_unique()) { - // assert(g_app_flavor == nullptr); - // g_app_flavor = this; - // We modify some app behavior when run under the server manager. auto* envval = getenv("BA_SERVER_WRAPPER_MANAGED"); server_wrapper_managed_ = (envval && strcmp(envval, "1") == 0); } void AppFlavor::PostInit() { - // If we've got a nice themed hardware cursor, show it. - // Otherwise, hide the hardware cursor; we'll draw it in software. - // (need to run this in postinit because SDL/etc. may not be inited yet - // as of AppFlavor::AppFlavor()). - g_platform->SetHardwareCursorVisible(g_buildconfig.hardware_cursor()); + // Sanity check: make sure asserts are stripped out of release builds + // (NDEBUG should do this). +#if !BA_DEBUG_BUILD +#ifndef NDEBUG +#error Expected NDEBUG to be defined for release builds. +#endif // NDEBUG + assert(true); +#endif // !BA_DEBUG_BUILD + + g_app->user_agent_string = g_platform->GetUserAgentString(); + + // Figure out where our data is and chdir there. + g_platform->SetupDataDirectory(); + + // Run these just to make sure these dirs exist. + // (otherwise they might not get made if nothing writes to them). + g_platform->GetConfigDirectory(); + g_platform->GetUserPythonDirectory(); } auto AppFlavor::ManagesEventLoop() const -> bool { @@ -186,7 +196,7 @@ void AppFlavor::OnResume() { // Also let the Python layer do what it needs to // (starting/stopping music, etc.). g_python->PushObjCall(Python::ObjID::kHandleAppResumeCall); - g_game->PushOnAppResumeCall(); + g_logic->PushOnAppResumeCall(); g_graphics->SetGyroEnabled(true); @@ -201,7 +211,7 @@ void AppFlavor::OnResume() { // If we've been completely backgrounded, // send a menu-press command to the game; this will // bring up a pause menu if we're in the game/etc. - g_game->PushMainMenuPressCall(nullptr); + g_logic->PushMainMenuPressCall(nullptr); } } @@ -268,11 +278,7 @@ void AppFlavor::PushNetworkSetupCall(int port, int telnet_port, const std::string& telnet_password) { thread()->PushCall([port, telnet_port, enable_telnet, telnet_password] { assert(InMainThread()); - // Kick these off if they don't exist. - // (do we want to support changing ports on existing ones?) - if (g_network_reader == nullptr) { - new NetworkReader(port); - } + g_network_reader->SetPort(port); if (g_app->telnet_server == nullptr && enable_telnet) { new TelnetServer(telnet_port); assert(g_app->telnet_server); @@ -291,15 +297,6 @@ void AppFlavor::PushPurchaseAckCall(const std::string& purchase, [purchase, order_id] { g_platform->PurchaseAck(purchase, order_id); }); } -void AppFlavor::PushGetScoresToBeatCall(const std::string& level, - const std::string& config, - void* py_callback) { - thread()->PushCall([level, config, py_callback] { - assert(InMainThread()); - g_platform->GetScoresToBeat(level, config, py_callback); - }); -} - void AppFlavor::PushPurchaseCall(const std::string& item) { thread()->PushCall([item] { assert(InMainThread()); @@ -366,10 +363,31 @@ void AppFlavor::PushResetAchievementsCall() { thread()->PushCall([] { g_platform->ResetAchievements(); }); } -void AppFlavor::OnBootstrapComplete() { +void AppFlavor::OnAppStart() { assert(InMainThread()); assert(g_input); + // If we're running in a terminal, print some info. + // if (g_platform->is_stdin_a_terminal()) { + { + char buffer[256]; + if (g_buildconfig.headless_build()) { + snprintf(buffer, sizeof(buffer), "BallisticaCore Headless %s build %d.", + kAppVersion, kAppBuildNumber); + } else { + snprintf(buffer, sizeof(buffer), "BallisticaCore %s build %d.", + kAppVersion, kAppBuildNumber); + } + Log(LogLevel::kInfo, buffer); + } + // } + + // If we've got a nice themed hardware cursor, show it. + // Otherwise, hide the hardware cursor; we'll draw it in software. + // (need to run this in postinit because SDL/etc. may not be inited yet + // as of AppFlavor::AppFlavor()). + g_platform->SetHardwareCursorVisible(g_buildconfig.hardware_cursor()); + if (!HeadlessMode()) { // On desktop systems we just assume keyboard input exists and add it // immediately. diff --git a/src/ballistica/app/app_flavor.h b/src/ballistica/app/app_flavor.h index 14688fb4..fb1f013a 100644 --- a/src/ballistica/app/app_flavor.h +++ b/src/ballistica/app/app_flavor.h @@ -93,7 +93,7 @@ class AppFlavor { return server_wrapper_managed_; } - virtual auto OnBootstrapComplete() -> void; + virtual auto OnAppStart() -> void; // Deferred calls that can be made from other threads. @@ -108,9 +108,6 @@ class AppFlavor { const std::string& game_version, int64_t score) -> void; auto PushAchievementReportCall(const std::string& achievement) -> void; - auto PushGetScoresToBeatCall(const std::string& level, - const std::string& config, void* py_callback) - -> void; auto PushOpenURLCall(const std::string& url) -> void; auto PushStringEditCall(const std::string& name, const std::string& value, int max_chars) -> void; diff --git a/src/ballistica/app/app_flavor_vr.cc b/src/ballistica/app/app_flavor_vr.cc index 129d8b51..4fa5f7a6 100644 --- a/src/ballistica/app/app_flavor_vr.cc +++ b/src/ballistica/app/app_flavor_vr.cc @@ -4,9 +4,9 @@ #include "ballistica/app/app_flavor_vr.h" #include "ballistica/core/thread.h" -#include "ballistica/game/game.h" #include "ballistica/graphics/graphics_server.h" #include "ballistica/graphics/renderer.h" +#include "ballistica/logic/logic.h" namespace ballistica { @@ -87,7 +87,7 @@ auto AppFlavorVR::VRSetHands(const VRHandsState& state) -> void { renderer->VRSetHands(state); // ALSO ship it off to the game/ui thread to actually handle input from it. - g_game->PushVRHandsState(state); + g_logic->PushVRHandsState(state); } auto AppFlavorVR::VRDrawEye(int eye, float yaw, float pitch, float roll, diff --git a/src/ballistica/app/stress_test.cc b/src/ballistica/app/stress_test.cc index 0dbea11a..dd63eb23 100644 --- a/src/ballistica/app/stress_test.cc +++ b/src/ballistica/app/stress_test.cc @@ -76,13 +76,13 @@ void StressTest::Update() { uint32_t texture_count = 0; uint32_t sound_count = 0; uint32_t node_count = 0; - if (g_media) { - model_count = g_media->total_model_count(); - collide_model_count = g_media->total_collide_model_count(); - texture_count = g_media->total_texture_count(); - sound_count = g_media->total_sound_count(); + if (g_assets) { + model_count = g_assets->total_model_count(); + collide_model_count = g_assets->total_collide_model_count(); + texture_count = g_assets->total_texture_count(); + sound_count = g_assets->total_sound_count(); } - assert(g_game); + assert(g_logic); std::string mem_usage = g_platform->GetMemUsageInfo(); fprintf(stress_test_stats_file_, "%d,%.1f,%d,%d,%d,%d,%d,%s\n", static_cast_check_fit(GetRealTime()), avg, node_count, diff --git a/src/ballistica/media/media.cc b/src/ballistica/assets/assets.cc similarity index 79% rename from src/ballistica/media/media.cc rename to src/ballistica/assets/assets.cc index b0e280d5..8bcc5ee1 100644 --- a/src/ballistica/media/media.cc +++ b/src/ballistica/assets/assets.cc @@ -1,33 +1,33 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/media/media.h" +#include "ballistica/assets/assets.h" #if !BA_OSTYPE_WINDOWS #include #endif +#include "ballistica/assets/assets_server.h" +#include "ballistica/assets/component/collide_model.h" +#include "ballistica/assets/component/data.h" +#include "ballistica/assets/component/model.h" +#include "ballistica/assets/component/texture.h" +#include "ballistica/assets/data/sound_data.h" #include "ballistica/audio/audio_server.h" #include "ballistica/core/thread.h" -#include "ballistica/game/game.h" #include "ballistica/generic/timer.h" #include "ballistica/graphics/graphics_server.h" #include "ballistica/graphics/text/text_packer.h" -#include "ballistica/media/component/collide_model.h" -#include "ballistica/media/component/data.h" -#include "ballistica/media/component/model.h" -#include "ballistica/media/component/texture.h" -#include "ballistica/media/data/sound_data.h" -#include "ballistica/media/media_server.h" +#include "ballistica/logic/logic.h" #include "ballistica/python/python_sys.h" namespace ballistica { -// Debug printing. +// Debug printing: #define BA_SHOW_LOADS_UNLOADS 0 #define SHOW_PRUNING_INFO 0 -// Standard prune time for unused media: 10 minutes (1000ms * 60 * 10). -#define STANDARD_MEDIA_PRUNE_TIME 600000 +// Standard prune time for unused assets: 10 minutes (1000ms * 60 * 10). +#define STANDARD_ASSET_PRUNE_TIME 600000 // More aggressive prune time for dynamically-generated text-textures: 10 // seconds. @@ -35,59 +35,53 @@ namespace ballistica { #define QR_TEXTURE_PRUNE_TIME 10000 -// How long we should spend loading media in each runPendingLoads() call. +// How long we should spend loading assets in each runPendingLoads() call. #define PENDING_LOAD_PROCESS_TIME 5 -void Media::Init() { - // Just create our singleton. - assert(g_media == nullptr); - g_media = new Media(); -} - -Media::Media() { - media_paths_.emplace_back("ba_data"); +Assets::Assets() { + asset_paths_.emplace_back("ba_data"); for (bool& have_pending_load : have_pending_loads_) { have_pending_load = false; } } -void Media::LoadSystemTexture(SystemTextureID id, const char* name) { - assert(media_lists_locked_); +void Assets::LoadSystemTexture(SystemTextureID id, const char* name) { + assert(asset_lists_locked_); system_textures_.push_back(GetTextureData(name)); assert(system_textures_.size() == static_cast(id) + 1); } -void Media::LoadSystemCubeMapTexture(SystemCubeMapTextureID id, - const char* name) { - assert(media_lists_locked_); +void Assets::LoadSystemCubeMapTexture(SystemCubeMapTextureID id, + const char* name) { + assert(asset_lists_locked_); system_cube_map_textures_.push_back(GetCubeMapTextureData(name)); assert(system_cube_map_textures_.size() == static_cast(id) + 1); } -void Media::LoadSystemSound(SystemSoundID id, const char* name) { +void Assets::LoadSystemSound(SystemSoundID id, const char* name) { system_sounds_.push_back(GetSoundData(name)); assert(system_sounds_.size() == static_cast(id) + 1); } -void Media::LoadSystemData(SystemDataID id, const char* name) { +void Assets::LoadSystemData(SystemDataID id, const char* name) { system_datas_.push_back(GetDataData(name)); assert(system_datas_.size() == static_cast(id) + 1); } -void Media::LoadSystemModel(SystemModelID id, const char* name) { +void Assets::LoadSystemModel(SystemModelID id, const char* name) { system_models_.push_back(GetModelData(name)); assert(system_models_.size() == static_cast(id) + 1); } -void Media::LoadSystemMedia() { +void Assets::LoadSystemAssets() { assert(InLogicThread()); - assert(g_audio_server && g_media_server && g_graphics_server); + assert(g_audio_server && g_assets_server && g_graphics_server); assert(g_graphics_server && g_graphics_server->texture_compression_types_are_set()); assert(g_graphics && g_graphics_server->texture_quality_set()); // Just grab the lock once for all this stuff for efficiency. - MediaListsLock lock; + AssetListLock lock; // System textures: LoadSystemTexture(SystemTextureID::kUIAtlas, "uiAtlas"); @@ -286,26 +280,24 @@ void Media::LoadSystemMedia() { LoadSystemModel(SystemModelID::kWing, "wing"); // Hooray! - system_media_loaded_ = true; + system_assets_loaded_ = true; } -Media::~Media() = default; - -void Media::PrintLoadInfo() { +void Assets::PrintLoadInfo() { std::string s; char buffer[256]; int num = 1; // Need to lock lists while iterating over them. - MediaListsLock lock; - s = "Media load results: (all times in milliseconds):\n"; + AssetListLock lock; + s = "Assets load results: (all times in milliseconds):\n"; snprintf(buffer, sizeof(buffer), " %-50s %10s %10s", "FILE", "PRELOAD_TIME", "LOAD_TIME"); s += buffer; - Log(s, true, false); + Log(LogLevel::kInfo, s); millisecs_t total_preload_time = 0; millisecs_t total_load_time = 0; - assert(media_lists_locked_); + assert(asset_lists_locked_); for (auto&& i : models_) { millisecs_t preload_time = i.second->preload_time(); millisecs_t load_time = i.second->load_time(); @@ -315,10 +307,10 @@ void Media::PrintLoadInfo() { i.second->GetName().c_str(), static_cast_check_fit(preload_time), static_cast_check_fit(load_time)); - Log(buffer, true, false); + Log(LogLevel::kInfo, buffer); num++; } - assert(media_lists_locked_); + assert(asset_lists_locked_); for (auto&& i : collide_models_) { millisecs_t preload_time = i.second->preload_time(); millisecs_t load_time = i.second->load_time(); @@ -328,10 +320,10 @@ void Media::PrintLoadInfo() { i.second->GetName().c_str(), static_cast_check_fit(preload_time), static_cast_check_fit(load_time)); - Log(buffer, true, false); + Log(LogLevel::kInfo, buffer); num++; } - assert(media_lists_locked_); + assert(asset_lists_locked_); for (auto&& i : sounds_) { millisecs_t preload_time = i.second->preload_time(); millisecs_t load_time = i.second->load_time(); @@ -341,10 +333,10 @@ void Media::PrintLoadInfo() { i.second->GetName().c_str(), static_cast_check_fit(preload_time), static_cast_check_fit(load_time)); - Log(buffer, true, false); + Log(LogLevel::kInfo, buffer); num++; } - assert(media_lists_locked_); + assert(asset_lists_locked_); for (auto&& i : datas_) { millisecs_t preload_time = i.second->preload_time(); millisecs_t load_time = i.second->load_time(); @@ -354,10 +346,10 @@ void Media::PrintLoadInfo() { i.second->GetName().c_str(), static_cast_check_fit(preload_time), static_cast_check_fit(load_time)); - Log(buffer, true, false); + Log(LogLevel::kInfo, buffer); num++; } - assert(media_lists_locked_); + assert(asset_lists_locked_); for (auto&& i : textures_) { millisecs_t preload_time = i.second->preload_time(); millisecs_t load_time = i.second->load_time(); @@ -367,7 +359,7 @@ void Media::PrintLoadInfo() { i.second->file_name_full().c_str(), static_cast_check_fit(preload_time), static_cast_check_fit(load_time)); - Log(buffer, true, false); + Log(LogLevel::kInfo, buffer); num++; } snprintf(buffer, sizeof(buffer), @@ -375,99 +367,100 @@ void Media::PrintLoadInfo() { "(feeding data to OpenGL, etc): %i", static_cast(total_preload_time), static_cast(total_load_time)); - Log(buffer, true, false); + Log(LogLevel::kInfo, buffer); } -void Media::MarkAllMediaForLoad() { +void Assets::MarkAllAssetsForLoad() { assert(InLogicThread()); // Need to keep lists locked while iterating over them. - MediaListsLock m_lock; + AssetListLock m_lock; for (auto&& i : textures_) { if (!i.second->preloaded()) { - MediaComponentData::LockGuard lock(i.second.get()); - have_pending_loads_[static_cast(MediaType::kTexture)] = true; + AssetComponentData::LockGuard lock(i.second.get()); + have_pending_loads_[static_cast(AssetType::kTexture)] = true; MarkComponentForLoad(i.second.get()); } } for (auto&& i : text_textures_) { if (!i.second->preloaded()) { - MediaComponentData::LockGuard lock(i.second.get()); - have_pending_loads_[static_cast(MediaType::kTexture)] = true; + AssetComponentData::LockGuard lock(i.second.get()); + have_pending_loads_[static_cast(AssetType::kTexture)] = true; MarkComponentForLoad(i.second.get()); } } for (auto&& i : qr_textures_) { if (!i.second->preloaded()) { - MediaComponentData::LockGuard lock(i.second.get()); - have_pending_loads_[static_cast(MediaType::kTexture)] = true; + AssetComponentData::LockGuard lock(i.second.get()); + have_pending_loads_[static_cast(AssetType::kTexture)] = true; MarkComponentForLoad(i.second.get()); } } for (auto&& i : models_) { if (!i.second->preloaded()) { - MediaComponentData::LockGuard lock(i.second.get()); - have_pending_loads_[static_cast(MediaType::kModel)] = true; + AssetComponentData::LockGuard lock(i.second.get()); + have_pending_loads_[static_cast(AssetType::kModel)] = true; MarkComponentForLoad(i.second.get()); } } } // Call this from the graphics thread to immediately unload all -// media used by it. (for when GL context gets lost, etc). -void Media::UnloadRendererBits(bool do_textures, bool do_models) { +// assets used by it. (for when GL context gets lost, etc). +void Assets::UnloadRendererBits(bool do_textures, bool do_models) { assert(InGraphicsThread()); // need to keep lists locked while iterating over them.. - MediaListsLock m_lock; + AssetListLock m_lock; if (do_textures) { - assert(media_lists_locked_); + assert(asset_lists_locked_); for (auto&& i : textures_) { - MediaComponentData::LockGuard lock(i.second.get()); + AssetComponentData::LockGuard lock(i.second.get()); i.second->Unload(true); } for (auto&& i : text_textures_) { - MediaComponentData::LockGuard lock(i.second.get()); + AssetComponentData::LockGuard lock(i.second.get()); i.second->Unload(true); } for (auto&& i : qr_textures_) { - MediaComponentData::LockGuard lock(i.second.get()); + AssetComponentData::LockGuard lock(i.second.get()); i.second->Unload(true); } } if (do_models) { for (auto&& i : models_) { - MediaComponentData::LockGuard lock(i.second.get()); + AssetComponentData::LockGuard lock(i.second.get()); i.second->Unload(true); } } } -auto Media::GetModelData(const std::string& file_name) +auto Assets::GetModelData(const std::string& file_name) -> Object::Ref { return GetComponentData(file_name, &models_); } -auto Media::GetSoundData(const std::string& file_name) +auto Assets::GetSoundData(const std::string& file_name) -> Object::Ref { return GetComponentData(file_name, &sounds_); } -auto Media::GetDataData(const std::string& file_name) -> Object::Ref { +auto Assets::GetDataData(const std::string& file_name) + -> Object::Ref { return GetComponentData(file_name, &datas_); } -auto Media::GetCollideModelData(const std::string& file_name) +auto Assets::GetCollideModelData(const std::string& file_name) -> Object::Ref { return GetComponentData(file_name, &collide_models_); } template -auto Media::GetComponentData( +auto Assets::GetComponentData( const std::string& file_name, std::unordered_map >* c_list) -> Object::Ref { assert(InLogicThread()); - assert(media_lists_locked_); + assert(asset_lists_locked_); auto i = c_list->find(file_name); if (i != c_list->end()) { return Object::Ref(i->second.get()); @@ -475,8 +468,8 @@ auto Media::GetComponentData( auto d(Object::New(file_name)); (*c_list)[file_name] = d; { - MediaComponentData::LockGuard lock(d.get()); - have_pending_loads_[static_cast(d->GetMediaType())] = true; + AssetComponentData::LockGuard lock(d.get()); + have_pending_loads_[static_cast(d->GetAssetType())] = true; MarkComponentForLoad(d.get()); } d->set_last_used_time(GetRealTime()); @@ -484,9 +477,9 @@ auto Media::GetComponentData( } } -auto Media::GetTextureData(TextPacker* packer) -> Object::Ref { +auto Assets::GetTextureData(TextPacker* packer) -> Object::Ref { assert(InLogicThread()); - assert(media_lists_locked_); + assert(asset_lists_locked_); const std::string& hash(packer->hash()); auto i = text_textures_.find(hash); if (i != text_textures_.end()) { @@ -495,8 +488,8 @@ auto Media::GetTextureData(TextPacker* packer) -> Object::Ref { auto d(Object::New(packer)); text_textures_[hash] = d; { - MediaComponentData::LockGuard lock(d.get()); - have_pending_loads_[static_cast(d->GetMediaType())] = true; + AssetComponentData::LockGuard lock(d.get()); + have_pending_loads_[static_cast(d->GetAssetType())] = true; MarkComponentForLoad(d.get()); } d->set_last_used_time(GetRealTime()); @@ -504,10 +497,10 @@ auto Media::GetTextureData(TextPacker* packer) -> Object::Ref { } } -auto Media::GetTextureDataQRCode(const std::string& url) +auto Assets::GetTextureDataQRCode(const std::string& url) -> Object::Ref { assert(InLogicThread()); - assert(media_lists_locked_); + assert(asset_lists_locked_); auto i = qr_textures_.find(url); if (i != qr_textures_.end()) { return Object::Ref(i->second.get()); @@ -515,8 +508,8 @@ auto Media::GetTextureDataQRCode(const std::string& url) auto d(Object::New(url)); qr_textures_[url] = d; { - MediaComponentData::LockGuard lock(d.get()); - have_pending_loads_[static_cast(d->GetMediaType())] = true; + AssetComponentData::LockGuard lock(d.get()); + have_pending_loads_[static_cast(d->GetAssetType())] = true; MarkComponentForLoad(d.get()); } d->set_last_used_time(GetRealTime()); @@ -526,10 +519,10 @@ auto Media::GetTextureDataQRCode(const std::string& url) // Eww can't recycle GetComponent here since we need extra stuff (tex-type arg) // ..should fix. -auto Media::GetCubeMapTextureData(const std::string& file_name) +auto Assets::GetCubeMapTextureData(const std::string& file_name) -> Object::Ref { assert(InLogicThread()); - assert(media_lists_locked_); + assert(asset_lists_locked_); auto i = textures_.find(file_name); if (i != textures_.end()) { return Object::Ref(i->second.get()); @@ -538,8 +531,8 @@ auto Media::GetCubeMapTextureData(const std::string& file_name) TextureMinQuality::kLow)); textures_[file_name] = d; { - MediaComponentData::LockGuard lock(d.get()); - have_pending_loads_[static_cast(d->GetMediaType())] = true; + AssetComponentData::LockGuard lock(d.get()); + have_pending_loads_[static_cast(d->GetAssetType())] = true; MarkComponentForLoad(d.get()); } d->set_last_used_time(GetRealTime()); @@ -549,10 +542,10 @@ auto Media::GetCubeMapTextureData(const std::string& file_name) // Eww; can't recycle GetComponent here since we need extra stuff (quality // settings, etc). Should fix. -auto Media::GetTextureData(const std::string& file_name) +auto Assets::GetTextureData(const std::string& file_name) -> Object::Ref { assert(InLogicThread()); - assert(media_lists_locked_); + assert(asset_lists_locked_); auto i = textures_.find(file_name); if (i != textures_.end()) { return Object::Ref(i->second.get()); @@ -594,8 +587,8 @@ auto Media::GetTextureData(const std::string& file_name) auto d(Object::New(file_name, TextureType::k2D, min_quality)); textures_[file_name] = d; { - MediaComponentData::LockGuard lock(d.get()); - have_pending_loads_[static_cast(d->GetMediaType())] = true; + AssetComponentData::LockGuard lock(d.get()); + have_pending_loads_[static_cast(d->GetAssetType())] = true; MarkComponentForLoad(d.get()); } d->set_last_used_time(GetRealTime()); @@ -603,7 +596,7 @@ auto Media::GetTextureData(const std::string& file_name) } } -void Media::MarkComponentForLoad(MediaComponentData* c) { +void Assets::MarkComponentForLoad(AssetComponentData* c) { assert(InLogicThread()); assert(c->locked()); @@ -614,111 +607,110 @@ void Media::MarkComponentForLoad(MediaComponentData* c) { // once it makes it back to us we can delete the ref (in // ClearPendingLoadsDoneList) - auto media_ptr = new Object::Ref(c); - g_media_server->thread()->PushRunnable( - Object::NewDeferred(media_ptr)); + auto asset_ref_ptr = new Object::Ref(c); + g_assets_server->PushPendingPreload(asset_ref_ptr); } #pragma clang diagnostic push #pragma ide diagnostic ignored "UnreachableCode" #pragma ide diagnostic ignored "ConstantFunctionResult" -auto Media::GetModelPendingLoadCount() -> int { - if (!have_pending_loads_[static_cast(MediaType::kModel)]) { +auto Assets::GetModelPendingLoadCount() -> int { + if (!have_pending_loads_[static_cast(AssetType::kModel)]) { return 0; } - MediaListsLock lock; - int total = GetComponentPendingLoadCount(&models_, MediaType::kModel); + AssetListLock lock; + int total = GetComponentPendingLoadCount(&models_, AssetType::kModel); if (total == 0) { // When fully loaded, stop counting. - have_pending_loads_[static_cast(MediaType::kModel)] = false; + have_pending_loads_[static_cast(AssetType::kModel)] = false; } return total; } -auto Media::GetTexturePendingLoadCount() -> int { - if (!have_pending_loads_[static_cast(MediaType::kTexture)]) { +auto Assets::GetTexturePendingLoadCount() -> int { + if (!have_pending_loads_[static_cast(AssetType::kTexture)]) { return 0; } - MediaListsLock lock; + AssetListLock lock; int total = - (GetComponentPendingLoadCount(&textures_, MediaType::kTexture) - + GetComponentPendingLoadCount(&text_textures_, MediaType::kTexture) - + GetComponentPendingLoadCount(&qr_textures_, MediaType::kTexture)); + (GetComponentPendingLoadCount(&textures_, AssetType::kTexture) + + GetComponentPendingLoadCount(&text_textures_, AssetType::kTexture) + + GetComponentPendingLoadCount(&qr_textures_, AssetType::kTexture)); if (total == 0) { // When fully loaded, stop counting. - have_pending_loads_[static_cast(MediaType::kTexture)] = false; + have_pending_loads_[static_cast(AssetType::kTexture)] = false; } return total; } -auto Media::GetSoundPendingLoadCount() -> int { - if (!have_pending_loads_[static_cast(MediaType::kSound)]) { +auto Assets::GetSoundPendingLoadCount() -> int { + if (!have_pending_loads_[static_cast(AssetType::kSound)]) { return 0; } - MediaListsLock lock; - int total = GetComponentPendingLoadCount(&sounds_, MediaType::kSound); + AssetListLock lock; + int total = GetComponentPendingLoadCount(&sounds_, AssetType::kSound); if (total == 0) { // When fully loaded, stop counting. - have_pending_loads_[static_cast(MediaType::kSound)] = false; + have_pending_loads_[static_cast(AssetType::kSound)] = false; } return total; } -auto Media::GetDataPendingLoadCount() -> int { - if (!have_pending_loads_[static_cast(MediaType::kData)]) { +auto Assets::GetDataPendingLoadCount() -> int { + if (!have_pending_loads_[static_cast(AssetType::kData)]) { return 0; } - MediaListsLock lock; - int total = GetComponentPendingLoadCount(&datas_, MediaType::kData); + AssetListLock lock; + int total = GetComponentPendingLoadCount(&datas_, AssetType::kData); if (total == 0) { // When fully loaded, stop counting. - have_pending_loads_[static_cast(MediaType::kData)] = false; + have_pending_loads_[static_cast(AssetType::kData)] = false; } return total; } -auto Media::GetCollideModelPendingLoadCount() -> int { - if (!have_pending_loads_[static_cast(MediaType::kCollideModel)]) { +auto Assets::GetCollideModelPendingLoadCount() -> int { + if (!have_pending_loads_[static_cast(AssetType::kCollideModel)]) { return 0; } - MediaListsLock lock; + AssetListLock lock; int total = - GetComponentPendingLoadCount(&collide_models_, MediaType::kCollideModel); + GetComponentPendingLoadCount(&collide_models_, AssetType::kCollideModel); if (total == 0) { // When fully loaded, stop counting. - have_pending_loads_[static_cast(MediaType::kCollideModel)] = false; + have_pending_loads_[static_cast(AssetType::kCollideModel)] = false; } return total; } #pragma clang diagnostic pop -auto Media::GetGraphicalPendingLoadCount() -> int { - // Each of these calls lock the media-lists so we don't. +auto Assets::GetGraphicalPendingLoadCount() -> int { + // Each of these calls lock the asset-lists so we don't. return GetModelPendingLoadCount() + GetTexturePendingLoadCount(); } -auto Media::GetPendingLoadCount() -> int { - // Each of these calls lock the media-lists so we don't. +auto Assets::GetPendingLoadCount() -> int { + // Each of these calls lock the asset-lists so we don't. return GetModelPendingLoadCount() + GetTexturePendingLoadCount() + GetDataPendingLoadCount() + GetSoundPendingLoadCount() + GetCollideModelPendingLoadCount(); } template -auto Media::GetComponentPendingLoadCount( - std::unordered_map >* t_list, MediaType type) +auto Assets::GetComponentPendingLoadCount( + std::unordered_map >* t_list, AssetType type) -> int { assert(InLogicThread()); - assert(media_lists_locked_); + assert(asset_lists_locked_); int c = 0; for (auto&& i : (*t_list)) { if (i.second.exists()) { if (i.second->TryLock()) { - MediaComponentData::LockGuard lock( - i.second.get(), MediaComponentData::LockGuard::Type::kInheritLock); + AssetComponentData::LockGuard lock( + i.second.get(), AssetComponentData::LockGuard::Type::kInheritLock); if (!i.second->loaded()) { c++; } @@ -731,26 +723,26 @@ auto Media::GetComponentPendingLoadCount( } // Runs the pending loads that need to run from the audio thread. -auto Media::RunPendingAudioLoads() -> bool { +auto Assets::RunPendingAudioLoads() -> bool { assert(InAudioThread()); return RunPendingLoadList(&pending_loads_sounds_); } // Runs the pending loads that need to run from the graphics thread. -auto Media::RunPendingGraphicsLoads() -> bool { +auto Assets::RunPendingGraphicsLoads() -> bool { assert(InGraphicsThread()); return RunPendingLoadList(&pending_loads_graphics_); } // Runs the pending loads that run in the main thread. Also clears the list of // done loads. -auto Media::RunPendingLoadsLogicThread() -> bool { +auto Assets::RunPendingLoadsLogicThread() -> bool { assert(InLogicThread()); return RunPendingLoadList(&pending_loads_other_); } template -auto Media::RunPendingLoadList(std::vector*>* c_list) -> bool { +auto Assets::RunPendingLoadList(std::vector*>* c_list) -> bool { bool flush = false; millisecs_t starttime = GetRealTime(); @@ -818,40 +810,40 @@ auto Media::RunPendingLoadList(std::vector*>* c_list) -> bool { } } - // if we dumped anything on the pending loads done list, shake the game thread - // to tell it to kill the reference.. + // if we dumped anything on the pending loads done list, shake the logic + // thread to tell it to kill the reference.. if (!l_finished.empty()) { - assert(g_game); - g_game->PushHavePendingLoadsDoneCall(); + assert(g_logic); + g_logic->PushHavePendingLoadsDoneCall(); } return (!l.empty()); } -void Media::Prune(int level) { +void Assets::Prune(int level) { assert(InLogicThread()); millisecs_t current_time = GetRealTime(); // need lists locked while accessing/modifying them - MediaListsLock lock; + AssetListLock lock; // we can specify level for more aggressive pruning (during memory warnings // and whatnot) - millisecs_t standard_media_prune_time = STANDARD_MEDIA_PRUNE_TIME; + millisecs_t standard_asset_prune_time = STANDARD_ASSET_PRUNE_TIME; millisecs_t text_texture_prune_time = TEXT_TEXTURE_PRUNE_TIME; millisecs_t qr_texture_prune_time = QR_TEXTURE_PRUNE_TIME; switch (level) { case 1: - standard_media_prune_time = 120000; // 2 min + standard_asset_prune_time = 120000; // 2 min text_texture_prune_time = 1000; // 1 sec qr_texture_prune_time = 1000; // 1 sec break; case 2: - standard_media_prune_time = 30000; // 30 sec + standard_asset_prune_time = 30000; // 30 sec text_texture_prune_time = 1000; // 1 sec qr_texture_prune_time = 1000; // 1 sec break; case 3: - standard_media_prune_time = 5000; // 5 sec + standard_asset_prune_time = 5000; // 5 sec text_texture_prune_time = 1000; // 1 sec qr_texture_prune_time = 1000; // 1 sec break; @@ -859,11 +851,11 @@ void Media::Prune(int level) { break; } - std::vector*> graphics_thread_unloads; - std::vector*> audio_thread_unloads; + std::vector*> graphics_thread_unloads; + std::vector*> audio_thread_unloads; #if SHOW_PRUNING_INFO - assert(media_lists_locked_); + assert(asset_lists_locked_); int old_texture_count = textures_.size(); int old_text_texture_count = text_textures_.size(); int old_qr_texture_count = qr_textures_.size(); @@ -873,13 +865,13 @@ void Media::Prune(int level) { #endif // SHOW_PRUNING_INFO // prune textures.. - assert(media_lists_locked_); + assert(asset_lists_locked_); for (auto i = textures_.begin(); i != textures_.end();) { TextureData* texture_data = i->second.get(); // attempt to prune if there are no references remaining except our own and // its been a while since it was used if (current_time - texture_data->last_used_time() - > standard_media_prune_time + > standard_asset_prune_time && (texture_data->object_strong_ref_count() <= 1)) { // if its preloaded/loaded we need to ask the graphics thread to unload it // first @@ -887,7 +879,7 @@ void Media::Prune(int level) { // allocate a reference to keep this texture_data alive while the unload // is happening graphics_thread_unloads.push_back( - new Object::Ref(texture_data)); + new Object::Ref(texture_data)); auto i_next = i; i_next++; textures_.erase(i); @@ -901,7 +893,7 @@ void Media::Prune(int level) { // prune text-textures more aggressively since we may generate lots of them // FIXME - we may want to prune based on total number of these instead of // time.. - assert(media_lists_locked_); + assert(asset_lists_locked_); for (auto i = text_textures_.begin(); i != text_textures_.end();) { TextureData* texture_data = i->second.get(); // attempt to prune if there are no references remaining except our own and @@ -914,7 +906,7 @@ void Media::Prune(int level) { // allocate a reference to keep this texture_data alive while the unload // is happening graphics_thread_unloads.push_back( - new Object::Ref(texture_data)); + new Object::Ref(texture_data)); auto i_next = i; i_next++; text_textures_.erase(i); @@ -926,7 +918,7 @@ void Media::Prune(int level) { } // prune textures - assert(media_lists_locked_); + assert(asset_lists_locked_); for (auto i = qr_textures_.begin(); i != qr_textures_.end();) { TextureData* texture_data = i->second.get(); // attempt to prune if there are no references remaining except our own and @@ -939,7 +931,7 @@ void Media::Prune(int level) { // allocate a reference to keep this texture_data alive while the unload // is happening graphics_thread_unloads.push_back( - new Object::Ref(texture_data)); + new Object::Ref(texture_data)); auto i_next = i; i_next++; qr_textures_.erase(i); @@ -951,12 +943,12 @@ void Media::Prune(int level) { } // prune models.. - assert(media_lists_locked_); + assert(asset_lists_locked_); for (auto i = models_.begin(); i != models_.end();) { ModelData* model_data = i->second.get(); // attempt to prune if there are no references remaining except our own and // its been a while since it was used - if (current_time - model_data->last_used_time() > standard_media_prune_time + if (current_time - model_data->last_used_time() > standard_asset_prune_time && (model_data->object_strong_ref_count() <= 1)) { // if its preloaded/loaded we need to ask the graphics thread to unload it // first @@ -964,7 +956,7 @@ void Media::Prune(int level) { // allocate a reference to keep this model_data alive while the unload // is happening graphics_thread_unloads.push_back( - new Object::Ref(model_data)); + new Object::Ref(model_data)); auto i_next = i; i_next++; models_.erase(i); @@ -976,16 +968,16 @@ void Media::Prune(int level) { } // Prune collide-models. - assert(media_lists_locked_); + assert(asset_lists_locked_); for (auto i = collide_models_.begin(); i != collide_models_.end();) { CollideModelData* collide_model_data = i->second.get(); // attempt to prune if there are no references remaining except our own and - // its been a while since it was used (unlike other media we never prune + // its been a while since it was used (unlike other assets we never prune // these if there's still references to them if (current_time - collide_model_data->last_used_time() - > standard_media_prune_time + > standard_asset_prune_time && (collide_model_data->object_strong_ref_count() <= 1)) { - // we can unload it immediately since that happens in the game thread... + // we can unload it immediately since that happens in the logic thread... collide_model_data->Unload(); auto i_next = i; ++i_next; @@ -1001,13 +993,13 @@ void Media::Prune(int level) { // sounds are still in active use by OpenAL and ensure references exist for // them somewhere while that is the case if (explicit_bool(false)) { - assert(media_lists_locked_); + assert(asset_lists_locked_); for (auto i = sounds_.begin(); i != sounds_.end();) { SoundData* sound_data = i->second.get(); // Attempt to prune if there are no references remaining except our own // and its been a while since it was used. if (current_time - sound_data->last_used_time() - > standard_media_prune_time + > standard_asset_prune_time && (sound_data->object_strong_ref_count() <= 1)) { // If its preloaded/loaded we need to ask the graphics thread to unload // it first. @@ -1015,7 +1007,7 @@ void Media::Prune(int level) { // Allocate a reference to keep this sound_data alive while the unload // is happening. audio_thread_unloads.push_back( - new Object::Ref(sound_data)); + new Object::Ref(sound_data)); auto i_next = i; i_next++; sounds_.erase(i); @@ -1035,7 +1027,7 @@ void Media::Prune(int level) { } #if SHOW_PRUNING_INFO - assert(media_lists_locked_); + assert(asset_lists_locked_); if (textures_.size() != old_texture_count) { Log("Textures pruned from " + std::to_string(old_texture_count) + " to " + std::to_string(textures_.size())); @@ -1063,7 +1055,7 @@ void Media::Prune(int level) { #endif // SHOW_PRUNING_INFO } -auto Media::FindMediaFile(FileType type, const std::string& name) +auto Assets::FindAssetFile(FileType type, const std::string& name) -> std::string { std::string file_out; @@ -1133,9 +1125,9 @@ auto Media::FindMediaFile(FileType type, const std::string& name) break; } - const std::vector& media_paths_used = media_paths_; + const std::vector& asset_paths_used = asset_paths_; - for (auto&& i : media_paths_used) { + for (auto&& i : asset_paths_used) { struct BA_STAT stats {}; file_out = i + "/" + prefix + name + ext; // NOLINT int result; @@ -1159,27 +1151,29 @@ auto Media::FindMediaFile(FileType type, const std::string& name) // We wanna fail gracefully for some types. if (type == FileType::kSound && name != "blank") { - Log("Unable to load audio: '" + name + "'; trying fallback..."); - return FindMediaFile(type, "blank"); + Log(LogLevel::kError, + "Unable to load audio: '" + name + "'; trying fallback..."); + return FindAssetFile(type, "blank"); } else if (type == FileType::kTexture && name != "white") { - Log("Unable to load texture: '" + name + "'; trying fallback..."); - return FindMediaFile(type, "white"); + Log(LogLevel::kError, + "Unable to load texture: '" + name + "'; trying fallback..."); + return FindAssetFile(type, "white"); } - throw Exception("Can't find media: \"" + name + "\""); + throw Exception("Can't find asset: \"" + name + "\""); // return file_out; } -void Media::AddPendingLoad(Object::Ref* c) { - switch ((**c).GetMediaType()) { - case MediaType::kTexture: - case MediaType::kModel: { +void Assets::AddPendingLoad(Object::Ref* c) { + switch ((**c).GetAssetType()) { + case AssetType::kTexture: + case AssetType::kModel: { // Tell the graphics thread there's pending loads... std::scoped_lock lock(pending_load_list_mutex_); pending_loads_graphics_.push_back(c); break; } - case MediaType::kSound: { + case AssetType::kSound: { // Tell the audio thread there's pending loads. { std::scoped_lock lock(pending_load_list_mutex_); @@ -1189,69 +1183,56 @@ void Media::AddPendingLoad(Object::Ref* c) { break; } default: { - // Tell the game thread there's pending loads. + // Tell the logic thread there's pending loads. { std::scoped_lock lock(pending_load_list_mutex_); pending_loads_other_.push_back(c); } - g_game->PushHavePendingLoadsCall(); + g_logic->PushHavePendingLoadsCall(); break; } } } -void Media::ClearPendingLoadsDoneList() { +void Assets::ClearPendingLoadsDoneList() { assert(InLogicThread()); std::scoped_lock lock(pending_load_list_mutex_); // Our explicitly-allocated reference pointer has made it back to us here in - // the game thread. + // the logic thread. // We can now kill the reference knowing that it's safe for this component // to die at any time (anyone needing it to be alive now should be holding a // reference themselves). - for (Object::Ref* i : pending_loads_done_) { + for (Object::Ref* i : pending_loads_done_) { delete i; } pending_loads_done_.clear(); } -void Media::PreloadRunnable::Run() { - assert(InMediaThread()); - - // add our pointer to one of the preload lists and shake our preload thread to - // wake it up - if ((**c).GetMediaType() == MediaType::kSound) { - g_media_server->pending_preloads_audio_.push_back(c); - } else { - g_media_server->pending_preloads_.push_back(c); - } - g_media_server->process_timer_->SetLength(0); -} - -void Media::AddPackage(const std::string& name, const std::string& path) { +void Assets::AddPackage(const std::string& name, const std::string& path) { // we don't protect package-path access so make sure its always from here.. assert(InLogicThread()); #if BA_DEBUG_BUILD if (packages_.find(name) != packages_.end()) { - Log("WARNING: adding duplicate package: '" + name + "'"); + Log(LogLevel::kWarning, "adding duplicate package: '" + name + "'"); } #endif // BA_DEBUG_BUILD packages_[name] = path; } -Media::MediaListsLock::MediaListsLock() { +Assets::AssetListLock::AssetListLock() { BA_DEBUG_FUNCTION_TIMER_BEGIN(); - g_media->media_lists_mutex_.lock(); - assert(!g_media->media_lists_locked_); - g_media->media_lists_locked_ = true; + g_assets->asset_lists_mutex_.lock(); + assert(!g_assets->asset_lists_locked_); + g_assets->asset_lists_locked_ = true; BA_DEBUG_FUNCTION_TIMER_END_THREAD(20); } -Media::MediaListsLock::~MediaListsLock() { - assert(g_media->media_lists_locked_); - g_media->media_lists_locked_ = false; - g_media->media_lists_mutex_.unlock(); +Assets::AssetListLock::~AssetListLock() { + assert(g_assets->asset_lists_locked_); + g_assets->asset_lists_locked_ = false; + g_assets->asset_lists_mutex_.unlock(); } } // namespace ballistica diff --git a/src/ballistica/media/media.h b/src/ballistica/assets/assets.h similarity index 65% rename from src/ballistica/media/media.h rename to src/ballistica/assets/assets.h index bdb3cd8a..a23bcefe 100644 --- a/src/ballistica/media/media.h +++ b/src/ballistica/assets/assets.h @@ -1,29 +1,26 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_MEDIA_MEDIA_H_ -#define BALLISTICA_MEDIA_MEDIA_H_ +#ifndef BALLISTICA_ASSETS_ASSETS_H_ +#define BALLISTICA_ASSETS_ASSETS_H_ #include #include #include #include -#include "ballistica/core/context.h" #include "ballistica/core/object.h" -#include "ballistica/generic/runnable.h" namespace ballistica { -/// Global media wrangling class. -class Media { +/// Global assets wrangling class. +class Assets { public: - static void Init(); - ~Media(); + Assets(); - /// Handy function to try to return a bit of media from a std::unordered_map + /// Handy function to try to return an asset from a std::unordered_map /// of weak-refs, loading/adding it if need be. template - static auto GetMedia( + static auto GetAsset( std::unordered_map >* list, const std::string& name, Scene* scene) -> Object::Ref { assert(InLogicThread()); @@ -43,10 +40,10 @@ class Media { } } - void AddPackage(const std::string& name, const std::string& path); - void Prune(int level = 0); + auto AddPackage(const std::string& name, const std::string& path) -> void; + auto Prune(int level = 0) -> void; - /// Finish loading any media that has been preloaded but still needs to be + /// Finish loading any assets that have been preloaded but still need to be /// loaded by the proper thread. auto RunPendingLoadsLogicThread() -> bool; @@ -55,26 +52,25 @@ class Media { /// Return true if graphics loads remain to be done. auto RunPendingGraphicsLoads() -> bool; - void ClearPendingLoadsDoneList(); + auto ClearPendingLoadsDoneList() -> void; template auto RunPendingLoadList(std::vector*>* cList) -> bool; /// This function takes a newly allocated pointer which /// is deleted once the load is completed. - void AddPendingLoad(Object::Ref* c); - struct PreloadRunnable; + auto AddPendingLoad(Object::Ref* c) -> void; enum class FileType { kModel, kCollisionModel, kTexture, kSound, kData }; - auto FindMediaFile(FileType fileType, const std::string& file_in) + auto FindAssetFile(FileType fileType, const std::string& file_in) -> std::string; /// Unload renderer-specific bits only (gl display lists, etc) - used when /// recreating/adjusting the renderer. - void UnloadRendererBits(bool textures, bool models); + auto UnloadRendererBits(bool textures, bool models) -> void; - /// Should be called from the game thread after UnloadRendererBits(); - /// kicks off bg loads for all existing unloaded media. - void MarkAllMediaForLoad(); - void PrintLoadInfo(); + /// Should be called from the logic thread after UnloadRendererBits(); + /// kicks off bg loads for all existing unloaded assets. + auto MarkAllAssetsForLoad() -> void; + auto PrintLoadInfo() -> void; auto GetModelPendingLoadCount() -> int; auto GetTexturePendingLoadCount() -> int; @@ -89,13 +85,13 @@ class Media { auto GetPendingLoadCount() -> int; /// You must hold one of these locks while calling Get*Data() below. - class MediaListsLock { + class AssetListLock { public: - MediaListsLock(); - ~MediaListsLock(); + AssetListLock(); + ~AssetListLock(); }; - /// Load/cache media (make sure you hold a MediaListsLock). + /// Load/cache assets (make sure you hold a AssetListLock). auto GetTextureData(const std::string& file_name) -> Object::Ref; auto GetTextureData(TextPacker* packer) -> Object::Ref; auto GetTextureDataQRCode(const std::string& file_name) @@ -110,32 +106,32 @@ class Media { // Get system assets. auto GetTexture(SystemTextureID id) -> TextureData* { - BA_PRECONDITION_FATAL(system_media_loaded_); // Revert to assert later. + BA_PRECONDITION_FATAL(system_assets_loaded_); // Revert to assert later. assert(InLogicThread()); assert(static_cast(id) < system_textures_.size()); return system_textures_[static_cast(id)].get(); } auto GetCubeMapTexture(SystemCubeMapTextureID id) -> TextureData* { - BA_PRECONDITION_FATAL(system_media_loaded_); // Revert to assert later. + BA_PRECONDITION_FATAL(system_assets_loaded_); // Revert to assert later. assert(InLogicThread()); assert(static_cast(id) < system_cube_map_textures_.size()); return system_cube_map_textures_[static_cast(id)].get(); } auto GetSound(SystemSoundID id) -> SoundData* { - BA_PRECONDITION_FATAL(system_media_loaded_); // Revert to assert later. + BA_PRECONDITION_FATAL(system_assets_loaded_); // Revert to assert later. assert(InLogicThread()); assert(static_cast(id) < system_sounds_.size()); return system_sounds_[static_cast(id)].get(); } auto GetModel(SystemModelID id) -> ModelData* { - BA_PRECONDITION_FATAL(system_media_loaded_); // Revert to assert later. + BA_PRECONDITION_FATAL(system_assets_loaded_); // Revert to assert later. assert(InLogicThread()); assert(static_cast(id) < system_models_.size()); return system_models_[static_cast(id)].get(); } - /// Load up hard-coded media for interface, etc. - void LoadSystemMedia(); + /// Load up hard-coded assets for interface, etc. + auto LoadSystemAssets() -> void; auto total_model_count() const -> uint32_t { return static_cast(models_.size()); @@ -150,24 +146,19 @@ class Media { auto total_collide_model_count() const -> uint32_t { return static_cast(collide_models_.size()); } - struct PreloadRunnable : public Runnable { - explicit PreloadRunnable(Object::Ref* c_in) : c(c_in) {} - void Run() override; - Object::Ref* c; - }; private: - Media(); - static void MarkComponentForLoad(MediaComponentData* c); - void LoadSystemTexture(SystemTextureID id, const char* name); - void LoadSystemCubeMapTexture(SystemCubeMapTextureID id, const char* name); - void LoadSystemSound(SystemSoundID id, const char* name); - void LoadSystemData(SystemDataID id, const char* name); - void LoadSystemModel(SystemModelID id, const char* name); + static auto MarkComponentForLoad(AssetComponentData* c) -> void; + auto LoadSystemTexture(SystemTextureID id, const char* name) -> void; + auto LoadSystemCubeMapTexture(SystemCubeMapTextureID id, const char* name) + -> void; + auto LoadSystemSound(SystemSoundID id, const char* name) -> void; + auto LoadSystemData(SystemDataID id, const char* name) -> void; + auto LoadSystemModel(SystemModelID id, const char* name) -> void; template auto GetComponentPendingLoadCount( - std::unordered_map >* t_list, MediaType type) + std::unordered_map >* t_list, AssetType type) -> int; template @@ -176,26 +167,26 @@ class Media { std::unordered_map >* c_list) -> Object::Ref; - std::vector media_paths_; - bool have_pending_loads_[static_cast(MediaType::kLast)]{}; + std::vector asset_paths_; + bool have_pending_loads_[static_cast(AssetType::kLast)]{}; std::unordered_map packages_; - // For use by MediaListsLock; don't manually acquire - std::mutex media_lists_mutex_; + // For use by AssetListLock; don't manually acquire + std::mutex asset_lists_mutex_; - // Will be true while a MediaListsLock exists. Good to debug-verify this - // during any media list access. - bool media_lists_locked_{}; + // Will be true while a AssetListLock exists. Good to debug-verify this + // during any asset list access. + bool asset_lists_locked_{}; - // 'hard-wired' internal media - bool system_media_loaded_{}; + // 'hard-wired' internal assets + bool system_assets_loaded_{}; std::vector > system_textures_; std::vector > system_cube_map_textures_; std::vector > system_sounds_; std::vector > system_datas_; std::vector > system_models_; - // All existing media by filename (including internal). + // All existing assets by filename (including internal). std::unordered_map > textures_; std::unordered_map > text_textures_; std::unordered_map > qr_textures_; @@ -207,13 +198,13 @@ class Media { // Components that have been preloaded but need to be loaded. std::mutex pending_load_list_mutex_; - std::vector*> pending_loads_graphics_; - std::vector*> pending_loads_sounds_; - std::vector*> pending_loads_datas_; - std::vector*> pending_loads_other_; - std::vector*> pending_loads_done_; + std::vector*> pending_loads_graphics_; + std::vector*> pending_loads_sounds_; + std::vector*> pending_loads_datas_; + std::vector*> pending_loads_other_; + std::vector*> pending_loads_done_; }; } // namespace ballistica -#endif // BALLISTICA_MEDIA_MEDIA_H_ +#endif // BALLISTICA_ASSETS_ASSETS_H_ diff --git a/src/ballistica/media/media_server.cc b/src/ballistica/assets/assets_server.cc similarity index 68% rename from src/ballistica/media/media_server.cc rename to src/ballistica/assets/assets_server.cc index 5592b1b6..81168ff9 100644 --- a/src/ballistica/media/media_server.cc +++ b/src/ballistica/assets/assets_server.cc @@ -1,34 +1,54 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/media/media_server.h" +#include "ballistica/assets/assets_server.h" +#include "ballistica/assets/assets.h" +#include "ballistica/assets/data/asset_component_data.h" #include "ballistica/core/thread.h" #include "ballistica/generic/huffman.h" #include "ballistica/generic/timer.h" #include "ballistica/generic/utils.h" #include "ballistica/graphics/graphics_server.h" -#include "ballistica/media/data/media_component_data.h" -#include "ballistica/media/media.h" namespace ballistica { -MediaServer::MediaServer(Thread* thread) - : thread_(thread), - writing_replay_(false), - replay_message_bytes_(0), - replays_broken_(false), - replay_out_file_(nullptr) { - assert(g_media_server == nullptr); - g_media_server = this; +AssetsServer::AssetsServer() { + // We're a singleton; make sure we don't already exist. + assert(g_assets_server == nullptr); - // get our thread to give us periodic processing time... - process_timer_ = this->thread()->NewTimer( - 1000, true, NewLambdaRunnable([this] { Process(); })); + // Spin up our thread. + thread_ = new Thread(ThreadTag::kAssets); + g_app->pausable_threads.push_back(thread_); } -MediaServer::~MediaServer() = default; +auto AssetsServer::OnAppStart() -> void { + thread_->PushCallSynchronous([this] { OnAppStartInThread(); }); +} -void MediaServer::PushBeginWriteReplayCall() { +auto AssetsServer::OnAppStartInThread() -> void { + assert(InAssetsThread()); + // get our thread to give us periodic processing time... + process_timer_ = + thread()->NewTimer(1000, true, NewLambdaRunnable([this] { Process(); })); +} + +auto AssetsServer::PushPendingPreload( + Object::Ref* asset_ref_ptr) -> void { + thread()->PushCall([this, asset_ref_ptr] { + assert(InAssetsThread()); + + // Add our pointer to one of the preload lists and shake our preload thread + // to wake it up + if ((**asset_ref_ptr).GetAssetType() == AssetType::kSound) { + pending_preloads_audio_.push_back(asset_ref_ptr); + } else { + pending_preloads_.push_back(asset_ref_ptr); + } + process_timer_->SetLength(0); + }); +} + +void AssetsServer::PushBeginWriteReplayCall() { thread()->PushCall([this] { if (replays_broken_) { return; @@ -37,7 +57,8 @@ void MediaServer::PushBeginWriteReplayCall() { // we only allow writing one replay at once; make sure that's actually the // case if (writing_replay_) { - Log("MediaServer got BeginWriteReplayCall while already writing"); + Log(LogLevel::kError, + "AssetsServer got BeginWriteReplayCall while already writing"); WriteReplayMessages(); if (replay_out_file_) { fclose(replay_out_file_); @@ -56,7 +77,8 @@ void MediaServer::PushBeginWriteReplayCall() { replay_bytes_written_ = 0; if (!replay_out_file_) { - Log("ERROR: unable to open output-stream file: '" + file_path + "'"); + Log(LogLevel::kError, + "unable to open output-stream file: '" + file_path + "'"); } else { // write file id and protocol-version // NOTE - we always write replays in our host protocol version @@ -67,19 +89,20 @@ void MediaServer::PushBeginWriteReplayCall() { || (fwrite(&version, sizeof(version), 1, replay_out_file_) != 1)) { fclose(replay_out_file_); replay_out_file_ = nullptr; - Log("error writing replay file header: " - + g_platform->GetErrnoString()); + Log(LogLevel::kError, "error writing replay file header: " + + g_platform->GetErrnoString()); } replay_bytes_written_ = 5; } // trigger our process timer to go off immediately // (we may need to wake it up) - g_media_server->process_timer_->SetLength(0); + g_assets_server->process_timer_->SetLength(0); }); } -void MediaServer::PushAddMessageToReplayCall(const std::vector& data) { +void AssetsServer::PushAddMessageToReplayCall( + const std::vector& data) { thread()->PushCall([this, data] { if (replays_broken_) { return; @@ -87,7 +110,8 @@ void MediaServer::PushAddMessageToReplayCall(const std::vector& data) { // sanity check.. if (!writing_replay_) { - Log("MediaServer got AddMessageToReplayCall while not writing replay"); + Log(LogLevel::kError, + "AssetsServer got AddMessageToReplayCall while not writing replay"); replays_broken_ = true; return; } @@ -97,7 +121,8 @@ void MediaServer::PushAddMessageToReplayCall(const std::vector& data) { // if we've got too much data built up (lets go with 10 megs for now), // abort if (replay_message_bytes_ > 10000000) { - Log("replay output buffer exceeded 10 megs; aborting replay"); + Log(LogLevel::kError, + "replay output buffer exceeded 10 megs; aborting replay"); fclose(replay_out_file_); replay_out_file_ = nullptr; replay_message_bytes_ = 0; @@ -110,7 +135,7 @@ void MediaServer::PushAddMessageToReplayCall(const std::vector& data) { }); } -void MediaServer::PushEndWriteReplayCall() { +void AssetsServer::PushEndWriteReplayCall() { thread()->PushCall([this] { if (replays_broken_) { return; @@ -118,7 +143,7 @@ void MediaServer::PushEndWriteReplayCall() { // sanity check.. if (!writing_replay_) { - Log("_finishWritingReplay called while not writing"); + Log(LogLevel::kError, "_finishWritingReplay called while not writing"); replays_broken_ = true; return; } @@ -134,7 +159,7 @@ void MediaServer::PushEndWriteReplayCall() { }); } -void MediaServer::WriteReplayMessages() { +void AssetsServer::WriteReplayMessages() { if (replay_out_file_) { for (auto&& i : replay_messages_) { std::vector data_compressed = g_utils->huffman()->compress(i); @@ -155,7 +180,8 @@ void MediaServer::WriteReplayMessages() { if (fwrite(&len8, 1, 1, replay_out_file_) != 1) { fclose(replay_out_file_); replay_out_file_ = nullptr; - Log("error writing replay file: " + g_platform->GetErrnoString()); + Log(LogLevel::kError, + "error writing replay file: " + g_platform->GetErrnoString()); return; } } @@ -166,14 +192,16 @@ void MediaServer::WriteReplayMessages() { if (fwrite(&len16, 2, 1, replay_out_file_) != 1) { fclose(replay_out_file_); replay_out_file_ = nullptr; - Log("error writing replay file: " + g_platform->GetErrnoString()); + Log(LogLevel::kError, + "error writing replay file: " + g_platform->GetErrnoString()); return; } } else { if (fwrite(&len32, 4, 1, replay_out_file_) != 1) { fclose(replay_out_file_); replay_out_file_ = nullptr; - Log("error writing replay file: " + g_platform->GetErrnoString()); + Log(LogLevel::kError, + "error writing replay file: " + g_platform->GetErrnoString()); return; } } @@ -184,7 +212,8 @@ void MediaServer::WriteReplayMessages() { if (result != 1) { fclose(replay_out_file_); replay_out_file_ = nullptr; - Log("error writing replay file: " + g_platform->GetErrnoString()); + Log(LogLevel::kError, + "error writing replay file: " + g_platform->GetErrnoString()); return; } replay_bytes_written_ += data_compressed.size() + 2; @@ -194,10 +223,10 @@ void MediaServer::WriteReplayMessages() { } } -void MediaServer::Process() { +void AssetsServer::Process() { // make sure we don't do any loading until we know what kind/quality of // textures we'll be loading - if (!g_media || !g_graphics_server + if (!g_assets || !g_graphics_server || !g_graphics_server->texture_compression_types_are_set() // NOLINT || !g_graphics_server->texture_quality_set()) { return; @@ -209,12 +238,12 @@ void MediaServer::Process() { if (!pending_preloads_.empty()) { (**pending_preloads_.back()).Preload(); // pass the ref-pointer along to the load queue - g_media->AddPendingLoad(pending_preloads_.back()); + g_assets->AddPendingLoad(pending_preloads_.back()); pending_preloads_.pop_back(); } else if (!pending_preloads_audio_.empty()) { (**pending_preloads_audio_.back()).Preload(); // pass the ref-pointer along to the load queue - g_media->AddPendingLoad(pending_preloads_audio_.back()); + g_assets->AddPendingLoad(pending_preloads_audio_.back()); pending_preloads_audio_.pop_back(); } diff --git a/src/ballistica/assets/assets_server.h b/src/ballistica/assets/assets_server.h new file mode 100644 index 00000000..e9be2e6a --- /dev/null +++ b/src/ballistica/assets/assets_server.h @@ -0,0 +1,42 @@ +// Released under the MIT License. See LICENSE for details. + +#ifndef BALLISTICA_ASSETS_ASSETS_SERVER_H_ +#define BALLISTICA_ASSETS_ASSETS_SERVER_H_ + +#include +#include + +#include "ballistica/core/object.h" + +namespace ballistica { + +class AssetsServer { + public: + AssetsServer(); + auto OnAppStart() -> void; + auto PushBeginWriteReplayCall() -> void; + auto PushEndWriteReplayCall() -> void; + auto PushAddMessageToReplayCall(const std::vector& data) -> void; + auto PushPendingPreload(Object::Ref* asset_ref_ptr) + -> void; + auto thread() const -> Thread* { return thread_; } + + private: + auto OnAppStartInThread() -> void; + void Process(); + void WriteReplayMessages(); + Thread* thread_{}; + FILE* replay_out_file_{}; + size_t replay_bytes_written_{}; + bool writing_replay_{}; + bool replays_broken_{}; + std::list > replay_messages_; + size_t replay_message_bytes_{}; + Timer* process_timer_{}; + std::vector*> pending_preloads_; + std::vector*> pending_preloads_audio_; +}; + +} // namespace ballistica + +#endif // BALLISTICA_ASSETS_ASSETS_SERVER_H_ diff --git a/src/ballistica/media/component/media_component.cc b/src/ballistica/assets/component/asset_component.cc similarity index 60% rename from src/ballistica/media/component/media_component.cc rename to src/ballistica/assets/component/asset_component.cc index 049aedf9..9e7ace5e 100644 --- a/src/ballistica/media/component/media_component.cc +++ b/src/ballistica/assets/component/asset_component.cc @@ -1,16 +1,16 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/media/component/media_component.h" +#include "ballistica/assets/component/asset_component.h" #include "ballistica/python/python_sys.h" #include "ballistica/scene/scene.h" namespace ballistica { -MediaComponent::MediaComponent(std::string name, Scene* scene) +AssetComponent::AssetComponent(std::string name, Scene* scene) : name_(std::move(name)), scene_(scene) {} -auto MediaComponent::GetPyRef(bool new_ref) -> PyObject* { +auto AssetComponent::GetPyRef(bool new_ref) -> PyObject* { if (!py_object_) { // if we have no python object, create it py_object_ = CreatePyObject(); @@ -22,11 +22,11 @@ auto MediaComponent::GetPyRef(bool new_ref) -> PyObject* { return py_object_; } -auto MediaComponent::GetObjectDescription() const -> std::string { - return ""; +auto AssetComponent::GetObjectDescription() const -> std::string { + return ""; } -void MediaComponent::ClearPyObject() { +void AssetComponent::ClearPyObject() { assert(py_object_ != nullptr); py_object_ = nullptr; } diff --git a/src/ballistica/media/component/media_component.h b/src/ballistica/assets/component/asset_component.h similarity index 82% rename from src/ballistica/media/component/media_component.h rename to src/ballistica/assets/component/asset_component.h index 84887de1..335f39c0 100644 --- a/src/ballistica/media/component/media_component.h +++ b/src/ballistica/assets/component/asset_component.h @@ -1,7 +1,7 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_MEDIA_COMPONENT_MEDIA_COMPONENT_H_ -#define BALLISTICA_MEDIA_COMPONENT_MEDIA_COMPONENT_H_ +#ifndef BALLISTICA_ASSETS_COMPONENT_ASSET_COMPONENT_H_ +#define BALLISTICA_ASSETS_COMPONENT_ASSET_COMPONENT_H_ #include @@ -10,9 +10,9 @@ namespace ballistica { -class MediaComponent : public Object { +class AssetComponent : public Object { public: - MediaComponent(std::string name, Scene* scene); + AssetComponent(std::string name, Scene* scene); auto name() const -> std::string { return name_; } // Returns true if this texture was created in the UI context. @@ -41,7 +41,7 @@ class MediaComponent : public Object { } protected: - virtual auto GetMediaComponentTypeName() const -> std::string = 0; + virtual auto GetAssetComponentTypeName() const -> std::string = 0; // Create a python representation of this object. virtual auto CreatePyObject() -> PyObject* = 0; @@ -60,4 +60,4 @@ class MediaComponent : public Object { } // namespace ballistica -#endif // BALLISTICA_MEDIA_COMPONENT_MEDIA_COMPONENT_H_ +#endif // BALLISTICA_ASSETS_COMPONENT_ASSET_COMPONENT_H_ diff --git a/src/ballistica/media/component/collide_model.cc b/src/ballistica/assets/component/collide_model.cc similarity index 67% rename from src/ballistica/media/component/collide_model.cc rename to src/ballistica/assets/component/collide_model.cc index d24731b4..99435d4f 100644 --- a/src/ballistica/media/component/collide_model.cc +++ b/src/ballistica/assets/component/collide_model.cc @@ -1,24 +1,24 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/media/component/collide_model.h" +#include "ballistica/assets/component/collide_model.h" -#include "ballistica/game/game_stream.h" #include "ballistica/python/class/python_class_collide_model.h" #include "ballistica/scene/scene.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { CollideModel::CollideModel(const std::string& name, Scene* scene) - : MediaComponent(name, scene), dead_(false) { + : AssetComponent(name, scene), dead_(false) { assert(InLogicThread()); if (scene) { - if (GameStream* os = scene->GetGameStream()) { + if (SceneStream* os = scene->GetSceneStream()) { os->AddCollideModel(this); } } { - Media::MediaListsLock lock; - collide_model_data_ = g_media->GetCollideModelData(name); + Assets::AssetListLock lock; + collide_model_data_ = g_assets->GetCollideModelData(name); } assert(collide_model_data_.exists()); } @@ -30,7 +30,7 @@ void CollideModel::MarkDead() { return; } if (Scene* s = scene()) { - if (GameStream* os = s->GetGameStream()) { + if (SceneStream* os = s->GetSceneStream()) { os->RemoveCollideModel(this); } } diff --git a/src/ballistica/media/component/collide_model.h b/src/ballistica/assets/component/collide_model.h similarity index 63% rename from src/ballistica/media/component/collide_model.h rename to src/ballistica/assets/component/collide_model.h index 7b8fa1e8..09671fa3 100644 --- a/src/ballistica/media/component/collide_model.h +++ b/src/ballistica/assets/component/collide_model.h @@ -1,18 +1,18 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_MEDIA_COMPONENT_COLLIDE_MODEL_H_ -#define BALLISTICA_MEDIA_COMPONENT_COLLIDE_MODEL_H_ +#ifndef BALLISTICA_ASSETS_COMPONENT_COLLIDE_MODEL_H_ +#define BALLISTICA_ASSETS_COMPONENT_COLLIDE_MODEL_H_ #include -#include "ballistica/media/component/media_component.h" -#include "ballistica/media/data/collide_model_data.h" -#include "ballistica/media/media.h" +#include "ballistica/assets/assets.h" +#include "ballistica/assets/component/asset_component.h" +#include "ballistica/assets/data/collide_model_data.h" namespace ballistica { // user-facing collide_model class -class CollideModel : public MediaComponent { +class CollideModel : public AssetComponent { public: CollideModel(const std::string& name, Scene* scene); ~CollideModel() override; @@ -23,7 +23,7 @@ class CollideModel : public MediaComponent { auto collide_model_data() const -> CollideModelData* { return collide_model_data_.get(); } - auto GetMediaComponentTypeName() const -> std::string override { + auto GetAssetComponentTypeName() const -> std::string override { return "CollideModel"; } void MarkDead(); @@ -38,4 +38,4 @@ class CollideModel : public MediaComponent { } // namespace ballistica -#endif // BALLISTICA_MEDIA_COMPONENT_COLLIDE_MODEL_H_ +#endif // BALLISTICA_ASSETS_COMPONENT_COLLIDE_MODEL_H_ diff --git a/src/ballistica/media/component/cube_map_texture.cc b/src/ballistica/assets/component/cube_map_texture.cc similarity index 59% rename from src/ballistica/media/component/cube_map_texture.cc rename to src/ballistica/assets/component/cube_map_texture.cc index 6116bf58..6af4d80b 100644 --- a/src/ballistica/media/component/cube_map_texture.cc +++ b/src/ballistica/assets/component/cube_map_texture.cc @@ -1,19 +1,19 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/media/component/cube_map_texture.h" +#include "ballistica/assets/component/cube_map_texture.h" -#include "ballistica/media/media.h" +#include "ballistica/assets/assets.h" namespace ballistica { CubeMapTexture::CubeMapTexture(const std::string& name, Scene* scene) - : MediaComponent(name, scene) { + : AssetComponent(name, scene) { assert(InLogicThread()); // cant currently add these to scenes so nothing to do here.. { - Media::MediaListsLock lock; - texture_data_ = g_media->GetCubeMapTextureData(name); + Assets::AssetListLock lock; + texture_data_ = g_assets->GetCubeMapTextureData(name); } assert(texture_data_.exists()); } diff --git a/src/ballistica/media/component/cube_map_texture.h b/src/ballistica/assets/component/cube_map_texture.h similarity index 59% rename from src/ballistica/media/component/cube_map_texture.h rename to src/ballistica/assets/component/cube_map_texture.h index c72e4623..db5ed159 100644 --- a/src/ballistica/media/component/cube_map_texture.h +++ b/src/ballistica/assets/component/cube_map_texture.h @@ -1,17 +1,17 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_MEDIA_COMPONENT_CUBE_MAP_TEXTURE_H_ -#define BALLISTICA_MEDIA_COMPONENT_CUBE_MAP_TEXTURE_H_ +#ifndef BALLISTICA_ASSETS_COMPONENT_CUBE_MAP_TEXTURE_H_ +#define BALLISTICA_ASSETS_COMPONENT_CUBE_MAP_TEXTURE_H_ #include -#include "ballistica/media/component/media_component.h" -#include "ballistica/media/data/texture_data.h" +#include "ballistica/assets/component/asset_component.h" +#include "ballistica/assets/data/texture_data.h" namespace ballistica { // user-facing texture class -class CubeMapTexture : public MediaComponent { +class CubeMapTexture : public AssetComponent { public: CubeMapTexture(const std::string& name, Scene* s); @@ -19,7 +19,7 @@ class CubeMapTexture : public MediaComponent { // note that a texture's data can change over time as different // versions are spooled in/out/etc auto GetTextureData() const -> TextureData* { return texture_data_.get(); } - auto GetMediaComponentTypeName() const -> std::string override { + auto GetAssetComponentTypeName() const -> std::string override { return "CubeMapTexture"; } @@ -29,4 +29,4 @@ class CubeMapTexture : public MediaComponent { } // namespace ballistica -#endif // BALLISTICA_MEDIA_COMPONENT_CUBE_MAP_TEXTURE_H_ +#endif // BALLISTICA_ASSETS_COMPONENT_CUBE_MAP_TEXTURE_H_ diff --git a/src/ballistica/media/component/data.cc b/src/ballistica/assets/component/data.cc similarity index 66% rename from src/ballistica/media/component/data.cc rename to src/ballistica/assets/component/data.cc index cf3dec87..17b23702 100644 --- a/src/ballistica/media/component/data.cc +++ b/src/ballistica/assets/component/data.cc @@ -1,25 +1,25 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/media/component/data.h" +#include "ballistica/assets/component/data.h" -#include "ballistica/game/game_stream.h" #include "ballistica/python/class/python_class_data.h" #include "ballistica/scene/scene.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { Data::Data(const std::string& name, Scene* scene) - : MediaComponent(name, scene), dead_(false) { + : AssetComponent(name, scene), dead_(false) { assert(InLogicThread()); if (scene) { - if (GameStream* os = scene->GetGameStream()) { + if (SceneStream* os = scene->GetSceneStream()) { os->AddData(this); } } { - Media::MediaListsLock lock; - data_data_ = g_media->GetDataData(name); + Assets::AssetListLock lock; + data_data_ = g_assets->GetDataData(name); } assert(data_data_.exists()); } @@ -31,7 +31,7 @@ void Data::MarkDead() { return; } if (Scene* s = scene()) { - if (GameStream* os = s->GetGameStream()) { + if (SceneStream* os = s->GetSceneStream()) { os->RemoveData(this); } } diff --git a/src/ballistica/media/component/data.h b/src/ballistica/assets/component/data.h similarity index 62% rename from src/ballistica/media/component/data.h rename to src/ballistica/assets/component/data.h index 30e926a2..fe3e917d 100644 --- a/src/ballistica/media/component/data.h +++ b/src/ballistica/assets/component/data.h @@ -1,22 +1,22 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_MEDIA_COMPONENT_DATA_H_ -#define BALLISTICA_MEDIA_COMPONENT_DATA_H_ +#ifndef BALLISTICA_ASSETS_COMPONENT_DATA_H_ +#define BALLISTICA_ASSETS_COMPONENT_DATA_H_ #include #include +#include "ballistica/assets/assets.h" +#include "ballistica/assets/component/asset_component.h" +#include "ballistica/assets/data/asset_component_data.h" +#include "ballistica/assets/data/data_data.h" #include "ballistica/ballistica.h" #include "ballistica/core/object.h" -#include "ballistica/media/component/media_component.h" -#include "ballistica/media/data/data_data.h" -#include "ballistica/media/data/media_component_data.h" -#include "ballistica/media/media.h" namespace ballistica { // user-facing data class -class Data : public MediaComponent { +class Data : public AssetComponent { public: Data(const std::string& name, Scene* scene); ~Data() override; @@ -25,7 +25,7 @@ class Data : public MediaComponent { // note that a data's data can change over time as different // versions are spooled in/out/etc. auto data_data() const -> DataData* { return data_data_.get(); } - auto GetMediaComponentTypeName() const -> std::string override { + auto GetAssetComponentTypeName() const -> std::string override { return "Data"; } void MarkDead(); @@ -40,4 +40,4 @@ class Data : public MediaComponent { } // namespace ballistica -#endif // BALLISTICA_MEDIA_COMPONENT_DATA_H_ +#endif // BALLISTICA_ASSETS_COMPONENT_DATA_H_ diff --git a/src/ballistica/media/component/model.cc b/src/ballistica/assets/component/model.cc similarity index 66% rename from src/ballistica/media/component/model.cc rename to src/ballistica/assets/component/model.cc index 77b60aef..196ebbf6 100644 --- a/src/ballistica/media/component/model.cc +++ b/src/ballistica/assets/component/model.cc @@ -1,25 +1,25 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/media/component/model.h" +#include "ballistica/assets/component/model.h" -#include "ballistica/game/game_stream.h" #include "ballistica/python/class/python_class_model.h" #include "ballistica/scene/scene.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { Model::Model(const std::string& name, Scene* scene) - : MediaComponent(name, scene), dead_(false) { + : AssetComponent(name, scene), dead_(false) { assert(InLogicThread()); if (scene) { - if (GameStream* os = scene->GetGameStream()) { + if (SceneStream* os = scene->GetSceneStream()) { os->AddModel(this); } } { - Media::MediaListsLock lock; - model_data_ = g_media->GetModelData(name); + Assets::AssetListLock lock; + model_data_ = g_assets->GetModelData(name); } assert(model_data_.exists()); } @@ -31,7 +31,7 @@ void Model::MarkDead() { return; } if (Scene* s = scene()) { - if (GameStream* os = s->GetGameStream()) { + if (SceneStream* os = s->GetSceneStream()) { os->RemoveModel(this); } } diff --git a/src/ballistica/media/component/model.h b/src/ballistica/assets/component/model.h similarity index 59% rename from src/ballistica/media/component/model.h rename to src/ballistica/assets/component/model.h index c7a8968b..239813a0 100644 --- a/src/ballistica/media/component/model.h +++ b/src/ballistica/assets/component/model.h @@ -1,23 +1,23 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_MEDIA_COMPONENT_MODEL_H_ -#define BALLISTICA_MEDIA_COMPONENT_MODEL_H_ +#ifndef BALLISTICA_ASSETS_COMPONENT_MODEL_H_ +#define BALLISTICA_ASSETS_COMPONENT_MODEL_H_ #include #include +#include "ballistica/assets/assets.h" +#include "ballistica/assets/component/asset_component.h" +#include "ballistica/assets/data/asset_component_data.h" +#include "ballistica/assets/data/model_data.h" +#include "ballistica/assets/data/model_renderer_data.h" #include "ballistica/ballistica.h" #include "ballistica/core/object.h" -#include "ballistica/media/component/media_component.h" -#include "ballistica/media/data/media_component_data.h" -#include "ballistica/media/data/model_data.h" -#include "ballistica/media/data/model_renderer_data.h" -#include "ballistica/media/media.h" namespace ballistica { // user-facing model class -class Model : public MediaComponent { +class Model : public AssetComponent { public: Model(const std::string& name, Scene* scene); ~Model() override; @@ -26,7 +26,7 @@ class Model : public MediaComponent { // note that a model's data can change over time as different // versions are spooled in/out/etc auto model_data() const -> ModelData* { return model_data_.get(); } - auto GetMediaComponentTypeName() const -> std::string override { + auto GetAssetComponentTypeName() const -> std::string override { return "Model"; } void MarkDead(); @@ -41,4 +41,4 @@ class Model : public MediaComponent { } // namespace ballistica -#endif // BALLISTICA_MEDIA_COMPONENT_MODEL_H_ +#endif // BALLISTICA_ASSETS_COMPONENT_MODEL_H_ diff --git a/src/ballistica/media/component/sound.cc b/src/ballistica/assets/component/sound.cc similarity index 61% rename from src/ballistica/media/component/sound.cc rename to src/ballistica/assets/component/sound.cc index babf5200..9c76aba2 100644 --- a/src/ballistica/media/component/sound.cc +++ b/src/ballistica/assets/component/sound.cc @@ -1,26 +1,26 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/media/component/sound.h" +#include "ballistica/assets/component/sound.h" -#include "ballistica/game/game_stream.h" -#include "ballistica/media/data/sound_data.h" -#include "ballistica/media/media.h" +#include "ballistica/assets/assets.h" +#include "ballistica/assets/data/sound_data.h" #include "ballistica/python/class/python_class_sound.h" #include "ballistica/scene/scene.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { Sound::Sound(const std::string& name, Scene* scene) - : MediaComponent(name, scene) { + : AssetComponent(name, scene) { assert(InLogicThread()); if (scene) { - if (GameStream* os = scene->GetGameStream()) { + if (SceneStream* os = scene->GetSceneStream()) { os->AddSound(this); } } { - Media::MediaListsLock lock; - sound_data_ = g_media->GetSoundData(name); + Assets::AssetListLock lock; + sound_data_ = g_assets->GetSoundData(name); } assert(sound_data_.exists()); } @@ -30,7 +30,7 @@ Sound::~Sound() { MarkDead(); } void Sound::MarkDead() { if (dead_) return; if (Scene* s = scene()) { - if (GameStream* os = s->GetGameStream()) { + if (SceneStream* os = s->GetSceneStream()) { os->RemoveSound(this); } } diff --git a/src/ballistica/media/component/sound.h b/src/ballistica/assets/component/sound.h similarity index 68% rename from src/ballistica/media/component/sound.h rename to src/ballistica/assets/component/sound.h index e712cced..ff0295b7 100644 --- a/src/ballistica/media/component/sound.h +++ b/src/ballistica/assets/component/sound.h @@ -1,16 +1,16 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_MEDIA_COMPONENT_SOUND_H_ -#define BALLISTICA_MEDIA_COMPONENT_SOUND_H_ +#ifndef BALLISTICA_ASSETS_COMPONENT_SOUND_H_ +#define BALLISTICA_ASSETS_COMPONENT_SOUND_H_ #include #include -#include "ballistica/media/component/media_component.h" +#include "ballistica/assets/component/asset_component.h" namespace ballistica { -class Sound : public MediaComponent { +class Sound : public AssetComponent { public: Sound(const std::string& name, Scene* scene); ~Sound() override; @@ -19,7 +19,7 @@ class Sound : public MediaComponent { // Note that a sound's data can change over time as different // versions are spooled in/out/etc. auto GetSoundData() const -> SoundData* { return sound_data_.get(); } - auto GetMediaComponentTypeName() const -> std::string override { + auto GetAssetComponentTypeName() const -> std::string override { return "Sound"; } void MarkDead(); @@ -34,4 +34,4 @@ class Sound : public MediaComponent { } // namespace ballistica -#endif // BALLISTICA_MEDIA_COMPONENT_SOUND_H_ +#endif // BALLISTICA_ASSETS_COMPONENT_SOUND_H_ diff --git a/src/ballistica/media/component/texture.cc b/src/ballistica/assets/component/texture.cc similarity index 64% rename from src/ballistica/media/component/texture.cc rename to src/ballistica/assets/component/texture.cc index 6a8c31d0..52994fdb 100644 --- a/src/ballistica/media/component/texture.cc +++ b/src/ballistica/assets/component/texture.cc @@ -1,37 +1,37 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/media/component/texture.h" +#include "ballistica/assets/component/texture.h" -#include "ballistica/game/game_stream.h" #include "ballistica/graphics/renderer.h" #include "ballistica/python/class/python_class_texture.h" #include "ballistica/scene/scene.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { Texture::Texture(const std::string& name, Scene* scene) - : MediaComponent(name, scene), dead_(false) { + : AssetComponent(name, scene), dead_(false) { assert(InLogicThread()); // Add to the provided scene to get a numeric ID. if (scene) { - if (GameStream* os = scene->GetGameStream()) { + if (SceneStream* os = scene->GetSceneStream()) { os->AddTexture(this); } } { - Media::MediaListsLock lock; - texture_data_ = g_media->GetTextureData(name); + Assets::AssetListLock lock; + texture_data_ = g_assets->GetTextureData(name); } assert(texture_data_.exists()); } // qrcode version -Texture::Texture(const std::string& qr_url) : MediaComponent(qr_url, nullptr) { +Texture::Texture(const std::string& qr_url) : AssetComponent(qr_url, nullptr) { assert(InLogicThread()); { - Media::MediaListsLock lock; - texture_data_ = g_media->GetTextureDataQRCode(qr_url); + Assets::AssetListLock lock; + texture_data_ = g_assets->GetTextureDataQRCode(qr_url); } assert(texture_data_.exists()); } @@ -43,7 +43,7 @@ void Texture::MarkDead() { return; } if (Scene* s = scene()) { - if (GameStream* os = s->GetGameStream()) { + if (SceneStream* os = s->GetSceneStream()) { os->RemoveTexture(this); } } diff --git a/src/ballistica/media/component/texture.h b/src/ballistica/assets/component/texture.h similarity index 67% rename from src/ballistica/media/component/texture.h rename to src/ballistica/assets/component/texture.h index d03ed2e5..892767ee 100644 --- a/src/ballistica/media/component/texture.h +++ b/src/ballistica/assets/component/texture.h @@ -1,17 +1,17 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_MEDIA_COMPONENT_TEXTURE_H_ -#define BALLISTICA_MEDIA_COMPONENT_TEXTURE_H_ +#ifndef BALLISTICA_ASSETS_COMPONENT_TEXTURE_H_ +#define BALLISTICA_ASSETS_COMPONENT_TEXTURE_H_ #include -#include "ballistica/media/component/media_component.h" -#include "ballistica/media/data/texture_data.h" +#include "ballistica/assets/component/asset_component.h" +#include "ballistica/assets/data/texture_data.h" namespace ballistica { // User-facing texture class. -class Texture : public MediaComponent { +class Texture : public AssetComponent { public: Texture(const std::string& name, Scene* scene); explicit Texture(const std::string& qr_url); @@ -21,7 +21,7 @@ class Texture : public MediaComponent { // Note that a texture's data can change over time as different // versions are spooled in/out/etc. auto texture_data() const -> TextureData* { return texture_data_.get(); } - auto GetMediaComponentTypeName() const -> std::string override { + auto GetAssetComponentTypeName() const -> std::string override { return "Texture"; } void MarkDead(); @@ -36,4 +36,4 @@ class Texture : public MediaComponent { } // namespace ballistica -#endif // BALLISTICA_MEDIA_COMPONENT_TEXTURE_H_ +#endif // BALLISTICA_ASSETS_COMPONENT_TEXTURE_H_ diff --git a/src/ballistica/media/data/media_component_data.cc b/src/ballistica/assets/data/asset_component_data.cc similarity index 84% rename from src/ballistica/media/data/media_component_data.cc rename to src/ballistica/assets/data/asset_component_data.cc index ed5f79c6..bb7e96ed 100644 --- a/src/ballistica/media/data/media_component_data.cc +++ b/src/ballistica/assets/data/asset_component_data.cc @@ -1,25 +1,25 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/media/data/media_component_data.h" +#include "ballistica/assets/data/asset_component_data.h" namespace ballistica { -MediaComponentData::MediaComponentData() { +AssetComponentData::AssetComponentData() { assert(InLogicThread()); - assert(g_media); + assert(g_assets); last_used_time_ = GetRealTime(); } -MediaComponentData::~MediaComponentData() { +AssetComponentData::~AssetComponentData() { // at the moment whoever owns the last reference to us // needs to make sure to unload us before we die.. // I feel like there should be a more elegant solution to that. - assert(g_media); + assert(g_assets); assert(!locked()); assert(!loaded()); } -void MediaComponentData::Preload(bool already_locked) { +void AssetComponentData::Preload(bool already_locked) { LockGuard lock(this, already_locked ? LockGuard::Type::kDontLock : LockGuard::Type::kLock); if (!preloaded_) { @@ -35,7 +35,7 @@ void MediaComponentData::Preload(bool already_locked) { } } -void MediaComponentData::Load(bool already_locked) { +void AssetComponentData::Load(bool already_locked) { LockGuard lock(this, already_locked ? LockGuard::Type::kDontLock : LockGuard::Type::kLock); if (!preloaded_) { @@ -57,7 +57,7 @@ void MediaComponentData::Load(bool already_locked) { } } -void MediaComponentData::Unload(bool already_locked) { +void AssetComponentData::Unload(bool already_locked) { LockGuard lock(this, already_locked ? LockGuard::Type::kDontLock : LockGuard::Type::kLock); @@ -80,7 +80,7 @@ void MediaComponentData::Unload(bool already_locked) { } } -MediaComponentData::LockGuard::LockGuard(MediaComponentData* data, Type type) +AssetComponentData::LockGuard::LockGuard(AssetComponentData* data, Type type) : data_(data) { switch (type) { case kLock: { @@ -100,7 +100,7 @@ MediaComponentData::LockGuard::LockGuard(MediaComponentData* data, Type type) } } -MediaComponentData::LockGuard::~LockGuard() { +AssetComponentData::LockGuard::~LockGuard() { if (holds_lock_) { data_->Unlock(); } diff --git a/src/ballistica/media/data/media_component_data.h b/src/ballistica/assets/data/asset_component_data.h similarity index 88% rename from src/ballistica/media/data/media_component_data.h rename to src/ballistica/assets/data/asset_component_data.h index 7ab99e25..4a2f91e8 100644 --- a/src/ballistica/media/data/media_component_data.h +++ b/src/ballistica/assets/data/asset_component_data.h @@ -1,7 +1,7 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_MEDIA_DATA_MEDIA_COMPONENT_DATA_H_ -#define BALLISTICA_MEDIA_DATA_MEDIA_COMPONENT_DATA_H_ +#ifndef BALLISTICA_ASSETS_DATA_ASSET_COMPONENT_DATA_H_ +#define BALLISTICA_ASSETS_DATA_ASSET_COMPONENT_DATA_H_ #include #include @@ -11,17 +11,17 @@ namespace ballistica { -/// Base class for loadable media components. -class MediaComponentData : public Object { +/// Base class for loadable asset components. +class AssetComponentData : public Object { public: - MediaComponentData(); - ~MediaComponentData() override; + AssetComponentData(); + ~AssetComponentData() override; void Preload(bool already_locked = false); void Load(bool already_locked = false); void Unload(bool already_locked = false); auto preloaded() const -> bool { return preloaded_; } auto loaded() const -> bool { return preloaded_ && loaded_; } - virtual auto GetMediaType() const -> MediaType = 0; + virtual auto GetAssetType() const -> AssetType = 0; // Return name or another identifier. For debugging purposes. virtual auto GetName() const -> std::string { return "invalid"; } @@ -35,14 +35,14 @@ class MediaComponentData : public Object { class LockGuard { public: enum Type { kLock, kInheritLock, kDontLock }; - explicit LockGuard(MediaComponentData* data, Type type = kLock); + explicit LockGuard(AssetComponentData* data, Type type = kLock); ~LockGuard(); // Does this guard hold a lock? auto holds_lock() const -> bool { return holds_lock_; } private: - MediaComponentData* data_ = nullptr; + AssetComponentData* data_ = nullptr; bool holds_lock_ = false; }; @@ -128,9 +128,9 @@ class MediaComponentData : public Object { bool preloaded_ = false; bool loaded_ = false; std::mutex mutex_; - BA_DISALLOW_CLASS_COPIES(MediaComponentData); + BA_DISALLOW_CLASS_COPIES(AssetComponentData); }; } // namespace ballistica -#endif // BALLISTICA_MEDIA_DATA_MEDIA_COMPONENT_DATA_H_ +#endif // BALLISTICA_ASSETS_DATA_ASSET_COMPONENT_DATA_H_ diff --git a/src/ballistica/media/data/collide_model_data.cc b/src/ballistica/assets/data/collide_model_data.cc similarity index 95% rename from src/ballistica/media/data/collide_model_data.cc rename to src/ballistica/assets/data/collide_model_data.cc index 6a19aeac..763a6d23 100644 --- a/src/ballistica/media/data/collide_model_data.cc +++ b/src/ballistica/assets/data/collide_model_data.cc @@ -1,15 +1,15 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/media/data/collide_model_data.h" +#include "ballistica/assets/data/collide_model_data.h" -#include "ballistica/media/media.h" +#include "ballistica/assets/assets.h" namespace ballistica { CollideModelData::CollideModelData(const std::string& file_name_in) : file_name_(file_name_in) { file_name_full_ = - g_media->FindMediaFile(Media::FileType::kCollisionModel, file_name_in); + g_assets->FindAssetFile(Assets::FileType::kCollisionModel, file_name_in); valid_ = true; } diff --git a/src/ballistica/media/data/collide_model_data.h b/src/ballistica/assets/data/collide_model_data.h similarity index 71% rename from src/ballistica/media/data/collide_model_data.h rename to src/ballistica/assets/data/collide_model_data.h index 3e3223d3..0dba9bfd 100644 --- a/src/ballistica/media/data/collide_model_data.h +++ b/src/ballistica/assets/data/collide_model_data.h @@ -1,26 +1,26 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_MEDIA_DATA_COLLIDE_MODEL_DATA_H_ -#define BALLISTICA_MEDIA_DATA_COLLIDE_MODEL_DATA_H_ +#ifndef BALLISTICA_ASSETS_DATA_COLLIDE_MODEL_DATA_H_ +#define BALLISTICA_ASSETS_DATA_COLLIDE_MODEL_DATA_H_ #include #include -#include "ballistica/media/data/media_component_data.h" +#include "ballistica/assets/data/asset_component_data.h" #include "ode/ode.h" namespace ballistica { // Loadable model for collision detection. -class CollideModelData : public MediaComponentData { +class CollideModelData : public AssetComponentData { public: CollideModelData() = default; explicit CollideModelData(const std::string& file_name_in); void DoPreload() override; void DoLoad() override; void DoUnload() override; - auto GetMediaType() const -> MediaType override { - return MediaType::kCollideModel; + auto GetAssetType() const -> AssetType override { + return AssetType::kCollideModel; } auto GetName() const -> std::string override { if (!file_name_full_.empty()) { @@ -44,4 +44,4 @@ class CollideModelData : public MediaComponentData { } // namespace ballistica -#endif // BALLISTICA_MEDIA_DATA_COLLIDE_MODEL_DATA_H_ +#endif // BALLISTICA_ASSETS_DATA_COLLIDE_MODEL_DATA_H_ diff --git a/src/ballistica/media/data/data_data.cc b/src/ballistica/assets/data/data_data.cc similarity index 81% rename from src/ballistica/media/data/data_data.cc rename to src/ballistica/assets/data/data_data.cc index 360d0d14..0a509fc4 100644 --- a/src/ballistica/media/data/data_data.cc +++ b/src/ballistica/assets/data/data_data.cc @@ -1,8 +1,8 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/media/data/data_data.h" +#include "ballistica/assets/data/data_data.h" -#include "ballistica/media/media.h" +#include "ballistica/assets/assets.h" #include "ballistica/python/python.h" #include "ballistica/python/python_sys.h" @@ -10,7 +10,7 @@ namespace ballistica { DataData::DataData(const std::string& file_name_in) : file_name_(file_name_in) { file_name_full_ = - g_media->FindMediaFile(Media::FileType::kData, file_name_in); + g_assets->FindAssetFile(Assets::FileType::kData, file_name_in); valid_ = true; } @@ -20,8 +20,8 @@ void DataData::DoPreload() { // in the following case: // - asset thread grabs payload lock for Preload() // - asset thread tries to grab GIL in Preload(); spins. - // - meanwhile, something in game thread has called Load() - // - game thread holds GIL by default and now spins waiting on payload lock. + // - meanwhile, something in logic thread has called Load() + // - logic thread holds GIL by default and now spins waiting on payload lock. // - deadlock :-( // ...so the new plan is to simply load the file into a string in Preload() diff --git a/src/ballistica/media/data/data_data.h b/src/ballistica/assets/data/data_data.h similarity index 75% rename from src/ballistica/media/data/data_data.h rename to src/ballistica/assets/data/data_data.h index 11888623..57baa42b 100644 --- a/src/ballistica/media/data/data_data.h +++ b/src/ballistica/assets/data/data_data.h @@ -1,16 +1,16 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_MEDIA_DATA_DATA_DATA_H_ -#define BALLISTICA_MEDIA_DATA_DATA_DATA_H_ +#ifndef BALLISTICA_ASSETS_DATA_DATA_DATA_H_ +#define BALLISTICA_ASSETS_DATA_DATA_DATA_H_ #include -#include "ballistica/media/data/media_component_data.h" +#include "ballistica/assets/data/asset_component_data.h" #include "ballistica/python/python_ref.h" namespace ballistica { -class DataData : public MediaComponentData { +class DataData : public AssetComponentData { public: DataData() = default; explicit DataData(const std::string& file_name_in); @@ -19,7 +19,7 @@ class DataData : public MediaComponentData { void DoLoad() override; void DoUnload() override; - auto GetMediaType() const -> MediaType override { return MediaType::kData; } + auto GetAssetType() const -> AssetType override { return AssetType::kData; } auto GetName() const -> std::string override { if (!file_name_full_.empty()) { return file_name_full_; @@ -44,4 +44,4 @@ class DataData : public MediaComponentData { } // namespace ballistica -#endif // BALLISTICA_MEDIA_DATA_DATA_DATA_H_ +#endif // BALLISTICA_ASSETS_DATA_DATA_DATA_H_ diff --git a/src/ballistica/media/data/model_data.cc b/src/ballistica/assets/data/model_data.cc similarity index 96% rename from src/ballistica/media/data/model_data.cc rename to src/ballistica/assets/data/model_data.cc index 637e4153..48e2cf99 100644 --- a/src/ballistica/media/data/model_data.cc +++ b/src/ballistica/assets/data/model_data.cc @@ -1,6 +1,6 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/media/data/model_data.h" +#include "ballistica/assets/data/model_data.h" #include "ballistica/graphics/graphics_server.h" #include "ballistica/graphics/renderer.h" @@ -10,7 +10,7 @@ namespace ballistica { ModelData::ModelData(const std::string& file_name_in) : file_name_(file_name_in) { file_name_full_ = - g_media->FindMediaFile(Media::FileType::kModel, file_name_in); + g_assets->FindAssetFile(Assets::FileType::kModel, file_name_in); valid_ = true; } diff --git a/src/ballistica/media/data/model_data.h b/src/ballistica/assets/data/model_data.h similarity index 81% rename from src/ballistica/media/data/model_data.h rename to src/ballistica/assets/data/model_data.h index cb155c6c..e5388922 100644 --- a/src/ballistica/media/data/model_data.h +++ b/src/ballistica/assets/data/model_data.h @@ -1,24 +1,24 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_MEDIA_DATA_MODEL_DATA_H_ -#define BALLISTICA_MEDIA_DATA_MODEL_DATA_H_ +#ifndef BALLISTICA_ASSETS_DATA_MODEL_DATA_H_ +#define BALLISTICA_ASSETS_DATA_MODEL_DATA_H_ #include #include -#include "ballistica/media/data/media_component_data.h" -#include "ballistica/media/data/model_renderer_data.h" +#include "ballistica/assets/data/asset_component_data.h" +#include "ballistica/assets/data/model_renderer_data.h" namespace ballistica { -class ModelData : public MediaComponentData { +class ModelData : public AssetComponentData { public: ModelData() = default; explicit ModelData(const std::string& file_name_in); void DoPreload() override; void DoLoad() override; void DoUnload() override; - auto GetMediaType() const -> MediaType override { return MediaType::kModel; } + auto GetAssetType() const -> AssetType override { return AssetType::kModel; } auto GetName() const -> std::string override { if (!file_name_full_.empty()) { return file_name_full_; @@ -64,4 +64,4 @@ class ModelData : public MediaComponentData { } // namespace ballistica -#endif // BALLISTICA_MEDIA_DATA_MODEL_DATA_H_ +#endif // BALLISTICA_ASSETS_DATA_MODEL_DATA_H_ diff --git a/src/ballistica/media/data/model_renderer_data.h b/src/ballistica/assets/data/model_renderer_data.h similarity index 52% rename from src/ballistica/media/data/model_renderer_data.h rename to src/ballistica/assets/data/model_renderer_data.h index ad23080a..4128e465 100644 --- a/src/ballistica/media/data/model_renderer_data.h +++ b/src/ballistica/assets/data/model_renderer_data.h @@ -1,7 +1,7 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_MEDIA_DATA_MODEL_RENDERER_DATA_H_ -#define BALLISTICA_MEDIA_DATA_MODEL_RENDERER_DATA_H_ +#ifndef BALLISTICA_ASSETS_DATA_MODEL_RENDERER_DATA_H_ +#define BALLISTICA_ASSETS_DATA_MODEL_RENDERER_DATA_H_ #include "ballistica/core/object.h" @@ -11,11 +11,11 @@ namespace ballistica { // this is provided by the renderer class ModelRendererData : public Object { public: - auto GetDefaultOwnerThread() const -> ThreadIdentifier override { - return ThreadIdentifier::kMain; + auto GetDefaultOwnerThread() const -> ThreadTag override { + return ThreadTag::kMain; } }; } // namespace ballistica -#endif // BALLISTICA_MEDIA_DATA_MODEL_RENDERER_DATA_H_ +#endif // BALLISTICA_ASSETS_DATA_MODEL_RENDERER_DATA_H_ diff --git a/src/ballistica/media/data/sound_data.cc b/src/ballistica/assets/data/sound_data.cc similarity index 94% rename from src/ballistica/media/data/sound_data.cc rename to src/ballistica/assets/data/sound_data.cc index e5c2ab40..5b257c0d 100644 --- a/src/ballistica/media/data/sound_data.cc +++ b/src/ballistica/assets/data/sound_data.cc @@ -1,6 +1,6 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/media/data/sound_data.h" +#include "ballistica/assets/data/sound_data.h" #if BA_ENABLE_AUDIO #if BA_USE_TREMOR_VORBIS @@ -10,8 +10,8 @@ #endif #endif // BA_ENABLE_AUDIO +#include "ballistica/assets/assets.h" #include "ballistica/audio/audio_server.h" -#include "ballistica/media/media.h" #include "ballistica/python/python.h" // Need to move away from OpenAL on Apple stuff. @@ -56,8 +56,8 @@ static auto LoadOgg(const char* file_name, std::vector* buffer, f = g_platform->FOpen(file_name, "rb"); if (f == nullptr) { fallback = true; - Log(std::string("Error: Can't open sound file '") + file_name - + "' for reading..."); + Log(LogLevel::kError, std::string("Can't open sound file '") + file_name + + "' for reading..."); // Attempt a fallback standin; if that doesn't work, throw in the towel. file_name = "data/global/audio/blank.ogg"; @@ -77,7 +77,8 @@ static auto LoadOgg(const char* file_name, std::vector* buffer, // Try opening the given file if (ov_open_callbacks(f, &ogg_file, nullptr, 0, callbacks) != 0) { - Log(std::string("Error decoding sound file '") + file_name + "'"); + Log(LogLevel::kError, + std::string("Error decoding sound file '") + file_name + "'"); fclose(f); @@ -199,8 +200,9 @@ static void LoadCachedOgg(const char* file_name, std::vector* buffer, // with invalid formats of 0 once. Report and ignore if we see // something like that. if (*format != AL_FORMAT_MONO16 && *format != AL_FORMAT_STEREO16) { - Log(std::string("Ignoring invalid audio cache of ") + file_name - + " with format " + std::to_string(*format)); + Log(LogLevel::kError, std::string("Ignoring invalid audio cache of ") + + file_name + " with format " + + std::to_string(*format)); } else { return; // SUCCESS!!!! } @@ -248,7 +250,7 @@ SoundData::SoundData(const std::string& file_name_in) #endif // BA_ENABLE_AUDIO last_play_time_(0) { file_name_full_ = - g_media->FindMediaFile(Media::FileType::kSound, file_name_in); + g_assets->FindAssetFile(Assets::FileType::kSound, file_name_in); valid_ = true; } diff --git a/src/ballistica/media/data/sound_data.h b/src/ballistica/assets/data/sound_data.h similarity index 82% rename from src/ballistica/media/data/sound_data.h rename to src/ballistica/assets/data/sound_data.h index f1bc3092..d24172de 100644 --- a/src/ballistica/media/data/sound_data.h +++ b/src/ballistica/assets/data/sound_data.h @@ -1,17 +1,17 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_MEDIA_DATA_SOUND_DATA_H_ -#define BALLISTICA_MEDIA_DATA_SOUND_DATA_H_ +#ifndef BALLISTICA_ASSETS_DATA_SOUND_DATA_H_ +#define BALLISTICA_ASSETS_DATA_SOUND_DATA_H_ #include #include +#include "ballistica/assets/data/asset_component_data.h" #include "ballistica/audio/al_sys.h" -#include "ballistica/media/data/media_component_data.h" namespace ballistica { -class SoundData : public MediaComponentData { +class SoundData : public AssetComponentData { public: SoundData() = default; explicit SoundData(const std::string& file_name_in); @@ -20,7 +20,7 @@ class SoundData : public MediaComponentData { // FIXME: Should make sure the sound_data isn't in use before unloading it. void DoUnload() override; - auto GetMediaType() const -> MediaType override { return MediaType::kSound; } + auto GetAssetType() const -> AssetType override { return AssetType::kSound; } auto GetName() const -> std::string override { if (!file_name_full_.empty()) return file_name_full_; @@ -55,4 +55,4 @@ class SoundData : public MediaComponentData { } // namespace ballistica -#endif // BALLISTICA_MEDIA_DATA_SOUND_DATA_H_ +#endif // BALLISTICA_ASSETS_DATA_SOUND_DATA_H_ diff --git a/src/ballistica/media/data/texture_data.cc b/src/ballistica/assets/data/texture_data.cc similarity index 98% rename from src/ballistica/media/data/texture_data.cc rename to src/ballistica/assets/data/texture_data.cc index d931abfd..11aa82f7 100644 --- a/src/ballistica/media/data/texture_data.cc +++ b/src/ballistica/assets/data/texture_data.cc @@ -1,7 +1,9 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/media/data/texture_data.h" +#include "ballistica/assets/data/texture_data.h" +#include "ballistica/assets/data/texture_preload_data.h" +#include "ballistica/assets/data/texture_renderer_data.h" #include "ballistica/graphics/graphics.h" #include "ballistica/graphics/graphics_server.h" #include "ballistica/graphics/renderer.h" @@ -9,8 +11,6 @@ #include "ballistica/graphics/texture/dds.h" #include "ballistica/graphics/texture/ktx.h" #include "ballistica/graphics/texture/pvr.h" -#include "ballistica/media/data/texture_preload_data.h" -#include "ballistica/media/data/texture_renderer_data.h" #include "external/qr_code_generator/QrCode.hpp" namespace ballistica { @@ -43,7 +43,8 @@ TextureData::TextureData() = default; TextureData::TextureData(const std::string& file_in, TextureType type_in, TextureMinQuality min_quality_in) : file_name_(file_in), type_(type_in), min_quality_(min_quality_in) { - file_name_full_ = g_media->FindMediaFile(Media::FileType::kTexture, file_in); + file_name_full_ = + g_assets->FindAssetFile(Assets::FileType::kTexture, file_in); valid_ = true; } diff --git a/src/ballistica/media/data/texture_data.h b/src/ballistica/assets/data/texture_data.h similarity index 84% rename from src/ballistica/media/data/texture_data.h rename to src/ballistica/assets/data/texture_data.h index 1083f101..e1ce22db 100644 --- a/src/ballistica/media/data/texture_data.h +++ b/src/ballistica/assets/data/texture_data.h @@ -1,17 +1,17 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_MEDIA_DATA_TEXTURE_DATA_H_ -#define BALLISTICA_MEDIA_DATA_TEXTURE_DATA_H_ +#ifndef BALLISTICA_ASSETS_DATA_TEXTURE_DATA_H_ +#define BALLISTICA_ASSETS_DATA_TEXTURE_DATA_H_ #include #include -#include "ballistica/media/data/media_component_data.h" +#include "ballistica/assets/data/asset_component_data.h" namespace ballistica { // Loadable texture media component. -class TextureData : public MediaComponentData { +class TextureData : public AssetComponentData { public: TextureData(); ~TextureData() override; @@ -28,8 +28,8 @@ class TextureData : public MediaComponentData { auto GetNameFull() const -> std::string override { return file_name_full(); } auto file_name() const -> const std::string& { return file_name_; } auto file_name_full() const -> const std::string& { return file_name_full_; } - auto GetMediaType() const -> MediaType override { - return MediaType::kTexture; + auto GetAssetType() const -> AssetType override { + return AssetType::kTexture; } void DoPreload() override; void DoLoad() override; @@ -59,4 +59,4 @@ class TextureData : public MediaComponentData { } // namespace ballistica -#endif // BALLISTICA_MEDIA_DATA_TEXTURE_DATA_H_ +#endif // BALLISTICA_ASSETS_DATA_TEXTURE_DATA_H_ diff --git a/src/ballistica/media/data/texture_preload_data.cc b/src/ballistica/assets/data/texture_preload_data.cc similarity index 99% rename from src/ballistica/media/data/texture_preload_data.cc rename to src/ballistica/assets/data/texture_preload_data.cc index 3f54a7d4..eda797fa 100644 --- a/src/ballistica/media/data/texture_preload_data.cc +++ b/src/ballistica/assets/data/texture_preload_data.cc @@ -1,13 +1,13 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/media/data/texture_preload_data.h" +#include "ballistica/assets/data/texture_preload_data.h" #if BA_OSTYPE_LINUX #include #endif +#include "ballistica/assets/component/texture.h" #include "ballistica/graphics/texture/ktx.h" -#include "ballistica/media/component/texture.h" namespace ballistica { diff --git a/src/ballistica/media/data/texture_preload_data.h b/src/ballistica/assets/data/texture_preload_data.h similarity index 86% rename from src/ballistica/media/data/texture_preload_data.h rename to src/ballistica/assets/data/texture_preload_data.h index db0454df..03c9d5c4 100644 --- a/src/ballistica/media/data/texture_preload_data.h +++ b/src/ballistica/assets/data/texture_preload_data.h @@ -1,7 +1,7 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_MEDIA_DATA_TEXTURE_PRELOAD_DATA_H_ -#define BALLISTICA_MEDIA_DATA_TEXTURE_PRELOAD_DATA_H_ +#ifndef BALLISTICA_ASSETS_DATA_TEXTURE_PRELOAD_DATA_H_ +#define BALLISTICA_ASSETS_DATA_TEXTURE_PRELOAD_DATA_H_ #include "ballistica/ballistica.h" @@ -37,4 +37,4 @@ class TexturePreloadData { } // namespace ballistica -#endif // BALLISTICA_MEDIA_DATA_TEXTURE_PRELOAD_DATA_H_ +#endif // BALLISTICA_ASSETS_DATA_TEXTURE_PRELOAD_DATA_H_ diff --git a/src/ballistica/media/data/texture_renderer_data.h b/src/ballistica/assets/data/texture_renderer_data.h similarity index 65% rename from src/ballistica/media/data/texture_renderer_data.h rename to src/ballistica/assets/data/texture_renderer_data.h index ca7a71de..f3467940 100644 --- a/src/ballistica/media/data/texture_renderer_data.h +++ b/src/ballistica/assets/data/texture_renderer_data.h @@ -1,7 +1,7 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_MEDIA_DATA_TEXTURE_RENDERER_DATA_H_ -#define BALLISTICA_MEDIA_DATA_TEXTURE_RENDERER_DATA_H_ +#ifndef BALLISTICA_ASSETS_DATA_TEXTURE_RENDERER_DATA_H_ +#define BALLISTICA_ASSETS_DATA_TEXTURE_RENDERER_DATA_H_ namespace ballistica { @@ -9,8 +9,8 @@ namespace ballistica { // this is extended by the renderer class TextureRendererData : public Object { public: - auto GetDefaultOwnerThread() const -> ThreadIdentifier override { - return ThreadIdentifier::kMain; + auto GetDefaultOwnerThread() const -> ThreadTag override { + return ThreadTag::kMain; } // Create the renderer data but don't load it in yet. @@ -24,4 +24,4 @@ class TextureRendererData : public Object { } // namespace ballistica -#endif // BALLISTICA_MEDIA_DATA_TEXTURE_RENDERER_DATA_H_ +#endif // BALLISTICA_ASSETS_DATA_TEXTURE_RENDERER_DATA_H_ diff --git a/src/ballistica/audio/al_sys.cc b/src/ballistica/audio/al_sys.cc index 11518df6..c39a14d2 100644 --- a/src/ballistica/audio/al_sys.cc +++ b/src/ballistica/audio/al_sys.cc @@ -16,13 +16,14 @@ namespace ballistica { void _check_al_error(const char* file, int line) { if (g_audio_server->paused()) { - Log(Utils::BaseName(file) + ":" + std::to_string(line) - + ": Checking OpenAL error while paused."); + Log(LogLevel::kError, Utils::BaseName(file) + ":" + std::to_string(line) + + ": Checking OpenAL error while paused."); } ALenum al_err = alGetError(); if (al_err != AL_NO_ERROR) { - Log(Utils::BaseName(file) + ":" + std::to_string(line) - + ": OpenAL Error: " + GetALErrorString(al_err) + ";"); + Log(LogLevel::kError, Utils::BaseName(file) + ":" + std::to_string(line) + + ": OpenAL Error: " + GetALErrorString(al_err) + + ";"); } } diff --git a/src/ballistica/audio/audio.cc b/src/ballistica/audio/audio.cc index 95238edc..8f4fef9a 100644 --- a/src/ballistica/audio/audio.cc +++ b/src/ballistica/audio/audio.cc @@ -2,20 +2,14 @@ #include "ballistica/audio/audio.h" +#include "ballistica/assets/data/sound_data.h" #include "ballistica/audio/audio_server.h" #include "ballistica/audio/audio_source.h" #include "ballistica/core/thread.h" -#include "ballistica/media/data/sound_data.h" namespace ballistica { -Audio::Audio() { assert(InLogicThread()); } - -void Audio::Init() { - // Init our singleton. - assert(g_audio == nullptr); - g_audio = new Audio(); -} +Audio::Audio() {} void Audio::Reset() { assert(InLogicThread()); diff --git a/src/ballistica/audio/audio.h b/src/ballistica/audio/audio.h index 3a0b40d0..1cc1f000 100644 --- a/src/ballistica/audio/audio.h +++ b/src/ballistica/audio/audio.h @@ -15,14 +15,15 @@ namespace ballistica { /// used by the game and/or other threads. class Audio { public: - static void Init(); - void Reset(); + Audio(); + auto Reset() -> void; - void SetVolumes(float music_volume, float sound_volume); + auto SetVolumes(float music_volume, float sound_volume) -> void; - void SetListenerPosition(const Vector3f& p); - void SetListenerOrientation(const Vector3f& forward, const Vector3f& up); - void SetSoundPitch(float pitch); + auto SetListenerPosition(const Vector3f& p) -> void; + auto SetListenerOrientation(const Vector3f& forward, const Vector3f& up) + -> void; + auto SetSoundPitch(float pitch) -> void; // Return a pointer to a locked sound source, or nullptr if they're all busy. // The sound source will be reset to standard settings (no loop, fade 1, pos @@ -41,9 +42,9 @@ class Audio { auto IsSoundPlaying(uint32_t play_id) -> bool; // Simple one-shot play functions. - void PlaySound(SoundData* s, float volume = 1.0f); - void PlaySoundAtPosition(SoundData* sound, float volume, float x, float y, - float z); + auto PlaySound(SoundData* s, float volume = 1.0f) -> void; + auto PlaySoundAtPosition(SoundData* sound, float volume, float x, float y, + float z) -> void; // Call this if you want to prevent repeated plays of the same sound. It'll // tell you if the sound has been played recently. The one-shot sound-play @@ -51,19 +52,17 @@ class Audio { auto ShouldPlay(SoundData* s) -> bool; // Hmm; shouldn't these be accessed through the Source class? - void PushSourceFadeOutCall(uint32_t play_id, uint32_t time); - void PushSourceStopSoundCall(uint32_t play_id); + auto PushSourceFadeOutCall(uint32_t play_id, uint32_t time) -> void; + auto PushSourceStopSoundCall(uint32_t play_id) -> void; - void AddClientSource(AudioSource* source); + auto AddClientSource(AudioSource* source) -> void; - void MakeSourceAvailable(AudioSource* source); + auto MakeSourceAvailable(AudioSource* source) -> void; auto available_sources_mutex() -> std::mutex& { return available_sources_mutex_; } private: - Audio(); - // Flat list of client sources indexed by id. std::vector client_sources_; diff --git a/src/ballistica/audio/audio_server.cc b/src/ballistica/audio/audio_server.cc index 5e3f4727..511d8a97 100644 --- a/src/ballistica/audio/audio_server.cc +++ b/src/ballistica/audio/audio_server.cc @@ -3,17 +3,17 @@ #include "ballistica/audio/audio_server.h" #include "ballistica/app/app.h" +#include "ballistica/assets/assets.h" +#include "ballistica/assets/data/sound_data.h" #include "ballistica/audio/al_sys.h" #include "ballistica/audio/audio.h" #include "ballistica/audio/audio_source.h" #include "ballistica/audio/audio_streamer.h" #include "ballistica/audio/ogg_stream.h" #include "ballistica/core/thread.h" -#include "ballistica/game/game.h" #include "ballistica/generic/timer.h" +#include "ballistica/logic/logic.h" #include "ballistica/math/vector3f.h" -#include "ballistica/media/data/sound_data.h" -#include "ballistica/media/media.h" // Need to move away from OpenAL on Apple stuff. #if __clang__ @@ -30,10 +30,10 @@ extern "C" void opensl_resume_playback(); extern std::string g_rift_audio_device_name; #endif -const int kAudioProcessIntervalNormal = 500; -const int kAudioProcessIntervalFade = 50; -const int kAudioProcessIntervalPendingLoad = 1; -const bool kShowInUseSounds = false; +const int kAudioProcessIntervalNormal{500}; +const int kAudioProcessIntervalFade{50}; +const int kAudioProcessIntervalPendingLoad{1}; +const bool kShowInUseSounds{}; int AudioServer::al_source_count_ = 0; @@ -55,7 +55,7 @@ class AudioServer::ThreadSource : public Object { // not be used. ThreadSource(AudioServer* audio_thread, int id, bool* valid); ~ThreadSource() override; - void Reset() { + auto Reset() -> void { SetIsMusic(false); SetPositional(true); SetPosition(0, 0, 0); @@ -66,18 +66,18 @@ class AudioServer::ThreadSource : public Object { /// Set whether a sound is "music". /// This influences which volume controls affect it. - void SetIsMusic(bool m); + auto SetIsMusic(bool m) -> void; /// Set whether a source is positional. /// A non-positional source's position coords are always relative to the /// listener - ie: 0, 0, 0 will always be centered. - void SetPositional(bool p); - void SetPosition(float x, float y, float z); - void SetGain(float g); - void SetFade(float f); - void SetLooping(bool loop); + auto SetPositional(bool p) -> void; + auto SetPosition(float x, float y, float z) -> void; + auto SetGain(float g) -> void; + auto SetFade(float f) -> void; + auto SetLooping(bool loop) -> void; auto Play(const Object::Ref* s) -> uint32_t; - void Stop(); + auto Stop() -> void; auto play_count() -> uint32_t { return play_count_; } auto is_streamed() const -> bool { return is_streamed_; } auto current_is_music() const -> bool { return current_is_music_; } @@ -86,41 +86,41 @@ class AudioServer::ThreadSource : public Object { auto play_id() const -> uint32_t { return (play_count_ << 16u) | (static_cast(id_) & 0xFFFFu); } - void UpdateAvailability(); - auto GetDefaultOwnerThread() const -> ThreadIdentifier override; + auto UpdateAvailability() -> void; + auto GetDefaultOwnerThread() const -> ThreadTag override; auto client_source() const -> AudioSource* { return client_source_.get(); } auto source_sound() const -> SoundData* { return source_sound_ ? source_sound_->get() : nullptr; } - void UpdatePitch(); - void UpdateVolume(); - void ExecStop(); - void ExecPlay(); - void Update(); + auto UpdatePitch() -> void; + auto UpdateVolume() -> void; + auto ExecStop() -> void; + auto ExecPlay() -> void; + auto Update() -> void; private: - bool looping_ = false; + bool looping_{}; std::unique_ptr client_source_; - float fade_ = 1.0f; - float gain_ = 1.0f; - AudioServer* audio_thread_; - bool valid_ = false; - const Object::Ref* source_sound_ = nullptr; - int id_; - uint32_t play_count_ = 0; - bool is_actually_playing_ = false; - bool want_to_play_ = false; + float fade_{1.0f}; + float gain_{1.0f}; + AudioServer* audio_thread_{}; + bool valid_{}; + const Object::Ref* source_sound_{}; + int id_{}; + uint32_t play_count_{}; + bool is_actually_playing_{}; + bool want_to_play_{}; #if BA_ENABLE_AUDIO - ALuint source_ = 0; + ALuint source_{}; #endif - bool is_streamed_ = false; + bool is_streamed_{}; /// Whether we should be designated as "music" next time we play. - bool is_music_ = false; + bool is_music_{}; /// Whether currently playing as music. - bool current_is_music_ = false; + bool current_is_music_{}; #if BA_ENABLE_AUDIO Object::Ref streamer_; @@ -144,7 +144,7 @@ struct AudioServer::SoundFadeNode { void AudioServer::SetPaused(bool pause) { if (!paused_) { if (!pause) { - Log("Error: got audio unpause request when already unpaused."); + Log(LogLevel::kError, "Got audio unpause request when already unpaused."); } else { #if BA_OSTYPE_IOS_TVOS // apple recommends this during audio-interruptions.. @@ -163,7 +163,7 @@ void AudioServer::SetPaused(bool pause) { } else { // unpause if requested.. if (pause) { - Log("Error: Got audio pause request when already paused."); + Log(LogLevel::kError, "Got audio pause request when already paused."); } else { #if BA_OSTYPE_IOS_TVOS // apple recommends this during audio-interruptions.. @@ -321,24 +321,34 @@ void AudioServer::PushSetListenerOrientationCall(const Vector3f& forward, }); } -AudioServer::AudioServer(Thread* thread) - : thread_(thread), impl_{new AudioServer::Impl()} { - // we're a singleton... +AudioServer::AudioServer() : impl_{new AudioServer::Impl()} { + // We're a singleton; make sure we don't already exist. assert(g_audio_server == nullptr); - g_audio_server = this; - thread->AddPauseCallback(NewLambdaRunnableRaw([this] { OnThreadPause(); })); - thread->AddResumeCallback(NewLambdaRunnableRaw([this] { OnThreadResume(); })); + // Spin up our thread. + thread_ = new Thread(ThreadTag::kAudio); + g_app->pausable_threads.push_back(thread_); +} + +auto AudioServer::OnAppStart() -> void { + thread_->PushCallSynchronous([this] { OnAppStartInThread(); }); +} + +auto AudioServer::OnAppStartInThread() -> void { + assert(InAudioThread()); + thread()->AddPauseCallback(NewLambdaRunnableRaw([this] { OnThreadPause(); })); + thread()->AddResumeCallback( + NewLambdaRunnableRaw([this] { OnThreadResume(); })); // Get our thread to give us periodic processing time. - process_timer_ = thread->NewTimer(kAudioProcessIntervalNormal, true, - NewLambdaRunnable([this] { Process(); })); + process_timer_ = thread()->NewTimer(kAudioProcessIntervalNormal, true, + NewLambdaRunnable([this] { Process(); })); #if BA_ENABLE_AUDIO // Bring up OpenAL stuff. { - const char* alDeviceName = nullptr; + const char* al_device_name = nullptr; // On the rift build in vr mode we need to make sure we open the rift audio // device. @@ -347,7 +357,7 @@ AudioServer::AudioServer(Thread* thread) ALboolean enumeration = alcIsExtensionPresent(nullptr, "ALC_ENUMERATE_ALL_EXT"); if (enumeration == AL_FALSE) { - Log("OpenAL enumeration extensions missing."); + Log(LogLevel::kError, "OpenAL enumeration extensions missing."); } else { const ALCchar* devices = alcGetString(nullptr, ALC_ALL_DEVICES_SPECIFIER); @@ -357,26 +367,26 @@ AudioServer::AudioServer(Thread* thread) // If the string is blank, we weren't able to find the oculus // audio device. In that case we'll just go with default. if (g_rift_audio_device_name != "") { - // Log("AL Devices list:"); - // Log("----------"); + // Log(LogLevel::kInfo, "AL Devices list:"); + // Log(LogLevel::kInfo, "----------"); while (device && *device != '\0' && next && *next != '\0') { // These names seem to be things like "OpenAL Soft on FOO" // ..we should be able to search for FOO. if (strstr(device, g_rift_audio_device_name.c_str())) { - alDeviceName = device; + al_device_name = device; } len = strlen(device); device += (len + 1); next += (len + 2); } - // Log("----------"); + // Log(LogLevel::kInfo, "----------"); } } } #endif // BA_RIFT_BUILD ALCdevice* device; - device = alcOpenDevice(alDeviceName); + device = alcOpenDevice(al_device_name); BA_PRECONDITION(device); impl_->alc_context_ = alcCreateContext(device, nullptr); BA_PRECONDITION(impl_->alc_context_); @@ -404,8 +414,8 @@ AudioServer::AudioServer(Thread* thread) sound_source_refs_.push_back(s); sources_.push_back(&(*s)); } else { - Log("Error: Made " + std::to_string(i) + " sources; (wanted " - + std::to_string(target_source_count) + ")."); + Log(LogLevel::kError, "Made " + std::to_string(i) + " sources; (wanted " + + std::to_string(target_source_count) + ")."); break; } } @@ -463,9 +473,10 @@ void AudioServer::UpdateAvailableSources() { // that probably means somebody's grabbing a source but never // resubmitting it. if (t - i->client_source()->last_lock_time() > 10000) { - Log("Error: Client audio source has been locked for too long; " + Log(LogLevel::kError, + "Client audio source has been locked for too long; " "probably leaked. (debug id " - + std::to_string(i->client_source()->lock_debug_id()) + ")"); + + std::to_string(i->client_source()->lock_debug_id()) + ")"); } continue; } @@ -601,7 +612,7 @@ void AudioServer::Process() { // If we're paused we don't do nothin'. if (!paused_) { // Do some loading... - have_pending_loads_ = g_media->RunPendingAudioLoads(); + have_pending_loads_ = g_assets->RunPendingAudioLoads(); // Keep that available-sources list filled. UpdateAvailableSources(); @@ -666,7 +677,7 @@ void AudioServer::FadeSoundOut(uint32_t play_id, uint32_t time) { std::make_pair(play_id, SoundFadeNode(play_id, time, true))); } -void AudioServer::DeleteMediaComponent(MediaComponentData* c) { +void AudioServer::DeleteAssetComponent(AssetComponentData* c) { assert(InAudioThread()); c->Unload(); delete c; @@ -684,8 +695,8 @@ AudioServer::ThreadSource::ThreadSource(AudioServer* audio_thread_in, int id_in, ALenum err = alGetError(); valid_ = (err == AL_NO_ERROR); if (!valid_) { - Log(std::string("Error: AL Error ") + GetALErrorString(err) - + " on source creation."); + Log(LogLevel::kError, std::string("AL Error ") + GetALErrorString(err) + + " on source creation."); } else { // In vr mode we keep the microphone a bit closer to the camera // for realism purposes, so we need stuff louder in general. @@ -702,7 +713,9 @@ AudioServer::ThreadSource::ThreadSource(AudioServer* audio_thread_in, int id_in, CHECK_AL_ERROR; } *valid_out = valid_; - if (valid_) al_source_count_++; + if (valid_) { + al_source_count_++; + } #endif // BA_ENABLE_AUDIO } @@ -734,9 +747,8 @@ AudioServer::ThreadSource::~ThreadSource() { #endif // BA_ENABLE_AUDIO } -auto AudioServer::ThreadSource::GetDefaultOwnerThread() const - -> ThreadIdentifier { - return ThreadIdentifier::kAudio; +auto AudioServer::ThreadSource::GetDefaultOwnerThread() const -> ThreadTag { + return ThreadTag::kAudio; } void AudioServer::ThreadSource::UpdateAvailability() { @@ -867,9 +879,9 @@ void AudioServer::ThreadSource::SetPosition(float x, float y, float z) { z = 500; } if (oob) { - BA_LOG_ONCE( - "Error: AudioServer::ThreadSource::SetPosition" - " got out-of-bounds value."); + BA_LOG_ONCE(LogLevel::kError, + "AudioServer::ThreadSource::SetPosition" + " got out-of-bounds value."); } ALfloat source_pos[] = {x, y, z}; alSourcefv(source_, AL_POSITION, source_pos); @@ -1033,7 +1045,7 @@ void AudioServer::ThreadSource::Stop() { // to free up... // (we can't kill media-refs outside the main thread) if (source_sound_) { - assert(g_media); + assert(g_assets); g_audio_server->AddSoundRefDelete(source_sound_); source_sound_ = nullptr; } @@ -1087,22 +1099,22 @@ void AudioServer::PushSetSoundPitchCall(float val) { void AudioServer::PushSetPausedCall(bool pause) { thread()->PushCall([this, pause] { if (g_buildconfig.ostype_android()) { - Log("Error: Shouldn't be getting SetPausedCall on android."); + Log(LogLevel::kError, "Shouldn't be getting SetPausedCall on android."); } SetPaused(pause); }); } void AudioServer::PushComponentUnloadCall( - const std::vector*>& components) { + const std::vector*>& components) { thread()->PushCall([this, components] { // Unload all components we were passed... for (auto&& i : components) { (**i).Unload(); } - // ...and then ship these pointers back to the game thread, so it can free + // ...and then ship these pointers back to the logic thread, so it can free // the references. - g_game->PushFreeMediaComponentRefsCall(components); + g_logic->PushFreeAssetComponentRefsCall(components); }); } @@ -1118,8 +1130,9 @@ void AudioServer::AddSoundRefDelete(const Object::Ref* c) { std::scoped_lock lock(sound_ref_delete_list_mutex_); sound_ref_delete_list_.push_back(c); } - // Now push a call to the game thread to do the deletes. - g_game->thread()->PushCall([] { g_audio_server->ClearSoundRefDeleteList(); }); + // Now push a call to the logic thread to do the deletes. + g_logic->thread()->PushCall( + [] { g_audio_server->ClearSoundRefDeleteList(); }); } void AudioServer::ClearSoundRefDeleteList() { @@ -1142,7 +1155,7 @@ void AudioServer::BeginInterruption() { break; } if (GetRealTime() - t > 1000) { - Log("Error: Timed out waiting for audio pause."); + Log(LogLevel::kError, "Timed out waiting for audio pause."); break; } Platform::SleepMS(2); @@ -1164,7 +1177,7 @@ void AudioServer::EndInterruption() { break; } if (GetRealTime() - t > 1000) { - Log("Error: Timed out waiting for audio unpause."); + Log(LogLevel::kError, "Timed out waiting for audio unpause."); break; } Platform::SleepMS(2); diff --git a/src/ballistica/audio/audio_server.h b/src/ballistica/audio/audio_server.h index d747b98e..e8d6720e 100644 --- a/src/ballistica/audio/audio_server.h +++ b/src/ballistica/audio/audio_server.h @@ -22,45 +22,47 @@ class AudioServer { return play_id >> 16u; } - explicit AudioServer(Thread* o); + AudioServer(); + auto OnAppStart() -> void; - void PushSetVolumesCall(float music_volume, float sound_volume); - void PushSetSoundPitchCall(float val); - void PushSetPausedCall(bool pause); + auto PushSetVolumesCall(float music_volume, float sound_volume) -> void; + auto PushSetSoundPitchCall(float val) -> void; + auto PushSetPausedCall(bool pause) -> void; - static void BeginInterruption(); - static void EndInterruption(); + static auto BeginInterruption() -> void; + static auto EndInterruption() -> void; - void PushSetListenerPositionCall(const Vector3f& p); - void PushSetListenerOrientationCall(const Vector3f& forward, - const Vector3f& up); - void PushResetCall(); - void PushHavePendingLoadsCall(); - void PushComponentUnloadCall( - const std::vector*>& components); + auto PushSetListenerPositionCall(const Vector3f& p) -> void; + auto PushSetListenerOrientationCall(const Vector3f& forward, + const Vector3f& up) -> void; + auto PushResetCall() -> void; + auto PushHavePendingLoadsCall() -> void; + auto PushComponentUnloadCall( + const std::vector*>& components) -> void; - /// For use by g_game_module(). - void ClearSoundRefDeleteList(); + /// For use by g_logic_module(). + auto ClearSoundRefDeleteList() -> void; auto paused() const -> bool { return paused_; } // Client sources use these to pass settings to the server. - void PushSourceSetIsMusicCall(uint32_t play_id, bool val); - void PushSourceSetPositionalCall(uint32_t play_id, bool val); - void PushSourceSetPositionCall(uint32_t play_id, const Vector3f& p); - void PushSourceSetGainCall(uint32_t play_id, float val); - void PushSourceSetFadeCall(uint32_t play_id, float val); - void PushSourceSetLoopingCall(uint32_t play_id, bool val); - void PushSourcePlayCall(uint32_t play_id, Object::Ref* sound); - void PushSourceStopCall(uint32_t play_id); - void PushSourceEndCall(uint32_t play_id); + auto PushSourceSetIsMusicCall(uint32_t play_id, bool val) -> void; + auto PushSourceSetPositionalCall(uint32_t play_id, bool val) -> void; + auto PushSourceSetPositionCall(uint32_t play_id, const Vector3f& p) -> void; + auto PushSourceSetGainCall(uint32_t play_id, float val) -> void; + auto PushSourceSetFadeCall(uint32_t play_id, float val) -> void; + auto PushSourceSetLoopingCall(uint32_t play_id, bool val) -> void; + auto PushSourcePlayCall(uint32_t play_id, Object::Ref* sound) + -> void; + auto PushSourceStopCall(uint32_t play_id) -> void; + auto PushSourceEndCall(uint32_t play_id) -> void; // Fade a playing sound out over the given time. If it is already // fading or does not exist, does nothing. - void FadeSoundOut(uint32_t play_id, uint32_t time); + auto FadeSoundOut(uint32_t play_id, uint32_t time) -> void; // Stop a sound from playing if it exists. - void StopSound(uint32_t play_id); + auto StopSound(uint32_t play_id) -> void; auto thread() const -> Thread* { return thread_; } @@ -68,16 +70,17 @@ class AudioServer { class ThreadSource; struct Impl; + auto OnAppStartInThread() -> void; ~AudioServer(); auto OnThreadPause() -> void; auto OnThreadResume() -> void; - void SetPaused(bool paused); + auto SetPaused(bool paused) -> void; - void SetMusicVolume(float volume); - void SetSoundVolume(float volume); - void SetSoundPitch(float pitch); + auto SetMusicVolume(float volume) -> void; + auto SetSoundVolume(float volume) -> void; + auto SetSoundPitch(float pitch) -> void; auto music_volume() -> float { return music_volume_; } auto sound_volume() -> float { return sound_volume_; } auto sound_pitch() -> float { return sound_pitch_; } @@ -85,22 +88,22 @@ class AudioServer { /// If a sound play id is currently playing, return the sound. auto GetPlayingSound(uint32_t play_id) -> ThreadSource*; - void Reset(); - void Process(); + auto Reset() -> void; + auto Process() -> void; /// Send a component to the audio thread to delete. - void DeleteMediaComponent(MediaComponentData* c); + auto DeleteAssetComponent(AssetComponentData* c) -> void; - void UpdateTimerInterval(); - void UpdateAvailableSources(); - void UpdateMusicPlayState(); - void ProcessSoundFades(); + auto UpdateTimerInterval() -> void; + auto UpdateAvailableSources() -> void; + auto UpdateMusicPlayState() -> void; + auto ProcessSoundFades() -> void; // Some threads such as audio hold onto allocated Media-Component-Refs to keep // media components alive that they need. Media-Component-Refs, however, must - // be disposed of in the game thread, so they are passed back to it through + // be disposed of in the logic thread, so they are passed back to it through // this function. - void AddSoundRefDelete(const Object::Ref* c); + auto AddSoundRefDelete(const Object::Ref* c) -> void; // Note: should use unique_ptr for this, but build fails on raspberry pi // (gcc 8.3.0). Works on Ubuntu 9.3 so should try again later. diff --git a/src/ballistica/audio/audio_source.cc b/src/ballistica/audio/audio_source.cc index 88c16257..4e391f34 100644 --- a/src/ballistica/audio/audio_source.cc +++ b/src/ballistica/audio/audio_source.cc @@ -2,10 +2,10 @@ #include "ballistica/audio/audio_source.h" +#include "ballistica/assets/data/sound_data.h" #include "ballistica/audio/audio.h" #include "ballistica/audio/audio_server.h" #include "ballistica/math/vector3f.h" -#include "ballistica/media/data/sound_data.h" namespace ballistica { @@ -41,7 +41,7 @@ void AudioSource::SetPosition(float x, float y, float z) { assert(client_queue_size_ > 0); #if BA_DEBUG_BUILD if (std::isnan(x) || std::isnan(y) || std::isnan(z)) { - Log("Error: Got nan value in AudioSource::SetPosition."); + Log(LogLevel::kError, "Got nan value in AudioSource::SetPosition."); } #endif g_audio_server->PushSourceSetPositionCall(play_id_, Vector3f(x, y, z)); diff --git a/src/ballistica/audio/audio_streamer.cc b/src/ballistica/audio/audio_streamer.cc index d44a9982..9cdee4a1 100644 --- a/src/ballistica/audio/audio_streamer.cc +++ b/src/ballistica/audio/audio_streamer.cc @@ -86,8 +86,9 @@ void AudioStreamer::Update() { // A fun anomaly in the linux version; we sometimes get more // "processed" buffers than we have queued. if (queued < processed) { - Log("Error: streamer oddness: queued(" + std::to_string(queued) - + "); processed(" + std::to_string(processed) + ")"); + Log(LogLevel::kError, "Streamer oddness: queued(" + std::to_string(queued) + + "); processed(" + std::to_string(processed) + + ")"); processed = queued; } diff --git a/src/ballistica/audio/audio_streamer.h b/src/ballistica/audio/audio_streamer.h index 82ce85f0..15eb8012 100644 --- a/src/ballistica/audio/audio_streamer.h +++ b/src/ballistica/audio/audio_streamer.h @@ -15,8 +15,8 @@ namespace ballistica { // Provider for streamed audio data. class AudioStreamer : public Object { public: - auto GetDefaultOwnerThread() const -> ThreadIdentifier override { - return ThreadIdentifier::kAudio; + auto GetDefaultOwnerThread() const -> ThreadTag override { + return ThreadTag::kAudio; } AudioStreamer(const char* file_name, ALuint source, bool loop); ~AudioStreamer() override; diff --git a/src/ballistica/audio/ogg_stream.cc b/src/ballistica/audio/ogg_stream.cc index 93286491..68b40ef6 100644 --- a/src/ballistica/audio/ogg_stream.cc +++ b/src/ballistica/audio/ogg_stream.cc @@ -88,7 +88,8 @@ void OggStream::DoStream(char* pcm, int* size, unsigned int* rate) { static bool reported_error = false; if (!reported_error) { reported_error = true; - Log("Error streaming ogg file: '" + file_name() + "'."); + Log(LogLevel::kError, + "Error streaming ogg file: '" + file_name() + "'."); } if (loops()) { ov_pcm_seek(&ogg_file_, 0); diff --git a/src/ballistica/ballistica.cc b/src/ballistica/ballistica.cc index 9e8a4227..578bebac 100644 --- a/src/ballistica/ballistica.cc +++ b/src/ballistica/ballistica.cc @@ -4,73 +4,69 @@ #include +#include "ballistica/app/app_config.h" #include "ballistica/app/app_flavor.h" +#include "ballistica/assets/assets.h" +#include "ballistica/assets/assets_server.h" +#include "ballistica/audio/audio.h" #include "ballistica/audio/audio_server.h" #include "ballistica/core/fatal_error.h" #include "ballistica/core/logging.h" #include "ballistica/core/thread.h" #include "ballistica/dynamics/bg/bg_dynamics_server.h" -#include "ballistica/game/account.h" #include "ballistica/graphics/graphics_server.h" +#include "ballistica/graphics/text/text_graphics.h" +#include "ballistica/input/input.h" #include "ballistica/internal/app_internal.h" -#include "ballistica/media/media_server.h" -#include "ballistica/networking/network_write_module.h" +#include "ballistica/logic/v1_account.h" +#include "ballistica/networking/network_reader.h" +#include "ballistica/networking/network_writer.h" +#include "ballistica/networking/networking.h" #include "ballistica/platform/platform.h" +#include "ballistica/platform/stdio_console.h" #include "ballistica/python/python.h" #include "ballistica/scene/scene.h" +#include "ballistica/scene/v1/scene_v1.h" +#include "ballistica/ui/ui.h" namespace ballistica { // These are set automatically via script; don't modify them here. -const int kAppBuildNumber = 20798; -const char* kAppVersion = "1.7.7"; +const int kAppBuildNumber = 20877; +const char* kAppVersion = "1.7.9"; // Our standalone globals. // These are separated out for easy access. // Everything else should go into App (or more ideally into a class). -int g_early_log_writes{10}; +int g_early_v1_cloud_log_writes{10}; -Account* g_account{}; -AppConfig* g_app_config{}; App* g_app{}; +AppConfig* g_app_config{}; AppInternal* g_app_internal{}; AppFlavor* g_app_flavor{}; +Assets* g_assets{}; +AssetsServer* g_assets_server{}; Audio* g_audio{}; AudioServer* g_audio_server{}; BGDynamics* g_bg_dynamics{}; BGDynamicsServer* g_bg_dynamics_server{}; -Game* g_game{}; +Context* g_context{}; Graphics* g_graphics{}; GraphicsServer* g_graphics_server{}; Input* g_input{}; +Logic* g_logic{}; Thread* g_main_thread{}; -Media* g_media{}; -MediaServer* g_media_server{}; -NetworkReader* g_network_reader{}; Networking* g_networking{}; -NetworkWriteModule* g_network_write_module{}; +NetworkReader* g_network_reader{}; +NetworkWriter* g_network_writer{}; Platform* g_platform{}; Python* g_python{}; -StdInputModule* g_std_input_module{}; +SceneV1* g_scene_v1{}; +StdioConsole* g_stdio_console{}; TextGraphics* g_text_graphics{}; UI* g_ui{}; Utils* g_utils{}; - -// Basic overview of our bootstrapping process: -// 1: All threads and globals are created and provisioned. Everything above -// should exist at the end of this step (if it is going to exist). -// Threads should not be talking to each other yet at this point. -// 2: The system is set in motion. Game thread is told to load/apply the config. -// This event kicks off an initial-screen-creation message sent to the -// graphics-server thread. Other systems are informed that bootstrapping -// is complete and that they are free to talk to each other. Initial -// input-devices are added, media loads can begin (at least ones not -// dependent on the screen/renderer), etc. -// 3: The initial screen is created on the graphics-server thread in response -// to the message sent from the game thread. A completion notice is sent -// back to the game thread when done. -// 4: Back on the game thread, any renderer-dependent media-loads/etc. can begin -// and lastly the initial game session is kicked off. +V1Account* g_v1_account{}; auto BallisticaMain(int argc, char** argv) -> int { try { @@ -83,77 +79,107 @@ auto BallisticaMain(int argc, char** argv) -> int { } // ------------------------------------------------------------------------- - // Phase 1: Create and provision all globals. + // Phase 1: "The board is set." // ------------------------------------------------------------------------- - // Absolute bare-bones basics. - g_app = new App(argc, argv); + // Here we instantiate all of our globals. Code here should + // avoid any logic that accesses other globals since they may + // not yet exist. + + // Minimal globals we must assign immediately as they ARE needed + // for construction of the others (would be great to eliminate this need). g_platform = Platform::Create(); + g_app = new App(argc, argv); + g_app_internal = CreateAppInternal(); + g_main_thread = new Thread(ThreadTag::kMain, ThreadSource::kWrapMain); - // If we're not running under a Python executable, we need to set up - // our own Python environment. - assert(g_python == nullptr); - g_python = new Python(); + // For everything else, we hold off until the end to actually assign + // them to their globals. This keeps us honest and catches any stray + // inter-global access that we might accidentally include in a + // constructor. + auto* app_flavor = g_platform->CreateAppFlavor(); + auto* python = Python::Create(); + auto* graphics = g_platform->CreateGraphics(); + auto* graphics_server = new GraphicsServer(); + auto* audio = new Audio(); + auto* audio_server = new AudioServer(); + auto* context = new Context(nullptr); + auto* text_graphics = new TextGraphics(); + auto* app_config = new AppConfig(); + auto* v1_account = new V1Account(); + auto* utils = new Utils(); + auto* assets = new Assets(); + auto* assets_server = new AssetsServer(); + auto* ui = Object::NewUnmanaged(); + auto* networking = new Networking(); + auto* network_reader = new NetworkReader(); + auto* network_writer = new NetworkWriter(); + auto* input = new Input(); + auto* logic = new Logic(); + auto* scene_v1 = new SceneV1(); + auto* bg_dynamics = HeadlessMode() ? nullptr : new BGDynamics; + auto* bg_dynamics_server = HeadlessMode() ? nullptr : new BGDynamicsServer; + auto* stdio_console = + g_buildconfig.enable_stdio_console() ? new StdioConsole() : nullptr; - // Create a Thread wrapper around the current (main) thread. - g_main_thread = new Thread(ThreadIdentifier::kMain, ThreadType::kMain); - Thread::UpdateMainThreadID(); + g_app_flavor = app_flavor; + g_python = python; + g_graphics = graphics; + g_graphics_server = graphics_server; + g_audio = audio; + g_audio_server = audio_server; + g_context = context; + g_text_graphics = text_graphics; + g_app_config = app_config; + g_v1_account = v1_account; + g_utils = utils; + g_assets = assets; + g_assets_server = assets_server; + g_ui = ui; + g_networking = networking; + g_network_reader = network_reader; + g_network_writer = network_writer; + g_input = input; + g_logic = logic; + g_scene_v1 = scene_v1; + g_bg_dynamics = bg_dynamics; + g_bg_dynamics_server = bg_dynamics_server; + g_stdio_console = stdio_console; - // Spin up our specific app variation (VR, headless, regular, etc.) - g_app_flavor = g_platform->CreateAppFlavor(); - g_app_flavor->PostInit(); - - g_account = new Account(); - g_utils = new Utils(); - Scene::Init(); - - // Spin up our other standard threads. - auto* media_thread{new Thread(ThreadIdentifier::kMedia)}; - g_app->pausable_threads.push_back(media_thread); - auto* audio_thread{new Thread(ThreadIdentifier::kAudio)}; - g_app->pausable_threads.push_back(audio_thread); - auto* logic_thread{new Thread(ThreadIdentifier::kLogic)}; - g_app->pausable_threads.push_back(logic_thread); - auto* network_write_thread{new Thread(ThreadIdentifier::kNetworkWrite)}; - g_app->pausable_threads.push_back(network_write_thread); - - // Spin up our subsystems in those threads. - logic_thread->PushCallSynchronous( - [logic_thread] { new Game(logic_thread); }); - network_write_thread->PushCallSynchronous([network_write_thread] { - new NetworkWriteModule(network_write_thread); - }); - media_thread->PushCallSynchronous( - [media_thread] { new MediaServer(media_thread); }); - new GraphicsServer(g_main_thread); - audio_thread->PushCallSynchronous( - [audio_thread] { new AudioServer(audio_thread); }); - - // Now let the platform spin up any other threads/modules it uses. - // (bg-dynamics in non-headless builds, stdin/stdout where applicable, - // etc.) - g_platform->CreateAuxiliaryModules(); - - // Ok at this point we can be considered up-and-running. g_app->is_bootstrapped = true; // ------------------------------------------------------------------------- - // Phase 2: Set things in motion. + // Phase 2: "The pieces are moving." // ------------------------------------------------------------------------- - // Let the app and platform do whatever else it wants here such as adding - // initial input devices/etc. - g_app_flavor->OnBootstrapComplete(); - g_platform->OnBootstrapComplete(); + // Allow our subsystems to start doing work in their own threads + // and communicating with other subsystems. Note that we may still + // want to run some things serially here and ordering may be important + // (for instance we want to give our main thread a chance to register + // all initial input devices with the logic thread before the logic + // thread applies the current config to them). - // Ok; now that we're bootstrapped, tell the game thread to read and apply - // the config which should kick off the real action. - g_game->PushApplyConfigCall(); + g_logic->OnAppStart(); + g_audio_server->OnAppStart(); + g_assets_server->OnAppStart(); + g_platform->OnAppStart(); + g_app_flavor->OnAppStart(); + if (g_stdio_console) { + g_stdio_console->OnAppStart(); + } + + // As the last step of this phase, tell the logic thread to apply + // the app config which will kick off screen creation and otherwise + // get the ball rolling. + g_logic->PushApplyConfigCall(); // ------------------------------------------------------------------------- - // Phase 3/4: Create a screen and/or kick off game (in other threads). + // Phase 3: "We come to it at last; the great battle of our time." // ------------------------------------------------------------------------- + // At this point all threads are off and running and we simply + // feed events until things end (or return and let the OS do that). + if (g_app_flavor->ManagesEventLoop()) { // On our event-loop-managing platforms we now simply sit in our event // loop until the app is quit. @@ -223,77 +249,87 @@ auto FatalError(const std::string& message) -> void { FatalError::ReportFatalError(message, false); bool exit_cleanly = !IsUnmodifiedBlessedBuild(); bool handled = FatalError::HandleFatalError(exit_cleanly, false); - assert(handled); + BA_PRECONDITION(handled); } +// FIXME: move this to g_app or whatnot. auto GetAppInstanceUUID() -> const std::string& { - static std::string session_id; - static bool have_session_id = false; + static std::string app_instance_uuid; + static bool have_app_instance_uuid = false; - if (!have_session_id) { + if (!have_app_instance_uuid) { if (g_python) { Python::ScopedInterpreterLock gil; auto uuid = g_python->obj(Python::ObjID::kUUIDStrCall).Call(); if (uuid.exists()) { - session_id = uuid.ValueAsString().c_str(); - have_session_id = true; + app_instance_uuid = uuid.ValueAsString().c_str(); + have_app_instance_uuid = true; } } - if (!have_session_id) { + if (!have_app_instance_uuid) { // As an emergency fallback simply use a single random number. - Log("WARNING: GetSessionUUID() using rand fallback."); + // We should probably simply disallow this before Python is up. + Log(LogLevel::kWarning, "GetSessionUUID() using rand fallback."); srand(static_cast( - Platform::GetCurrentMilliseconds())); // NOLINT - session_id = std::to_string(static_cast(rand())); // NOLINT - have_session_id = true; + Platform::GetCurrentMilliseconds())); // NOLINT + app_instance_uuid = + std::to_string(static_cast(rand())); // NOLINT + have_app_instance_uuid = true; } - if (session_id.size() >= 100) { - Log("WARNING: session id longer than it should be."); + if (app_instance_uuid.size() >= 100) { + Log(LogLevel::kWarning, "session id longer than it should be."); } } - return session_id; -} - -auto InLogicThread() -> bool { - return (g_game && g_game->thread()->IsCurrent()); + return app_instance_uuid; } auto InMainThread() -> bool { - return (g_app && std::this_thread::get_id() == g_app->main_thread_id); + assert(g_main_thread); // Root out early use of this. + return (g_main_thread->IsCurrent()); +} + +auto InLogicThread() -> bool { + assert(g_app && g_app->is_bootstrapped); // Root out early use of this. + return (g_logic && g_logic->thread()->IsCurrent()); } auto InGraphicsThread() -> bool { + assert(g_app && g_app->is_bootstrapped); // Root out early use of this. return (g_graphics_server && g_graphics_server->thread()->IsCurrent()); } auto InAudioThread() -> bool { + assert(g_app && g_app->is_bootstrapped); // Root out early use of this. return (g_audio_server && g_audio_server->thread()->IsCurrent()); } auto InBGDynamicsThread() -> bool { + assert(g_app && g_app->is_bootstrapped); // Root out early use of this. return (g_bg_dynamics_server && g_bg_dynamics_server->thread()->IsCurrent()); } -auto InMediaThread() -> bool { - return (g_media_server && g_media_server->thread()->IsCurrent()); +auto InAssetsThread() -> bool { + assert(g_app && g_app->is_bootstrapped); // Root out early use of this. + return (g_assets_server && g_assets_server->thread()->IsCurrent()); } auto InNetworkWriteThread() -> bool { - return (g_network_write_module - && g_network_write_module->thread()->IsCurrent()); + assert(g_app && g_app->is_bootstrapped); // Root out early use of this. + return (g_network_writer && g_network_writer->thread()->IsCurrent()); } -auto Log(const std::string& msg, bool to_stdout, bool to_server) -> void { - Logging::Log(msg, to_stdout, to_server); +auto Log(LogLevel level, const std::string& msg) -> void { + Logging::Log(level, msg); } auto IsVRMode() -> bool { return g_app->vr_mode; } void ScreenMessage(const std::string& s, const Vector3f& color) { - if (g_game) { - g_game->PushScreenMessage(s, color); + if (g_logic) { + g_logic->PushScreenMessage(s, color); } else { - Log("ScreenMessage before g_game init (will be lost): '" + s + "'"); + Log(LogLevel::kError, + "ScreenMessage before g_logic init (will be lost): '" + s + "'"); } } diff --git a/src/ballistica/ballistica.h b/src/ballistica/ballistica.h index 325b02a0..d4aef692 100644 --- a/src/ballistica/ballistica.h +++ b/src/ballistica/ballistica.h @@ -5,28 +5,14 @@ // Try to ensure they're providing proper config stuff. #ifndef BA_HAVE_CONFIG -#error platform config has not been defined! +#error ballistica platform config has not been defined! #endif -// FIXME: We need to update to C++17 to get unified std::abs(). -// Until we do that, int types are defined in -// and float/double in , meaning its possible to call the wrong -// version if we aren't careful and only include one header. -// For now just including both here at the top level to hopefully -// minimize problems. -// UPDATE: We should now be building with C++17 everywhere; should add a -// check to ensure that is the case and can simplify this. #ifdef __cplusplus -#include -#include -#include -#include -#include #include -#include -#include #endif +// Minimum functionality we want available everywhere we are included. #include "ballistica/core/exception.h" #include "ballistica/core/inline.h" #include "ballistica/core/macros.h" @@ -116,8 +102,8 @@ const float kGameStepSeconds = (static_cast(kGameStepMilliseconds) / 1000.0f); // Globals. -extern int g_early_log_writes; -extern Account* g_account; +extern int g_early_v1_cloud_log_writes; +extern V1Account* g_v1_account; extern AppFlavor* g_app_flavor; extern AppConfig* g_app_config; extern App* g_app; @@ -127,19 +113,20 @@ extern AudioServer* g_audio_server; extern BGDynamics* g_bg_dynamics; extern BGDynamicsServer* g_bg_dynamics_server; extern Context* g_context; -extern Game* g_game; extern Graphics* g_graphics; extern GraphicsServer* g_graphics_server; extern Input* g_input; +extern Logic* g_logic; extern Thread* g_main_thread; -extern Media* g_media; -extern MediaServer* g_media_server; +extern Assets* g_assets; +extern AssetsServer* g_assets_server; extern Networking* g_networking; extern NetworkReader* g_network_reader; -extern NetworkWriteModule* g_network_write_module; +extern NetworkWriter* g_network_writer; extern Platform* g_platform; extern Python* g_python; -extern StdInputModule* g_std_input_module; +extern SceneV1* g_scene_v1; +extern StdioConsole* g_stdio_console; extern TextGraphics* g_text_graphics; extern UI* g_ui; extern Utils* g_utils; @@ -172,17 +159,20 @@ auto InGraphicsThread() -> bool; // (main and graphics are same currently) auto InLogicThread() -> bool; auto InAudioThread() -> bool; auto InBGDynamicsThread() -> bool; -auto InMediaThread() -> bool; +auto InAssetsThread() -> bool; auto InNetworkWriteThread() -> bool; /// Return a human-readable name for the current thread. auto GetCurrentThreadName() -> std::string; -/// Write a string to the log. -/// This will go to stdout, windows debug log, android log, etc. -/// A trailing newline will be added. -auto Log(const std::string& msg, bool to_stdout = true, bool to_server = true) - -> void; +/// Submit a log entry. +/// Can be called from any thread at any time. +/// Use either this or Python printing functionality for anything +/// that should be seen by the user, as both of those will end up +/// in the in-app console, cloud based consoles, android log, etc. +/// Regular C level prints to stdout/stderr will not and will only +/// be visible on some platforms. +auto Log(LogLevel level, const std::string& msg) -> void; /// Log a fatal error and kill the app. /// Can be called from any thread at any time. diff --git a/src/ballistica/config/config_cmake.h b/src/ballistica/config/config_cmake.h index 4c1fdb94..ececdaf0 100644 --- a/src/ballistica/config/config_cmake.h +++ b/src/ballistica/config/config_cmake.h @@ -62,7 +62,7 @@ #define BA_ENABLE_EXECINFO_BACKTRACES 1 // Allow stdin commands too. -#define BA_USE_STDIN_THREAD 1 +#define BA_ENABLE_STDIO_CONSOLE 1 #define BA_DEFINE_MAIN 1 diff --git a/src/ballistica/config/config_common.h b/src/ballistica/config/config_common.h index b807eacb..4a00f930 100644 --- a/src/ballistica/config/config_common.h +++ b/src/ballistica/config/config_common.h @@ -122,8 +122,8 @@ namespace ballistica { #error platform string undefined #endif -#ifndef BA_USE_STDIN_THREAD -#define BA_USE_STDIN_THREAD 0 +#ifndef BA_ENABLE_STDIO_CONSOLE +#define BA_ENABLE_STDIO_CONSOLE 0 #endif #ifndef BA_HARDWARE_CURSOR @@ -240,7 +240,9 @@ class BuildConfig { bool use_store_kit() const { return EXPBOOL_(BA_USE_STORE_KIT); } bool use_game_center() const { return EXPBOOL_(BA_USE_GAME_CENTER); } - bool use_stdin_thread() const { return EXPBOOL_(BA_USE_STDIN_THREAD); } + bool enable_stdio_console() const { + return EXPBOOL_(BA_ENABLE_STDIO_CONSOLE); + } bool enable_os_font_rendering() const { return EXPBOOL_(BA_ENABLE_OS_FONT_RENDERING); } diff --git a/src/ballistica/config/config_windows_generic.h b/src/ballistica/config/config_windows_generic.h index da1b56ee..eadd9a58 100644 --- a/src/ballistica/config/config_windows_generic.h +++ b/src/ballistica/config/config_windows_generic.h @@ -5,7 +5,7 @@ // note: define overrides BEFORE common makefile -#define BA_USE_STDIN_THREAD 1 +#define BA_ENABLE_STDIO_CONSOLE 1 #define BA_SDL_BUILD 1 #define BA_SDL2_BUILD 1 diff --git a/src/ballistica/config/config_windows_headless.h b/src/ballistica/config/config_windows_headless.h index cc98856d..1d205ed3 100644 --- a/src/ballistica/config/config_windows_headless.h +++ b/src/ballistica/config/config_windows_headless.h @@ -6,7 +6,7 @@ // note: define overrides BEFORE common header #define BA_HEADLESS_BUILD 1 -#define BA_USE_STDIN_THREAD 1 +#define BA_ENABLE_STDIO_CONSOLE 1 #define BA_MINSDL_BUILD 1 diff --git a/src/ballistica/core/context.cc b/src/ballistica/core/context.cc index d21d96fe..c99d265e 100644 --- a/src/ballistica/core/context.cc +++ b/src/ballistica/core/context.cc @@ -2,20 +2,12 @@ #include "ballistica/core/context.h" -#include "ballistica/game/host_activity.h" #include "ballistica/generic/runnable.h" +#include "ballistica/logic/host_activity.h" #include "ballistica/ui/ui.h" namespace ballistica { -// Dynamically allocate this; don't want it torn down on quit. -Context* g_context = nullptr; - -void Context::Init() { - assert(!g_context); - g_context = new Context(nullptr); -} - ContextTarget::ContextTarget() = default; ContextTarget::~ContextTarget() = default; @@ -108,7 +100,7 @@ auto ContextTarget::NewTimer(TimeType timetype, TimerMedium length, bool repeat, void ContextTarget::DeleteTimer(TimeType timetype, int timer_id) { // We throw on NewTimer; lets just ignore anything that comes // through here to avoid messing up destructors. - Log("ContextTarget::DeleteTimer() called; unexpected."); + Log(LogLevel::kError, "ContextTarget::DeleteTimer() called; unexpected."); } auto ContextTarget::GetTime(TimeType timetype) -> millisecs_t { diff --git a/src/ballistica/core/context.h b/src/ballistica/core/context.h index abd8a61e..bd96ec13 100644 --- a/src/ballistica/core/context.h +++ b/src/ballistica/core/context.h @@ -14,18 +14,16 @@ namespace ballistica { // effects properly apply to the place they came from. class Context { public: - static void Init(); - static auto current() -> const Context& { assert(g_context); - // Context can only be accessed from the game thread. + // Context can only be accessed from the logic thread. BA_PRECONDITION(InLogicThread()); return *g_context; } static void set_current(const Context& context) { - // Context can only be accessed from the game thread. + // Context can only be accessed from the logic thread. BA_PRECONDITION(InLogicThread()); *g_context = context; diff --git a/src/ballistica/core/fatal_error.cc b/src/ballistica/core/fatal_error.cc index 8459de92..9df548db 100644 --- a/src/ballistica/core/fatal_error.cc +++ b/src/ballistica/core/fatal_error.cc @@ -27,7 +27,7 @@ auto FatalError::ReportFatalError(const std::string& message, // error to the user and exiting the app cleanly (so we don't pollute our // crash records with results of user tinkering). - // Give the platform the opportunity to completely override our handling. + // Give the platform the opportunity to augment or override our handling. if (g_platform) { auto handled = g_platform->ReportFatalError(message, in_top_level_exception_handler); @@ -38,7 +38,8 @@ auto FatalError::ReportFatalError(const std::string& message, std::string dialog_msg = message; if (!dialog_msg.empty()) { - dialog_msg += "\n"; + // Why was this here? + // dialog_msg += "\n"; } auto starttime = time(nullptr); @@ -69,11 +70,16 @@ auto FatalError::ReportFatalError(const std::string& message, } } - // Prevent the early-log insta-send mechanism from firing since we do - // basically the same thing ourself here (avoid sending the same logs twice). - g_early_log_writes = 0; + // Prevent the early-v1-cloud-log insta-send mechanism from firing since + // we do basically the same thing ourself here (avoid sending the same + // logs twice). + g_early_v1_cloud_log_writes = 0; - Logging::Log(logmsg); + // Add this to our V1CloudLog which we'll be attempting to send momentarily, + // and also go to platform-specific logging and good ol' stderr. + Logging::V1CloudLog(logmsg); + Logging::DisplayLog("root", LogLevel::kCritical, logmsg); + fprintf(stderr, "%s\n", logmsg.c_str()); std::string prefix = "FATAL-ERROR-LOG:"; std::string suffix; @@ -83,7 +89,7 @@ auto FatalError::ReportFatalError(const std::string& message, if (g_app == nullptr) { suffix = logmsg; } - g_app_internal->DirectSendLogs(prefix, suffix, true, &result); + g_app_internal->DirectSendV1CloudLogs(prefix, suffix, true, &result); // If we're able to show a fatal-error dialog synchronously, do so. if (g_platform && g_platform->CanShowBlockingFatalErrorDialog()) { @@ -148,10 +154,10 @@ auto FatalError::HandleFatalError(bool exit_cleanly, // bring the app down ourself. if (!in_top_level_exception_handler) { if (exit_cleanly) { - Log("Calling exit(1)..."); + Logging::DisplayLog("root", LogLevel::kCritical, "Calling exit(1)..."); exit(1); } else { - Log("Calling abort()..."); + Logging::DisplayLog("root", LogLevel::kCritical, "Calling abort()..."); abort(); } } diff --git a/src/ballistica/core/logging.cc b/src/ballistica/core/logging.cc index 5635ceb4..50a66c90 100644 --- a/src/ballistica/core/logging.cc +++ b/src/ballistica/core/logging.cc @@ -5,106 +5,103 @@ #include #include "ballistica/app/app.h" -#include "ballistica/game/game.h" #include "ballistica/internal/app_internal.h" +#include "ballistica/logic/logic.h" #include "ballistica/networking/telnet_server.h" #include "ballistica/platform/platform.h" #include "ballistica/python/python.h" namespace ballistica { -static void PrintCommon(const std::string& s) { +auto Logging::Log(LogLevel level, const std::string& msg) -> void { + // This is basically up to Python to handle, but its up to us + // if Python's not up yet. + if (!g_python) { + // Make an attempt to get it at least seen (and note the fact + // that its super-early). + const char* errmsg{"Logging::Log() called before g_python exists."}; + if (g_platform) { + g_platform->DisplayLog("root", LogLevel::kError, errmsg); + g_platform->DisplayLog("root", level, msg); + } + fprintf(stderr, "%s\n%s\n", errmsg, msg.c_str()); + return; + } + + // All we want to do is call Python logging.XXX. That's on Python. + g_python->LoggingCall(level, msg); +} + +auto Logging::DisplayLog(const std::string& name, LogLevel level, + const std::string& msg) -> void { + auto msgnewline{msg + "\n"}; + // Print to in-game console. { - if (g_game != nullptr) { - g_game->PushConsolePrintCall(s); + if (g_logic != nullptr) { + g_logic->PushConsolePrintCall(msgnewline); } else { if (g_platform != nullptr) { - g_platform->HandleLog( - "Warning: Log() called before game-thread setup; " - "will not appear on in-game console.\n"); + g_platform->DisplayLog("root", LogLevel::kWarning, + "DisplayLog() called before logic-thread setup; " + "will not appear on in-game console."); } } } + // Print to any telnet clients. if (g_app && g_app->telnet_server) { - g_app->telnet_server->PushPrint(s); + g_app->telnet_server->PushPrint(msgnewline); } + + // Ship to platform-specific display mechanisms (android log, etc). + g_platform->DisplayLog(name, level, msg); } -void Logging::PrintStdout(const std::string& s, bool flush) { - fprintf(stdout, "%s", s.c_str()); - if (flush) { - fflush(stdout); - } - PrintCommon(s); -} - -void Logging::PrintStderr(const std::string& s, bool flush) { - fprintf(stderr, "%s", s.c_str()); - if (flush) { - fflush(stderr); - } - PrintCommon(s); -} - -void Logging::Log(const std::string& msg, bool to_stdout, bool to_server) { - if (to_stdout) { - PrintStdout(msg + "\n", true); +auto Logging::V1CloudLog(const std::string& msg) -> void { + // Route through platform-specific loggers if present. + // (things like Crashlytics crash-logging) + if (g_platform) { + Platform::DebugLog(msg); } - // Ship to the platform logging mechanism (android-log, stderr, etc.) - // if that's available yet. - if (g_platform != nullptr) { - g_platform->HandleLog(msg); - } - - // Ship to master-server/etc. - if (to_server) { - // Route through platform-specific loggers if present. - // (things like Crashlytics crash-logging) - if (g_platform) { - Platform::DebugLog(msg); - } - - // Add to our complete log. - if (g_app != nullptr) { - std::scoped_lock lock(g_app->log_mutex); - if (!g_app->log_full) { - (g_app->log) += (msg + "\n"); - if ((g_app->log).size() > 10000) { - // Allow some reasonable overflow for last statement. - if ((g_app->log).size() > 100000) { - // FIXME: This could potentially chop up utf-8 chars. - (g_app->log).resize(100000); - } - g_app->log += "\n\n"; - g_app->log_full = true; + // Add to our complete v1-cloud-log. + if (g_app != nullptr) { + std::scoped_lock lock(g_app->v1_cloud_log_mutex); + if (!g_app->v1_cloud_log_full) { + (g_app->v1_cloud_log) += (msg + "\n"); + if ((g_app->v1_cloud_log).size() > 10000) { + // Allow some reasonable overflow for last statement. + if ((g_app->v1_cloud_log).size() > 100000) { + // FIXME: This could potentially chop up utf-8 chars. + (g_app->v1_cloud_log).resize(100000); } + g_app->v1_cloud_log += "\n\n"; + g_app->v1_cloud_log_full = true; } } + } - // If the game is fully bootstrapped, let the Python layer handle logs. - // It will group log messages intelligently and ship them to the - // master server with various other context info included. - if (g_app && g_app->is_bootstrapped) { - assert(g_python != nullptr); - g_python->PushObjCall(Python::ObjID::kHandleLogCall); - } else { - // For log messages during bootstrapping we ship them immediately since - // we don't know if the Python layer is (or will be) able to. - if (g_early_log_writes > 0) { - g_early_log_writes -= 1; - std::string logprefix = "EARLY-LOG:"; - std::string logsuffix; + // If the game is fully bootstrapped, let the Python layer handle logs. + // It will group log messages intelligently and ship them to the + // master server with various other context info included. + if (g_app && g_app->is_bootstrapped) { + assert(g_python != nullptr); + g_python->PushObjCall(Python::ObjID::kHandleV1CloudLogCall); + } else { + // For log messages during bootstrapping we ship them immediately since + // we don't know if the Python layer is (or will be) able to. + if (g_early_v1_cloud_log_writes > 0) { + g_early_v1_cloud_log_writes -= 1; + std::string logprefix = "EARLY-LOG:"; + std::string logsuffix; - // If we're an early enough error, our global log isn't even available, - // so include this specific message as a suffix instead. - if (g_app == nullptr) { - logsuffix = msg; - } - g_app_internal->DirectSendLogs(logprefix, logsuffix, false); + // If we're an early enough error, our global log isn't even available, + // so include this specific message as a suffix instead. + if (g_app == nullptr) { + logsuffix = msg; } + g_app_internal->DirectSendV1CloudLogs(logprefix, logsuffix, false); } } } diff --git a/src/ballistica/core/logging.h b/src/ballistica/core/logging.h index 66d41ed9..6c7fb319 100644 --- a/src/ballistica/core/logging.h +++ b/src/ballistica/core/logging.h @@ -5,23 +5,33 @@ #include +#include "ballistica/ballistica.h" + namespace ballistica { class Logging { public: - /// Print a string directly to stdout as well as the in-game console - /// and any connected telnet consoles. - static auto PrintStdout(const std::string& s, bool flush = false) -> void; + /// Write a message to the log. Intended for logging use in C++ code. + /// This is safe to call by any thread at any time. In general it simply + /// passes through to the equivalent Python log calls: logging.info, + /// logging.warning, etc. + /// Note that log messages often must go through some background + /// processing before being seen by the user, meaning they may not + /// always work well for tight debugging purposes. In cases such as these, + /// printf() type calls or calling DisplayLog() directly may work better. + /// (though both of these should be avoided in permanent code). + static auto Log(LogLevel level, const std::string& msg) -> void; - /// Print a string directly to stderr as well as the in-game console - /// and any connected telnet consoles. - static auto PrintStderr(const std::string& s, bool flush = false) -> void; + /// Immediately display a log message in the in-game console, + /// platform-specific logs, etc. This generally should not be called + /// directly but instead wired up to display messages coming from the + /// Python logging system. + static auto DisplayLog(const std::string& name, LogLevel level, + const std::string& msg) -> void; - /// Write a string to the debug log. - /// This will go to stdout, windows debug log, android log, etc. depending - /// on the platform. - static auto Log(const std::string& msg, bool to_stdout = true, - bool to_server = true) -> void; + /// Write a message to the v1 cloud log. This is considered legacy + /// and will be phased out eventually. + static auto V1CloudLog(const std::string& msg) -> void; }; } // namespace ballistica diff --git a/src/ballistica/core/macros.cc b/src/ballistica/core/macros.cc index ed4a25bb..27f436bf 100644 --- a/src/ballistica/core/macros.cc +++ b/src/ballistica/core/macros.cc @@ -18,8 +18,8 @@ void MacroFunctionTimerEnd(millisecs_t starttime, millisecs_t time, } millisecs_t endtime = g_platform->GetTicks(); if (endtime - starttime > time) { - Log("Warning: " + std::to_string(endtime - starttime) - + " milliseconds spent in " + funcname); + Log(LogLevel::kWarning, std::to_string(endtime - starttime) + + " milliseconds spent in " + funcname); } } @@ -32,9 +32,9 @@ void MacroFunctionTimerEndThread(millisecs_t starttime, millisecs_t time, } millisecs_t endtime = g_platform->GetTicks(); if (endtime - starttime > time) { - Log("Warning: " + std::to_string(endtime - starttime) - + " milliseconds spent by " + ballistica::GetCurrentThreadName() - + " thread in " + funcname); + Log(LogLevel::kWarning, + std::to_string(endtime - starttime) + " milliseconds spent by " + + ballistica::GetCurrentThreadName() + " thread in " + funcname); } } @@ -47,8 +47,9 @@ void MacroFunctionTimerEndEx(millisecs_t starttime, millisecs_t time, } millisecs_t endtime = g_platform->GetTicks(); if (endtime - starttime > time) { - Log("Warning: " + std::to_string(endtime - starttime) - + " milliseconds spent in " + funcname + " for " + what); + Log(LogLevel::kWarning, std::to_string(endtime - starttime) + + " milliseconds spent in " + funcname + " for " + + what); } } @@ -62,9 +63,10 @@ void MacroFunctionTimerEndThreadEx(millisecs_t starttime, millisecs_t time, } millisecs_t endtime = g_platform->GetTicks(); if (endtime - starttime > time) { - Log("Warning: " + std::to_string(endtime - starttime) - + " milliseconds spent by " + ballistica::GetCurrentThreadName() - + " thread in " + funcname + " for " + what); + Log(LogLevel::kWarning, std::to_string(endtime - starttime) + + " milliseconds spent by " + + ballistica::GetCurrentThreadName() + + " thread in " + funcname + " for " + what); } } @@ -77,9 +79,9 @@ void MacroTimeCheckEnd(millisecs_t starttime, millisecs_t time, } millisecs_t e = g_platform->GetTicks(); if (e - starttime > time) { - Log(std::string("Warning: ") + name + " took " - + std::to_string(e - starttime) + " milliseconds; " + file + " line " - + std::to_string(line)); + Log(LogLevel::kWarning, + std::string(name) + " took " + std::to_string(e - starttime) + + " milliseconds; " + file + " line " + std::to_string(line)); } } @@ -88,19 +90,19 @@ void MacroLogErrorTrace(const std::string& msg, const char* fname, int line) { snprintf(buffer, sizeof(buffer), "%s:%d:", fname, line); buffer[sizeof(buffer) - 1] = 0; Python::PrintStackTrace(); - Log(std::string(buffer) + " error: " + msg); + Log(LogLevel::kError, std::string(buffer) + " error: " + msg); } void MacroLogError(const std::string& msg, const char* fname, int line) { char e_buffer[2048]; snprintf(e_buffer, sizeof(e_buffer), "%s:%d:", fname, line); e_buffer[sizeof(e_buffer) - 1] = 0; - ballistica::Log(std::string(e_buffer) + " error: " + msg); + Log(LogLevel::kError, std::string(e_buffer) + " error: " + msg); } void MacroLogPythonTrace(const std::string& msg) { Python::PrintStackTrace(); - Log(msg); + Log(LogLevel::kError, msg); } } // namespace ballistica diff --git a/src/ballistica/core/macros.h b/src/ballistica/core/macros.h index 65b81043..85c70105 100644 --- a/src/ballistica/core/macros.h +++ b/src/ballistica/core/macros.h @@ -86,11 +86,11 @@ } \ ((void)0) // (see 'Trailing-semicolon note' at top) -#define BA_LOG_ONCE(msg) \ +#define BA_LOG_ONCE(lvl, msg) \ { \ static bool did_log_here = false; \ if (!did_log_here) { \ - ballistica::Log(msg); \ + ballistica::Log(lvl, msg); \ did_log_here = true; \ } \ } \ @@ -120,12 +120,12 @@ /// Test a condition and simply print a log message if it fails (on both debug /// and release builds) -#define BA_PRECONDITION_LOG(b) \ - { \ - if (!(b)) { \ - Log("Precondition failed: " #b); \ - } \ - } \ +#define BA_PRECONDITION_LOG(b) \ + { \ + if (!(b)) { \ + Log(LogLevel::kError, "Precondition failed: " #b); \ + } \ + } \ ((void)0) // (see 'Trailing-semicolon note' at top) /// Test a condition and abort the program if it fails (on both debug diff --git a/src/ballistica/core/object.cc b/src/ballistica/core/object.cc index 15634bcf..ee3280ee 100644 --- a/src/ballistica/core/object.cc +++ b/src/ballistica/core/object.cc @@ -54,9 +54,9 @@ void Object::PrintObjects() { assert(count == g_app->object_count); } } - Log(s); + Log(LogLevel::kInfo, s); #else - Log("PrintObjects() only functions in debug builds."); + Log(LogLevel::kInfo, "PrintObjects() only functions in debug builds."); #endif // BA_DEBUG_BUILD } @@ -126,8 +126,8 @@ auto Object::GetObjectDescription() const -> std::string { + ">"; } -auto Object::GetDefaultOwnerThread() const -> ThreadIdentifier { - return ThreadIdentifier::kLogic; +auto Object::GetDefaultOwnerThread() const -> ThreadTag { + return ThreadTag::kLogic; } auto Object::GetThreadOwnership() const -> Object::ThreadOwnership { @@ -141,19 +141,19 @@ auto Object::GetThreadOwnership() const -> Object::ThreadOwnership { #if BA_DEBUG_BUILD -static auto GetCurrentThreadIdentifier() -> ThreadIdentifier { +static auto GetCurrentThreadTag() -> ThreadTag { if (InMainThread()) { - return ThreadIdentifier::kMain; + return ThreadTag::kMain; } else if (InLogicThread()) { - return ThreadIdentifier::kLogic; + return ThreadTag::kLogic; } else if (InAudioThread()) { - return ThreadIdentifier::kAudio; + return ThreadTag::kAudio; } else if (InNetworkWriteThread()) { - return ThreadIdentifier::kNetworkWrite; - } else if (InMediaThread()) { - return ThreadIdentifier::kMedia; + return ThreadTag::kNetworkWrite; + } else if (InAssetsThread()) { + return ThreadTag::kAssets; } else if (InBGDynamicsThread()) { - return ThreadIdentifier::kBGDynamics; + return ThreadTag::kBGDynamics; } else { throw Exception(std::string("unrecognized thread: ") + GetCurrentThreadName()); @@ -166,8 +166,8 @@ auto Object::ObjectUpdateForAcquire() -> void { // If we're set to use the next-referencing thread and haven't set one // yet, do so. if (thread_ownership == ThreadOwnership::kNextReferencing - && owner_thread_ == ThreadIdentifier::kInvalid) { - owner_thread_ = GetCurrentThreadIdentifier(); + && owner_thread_ == ThreadTag::kInvalid) { + owner_thread_ = GetCurrentThreadTag(); } } @@ -178,7 +178,7 @@ auto Object::ObjectThreadCheck() -> void { ThreadOwnership thread_ownership = GetThreadOwnership(); - ThreadIdentifier t; + ThreadTag t; if (thread_ownership == ThreadOwnership::kClassDefault) { t = GetDefaultOwnerThread(); } else { @@ -189,32 +189,32 @@ auto Object::ObjectThreadCheck() -> void { + "; expected " THREADNAME " thread; got " \ + GetCurrentThreadName()) switch (t) { - case ThreadIdentifier::kMain: + case ThreadTag::kMain: if (!InMainThread()) { DO_FAIL("Main"); } break; - case ThreadIdentifier::kLogic: + case ThreadTag::kLogic: if (!InLogicThread()) { DO_FAIL("Logic"); } break; - case ThreadIdentifier::kAudio: + case ThreadTag::kAudio: if (!InAudioThread()) { DO_FAIL("Audio"); } break; - case ThreadIdentifier::kNetworkWrite: + case ThreadTag::kNetworkWrite: if (!InNetworkWriteThread()) { DO_FAIL("NetworkWrite"); } break; - case ThreadIdentifier::kMedia: - if (!InMediaThread()) { - DO_FAIL("Media"); + case ThreadTag::kAssets: + if (!InAssetsThread()) { + DO_FAIL("Assets"); } break; - case ThreadIdentifier::kBGDynamics: + case ThreadTag::kBGDynamics: if (!InBGDynamicsThread()) { DO_FAIL("BGDynamics"); } diff --git a/src/ballistica/core/object.h b/src/ballistica/core/object.h index 974e5e45..6847e43b 100644 --- a/src/ballistica/core/object.h +++ b/src/ballistica/core/object.h @@ -45,7 +45,7 @@ class Object { /// it can perform sanity-tests to make sure references are not being /// added at incorrect times or from incorrect threads. /// The default implementation uses the per-object - /// ThreadOwnership/ThreadIdentifier values accessible below. NOTE: this + /// ThreadOwnership/ThreadTag values accessible below. NOTE: this /// check runs only in the debug build so don't add any logical side-effects! virtual void ObjectThreadCheck(); @@ -59,15 +59,15 @@ class Object { /// Return the exact thread to check for with ThreadOwnership::kClassDefault /// (in the default ObjectThreadCheck implementation at least). - /// Default returns ThreadIdentifier::kLogic - virtual auto GetDefaultOwnerThread() const -> ThreadIdentifier; + /// Default returns ThreadTag::kLogic + virtual auto GetDefaultOwnerThread() const -> ThreadTag; /// Set thread ownership for an individual object. void SetThreadOwnership(ThreadOwnership ownership) { #if BA_DEBUG_BUILD thread_ownership_ = ownership; if (thread_ownership_ == ThreadOwnership::kNextReferencing) { - owner_thread_ = ThreadIdentifier::kInvalid; + owner_thread_ = ThreadTag::kInvalid; } #endif } @@ -560,7 +560,7 @@ class Object { Object* object_next_{}; Object* object_prev_{}; ThreadOwnership thread_ownership_{ThreadOwnership::kClassDefault}; - ThreadIdentifier owner_thread_{ThreadIdentifier::kInvalid}; + ThreadTag owner_thread_{ThreadTag::kInvalid}; bool thread_checks_enabled_{true}; millisecs_t object_birth_time_{}; bool object_printed_warning_{}; diff --git a/src/ballistica/core/thread.cc b/src/ballistica/core/thread.cc index 4929338e..de2b5985 100644 --- a/src/ballistica/core/thread.cc +++ b/src/ballistica/core/thread.cc @@ -11,7 +11,6 @@ namespace ballistica { void Thread::SetInternalThreadName(const std::string& name) { std::scoped_lock lock(g_app->thread_name_map_mutex); - std::thread::id thread_id = std::this_thread::get_id(); g_app->thread_name_map[std::this_thread::get_id()] = name; } @@ -44,26 +43,54 @@ auto Thread::RunLogicThread(void* data) -> int { return static_cast(data)->ThreadMain(); } +auto Thread::RunLogicThreadP(void* data) -> void* { + static_cast(data)->ThreadMain(); + return nullptr; +} + auto Thread::RunAudioThread(void* data) -> int { return static_cast(data)->ThreadMain(); } +auto Thread::RunAudioThreadP(void* data) -> void* { + static_cast(data)->ThreadMain(); + return nullptr; +} auto Thread::RunBGDynamicThread(void* data) -> int { return static_cast(data)->ThreadMain(); } +auto Thread::RunBGDynamicThreadP(void* data) -> void* { + static_cast(data)->ThreadMain(); + return nullptr; +} + auto Thread::RunNetworkWriteThread(void* data) -> int { return static_cast(data)->ThreadMain(); } +auto Thread::RunNetworkWriteThreadP(void* data) -> void* { + static_cast(data)->ThreadMain(); + return nullptr; +} + auto Thread::RunStdInputThread(void* data) -> int { return static_cast(data)->ThreadMain(); } +auto Thread::RunStdInputThreadP(void* data) -> void* { + static_cast(data)->ThreadMain(); + return nullptr; +} -auto Thread::RunMediaThread(void* data) -> int { +auto Thread::RunAssetsThread(void* data) -> int { return static_cast(data)->ThreadMain(); } +auto Thread::RunAssetsThreadP(void* data) -> void* { + static_cast(data)->ThreadMain(); + return nullptr; +} + void Thread::SetPaused(bool paused) { // Can be toggled from the main thread only. assert(std::this_thread::get_id() == g_app->main_thread_id); @@ -85,7 +112,7 @@ void Thread::WaitForNextEvent(bool single_cycle) { } // While we're waiting, allow other python threads to run. - if (owns_python_) { + if (acquires_python_gil_) { g_python->ReleaseGIL(); } @@ -96,29 +123,29 @@ void Thread::WaitForNextEvent(bool single_cycle) { millisecs_t wait_time = timers_.GetTimeToNextExpire(real_time); if (wait_time > 0) { std::unique_lock lock(thread_message_mutex_); - if (thread_message_count_ == 0) { + if (thread_messages_.empty()) { thread_message_cv_.wait_for(lock, std::chrono::milliseconds(wait_time), [this] { // Go back to sleep on spurious wakeups // if we didn't wind up with any new // messages. - return (thread_message_count_ > 0); + return !thread_messages_.empty(); }); } } } else { // Not running timers; just wait indefinitely for the next message. std::unique_lock lock(thread_message_mutex_); - if (thread_message_count_ == 0) { + if (thread_messages_.empty()) { thread_message_cv_.wait(lock, [this] { // Go back to sleep on spurious wakeups // (if we didn't wind up with any new messages). - return (thread_message_count_ > 0); + return !(thread_messages_.empty()); }); } } - if (owns_python_) { + if (acquires_python_gil_) { g_python->AcquireGIL(); } } @@ -203,49 +230,69 @@ void Thread::GetThreadMessages(std::list* messages) { // Make sure they passed an empty one in. assert(messages->empty()); - if (thread_message_count_ > 0) { - std::unique_lock lock(thread_message_mutex_); - assert(thread_messages_.size() == thread_message_count_); + std::scoped_lock lock(thread_message_mutex_); + if (!thread_messages_.empty()) { messages->swap(thread_messages_); - thread_message_count_ = 0; } } -Thread::Thread(ThreadIdentifier identifier_in, ThreadType type_in) - : type_(type_in), identifier_(identifier_in) { - switch (type_) { - case ThreadType::kStandard: { - // Lock down until the thread is up and running. It'll unlock us when - // it's ready to go. +Thread::Thread(ThreadTag identifier_in, ThreadSource source) + : source_(source), identifier_(identifier_in) { + switch (source_) { + case ThreadSource::kCreate: { int (*func)(void*); + void* (*funcp)(void*); switch (identifier_) { - case ThreadIdentifier::kLogic: + case ThreadTag::kLogic: func = RunLogicThread; + funcp = RunLogicThreadP; break; - case ThreadIdentifier::kMedia: - func = RunMediaThread; + case ThreadTag::kAssets: + func = RunAssetsThread; + funcp = RunAssetsThreadP; break; - case ThreadIdentifier::kMain: + case ThreadTag::kMain: // Shouldn't happen; this thread gets wrapped; not launched. throw Exception(); - case ThreadIdentifier::kAudio: + case ThreadTag::kAudio: func = RunAudioThread; + funcp = RunAudioThreadP; break; - case ThreadIdentifier::kBGDynamics: + case ThreadTag::kBGDynamics: func = RunBGDynamicThread; + funcp = RunBGDynamicThreadP; break; - case ThreadIdentifier::kNetworkWrite: + case ThreadTag::kNetworkWrite: func = RunNetworkWriteThread; + funcp = RunNetworkWriteThreadP; break; - case ThreadIdentifier::kStdin: + case ThreadTag::kStdin: func = RunStdInputThread; + funcp = RunStdInputThreadP; break; default: throw Exception(); } - // Let 'er rip. - thread_ = new std::thread(func, this); + // Let 'er rip. + + // NOTE: Apple platforms have a default secondary thread stack size + // of 512k which I've found to be insufficient in cases of heavy + // Python recursion or large simulations. It sounds like Windows + // and Android might have 1mb as default; let's try to standardize + // on that across the board. Unfortunately we have to use pthreads + // to get custom stack sizes; std::thread stupidly doesn't support it. + + // FIXME - move this to platform. +#if BA_OSTYPE_MACOS || BA_OSTYPE_IOS_TVOS || BA_OSTYPE_LINUX + pthread_attr_t attr; + BA_PRECONDITION(pthread_attr_init(&attr) == 0); + BA_PRECONDITION(pthread_attr_setstacksize(&attr, 1024 * 1024) == 0); + pthread_t thread; + pthread_create(&thread, &attr, funcp, this); +#else + new std::thread(func, this); +#endif // Block until the thread is bootstrapped. // (maybe not necessary, but let's be cautious in case we'd @@ -255,7 +302,7 @@ Thread::Thread(ThreadIdentifier identifier_in, ThreadType type_in) break; } - case ThreadType::kMain: { + case ThreadSource::kWrapMain: { // We've got no thread of our own to launch // so we run our setup stuff right here instead of off in some. assert(std::this_thread::get_id() == g_app->main_thread_id); @@ -275,41 +322,41 @@ Thread::Thread(ThreadIdentifier identifier_in, ThreadType type_in) auto Thread::ThreadMain() -> int { try { - assert(type_ == ThreadType::kStandard); + assert(source_ == ThreadSource::kCreate); thread_id_ = std::this_thread::get_id(); const char* name; const char* id_string; switch (identifier_) { - case ThreadIdentifier::kLogic: + case ThreadTag::kLogic: name = "logic"; id_string = "ballistica logic"; break; - case ThreadIdentifier::kStdin: + case ThreadTag::kStdin: name = "stdin"; id_string = "ballistica stdin"; break; - case ThreadIdentifier::kMedia: - name = "media"; - id_string = "ballistica media"; + case ThreadTag::kAssets: + name = "assets"; + id_string = "ballistica assets"; break; - case ThreadIdentifier::kFileOut: + case ThreadTag::kFileOut: name = "fileout"; id_string = "ballistica file-out"; break; - case ThreadIdentifier::kMain: + case ThreadTag::kMain: name = "main"; id_string = "ballistica main"; break; - case ThreadIdentifier::kAudio: + case ThreadTag::kAudio: name = "audio"; id_string = "ballistica audio"; break; - case ThreadIdentifier::kBGDynamics: + case ThreadTag::kBGDynamics: name = "bgdynamics"; id_string = "ballistica bg-dynamics"; break; - case ThreadIdentifier::kNetworkWrite: + case ThreadTag::kNetworkWrite: name = "networkwrite"; id_string = "ballistica network writing"; break; @@ -322,10 +369,7 @@ auto Thread::ThreadMain() -> int { // Mark ourself as bootstrapped and signal listeners so // anyone waiting for us to spin up can move along. - { - std::scoped_lock lock(client_listener_mutex_); - bootstrapped_ = true; - } + bootstrapped_ = true; client_listener_cv_.notify_all(); // Now just run our loop until we die. @@ -358,15 +402,17 @@ auto Thread::ThreadMain() -> int { } } -void Thread::SetOwnsPython() { - owns_python_ = true; +void Thread::SetAcquiresPythonGIL() { + assert(!acquires_python_gil_); // This should be called exactly once. + assert(IsCurrent()); + acquires_python_gil_ = true; g_python->AcquireGIL(); } // Explicitly kill the main thread. void Thread::Quit() { - assert(type_ == ThreadType::kMain); - if (type_ == ThreadType::kMain) { + assert(source_ == ThreadSource::kWrapMain); + if (source_ == ThreadSource::kWrapMain) { done_ = true; } } @@ -382,8 +428,9 @@ void Thread::LogThreadMessageTally() { writing_tally_ = true; std::unordered_map tally; - Log("Thread message tally (" + std::to_string(thread_messages_.size()) - + " in list):"); + Log(LogLevel::kError, "Thread message tally (" + + std::to_string(thread_messages_.size()) + + " in list):"); for (auto&& m : thread_messages_) { std::string s; switch (m.type) { @@ -417,8 +464,8 @@ void Thread::LogThreadMessageTally() { } int entry = 1; for (auto&& i : tally) { - Log(" #" + std::to_string(entry++) + " (" + std::to_string(i.second) - + "x): " + i.first); + Log(LogLevel::kError, " #" + std::to_string(entry++) + " (" + + std::to_string(i.second) + "x): " + i.first); } writing_tally_ = false; } @@ -432,13 +479,10 @@ void Thread::PushThreadMessage(const ThreadMessage& t) { // Plop the data on to the list; we're assuming the mutex is locked. thread_messages_.push_back(t); - // Keep our own count; apparently size() on an stl list involves - // iterating. + // Keep our own count; apparently size() on an stl list involves iterating. // FIXME: Actually I don't think this is the case anymore; should check. - thread_message_count_++; - assert(thread_message_count_ == thread_messages_.size()); - // Show message count states. + // Debugging: show message count states. if (explicit_bool(false)) { static int one_off = 0; static int foo = 0; @@ -446,7 +490,7 @@ void Thread::PushThreadMessage(const ThreadMessage& t) { one_off++; // Show momemtary spikes. - if (thread_message_count_ > 100 && one_off > 100) { + if (thread_messages_.size() > 100 && one_off > 100) { one_off = 0; foo = 999; } @@ -454,24 +498,25 @@ void Thread::PushThreadMessage(const ThreadMessage& t) { // Show count periodically. if ((std::this_thread::get_id() == g_app->main_thread_id) && foo > 100) { foo = 0; - Log("MSG COUNT " + std::to_string(thread_message_count_)); + Log(LogLevel::kInfo, + "MSG COUNT " + std::to_string(thread_messages_.size())); } } - if (thread_message_count_ > 1000) { + if (thread_messages_.size() > 1000) { static bool sent_error = false; if (!sent_error) { sent_error = true; - Log("Error: ThreadMessage list > 1000 in thread: " - + GetCurrentThreadName()); + Log(LogLevel::kError, + "ThreadMessage list > 1000 in thread: " + GetCurrentThreadName()); LogThreadMessageTally(); } } // Prevent runaway mem usage if the list gets out of control. - if (thread_message_count_ > 10000) { - throw Exception("KILLING APP: ThreadMessage list > 10000 in thread: " - + GetCurrentThreadName()); + if (thread_messages_.size() > 10000) { + FatalError("ThreadMessage list > 10000 in thread: " + + GetCurrentThreadName()); } // Unlock thread-message list and inform thread that there's something @@ -618,5 +663,12 @@ auto Thread::CheckPushSafety() -> bool { return CheckPushRunnableSafety(); } } +auto Thread::CheckPushRunnableSafety() -> bool { + std::scoped_lock lock(client_listener_mutex_); + + // We first complain when we get to 1000 queued messages so + // let's consider things unsafe when we're halfway there. + return (thread_messages_.size() < kThreadMessageSafetyThreshold); +} } // namespace ballistica diff --git a/src/ballistica/core/thread.h b/src/ballistica/core/thread.h index 4feabd0d..d5a63d01 100644 --- a/src/ballistica/core/thread.h +++ b/src/ballistica/core/thread.h @@ -23,8 +23,7 @@ const int kThreadMessageSafetyThreshold{500}; // A thread with a built-in event loop. class Thread { public: - explicit Thread(ThreadIdentifier id, - ThreadType type_in = ThreadType::kStandard); + explicit Thread(ThreadTag id, ThreadSource source = ThreadSource::kCreate); virtual ~Thread(); auto ClearCurrentThreadName() -> void; @@ -44,7 +43,7 @@ class Thread { // Used to quit the main thread. void Quit(); - void SetOwnsPython(); + void SetAcquiresPythonGIL(); void SetPaused(bool paused); auto thread_id() const -> std::thread::id { return thread_id_; } @@ -55,13 +54,7 @@ class Thread { void set_thread_id(std::thread::id id) { thread_id_ = id; } auto RunEventLoop(bool single_cycle = false) -> int; - auto identifier() const -> ThreadIdentifier { return identifier_; } - - auto CheckPushRunnableSafety() -> bool { - // We first complain when we get to 1000 queued messages so - // let's consider things unsafe when we're halfway there. - return (thread_message_count_ < kThreadMessageSafetyThreshold); - } + auto identifier() const -> ThreadTag { return identifier_; } // Register a timer to run on the thread. auto NewTimer(millisecs_t length, bool repeat, @@ -116,7 +109,7 @@ class Thread { explicit ThreadMessage(Type type, Runnable* runnable, bool* completion_flag) : type(type), runnable(runnable), completion_flag{completion_flag} {} }; - + auto CheckPushRunnableSafety() -> bool; auto SetInternalThreadName(const std::string& name) -> void; auto WaitForNextEvent(bool single_cycle) -> void; auto LoopUpkeep(bool once) -> void; @@ -132,12 +125,12 @@ class Thread { int messages_since_paused_{}; millisecs_t last_paused_message_report_time_{}; bool done_{}; - ThreadType type_; + ThreadSource source_; int listen_sd_{}; std::thread::id thread_id_{}; - ThreadIdentifier identifier_{ThreadIdentifier::kInvalid}; + ThreadTag identifier_{ThreadTag::kInvalid}; millisecs_t last_complaint_time_{}; - bool owns_python_{}; + bool acquires_python_gil_{}; // FIXME: Should generalize this to some sort of PlatformThreadData class. #if BA_XCODE_BUILD @@ -148,11 +141,17 @@ class Thread { // different thread groups makes its easy to see which thread is which // in profilers, backtraces, etc. static auto RunLogicThread(void* data) -> int; + static auto RunLogicThreadP(void* data) -> void*; static auto RunAudioThread(void* data) -> int; + static auto RunAudioThreadP(void* data) -> void*; static auto RunBGDynamicThread(void* data) -> int; + static auto RunBGDynamicThreadP(void* data) -> void*; static auto RunNetworkWriteThread(void* data) -> int; + static auto RunNetworkWriteThreadP(void* data) -> void*; static auto RunStdInputThread(void* data) -> int; - static auto RunMediaThread(void* data) -> int; + static auto RunStdInputThreadP(void* data) -> void*; + static auto RunAssetsThread(void* data) -> int; + static auto RunAssetsThreadP(void* data) -> void*; auto ThreadMain() -> int; auto GetThreadMessages(std::list* messages) -> void; @@ -162,12 +161,11 @@ class Thread { auto RunPauseCallbacks() -> void; auto RunResumeCallbacks() -> void; - int thread_message_count_{}; bool bootstrapped_{}; std::list> runnables_; std::list pause_callbacks_; std::list resume_callbacks_; - std::thread* thread_{}; + // std::thread* thread_{}; std::condition_variable thread_message_cv_; std::mutex thread_message_mutex_; std::list thread_messages_; diff --git a/src/ballistica/core/types.h b/src/ballistica/core/types.h index ffb27cf6..663d0ad9 100644 --- a/src/ballistica/core/types.h +++ b/src/ballistica/core/types.h @@ -34,12 +34,12 @@ typedef int64_t millisecs_t; // avoid pulling in their full headers as much as possible // to keep compile times down. -class Account; class AppFlavor; class AppConfig; class App; class AppInternal; class AreaOfInterest; +class Assets; class Audio; class AudioServer; class AudioStreamer; @@ -81,7 +81,6 @@ class DataData; class Dynamics; class FrameDef; struct FriendScoreSet; -class Game; class GLContext; class GlobalsNode; class Graphics; @@ -97,15 +96,15 @@ struct JointFixedEF; class Joystick; class JsonDict; class KeyboardInput; +class Logic; class Material; class MaterialAction; class MaterialComponent; class MaterialConditionNode; class MaterialContext; class Matrix44f; -class Media; -class MediaComponentData; -class MediaServer; +class AssetComponentData; +class AssetsServer; class MeshBufferBase; class MeshBufferVertexSprite; class MeshBufferVertexSimpleFull; @@ -124,7 +123,7 @@ class NetClientThread; class NetGraph; class Networking; class NetworkReader; -class NetworkWriteModule; +class NetworkWriter; class Node; class NodeType; class NodeAttribute; @@ -132,7 +131,6 @@ class NodeAttributeConnection; class NodeAttributeUnbound; class Object; class ObjectComponent; -class GameStream; class Part; class Python; class Platform; @@ -164,6 +162,8 @@ class RootUI; class RootWidget; class Runnable; class Scene; +class SceneV1; +class SceneStream; class ScoreToBeat; class SDLApp; class SDLContext; @@ -174,7 +174,7 @@ class SoundData; class SpriteMesh; class StackWidget; class StressTest; -class StdInputModule; +class StdioConsole; class Module; class TelnetServer; class TestInput; @@ -197,6 +197,7 @@ class Vector2f; class Vector3f; class Vector4f; class AppFlavorVR; +class V1Account; class VRGraphics; class Widget; @@ -403,7 +404,7 @@ enum class SpecialChar { kLast // Sentinel }; -enum class MediaType { kTexture, kCollideModel, kModel, kSound, kData, kLast }; +enum class AssetType { kTexture, kCollideModel, kModel, kSound, kData, kLast }; /// Python exception types we can raise from our own exceptions. enum class PyExcType { @@ -423,6 +424,8 @@ enum class PyExcType { kWidgetNotFound }; +enum class LogLevel { kDebug, kInfo, kWarning, kError, kCritical }; + enum class SystemTextureID { kUIAtlas, kButtonSquare, @@ -986,20 +989,20 @@ enum class NodeAttributeType { kCollideModelArray }; -enum class ThreadType { +enum class ThreadSource { /// A normal thread spun up by us. - kStandard, + kCreate, /// For wrapping a ballistica thread around the existing main thread. - kMain + kWrapMain }; /// Used for module-thread identification. /// Mostly just for debugging, through a few things are affected by this /// (the Logic thread manages the python GIL, etc). -enum class ThreadIdentifier { +enum class ThreadTag { kInvalid, kLogic, - kMedia, + kAssets, kFileOut, kMain, kAudio, diff --git a/src/ballistica/dynamics/bg/bg_dynamics.cc b/src/ballistica/dynamics/bg/bg_dynamics.cc index dacfbce6..6a3565f3 100644 --- a/src/ballistica/dynamics/bg/bg_dynamics.cc +++ b/src/ballistica/dynamics/bg/bg_dynamics.cc @@ -2,6 +2,7 @@ #include "ballistica/dynamics/bg/bg_dynamics.h" +#include "ballistica/assets/component/collide_model.h" #include "ballistica/core/thread.h" #include "ballistica/dynamics/bg/bg_dynamics_draw_snapshot.h" #include "ballistica/dynamics/bg/bg_dynamics_fuse_data.h" @@ -10,19 +11,12 @@ #include "ballistica/graphics/component/object_component.h" #include "ballistica/graphics/component/smoke_component.h" #include "ballistica/graphics/component/sprite_component.h" -#include "ballistica/media/component/collide_model.h" namespace ballistica { -void BGDynamics::Init() { - // Just init our singleton. - new BGDynamics(); -} - BGDynamics::BGDynamics() { - assert(InLogicThread()); + // We're a singleton; make sure we don't already exist. assert(g_bg_dynamics == nullptr); - g_bg_dynamics = this; } void BGDynamics::AddTerrain(CollideModelData* o) { @@ -188,7 +182,7 @@ void BGDynamics::Draw(FrameDef* frame_def) { c.SetCameraAligned(true); c.SetColor(2.0f, 2.0f, 2.0f, 1.0f); c.SetOverlay(draw_in_overlay); - c.SetTexture(g_media->GetTexture(SystemTextureID::kSparks)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kSparks)); c.DrawMesh(sparks_mesh_.get(), kModelDrawFlagNoReflection); c.Submit(); } @@ -203,7 +197,7 @@ void BGDynamics::Draw(FrameDef* frame_def) { lights_mesh_->SetData( Object::Ref>(ds->light_vertices)); SpriteComponent c(frame_def->light_shadow_pass()); - c.SetTexture(g_media->GetTexture(SystemTextureID::kLightSoft)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kLightSoft)); c.DrawMesh(lights_mesh_.get()); c.Submit(); } @@ -216,7 +210,7 @@ void BGDynamics::Draw(FrameDef* frame_def) { shadows_mesh_->SetData( Object::Ref>(ds->shadow_vertices)); SpriteComponent c(frame_def->light_shadow_pass()); - c.SetTexture(g_media->GetTexture(SystemTextureID::kLight)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kLight)); c.DrawMesh(shadows_mesh_.get()); c.Submit(); } @@ -268,7 +262,7 @@ void BGDynamics::Draw(FrameDef* frame_def) { Object::Ref>(ds->fuse_vertices)); { // Draw! ObjectComponent c(frame_def->beauty_pass()); - c.SetTexture(g_media->GetTexture(SystemTextureID::kFuse)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kFuse)); c.DrawMesh(fuses_mesh_.get(), kModelDrawFlagNoReflection); c.Submit(); } @@ -286,16 +280,16 @@ void BGDynamics::DrawChunks(FrameDef* frame_def, ModelData* model; switch (chunk_type) { case BGDynamicsChunkType::kFlagStand: - model = g_media->GetModel(SystemModelID::kFlagStand); + model = g_assets->GetModel(SystemModelID::kFlagStand); break; case BGDynamicsChunkType::kSplinter: - model = g_media->GetModel(SystemModelID::kShrapnelBoard); + model = g_assets->GetModel(SystemModelID::kShrapnelBoard); break; case BGDynamicsChunkType::kSlime: - model = g_media->GetModel(SystemModelID::kShrapnelSlime); + model = g_assets->GetModel(SystemModelID::kShrapnelSlime); break; default: - model = g_media->GetModel(SystemModelID::kShrapnel1); + model = g_assets->GetModel(SystemModelID::kShrapnel1); break; } ObjectComponent c(frame_def->beauty_pass()); @@ -303,20 +297,20 @@ void BGDynamics::DrawChunks(FrameDef* frame_def, // Set up shading. switch (chunk_type) { case BGDynamicsChunkType::kRock: { - c.SetTexture(g_media->GetTexture(SystemTextureID::kShrapnel1)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kShrapnel1)); c.SetReflection(ReflectionType::kSoft); c.SetReflectionScale(0.2f, 0.2f, 0.2f); c.SetColor(0.6f, 0.6f, 0.5f); break; } case BGDynamicsChunkType::kIce: { - c.SetTexture(g_media->GetTexture(SystemTextureID::kShrapnel1)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kShrapnel1)); c.SetReflection(ReflectionType::kSharp); c.SetAddColor(0.5f, 0.5f, 0.9f); break; } case BGDynamicsChunkType::kSlime: { - c.SetTexture(g_media->GetTexture(SystemTextureID::kShrapnel1)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kShrapnel1)); c.SetReflection(ReflectionType::kSharper); c.SetReflectionScale(3.0f, 3.0f, 3.0f); c.SetColor(0.0f, 0.0f, 0.0f); @@ -324,13 +318,13 @@ void BGDynamics::DrawChunks(FrameDef* frame_def, break; } case BGDynamicsChunkType::kMetal: { - c.SetTexture(g_media->GetTexture(SystemTextureID::kShrapnel1)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kShrapnel1)); c.SetReflection(ReflectionType::kPowerup); c.SetColor(0.5f, 0.5f, 0.55f); break; } case BGDynamicsChunkType::kSpark: { - c.SetTexture(g_media->GetTexture(SystemTextureID::kShrapnel1)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kShrapnel1)); c.SetReflection(ReflectionType::kSharp); c.SetColor(0.0f, 0.0f, 0.0f, 1.0f); c.SetReflectionScale(4.0f, 3.0f, 2.0f); @@ -338,7 +332,7 @@ void BGDynamics::DrawChunks(FrameDef* frame_def, break; } case BGDynamicsChunkType::kSplinter: { - c.SetTexture(g_media->GetTexture(SystemTextureID::kShrapnel1)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kShrapnel1)); c.SetReflection(ReflectionType::kSoft); c.SetColor(1.0f, 0.8f, 0.5f); break; @@ -347,7 +341,7 @@ void BGDynamics::DrawChunks(FrameDef* frame_def, c.SetTransparent(true); c.SetPremultiplied(true); c.SetLightShadow(LightShadowType::kNone); - c.SetTexture(g_media->GetTexture(SystemTextureID::kShrapnel1)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kShrapnel1)); c.SetReflection(ReflectionType::kSharp); c.SetReflectionScale(0.5f, 0.4f, 0.3f); c.SetColor(0.2f, 0.15f, 0.15f, 0.07f); @@ -355,7 +349,7 @@ void BGDynamics::DrawChunks(FrameDef* frame_def, break; } case BGDynamicsChunkType::kFlagStand: { - c.SetTexture(g_media->GetTexture(SystemTextureID::kFlagPole)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kFlagPole)); c.SetReflection(ReflectionType::kSharp); c.SetColor(0.9f, 0.6f, 0.3f, 1.0f); break; diff --git a/src/ballistica/dynamics/bg/bg_dynamics.h b/src/ballistica/dynamics/bg/bg_dynamics.h index 1115f547..264b0b26 100644 --- a/src/ballistica/dynamics/bg/bg_dynamics.h +++ b/src/ballistica/dynamics/bg/bg_dynamics.h @@ -45,10 +45,10 @@ class BGDynamicsEmission { BGDynamicsTendrilType tendril_type{BGDynamicsTendrilType::kSmoke}; }; -// client (game thread) functionality for bg dynamics +// client (logic thread) functionality for bg dynamics class BGDynamics { public: - static void Init(); + BGDynamics(); void Emit(const BGDynamicsEmission& def); void Step(const Vector3f& cam_pos); @@ -68,7 +68,6 @@ class BGDynamics { void SetDrawSnapshot(BGDynamicsDrawSnapshot* s); private: - BGDynamics(); void DrawChunks(FrameDef* frame_def, std::vector* instances, BGDynamicsChunkType chunk_type); Object::Ref lights_mesh_; diff --git a/src/ballistica/dynamics/bg/bg_dynamics_draw_snapshot.h b/src/ballistica/dynamics/bg/bg_dynamics_draw_snapshot.h index 625b8f76..777843ae 100644 --- a/src/ballistica/dynamics/bg/bg_dynamics_draw_snapshot.h +++ b/src/ballistica/dynamics/bg/bg_dynamics_draw_snapshot.h @@ -10,7 +10,7 @@ namespace ballistica { // Big chunk of data sent back from the bg-dynamics server thread -// to the game thread for drawing. +// to the logic thread for drawing. class BGDynamicsDrawSnapshot { public: struct TendrilShadow { diff --git a/src/ballistica/dynamics/bg/bg_dynamics_server.cc b/src/ballistica/dynamics/bg/bg_dynamics_server.cc index 427d0edd..cbcd8821 100644 --- a/src/ballistica/dynamics/bg/bg_dynamics_server.cc +++ b/src/ballistica/dynamics/bg/bg_dynamics_server.cc @@ -2,6 +2,7 @@ #include "ballistica/dynamics/bg/bg_dynamics_server.h" +#include "ballistica/assets/component/collide_model.h" #include "ballistica/core/thread.h" #include "ballistica/dynamics/bg/bg_dynamics_draw_snapshot.h" #include "ballistica/dynamics/bg/bg_dynamics_fuse_data.h" @@ -9,10 +10,9 @@ #include "ballistica/dynamics/bg/bg_dynamics_shadow_data.h" #include "ballistica/dynamics/bg/bg_dynamics_volume_light_data.h" #include "ballistica/dynamics/collision_cache.h" -#include "ballistica/game/game.h" #include "ballistica/generic/utils.h" #include "ballistica/graphics/graphics_server.h" -#include "ballistica/media/component/collide_model.h" +#include "ballistica/logic/logic.h" namespace ballistica { @@ -87,7 +87,7 @@ class BGDynamicsServer::Terrain { // back to the main thread to get freed. if (collide_model_) { Object::Ref* ref = collide_model_; - g_game->thread()->PushCall([ref] { + g_logic->thread()->PushCall([ref] { (**ref).set_last_used_time(GetRealTime()); delete ref; }); @@ -539,12 +539,12 @@ void BGDynamicsServer::ParticleSet::UpdateAndCreateSnapshot( auto* ibuf = Object::NewDeferred(p_count * 6); - // Game thread is default owner; needs to be us until we hand it over. + // Logic thread is default owner; needs to be us until we hand it over. ibuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing); *index_buffer = Object::MakeRefCounted(ibuf); auto* vbuf = Object::NewDeferred(p_count * 4); - // Game thread is default owner; needs to be us until we hand it over. + // Logic thread is default owner; needs to be us until we hand it over. vbuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing); *buffer = Object::MakeRefCounted(vbuf); @@ -660,12 +660,15 @@ void BGDynamicsServer::ParticleSet::UpdateAndCreateSnapshot( current_set = !current_set; } -BGDynamicsServer::BGDynamicsServer(Thread* thread) - : thread_(thread), - height_cache_(new BGDynamicsHeightCache()), +BGDynamicsServer::BGDynamicsServer() + : height_cache_(new BGDynamicsHeightCache()), collision_cache_(new CollisionCache) { + // We're a singleton; make sure we don't already exist. BA_PRECONDITION(g_bg_dynamics_server == nullptr); - g_bg_dynamics_server = this; + + // Spin up our thread. + thread_ = new Thread(ThreadTag::kBGDynamics); + g_app->pausable_threads.push_back(thread_); // NOLINTNEXTLINE(cppcoreguidelines-prefer-member-initializer) ode_world_ = dWorldCreate(); @@ -1356,7 +1359,8 @@ void BGDynamicsServer::Emit(const BGDynamicsEmission& def) { } default: { int t = static_cast(def.emit_type); - BA_LOG_ONCE("Invalid bg-dynamics emit type: " + std::to_string(t)); + BA_LOG_ONCE(LogLevel::kError, + "Invalid bg-dynamics emit type: " + std::to_string(t)); break; } } @@ -1593,14 +1597,14 @@ auto BGDynamicsServer::CreateDrawSnapshot() -> BGDynamicsDrawSnapshot* { if (shadow_max_count > 0) { auto* ibuf = Object::NewDeferred(shadow_max_count * 6); - // Game thread is default owner; needs to be us until we hand it over. + // Logic thread is default owner; needs to be us until we hand it over. ibuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing); ss->shadow_indices = Object::MakeRefCounted(ibuf); s_index = &ss->shadow_indices->elements[0]; auto* vbuf = Object::NewDeferred(shadow_max_count * 4); - // Game thread is default owner; needs to be us until we hand it over. + // Logic thread is default owner; needs to be us until we hand it over. vbuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing); ss->shadow_vertices = Object::MakeRefCounted(vbuf); s_vertex = &ss->shadow_vertices->elements[0]; @@ -1609,14 +1613,14 @@ auto BGDynamicsServer::CreateDrawSnapshot() -> BGDynamicsDrawSnapshot* { if (light_max_count > 0) { auto* ibuf = Object::NewDeferred(light_max_count * 6); - // Game thread is default owner; needs to be us until we hand it over. + // Logic thread is default owner; needs to be us until we hand it over. ibuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing); ss->light_indices = Object::MakeRefCounted(ibuf); l_index = &ss->light_indices->elements[0]; auto* vbuf = Object::NewDeferred(light_max_count * 4); - // Game thread is default owner; needs to be us until we hand it over. + // Logic thread is default owner; needs to be us until we hand it over. vbuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing); ss->light_vertices = Object::MakeRefCounted(vbuf); l_vertex = &ss->light_vertices->elements[0]; @@ -1965,14 +1969,14 @@ auto BGDynamicsServer::CreateDrawSnapshot() -> BGDynamicsDrawSnapshot* { if (smoke_slice_count > 0) { auto* ibuf = Object::NewDeferred(smoke_index_count); - // Game thread is default owner; needs to be us until we hand it over. + // Logic thread is default owner; needs to be us until we hand it over. ibuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing); ss->tendril_indices = Object::MakeRefCounted(ibuf); uint16_t* index = &ss->tendril_indices->elements[0]; auto* vbuf = Object::NewDeferred(smoke_slice_count * 2); - // Game thread is default owner; needs to be us until we hand it over. + // Logic thread is default owner; needs to be us until we hand it over. vbuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing); ss->tendril_vertices = Object::MakeRefCounted(vbuf); VertexSmokeFull* v = &ss->tendril_vertices->elements[0]; @@ -2130,13 +2134,13 @@ auto BGDynamicsServer::CreateDrawSnapshot() -> BGDynamicsDrawSnapshot* { auto* ibuf = Object::NewDeferred(index_count); - // Game thread is default owner; needs to be us until we hand it over. + // Logic thread is default owner; needs to be us until we hand it over. ibuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing); ss->fuse_indices = Object::MakeRefCounted(ibuf); auto* vbuf = Object::NewDeferred(vertex_count); - // Game thread is default owner; needs to be us until we hand it over. + // Logic thread is default owner; needs to be us until we hand it over. vbuf->SetThreadOwnership(Object::ThreadOwnership::kNextReferencing); ss->fuse_vertices = Object::MakeRefCounted(vbuf); @@ -2312,7 +2316,7 @@ void BGDynamicsServer::Step(StepData* step_data) { // Now generate a snapshot of our state and send it to the game thread, // so they can draw us. BGDynamicsDrawSnapshot* snapshot = CreateDrawSnapshot(); - g_game->thread()->PushCall([snapshot] { + g_logic->thread()->PushCall([snapshot] { snapshot->SetLogicThreadOwnership(); g_bg_dynamics->SetDrawSnapshot(snapshot); }); diff --git a/src/ballistica/dynamics/bg/bg_dynamics_server.h b/src/ballistica/dynamics/bg/bg_dynamics_server.h index 90a229f5..8e6a2e61 100644 --- a/src/ballistica/dynamics/bg/bg_dynamics_server.h +++ b/src/ballistica/dynamics/bg/bg_dynamics_server.h @@ -66,8 +66,8 @@ class BGDynamicsServer { }; class StepData : public Object { public: - auto GetDefaultOwnerThread() const -> ThreadIdentifier override { - return ThreadIdentifier::kBGDynamics; + auto GetDefaultOwnerThread() const -> ThreadTag override { + return ThreadTag::kBGDynamics; } Vector3f cam_pos{0.0f, 0.0f, 0.0f}; @@ -80,7 +80,7 @@ class BGDynamicsServer { std::vector > fuse_step_data_; }; - explicit BGDynamicsServer(Thread* thread); + BGDynamicsServer(); auto time() const -> uint32_t { return time_; } auto graphics_quality() const -> GraphicsQuality { return graphics_quality_; } diff --git a/src/ballistica/dynamics/collision_cache.cc b/src/ballistica/dynamics/collision_cache.cc index dc3306d1..8568d0f6 100644 --- a/src/ballistica/dynamics/collision_cache.cc +++ b/src/ballistica/dynamics/collision_cache.cc @@ -17,12 +17,12 @@ CollisionCache::~CollisionCache() { dGeomDestroy(test_box_); } -void CollisionCache::SetGeoms(const std::vector& geoms) { +auto CollisionCache::SetGeoms(const std::vector& geoms) -> void { dirty_ = true; geoms_ = geoms; } -void CollisionCache::Draw(FrameDef* frame_def) { +auto CollisionCache::Draw(FrameDef* frame_def) -> void { if (cells_.empty()) { return; } @@ -36,7 +36,7 @@ void CollisionCache::Draw(FrameDef* frame_def) { c.Scale(x_max_ - x_min_, 1, z_max_ - z_min_); c.PushTransform(); c.Scale(1, 0.01f, 1); - c.DrawModel(g_media->GetModel(SystemModelID::kBox)); + c.DrawModel(g_assets->GetModel(SystemModelID::kBox)); c.PopTransform(); c.Translate(-0.5f + 0.5f * cell_width, 0, -0.5f + 0.5f * cell_height); for (int x = 0; x < grid_width_; x++) { @@ -53,7 +53,7 @@ void CollisionCache::Draw(FrameDef* frame_def) { cells_[cell_index].height_confirmed_collide_, static_cast(z) / static_cast(grid_height_)); c.Scale(0.95f * cell_width, 0.01f, 0.95f * cell_height); - c.DrawModel(g_media->GetModel(SystemModelID::kBox)); + c.DrawModel(g_assets->GetModel(SystemModelID::kBox)); c.PopTransform(); if (glow_[cell_index]) { c.SetColor(1, 1, 1, 0.2f); @@ -65,7 +65,7 @@ void CollisionCache::Draw(FrameDef* frame_def) { cells_[cell_index].height_confirmed_empty_, static_cast(z) / static_cast(grid_height_)); c.Scale(0.95f * cell_width, 0.01f, 0.95f * cell_height); - c.DrawModel(g_media->GetModel(SystemModelID::kBox)); + c.DrawModel(g_assets->GetModel(SystemModelID::kBox)); c.PopTransform(); glow_[cell_index] = 0; } @@ -84,7 +84,7 @@ void CollisionCache::Draw(FrameDef* frame_def) { c2.Scale(x_max_ - x_min_, 1, z_max_ - z_min_); c2.PushTransform(); c2.Scale(1, 0.01f, 1); - c2.DrawModel(g_media->GetModel(SystemModelID::kBox)); + c2.DrawModel(g_assets->GetModel(SystemModelID::kBox)); c2.PopTransform(); c2.Translate(-0.5f + 0.5f * cell_width2, 0, -0.5f + 0.5f * cell_height2); for (int x = 0; x < grid_width_; x++) { @@ -101,7 +101,7 @@ void CollisionCache::Draw(FrameDef* frame_def) { cells_[cell_index].height_confirmed_empty_, static_cast(z) / static_cast(grid_height_)); c2.Scale(0.95f * cell_width2, 0.01f, 0.95f * cell_height2); - c2.DrawModel(g_media->GetModel(SystemModelID::kBox)); + c2.DrawModel(g_assets->GetModel(SystemModelID::kBox)); c2.PopTransform(); if (glow_[cell_index]) { c2.SetColor(1, 1, 1, 0.2f); @@ -113,7 +113,7 @@ void CollisionCache::Draw(FrameDef* frame_def) { cells_[cell_index].height_confirmed_collide_, static_cast(z) / static_cast(grid_height_)); c2.Scale(0.95f * cell_width2, 0.01f, 0.95f * cell_height2); - c2.DrawModel(g_media->GetModel(SystemModelID::kBox)); + c2.DrawModel(g_assets->GetModel(SystemModelID::kBox)); c2.PopTransform(); glow_[cell_index] = 0; @@ -124,7 +124,7 @@ void CollisionCache::Draw(FrameDef* frame_def) { } } -void CollisionCache::Precalc() { +auto CollisionCache::Precalc() -> void { Update(); if (precalc_index_ >= cells_.size()) { @@ -138,8 +138,8 @@ void CollisionCache::Precalc() { TestCell(precalc_index_++, x, z); } -void CollisionCache::CollideAgainstGeom(dGeomID g1, void* data, - dNearCallback* callback) { +auto CollisionCache::CollideAgainstGeom(dGeomID g1, void* data, + dNearCallback* callback) -> void { // Update bounds, test for quick out against our height map, // and proceed to a full test on a positive result. g1->recomputeAABB(); @@ -204,7 +204,7 @@ void CollisionCache::CollideAgainstGeom(dGeomID g1, void* data, } } -void CollisionCache::TestCell(size_t cell_index, int x, int z) { +auto CollisionCache::TestCell(size_t cell_index, int x, int z) -> void { int t_count = static_cast(geoms_.size()); float top = cells_[cell_index].height_confirmed_empty_; @@ -252,8 +252,8 @@ void CollisionCache::TestCell(size_t cell_index, int x, int z) { } } -void CollisionCache::CollideAgainstSpace(dSpaceID space, void* data, - dNearCallback* callback) { +auto CollisionCache::CollideAgainstSpace(dSpaceID space, void* data, + dNearCallback* callback) -> void { // We handle our own testing against trimeshes, so we can bring our fancy // caching into play. if (!geoms_.empty()) { @@ -264,7 +264,7 @@ void CollisionCache::CollideAgainstSpace(dSpaceID space, void* data, } } -void CollisionCache::Update() { +auto CollisionCache::Update() -> void { if (!dirty_) { return; } diff --git a/src/ballistica/dynamics/collision_cache.h b/src/ballistica/dynamics/collision_cache.h index 471f7c9a..ea8e02c7 100644 --- a/src/ballistica/dynamics/collision_cache.h +++ b/src/ballistica/dynamics/collision_cache.h @@ -27,7 +27,7 @@ class CollisionCache { // Call this periodically (once per cycle or so) to slowly fill in // the cache so there's less to do during spurts of activity; - void Precalc(); + auto Precalc() -> void; private: auto TestCell(size_t cell_index, int x, int z) -> void; diff --git a/src/ballistica/dynamics/dynamics.cc b/src/ballistica/dynamics/dynamics.cc index 813f1814..3b9aa0b1 100644 --- a/src/ballistica/dynamics/dynamics.cc +++ b/src/ballistica/dynamics/dynamics.cc @@ -2,6 +2,7 @@ #include "ballistica/dynamics/dynamics.h" +#include "ballistica/assets/component/sound.h" #include "ballistica/audio/audio.h" #include "ballistica/audio/audio_source.h" #include "ballistica/dynamics/collision.h" @@ -9,7 +10,6 @@ #include "ballistica/dynamics/material/material_action.h" #include "ballistica/dynamics/part.h" #include "ballistica/graphics/renderer.h" -#include "ballistica/media/component/sound.h" #include "ballistica/scene/scene.h" #include "ode/ode_collision_kernel.h" #include "ode/ode_collision_util.h" @@ -141,7 +141,8 @@ Dynamics::Dynamics(Scene* scene_in) Dynamics::~Dynamics() { if (in_process_) { - Log("Error: Dynamics going down within Process() call;" + Log(LogLevel::kError, + "Dynamics going down within Process() call;" " should not happen."); } ShutdownODE(); @@ -157,7 +158,7 @@ void Dynamics::Draw(FrameDef* frame_def) { c.PushTransform(); c.Translate(i.x(), i.y(), i.z()); c.scaleUniform(0.05f); - c.DrawModel(g_media->GetModel(Media::BOX_MODEL)); + c.DrawModel(g_assets->GetModel(Assets::BOX_MODEL)); c.PopTransform(); } c.Submit(); diff --git a/src/ballistica/dynamics/material/impact_sound_material_action.cc b/src/ballistica/dynamics/material/impact_sound_material_action.cc index fa06c9ea..fd4952a4 100644 --- a/src/ballistica/dynamics/material/impact_sound_material_action.cc +++ b/src/ballistica/dynamics/material/impact_sound_material_action.cc @@ -4,10 +4,10 @@ #include "ballistica/dynamics/dynamics.h" #include "ballistica/dynamics/material/material_context.h" -#include "ballistica/game/game_stream.h" -#include "ballistica/game/session/client_session.h" #include "ballistica/generic/utils.h" #include "ballistica/graphics/graphics_server.h" +#include "ballistica/logic/session/client_session.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { @@ -17,7 +17,7 @@ auto ImpactSoundMaterialAction::GetFlattenedSize() -> size_t { } void ImpactSoundMaterialAction::Flatten(char** buffer, - GameStream* output_stream) { + SceneStream* output_stream) { assert(sounds.size() < 100); auto sound_count{static_cast(sounds.size())}; Utils::EmbedInt8(buffer, sound_count); diff --git a/src/ballistica/dynamics/material/impact_sound_material_action.h b/src/ballistica/dynamics/material/impact_sound_material_action.h index b64368c9..c3b2398a 100644 --- a/src/ballistica/dynamics/material/impact_sound_material_action.h +++ b/src/ballistica/dynamics/material/impact_sound_material_action.h @@ -5,9 +5,9 @@ #include +#include "ballistica/assets/component/sound.h" #include "ballistica/ballistica.h" #include "ballistica/dynamics/material/material_action.h" -#include "ballistica/media/component/sound.h" namespace ballistica { @@ -26,7 +26,7 @@ class ImpactSoundMaterialAction : public MaterialAction { const Object::Ref& p) override; auto GetType() const -> Type override { return Type::IMPACT_SOUND; } auto GetFlattenedSize() -> size_t override; - void Flatten(char** buffer, GameStream* output_stream) override; + void Flatten(char** buffer, SceneStream* output_stream) override; void Restore(const char** buffer, ClientSession* cs) override; private: diff --git a/src/ballistica/dynamics/material/material.cc b/src/ballistica/dynamics/material/material.cc index 397f295c..4a6e07a5 100644 --- a/src/ballistica/dynamics/material/material.cc +++ b/src/ballistica/dynamics/material/material.cc @@ -5,9 +5,9 @@ // #include "ballistica/dynamics/material/material_action.h" #include "ballistica/dynamics/material/material_component.h" #include "ballistica/dynamics/material/material_condition_node.h" -#include "ballistica/game/game_stream.h" #include "ballistica/python/python_sys.h" #include "ballistica/scene/scene.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { @@ -16,7 +16,7 @@ Material::Material(std::string name_in, Scene* scene) // If we're being made in a scene with an output stream, // write ourself to it. assert(scene); - if (GameStream* os = scene->GetGameStream()) { + if (SceneStream* os = scene->GetSceneStream()) { os->AddMaterial(this); } } @@ -30,7 +30,7 @@ void Material::MarkDead() { // If we're in a scene with an output-stream, inform them of our demise. Scene* scene = scene_.get(); if (scene) { - if (GameStream* os = scene->GetGameStream()) { + if (SceneStream* os = scene->GetSceneStream()) { os->RemoveMaterial(this); } } @@ -62,13 +62,13 @@ void Material::Apply(MaterialContext* s, const Part* src_part, void Material::AddComponent(const Object::Ref& c) { // If there's an output stream, push this to that first - if (GameStream* output_stream = scene()->GetGameStream()) { + if (SceneStream* output_stream = scene()->GetSceneStream()) { output_stream->AddMaterialComponent(this, c.get()); } components_.push_back(c); } -void Material::DumpComponents(GameStream* out) { +void Material::DumpComponents(SceneStream* out) { for (auto& i : components_) { assert(i.exists()); out->AddMaterialComponent(this, i.get()); diff --git a/src/ballistica/dynamics/material/material.h b/src/ballistica/dynamics/material/material.h index 85b5c7a4..a8f86199 100644 --- a/src/ballistica/dynamics/material/material.h +++ b/src/ballistica/dynamics/material/material.h @@ -30,7 +30,7 @@ class Material : public Object { auto BorrowPyRef() -> PyObject* { return GetPyRef(false); } void MarkDead(); auto scene() const -> Scene* { return scene_.get(); } - void DumpComponents(GameStream* out); + void DumpComponents(SceneStream* out); auto stream_id() const -> int64_t { return stream_id_; } void set_stream_id(int64_t val) { assert(stream_id_ == -1); diff --git a/src/ballistica/dynamics/material/material_action.h b/src/ballistica/dynamics/material/material_action.h index 443992fb..b722bf73 100644 --- a/src/ballistica/dynamics/material/material_action.h +++ b/src/ballistica/dynamics/material/material_action.h @@ -28,7 +28,7 @@ class MaterialAction : public Object { const Object::Ref& p) = 0; virtual void Execute(Node* node1, Node* node2, Scene* scene) {} virtual auto GetFlattenedSize() -> size_t { return 0; } - virtual void Flatten(char** buffer, GameStream* output_stream) {} + virtual void Flatten(char** buffer, SceneStream* output_stream) {} virtual void Restore(const char** buffer, ClientSession* cs) {} auto IsNeededOnClient() -> bool { switch (GetType()) { diff --git a/src/ballistica/dynamics/material/material_component.cc b/src/ballistica/dynamics/material/material_component.cc index 6e7fb561..76f4ace9 100644 --- a/src/ballistica/dynamics/material/material_component.cc +++ b/src/ballistica/dynamics/material/material_component.cc @@ -143,7 +143,7 @@ auto MaterialComponent::GetFlattenedSize() -> size_t { return size; } -void MaterialComponent::Flatten(char** buffer, GameStream* output_stream) { +void MaterialComponent::Flatten(char** buffer, SceneStream* output_stream) { // Embed a byte telling whether we have conditions. Utils::EmbedInt8(buffer, conditions.exists()); @@ -213,8 +213,9 @@ void MaterialComponent::Restore(const char** buffer, ClientSession* cs) { action = Object::New(); break; default: - Log("Error: Invalid material action: '" - + std::to_string(static_cast(type)) + "'."); + Log(LogLevel::kError, "Invalid material action: '" + + std::to_string(static_cast(type)) + + "'."); throw Exception(); } action->Restore(buffer, cs); diff --git a/src/ballistica/dynamics/material/material_component.h b/src/ballistica/dynamics/material/material_component.h index a7ec3abf..ee6870aa 100644 --- a/src/ballistica/dynamics/material/material_component.h +++ b/src/ballistica/dynamics/material/material_component.h @@ -13,12 +13,12 @@ namespace ballistica { // A component of a material - comprises one or more conditions and actions. class MaterialComponent : public Object { public: - auto GetDefaultOwnerThread() const -> ThreadIdentifier override { - return ThreadIdentifier::kLogic; + auto GetDefaultOwnerThread() const -> ThreadTag override { + return ThreadTag::kLogic; } auto GetFlattenedSize() -> size_t; - void Flatten(char** buffer, GameStream* output_stream); + void Flatten(char** buffer, SceneStream* output_stream); void Restore(const char** buffer, ClientSession* cs); // Actions are stored as shared pointers so references diff --git a/src/ballistica/dynamics/material/material_condition_node.cc b/src/ballistica/dynamics/material/material_condition_node.cc index 895c9f07..6053ffbf 100644 --- a/src/ballistica/dynamics/material/material_condition_node.cc +++ b/src/ballistica/dynamics/material/material_condition_node.cc @@ -3,9 +3,9 @@ #include "ballistica/dynamics/material/material_condition_node.h" #include "ballistica/dynamics/material/material.h" -#include "ballistica/game/game_stream.h" -#include "ballistica/game/session/client_session.h" #include "ballistica/generic/utils.h" +#include "ballistica/logic/session/client_session.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { @@ -22,7 +22,7 @@ auto MaterialConditionNode::GetFlattenedSize() -> size_t { return size; } -void MaterialConditionNode::Flatten(char** buffer, GameStream* output_stream) { +void MaterialConditionNode::Flatten(char** buffer, SceneStream* output_stream) { // Pack our opmode in. Or if we're a leaf note stick zero in. Utils::EmbedInt8(buffer, static_cast(opmode)); if (opmode == OpMode::LEAF_NODE) { diff --git a/src/ballistica/dynamics/material/material_condition_node.h b/src/ballistica/dynamics/material/material_condition_node.h index 0764bf31..dc6ea0b5 100644 --- a/src/ballistica/dynamics/material/material_condition_node.h +++ b/src/ballistica/dynamics/material/material_condition_node.h @@ -50,7 +50,7 @@ class MaterialConditionNode : public Object { } } auto GetFlattenedSize() -> size_t; - void Flatten(char** buffer, GameStream* output_stream); + void Flatten(char** buffer, SceneStream* output_stream); void Restore(const char** buffer, ClientSession* cs); }; diff --git a/src/ballistica/dynamics/material/material_context.cc b/src/ballistica/dynamics/material/material_context.cc index 9f26a4f9..6d50a332 100644 --- a/src/ballistica/dynamics/material/material_context.cc +++ b/src/ballistica/dynamics/material/material_context.cc @@ -2,10 +2,10 @@ #include "ballistica/dynamics/material/material_context.h" +#include "ballistica/assets/component/sound.h" #include "ballistica/audio/audio.h" #include "ballistica/dynamics/dynamics.h" #include "ballistica/dynamics/material/material_action.h" -#include "ballistica/media/component/sound.h" #include "ballistica/scene/scene.h" namespace ballistica { diff --git a/src/ballistica/dynamics/material/node_message_material_action.h b/src/ballistica/dynamics/material/node_message_material_action.h index eb1ecd85..64eae91c 100644 --- a/src/ballistica/dynamics/material/node_message_material_action.h +++ b/src/ballistica/dynamics/material/node_message_material_action.h @@ -27,7 +27,7 @@ class NodeMessageMaterialAction : public MaterialAction { // 1 byte for bools + data return static_cast(1 + data.GetFlattenedSize()); } - void Flatten(char** buffer, GameStream* output_stream) override { + void Flatten(char** buffer, SceneStream* output_stream) override { Utils::EmbedBools(buffer, target_other, at_disconnect); data.embed(buffer); } diff --git a/src/ballistica/dynamics/material/node_mod_material_action.cc b/src/ballistica/dynamics/material/node_mod_material_action.cc index 9e799a45..0e207f45 100644 --- a/src/ballistica/dynamics/material/node_mod_material_action.cc +++ b/src/ballistica/dynamics/material/node_mod_material_action.cc @@ -13,7 +13,7 @@ auto NodeModMaterialAction::GetType() const -> MaterialAction::Type { auto NodeModMaterialAction::GetFlattenedSize() -> size_t { return 1 + 4; } -void NodeModMaterialAction::Flatten(char** buffer, GameStream* output_stream) { +void NodeModMaterialAction::Flatten(char** buffer, SceneStream* output_stream) { Utils::EmbedInt8(buffer, static_cast(attr)); Utils::EmbedFloat32(buffer, attr_val); } diff --git a/src/ballistica/dynamics/material/node_mod_material_action.h b/src/ballistica/dynamics/material/node_mod_material_action.h index 27fd9a3d..77902e5f 100644 --- a/src/ballistica/dynamics/material/node_mod_material_action.h +++ b/src/ballistica/dynamics/material/node_mod_material_action.h @@ -19,7 +19,7 @@ class NodeModMaterialAction : public MaterialAction { const Object::Ref& p) override; auto GetType() const -> Type override; auto GetFlattenedSize() -> size_t override; - void Flatten(char** buffer, GameStream* output_stream) override; + void Flatten(char** buffer, SceneStream* output_stream) override; void Restore(const char** buffer, ClientSession* cs) override; }; diff --git a/src/ballistica/dynamics/material/part_mod_material_action.cc b/src/ballistica/dynamics/material/part_mod_material_action.cc index 95599bc4..62a18200 100644 --- a/src/ballistica/dynamics/material/part_mod_material_action.cc +++ b/src/ballistica/dynamics/material/part_mod_material_action.cc @@ -13,7 +13,7 @@ auto PartModMaterialAction::GetType() const -> MaterialAction::Type { auto PartModMaterialAction::GetFlattenedSize() -> size_t { return 1 + 4; } -void PartModMaterialAction::Flatten(char** buffer, GameStream* output_stream) { +void PartModMaterialAction::Flatten(char** buffer, SceneStream* output_stream) { Utils::EmbedInt8(buffer, static_cast(attr)); Utils::EmbedFloat32(buffer, attr_val); } diff --git a/src/ballistica/dynamics/material/part_mod_material_action.h b/src/ballistica/dynamics/material/part_mod_material_action.h index 151e0dde..f192e59d 100644 --- a/src/ballistica/dynamics/material/part_mod_material_action.h +++ b/src/ballistica/dynamics/material/part_mod_material_action.h @@ -19,7 +19,7 @@ class PartModMaterialAction : public MaterialAction { const Object::Ref& p) override; auto GetType() const -> Type override; auto GetFlattenedSize() -> size_t override; - void Flatten(char** buffer, GameStream* output_stream) override; + void Flatten(char** buffer, SceneStream* output_stream) override; void Restore(const char** buffer, ClientSession* cs) override; }; diff --git a/src/ballistica/dynamics/material/roll_sound_material_action.cc b/src/ballistica/dynamics/material/roll_sound_material_action.cc index feffbaef..363e9492 100644 --- a/src/ballistica/dynamics/material/roll_sound_material_action.cc +++ b/src/ballistica/dynamics/material/roll_sound_material_action.cc @@ -2,20 +2,20 @@ #include "ballistica/dynamics/material/roll_sound_material_action.h" +#include "ballistica/assets/component/sound.h" #include "ballistica/dynamics/dynamics.h" #include "ballistica/dynamics/material/material_context.h" -#include "ballistica/game/game_stream.h" -#include "ballistica/game/session/client_session.h" #include "ballistica/generic/utils.h" #include "ballistica/graphics/graphics_server.h" -#include "ballistica/media/component/sound.h" +#include "ballistica/logic/session/client_session.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { auto RollSoundMaterialAction::GetFlattenedSize() -> size_t { return 4 + 2 + 2; } void RollSoundMaterialAction::Flatten(char** buffer, - GameStream* output_stream) { + SceneStream* output_stream) { Utils::EmbedInt32NBO(buffer, static_cast_check_fit( output_stream->GetSoundID(sound.get()))); Utils::EmbedFloat16NBO(buffer, target_impulse); diff --git a/src/ballistica/dynamics/material/roll_sound_material_action.h b/src/ballistica/dynamics/material/roll_sound_material_action.h index 37716c92..88e25b80 100644 --- a/src/ballistica/dynamics/material/roll_sound_material_action.h +++ b/src/ballistica/dynamics/material/roll_sound_material_action.h @@ -3,9 +3,9 @@ #ifndef BALLISTICA_DYNAMICS_MATERIAL_ROLL_SOUND_MATERIAL_ACTION_H_ #define BALLISTICA_DYNAMICS_MATERIAL_ROLL_SOUND_MATERIAL_ACTION_H_ +#include "ballistica/assets/component/sound.h" #include "ballistica/ballistica.h" #include "ballistica/dynamics/material/material_action.h" -#include "ballistica/media/component/sound.h" namespace ballistica { @@ -24,7 +24,7 @@ class RollSoundMaterialAction : public MaterialAction { const Object::Ref& p) override; auto GetType() const -> Type override { return Type::ROLL_SOUND; } auto GetFlattenedSize() -> size_t override; - void Flatten(char** buffer, GameStream* output_stream) override; + void Flatten(char** buffer, SceneStream* output_stream) override; void Restore(const char** buffer, ClientSession* cs) override; }; diff --git a/src/ballistica/dynamics/material/skid_sound_material_action.cc b/src/ballistica/dynamics/material/skid_sound_material_action.cc index b608355c..838e3b7f 100644 --- a/src/ballistica/dynamics/material/skid_sound_material_action.cc +++ b/src/ballistica/dynamics/material/skid_sound_material_action.cc @@ -4,17 +4,17 @@ #include "ballistica/dynamics/dynamics.h" #include "ballistica/dynamics/material/material_context.h" -#include "ballistica/game/game_stream.h" -#include "ballistica/game/session/client_session.h" #include "ballistica/generic/utils.h" #include "ballistica/graphics/graphics_server.h" +#include "ballistica/logic/session/client_session.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { auto SkidSoundMaterialAction::GetFlattenedSize() -> size_t { return 4 + 2 + 2; } void SkidSoundMaterialAction::Flatten(char** buffer, - GameStream* output_stream) { + SceneStream* output_stream) { Utils::EmbedInt32NBO(buffer, static_cast_check_fit( output_stream->GetSoundID(sound.get()))); Utils::EmbedFloat16NBO(buffer, target_impulse); diff --git a/src/ballistica/dynamics/material/skid_sound_material_action.h b/src/ballistica/dynamics/material/skid_sound_material_action.h index 2179e426..63723629 100644 --- a/src/ballistica/dynamics/material/skid_sound_material_action.h +++ b/src/ballistica/dynamics/material/skid_sound_material_action.h @@ -3,9 +3,9 @@ #ifndef BALLISTICA_DYNAMICS_MATERIAL_SKID_SOUND_MATERIAL_ACTION_H_ #define BALLISTICA_DYNAMICS_MATERIAL_SKID_SOUND_MATERIAL_ACTION_H_ +#include "ballistica/assets/component/sound.h" #include "ballistica/ballistica.h" #include "ballistica/dynamics/material/material_action.h" -#include "ballistica/media/component/sound.h" namespace ballistica { @@ -24,7 +24,7 @@ class SkidSoundMaterialAction : public MaterialAction { const Object::Ref& p) override; auto GetType() const -> Type override { return Type::SKID_SOUND; } auto GetFlattenedSize() -> size_t override; - void Flatten(char** buffer, GameStream* output_stream) override; + void Flatten(char** buffer, SceneStream* output_stream) override; void Restore(const char** buffer, ClientSession* cs) override; }; diff --git a/src/ballistica/dynamics/material/sound_material_action.cc b/src/ballistica/dynamics/material/sound_material_action.cc index db5bd6b7..30d12696 100644 --- a/src/ballistica/dynamics/material/sound_material_action.cc +++ b/src/ballistica/dynamics/material/sound_material_action.cc @@ -3,9 +3,9 @@ #include "ballistica/dynamics/material/sound_material_action.h" #include "ballistica/dynamics/material/material_context.h" -#include "ballistica/game/game_stream.h" -#include "ballistica/game/session/client_session.h" #include "ballistica/generic/utils.h" +#include "ballistica/logic/session/client_session.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { @@ -18,7 +18,7 @@ void SoundMaterialAction::Apply(MaterialContext* context, const Part* src_part, auto SoundMaterialAction::GetFlattenedSize() -> size_t { return 4 + 2; } -void SoundMaterialAction::Flatten(char** buffer, GameStream* output_stream) { +void SoundMaterialAction::Flatten(char** buffer, SceneStream* output_stream) { Utils::EmbedInt32NBO(buffer, static_cast_check_fit( output_stream->GetSoundID(sound_.get()))); Utils::EmbedFloat16NBO(buffer, volume_); diff --git a/src/ballistica/dynamics/material/sound_material_action.h b/src/ballistica/dynamics/material/sound_material_action.h index ddd46745..8e6089b5 100644 --- a/src/ballistica/dynamics/material/sound_material_action.h +++ b/src/ballistica/dynamics/material/sound_material_action.h @@ -3,9 +3,9 @@ #ifndef BALLISTICA_DYNAMICS_MATERIAL_SOUND_MATERIAL_ACTION_H_ #define BALLISTICA_DYNAMICS_MATERIAL_SOUND_MATERIAL_ACTION_H_ +#include "ballistica/assets/component/sound.h" #include "ballistica/ballistica.h" #include "ballistica/dynamics/material/material_action.h" -#include "ballistica/media/component/sound.h" namespace ballistica { @@ -19,7 +19,7 @@ class SoundMaterialAction : public MaterialAction { const Object::Ref& p) override; auto GetType() const -> Type override { return Type::SOUND; } auto GetFlattenedSize() -> size_t override; - void Flatten(char** buffer, GameStream* output_stream) override; + void Flatten(char** buffer, SceneStream* output_stream) override; void Restore(const char** buffer, ClientSession* cs) override; private: diff --git a/src/ballistica/dynamics/part.cc b/src/ballistica/dynamics/part.cc index 2b7436b3..17263e5e 100644 --- a/src/ballistica/dynamics/part.cc +++ b/src/ballistica/dynamics/part.cc @@ -97,7 +97,8 @@ void Part::SetCollidingWith(int64_t node_id, int part, bool colliding, for (auto&& i : collisions_) { if (i.node == node_id && i.part == part) { BA_PRECONDITION(node()); - Log("Error: Got SetCollidingWith for part already colliding with."); + Log(LogLevel::kError, + "Got SetCollidingWith for part already colliding with."); return; } } @@ -117,7 +118,8 @@ void Part::SetCollidingWith(int64_t node_id, int part, bool colliding, return; } } - Log("Error: Got SetCollidingWith (separated) call for part we're " + Log(LogLevel::kError, + "Got SetCollidingWith (separated) call for part we're " "not colliding with."); } } diff --git a/src/ballistica/dynamics/part.h b/src/ballistica/dynamics/part.h index b0bc494f..042ab6d1 100644 --- a/src/ballistica/dynamics/part.h +++ b/src/ballistica/dynamics/part.h @@ -71,7 +71,7 @@ class Part : public Object { // node/part combo. auto IsCollidingWith(int64_t node, int part) const -> bool; - // Used by g_game to inform us we're now colliding with another part + // Used by g_logic to inform us we're now colliding with another part // if colliding is false, we've stopped colliding with this part. void SetCollidingWith(int64_t node_id, int part, bool colliding, bool physical); diff --git a/src/ballistica/dynamics/rigid_body.cc b/src/ballistica/dynamics/rigid_body.cc index 62ed4fc0..2e795adb 100644 --- a/src/ballistica/dynamics/rigid_body.cc +++ b/src/ballistica/dynamics/rigid_body.cc @@ -2,11 +2,11 @@ #include "ballistica/dynamics/rigid_body.h" +#include "ballistica/assets/component/collide_model.h" #include "ballistica/dynamics/dynamics.h" #include "ballistica/dynamics/part.h" #include "ballistica/generic/utils.h" #include "ballistica/graphics/renderer.h" -#include "ballistica/media/component/collide_model.h" #include "ballistica/scene/scene.h" #include "ode/ode_collision_util.h" @@ -168,7 +168,7 @@ auto RigidBody::Check() -> void { if (std::isnan(q[3])) err = true; if (err) { - Log("Error: Got error in rbd values!"); + Log(LogLevel::kError, "Got error in rbd values!"); } #if BA_DEBUG_BUILD for (int i = 0; i < 3; i++) { diff --git a/src/ballistica/game/score_to_beat.h b/src/ballistica/game/score_to_beat.h deleted file mode 100644 index 39b084b8..00000000 --- a/src/ballistica/game/score_to_beat.h +++ /dev/null @@ -1,28 +0,0 @@ -// Released under the MIT License. See LICENSE for details. - -#ifndef BALLISTICA_GAME_SCORE_TO_BEAT_H_ -#define BALLISTICA_GAME_SCORE_TO_BEAT_H_ - -#include -#include - -namespace ballistica { - -// Do we still need this? -class ScoreToBeat { - public: - ScoreToBeat(std::string player_in, std::string type_in, std::string value_in, - double timeIn) - : player(std::move(player_in)), - type(std::move(type_in)), - value(std::move(value_in)), - time(timeIn) {} - std::string player; - std::string type; - std::string value; - double time; -}; - -} // namespace ballistica - -#endif // BALLISTICA_GAME_SCORE_TO_BEAT_H_ diff --git a/src/ballistica/generic/huffman.cc b/src/ballistica/generic/huffman.cc index 6e35daba..0aec4919 100644 --- a/src/ballistica/generic/huffman.cc +++ b/src/ballistica/generic/huffman.cc @@ -407,7 +407,8 @@ void Huffman::decompress(const char* data, uint32_t length, // fixme?? if (bytes > kMaxPacketSize) { - Log("HUFFMAN DECOMPRESSING TO TOO LARGE: " + std::to_string(bytes)); + Log(LogLevel::kError, "HUFFMAN DECOMPRESSING TO TOO LARGE: " + + std::to_string(bytes)); } assert(bytes <= kMaxPacketSize); @@ -459,7 +460,7 @@ void Huffman::train(const char* buffer, int len) { len--; } if (total_length > kTrainingLength) { - Log("HUFFMAN TRAINING COMPLETE:"); + Log(LogLevel::kInfo, "HUFFMAN TRAINING COMPLETE:"); build(); @@ -470,7 +471,7 @@ void Huffman::train(const char* buffer, int len) { if (i < 255) s += ","; } s += "}"; - Log("FINAL: " + s); + Log(LogLevel::kInfo, "FINAL: " + s); } } #endif // HUFFMAN_TRAINING_MODE diff --git a/src/ballistica/generic/json.cc b/src/ballistica/generic/json.cc index 21429698..d90fecfb 100644 --- a/src/ballistica/generic/json.cc +++ b/src/ballistica/generic/json.cc @@ -29,6 +29,7 @@ #include "ballistica/generic/json.h" #include +#include #if BA_OSTYPE_LINUX #include diff --git a/src/ballistica/generic/real_timer.h b/src/ballistica/generic/real_timer.h index d59d23b4..88ce26f4 100644 --- a/src/ballistica/generic/real_timer.h +++ b/src/ballistica/generic/real_timer.h @@ -5,8 +5,8 @@ #include "ballistica/ballistica.h" #include "ballistica/core/object.h" -#include "ballistica/game/game.h" #include "ballistica/generic/runnable.h" +#include "ballistica/logic/logic.h" namespace ballistica { @@ -16,18 +16,18 @@ template class RealTimer : public Object { public: RealTimer(millisecs_t length, bool repeat, T* delegate) { - assert(g_game); + assert(g_logic); assert(InLogicThread()); - timer_id_ = g_game->NewRealTimer( + timer_id_ = g_logic->NewRealTimer( length, repeat, Object::New(delegate, this)); } void SetLength(uint32_t length) { assert(InLogicThread()); - g_game->SetRealTimerLength(timer_id_, length); + g_logic->SetRealTimerLength(timer_id_, length); } ~RealTimer() override { assert(InLogicThread()); - g_game->DeleteRealTimer(timer_id_); + g_logic->DeleteRealTimer(timer_id_); } private: diff --git a/src/ballistica/generic/timer_list.cc b/src/ballistica/generic/timer_list.cc index 533b6c0b..b92b8b5d 100644 --- a/src/ballistica/generic/timer_list.cc +++ b/src/ballistica/generic/timer_list.cc @@ -20,14 +20,14 @@ TimerList::~TimerList() { if (g_buildconfig.debug_build()) { if (timer_count_active_ != 0) { - Log("Error: Invalid timerlist state on teardown."); + Log(LogLevel::kError, "Invalid timerlist state on teardown."); } if (timer_count_inactive_ != 0) { - Log("Error: Invalid timerlist state on teardown."); + Log(LogLevel::kError, "Invalid timerlist state on teardown."); } if (!((timer_count_total_ == 0) || (client_timer_ != nullptr && timer_count_total_ == 1))) { - Log("Error: Invalid timerlist state on teardown."); + Log(LogLevel::kError, "Invalid timerlist state on teardown."); } } } diff --git a/src/ballistica/generic/utils.cc b/src/ballistica/generic/utils.cc index a7f341d1..2ac25a04 100644 --- a/src/ballistica/generic/utils.cc +++ b/src/ballistica/generic/utils.cc @@ -136,9 +136,10 @@ Utils::Utils() { // it was parsed from. Use this to adjust the filtering as necessary so // the resulting type name matches what is expected. if (explicit_bool(false)) { - Log("static_type_name check; name is '" - + static_type_name() + "' debug_full is '" - + static_type_name(true) + "'"); + Log(LogLevel::kError, + "static_type_name check; name is '" + + static_type_name() + "' debug_full is '" + + static_type_name(true) + "'"); } // We now bake these in so they match across platforms... @@ -261,8 +262,9 @@ auto Utils::GetValidUTF8(const char* str, const char* loc) -> std::string { } } logged_count++; - Log("GOT INVALID UTF8 SEQUENCE: (" + log_str + "); RETURNING '" + to - + "'; LOC '" + loc + "'"); + Log(LogLevel::kError, "GOT INVALID UTF8 SEQUENCE: (" + log_str + + "); RETURNING '" + to + "'; LOC '" + loc + + "'"); } } else { diff --git a/src/ballistica/graphics/camera.cc b/src/ballistica/graphics/camera.cc index 29cfef26..c22d1e06 100644 --- a/src/ballistica/graphics/camera.cc +++ b/src/ballistica/graphics/camera.cc @@ -727,7 +727,7 @@ void Camera::Update(millisecs_t elapsed) { } if (g_graphics->camera_shake_disabled()) { - shake_vel_ = {0, 0, 0}; + shake_pos_ = {0, 0, 0}; } } diff --git a/src/ballistica/graphics/component/object_component.cc b/src/ballistica/graphics/component/object_component.cc index 0058b61c..e8d1a6bd 100644 --- a/src/ballistica/graphics/component/object_component.cc +++ b/src/ballistica/graphics/component/object_component.cc @@ -9,7 +9,7 @@ void ObjectComponent::WriteConfig() { // This is not a common case and easier than forking all our shaders to // create non-textured versions. if (!texture_.exists()) { - texture_ = g_media->GetTexture(SystemTextureID::kWhite); + texture_ = g_assets->GetTexture(SystemTextureID::kWhite); } if (reflection_ == ReflectionType::kNone) { assert(!double_sided_); // Unsupported combo. @@ -58,7 +58,7 @@ void ObjectComponent::WriteConfig() { cmd_buffer_->PutTexture(texture_); SystemCubeMapTextureID r = Graphics::CubeMapFromReflectionType(reflection_); - cmd_buffer_->PutCubeMapTexture(g_media->GetCubeMapTexture(r)); + cmd_buffer_->PutCubeMapTexture(g_assets->GetCubeMapTexture(r)); } else { ConfigForShading(ShadingType::kObjectReflectTransparent); cmd_buffer_->PutInt(premultiplied_); @@ -68,7 +68,7 @@ void ObjectComponent::WriteConfig() { cmd_buffer_->PutTexture(texture_); SystemCubeMapTextureID r = Graphics::CubeMapFromReflectionType(reflection_); - cmd_buffer_->PutCubeMapTexture(g_media->GetCubeMapTexture(r)); + cmd_buffer_->PutCubeMapTexture(g_assets->GetCubeMapTexture(r)); } } else { ConfigForShading(ShadingType::kObjectReflect); @@ -79,7 +79,7 @@ void ObjectComponent::WriteConfig() { cmd_buffer_->PutTexture(texture_); SystemCubeMapTextureID r = Graphics::CubeMapFromReflectionType(reflection_); - cmd_buffer_->PutCubeMapTexture(g_media->GetCubeMapTexture(r)); + cmd_buffer_->PutCubeMapTexture(g_assets->GetCubeMapTexture(r)); } } else { // With add. @@ -100,7 +100,7 @@ void ObjectComponent::WriteConfig() { cmd_buffer_->PutTexture(colorize_texture_); SystemCubeMapTextureID r = Graphics::CubeMapFromReflectionType(reflection_); - cmd_buffer_->PutCubeMapTexture(g_media->GetCubeMapTexture(r)); + cmd_buffer_->PutCubeMapTexture(g_assets->GetCubeMapTexture(r)); } else { ConfigForShading(ShadingType::kObjectReflectLightShadowColorized); cmd_buffer_->PutInt(static_cast(light_shadow_)); @@ -112,7 +112,7 @@ void ObjectComponent::WriteConfig() { cmd_buffer_->PutTexture(colorize_texture_); SystemCubeMapTextureID r = Graphics::CubeMapFromReflectionType(reflection_); - cmd_buffer_->PutCubeMapTexture(g_media->GetCubeMapTexture(r)); + cmd_buffer_->PutCubeMapTexture(g_assets->GetCubeMapTexture(r)); } } else { if (double_sided_) { @@ -125,7 +125,7 @@ void ObjectComponent::WriteConfig() { cmd_buffer_->PutTexture(texture_); SystemCubeMapTextureID r = Graphics::CubeMapFromReflectionType(reflection_); - cmd_buffer_->PutCubeMapTexture(g_media->GetCubeMapTexture(r)); + cmd_buffer_->PutCubeMapTexture(g_assets->GetCubeMapTexture(r)); } else { ConfigForShading(ShadingType::kObjectReflectLightShadow); cmd_buffer_->PutInt(static_cast(light_shadow_)); @@ -136,7 +136,7 @@ void ObjectComponent::WriteConfig() { cmd_buffer_->PutTexture(texture_); SystemCubeMapTextureID r = Graphics::CubeMapFromReflectionType(reflection_); - cmd_buffer_->PutCubeMapTexture(g_media->GetCubeMapTexture(r)); + cmd_buffer_->PutCubeMapTexture(g_assets->GetCubeMapTexture(r)); } } } else { @@ -157,7 +157,7 @@ void ObjectComponent::WriteConfig() { cmd_buffer_->PutTexture(colorize_texture_); SystemCubeMapTextureID r = Graphics::CubeMapFromReflectionType(reflection_); - cmd_buffer_->PutCubeMapTexture(g_media->GetCubeMapTexture(r)); + cmd_buffer_->PutCubeMapTexture(g_assets->GetCubeMapTexture(r)); } else { ConfigForShading( ShadingType::kObjectReflectLightShadowAddColorized); @@ -171,7 +171,7 @@ void ObjectComponent::WriteConfig() { cmd_buffer_->PutTexture(colorize_texture_); SystemCubeMapTextureID r = Graphics::CubeMapFromReflectionType(reflection_); - cmd_buffer_->PutCubeMapTexture(g_media->GetCubeMapTexture(r)); + cmd_buffer_->PutCubeMapTexture(g_assets->GetCubeMapTexture(r)); } } else { ConfigForShading(ShadingType::kObjectReflectLightShadowAdd); @@ -183,7 +183,7 @@ void ObjectComponent::WriteConfig() { cmd_buffer_->PutTexture(texture_); SystemCubeMapTextureID r = Graphics::CubeMapFromReflectionType(reflection_); - cmd_buffer_->PutCubeMapTexture(g_media->GetCubeMapTexture(r)); + cmd_buffer_->PutCubeMapTexture(g_assets->GetCubeMapTexture(r)); } } } diff --git a/src/ballistica/graphics/component/render_component.h b/src/ballistica/graphics/component/render_component.h index 08ac4fea..a85f9bff 100644 --- a/src/ballistica/graphics/component/render_component.h +++ b/src/ballistica/graphics/component/render_component.h @@ -15,7 +15,8 @@ class RenderComponent { : state_(State::kConfiguring), pass_(pass), cmd_buffer_(nullptr) {} ~RenderComponent() { if (state_ != State::kSubmitted) { - Log("Error: RenderComponent dying without submit() having been called."); + Log(LogLevel::kError, + "RenderComponent dying without submit() having been called."); } } void DrawModel(ModelData* model, uint32_t flags = 0) { diff --git a/src/ballistica/graphics/component/simple_component.cc b/src/ballistica/graphics/component/simple_component.cc index 0b31e541..b6816201 100644 --- a/src/ballistica/graphics/component/simple_component.cc +++ b/src/ballistica/graphics/component/simple_component.cc @@ -132,7 +132,7 @@ void SimpleComponent::WriteConfig() { colorize_color2_g_, colorize_color2_b_); cmd_buffer_->PutTexture(texture_); cmd_buffer_->PutTexture( - g_media->GetTexture(SystemTextureID::kBlack)); + g_assets->GetTexture(SystemTextureID::kBlack)); cmd_buffer_->PutTexture(mask_texture_); } else { ConfigForShading( @@ -203,7 +203,8 @@ void SimpleComponent::WriteConfig() { colorize_color_b_, colorize_color2_r_, colorize_color2_g_, colorize_color2_b_); cmd_buffer_->PutTexture(texture_); - cmd_buffer_->PutTexture(g_media->GetTexture(SystemTextureID::kBlack)); + cmd_buffer_->PutTexture( + g_assets->GetTexture(SystemTextureID::kBlack)); cmd_buffer_->PutTexture(mask_texture_); } else { // if no color was provided we can do a super-cheap version diff --git a/src/ballistica/graphics/component/smoke_component.cc b/src/ballistica/graphics/component/smoke_component.cc index 9d7aaa35..617c9053 100644 --- a/src/ballistica/graphics/component/smoke_component.cc +++ b/src/ballistica/graphics/component/smoke_component.cc @@ -8,11 +8,11 @@ void SmokeComponent::WriteConfig() { if (overlay_) { ConfigForShading(ShadingType::kSmokeOverlay); cmd_buffer_->PutFloats(color_r_, color_g_, color_b_, color_a_); - cmd_buffer_->PutTexture(g_media->GetTexture(SystemTextureID::kSmoke)); + cmd_buffer_->PutTexture(g_assets->GetTexture(SystemTextureID::kSmoke)); } else { ConfigForShading(ShadingType::kSmoke); cmd_buffer_->PutFloats(color_r_, color_g_, color_b_, color_a_); - cmd_buffer_->PutTexture(g_media->GetTexture(SystemTextureID::kSmoke)); + cmd_buffer_->PutTexture(g_assets->GetTexture(SystemTextureID::kSmoke)); } } diff --git a/src/ballistica/graphics/component/sprite_component.cc b/src/ballistica/graphics/component/sprite_component.cc index d9f052bc..32a2ade4 100644 --- a/src/ballistica/graphics/component/sprite_component.cc +++ b/src/ballistica/graphics/component/sprite_component.cc @@ -9,7 +9,7 @@ void SpriteComponent::WriteConfig() { // this is not a common case and easier than forking all our shaders // to create non-textured versions. if (!texture_.exists()) { - texture_ = g_media->GetTexture(SystemTextureID::kWhite); + texture_ = g_assets->GetTexture(SystemTextureID::kWhite); } if (exponent_ == 1) { ConfigForShading(ShadingType::kSprite); diff --git a/src/ballistica/graphics/frame_def.h b/src/ballistica/graphics/frame_def.h index 5df6de21..90db34ae 100644 --- a/src/ballistica/graphics/frame_def.h +++ b/src/ballistica/graphics/frame_def.h @@ -6,14 +6,14 @@ #include #include +#include "ballistica/assets/data/asset_component_data.h" #include "ballistica/math/matrix44f.h" #include "ballistica/math/vector2f.h" -#include "ballistica/media/data/media_component_data.h" namespace ballistica { -/// A flattened representation of a frame; generated by the game thread and sent -/// to the graphics thread to render. +/// A flattened representation of a frame; generated by the logic thread and +/// sent to the graphics thread to render. class FrameDef { public: auto light_pass() -> RenderPass* { return light_pass_.get(); } @@ -101,7 +101,7 @@ class FrameDef { auto has_depth_texture() const -> bool { return (quality_ >= GraphicsQuality::kHigh); } - void AddComponent(const Object::Ref& component) { + void AddComponent(const Object::Ref& component) { // Add a reference to this component only if we havn't yet. if (component->last_frame_def_num() != frame_number_) { component->set_last_frame_def_num(frame_number_); @@ -156,7 +156,7 @@ class FrameDef { return mesh_index_sizes_; } auto media_components() const - -> const std::vector>& { + -> const std::vector>& { return media_components_; } @@ -189,7 +189,7 @@ class FrameDef { std::vector> meshes_; std::vector> mesh_buffers_; std::vector mesh_index_sizes_; - std::vector> media_components_; + std::vector> media_components_; #if BA_DEBUG_BUILD // Sanity checking: make sure components are completely submitted diff --git a/src/ballistica/graphics/framebuffer.h b/src/ballistica/graphics/framebuffer.h index cf3d22e2..df552233 100644 --- a/src/ballistica/graphics/framebuffer.h +++ b/src/ballistica/graphics/framebuffer.h @@ -9,8 +9,8 @@ namespace ballistica { class Framebuffer : public Object { public: - auto GetDefaultOwnerThread() const -> ThreadIdentifier override { - return ThreadIdentifier::kMain; + auto GetDefaultOwnerThread() const -> ThreadTag override { + return ThreadTag::kMain; } }; diff --git a/src/ballistica/graphics/gl/gl_sys.cc b/src/ballistica/graphics/gl/gl_sys.cc index 7709f2af..f804fc39 100644 --- a/src/ballistica/graphics/gl/gl_sys.cc +++ b/src/ballistica/graphics/gl/gl_sys.cc @@ -24,12 +24,12 @@ #endif #if BA_DEBUG_BUILD -#define DEBUG_CHECK_GL_ERROR \ - { \ - GLenum err = glGetError(); \ - if (err != GL_NO_ERROR) \ - Log("OPENGL ERROR AT LINE " + std::to_string(__LINE__) + ": " \ - + GLErrorToString(err)); \ +#define DEBUG_CHECK_GL_ERROR \ + { \ + GLenum err = glGetError(); \ + if (err != GL_NO_ERROR) \ + Log(LogLevel::kError, "OPENGL ERROR AT LINE " + std::to_string(__LINE__) \ + + ": " + GLErrorToString(err)); \ } #else #define DEBUG_CHECK_GL_ERROR @@ -315,7 +315,7 @@ void GLContext::SetVSync(bool enable) { GLContext::~GLContext() { if (!InMainThread()) { - Log("Error: GLContext dying in non-graphics thread"); + Log(LogLevel::kError, "GLContext dying in non-graphics thread"); } #if BA_SDL2_BUILD diff --git a/src/ballistica/graphics/gl/renderer_gl.cc b/src/ballistica/graphics/gl/renderer_gl.cc index 5bbaf731..1b6f0988 100644 --- a/src/ballistica/graphics/gl/renderer_gl.cc +++ b/src/ballistica/graphics/gl/renderer_gl.cc @@ -3,11 +3,11 @@ #if BA_ENABLE_OPENGL #include "ballistica/graphics/gl/renderer_gl.h" +#include "ballistica/assets/data/texture_preload_data.h" +#include "ballistica/assets/data/texture_renderer_data.h" #include "ballistica/graphics/component/special_component.h" #include "ballistica/graphics/graphics_server.h" #include "ballistica/graphics/mesh/mesh_renderer_data.h" -#include "ballistica/media/data/texture_preload_data.h" -#include "ballistica/media/data/texture_renderer_data.h" #if BA_OSTYPE_IOS_TVOS #include "ballistica/platform/apple/apple_utils.h" @@ -144,10 +144,10 @@ static void _check_gl_error(int line) { const char* version = (const char*)glGetString(GL_VERSION); const char* vendor = (const char*)glGetString(GL_VENDOR); const char* renderer = (const char*)glGetString(GL_RENDERER); - Log("Error: OpenGL Error at line " + std::to_string(line) + ": " - + GLErrorToString(err) + "\nrenderer: " + renderer - + "\nvendor: " + vendor + "\nversion: " + version - + "\ntime: " + std::to_string(GetRealTime())); + Log(LogLevel::kError, "OpenGL Error at line " + std::to_string(line) + ": " + + GLErrorToString(err) + "\nrenderer: " + renderer + + "\nvendor: " + vendor + "\nversion: " + version + + "\ntime: " + std::to_string(GetRealTime())); } } @@ -256,16 +256,16 @@ void RendererGL::CheckGLExtensions() { // if we require ES3 if (have_es3) { g_running_es3 = true; - Log(std::string("Using OpenGL ES 3 (vendor: ") + vendor - + ", renderer: " + renderer + ", version: " + version_str + ")", - false, false); + Log(LogLevel::kInfo, std::string("Using OpenGL ES 3 (vendor: ") + vendor + + ", renderer: " + renderer + + ", version: " + version_str + ")"); } else { #if !BA_USE_ES3_INCLUDES g_running_es3 = false; - Log(std::string("USING OPENGL ES2 (vendor: ") + vendor - + ", renderer: " + renderer + ", version: " + version_str + ")", - false, false); + Log(LogLevel::kInfo, std::string("USING OPENGL ES2 (vendor: ") + vendor + + ", renderer: " + renderer + + ", version: " + version_str + ")"); // Can still support some stuff like framebuffer-blit with es2 extensions. assert(glBlitFramebuffer == nullptr || !first_extension_check_); @@ -381,7 +381,7 @@ void RendererGL::CheckGLExtensions() { c_types.push_back(TextureCompressionType::kETC1); } else { #if BA_OSTYPE_ANDROID - Log("Android device missing ETC1 support"); + Log(LogLevel::kError, "Android device missing ETC1 support"); #endif } @@ -509,7 +509,7 @@ void RendererGL::CheckGLExtensions() { &samples[0]); g_msaa_max_samples_rgb565 = samples[0]; } else { - BA_LOG_ONCE("Got 0 samplecounts for RGB565"); + BA_LOG_ONCE(LogLevel::kError, "Got 0 samplecounts for RGB565"); g_msaa_max_samples_rgb565 = 0; } } @@ -525,7 +525,7 @@ void RendererGL::CheckGLExtensions() { &samples[0]); g_msaa_max_samples_rgb8 = samples[0]; } else { - BA_LOG_ONCE("Got 0 samplecounts for RGB8"); + BA_LOG_ONCE(LogLevel::kError, "Got 0 samplecounts for RGB8"); g_msaa_max_samples_rgb8 = 0; } } @@ -539,10 +539,10 @@ void RendererGL::CheckGLExtensions() { #if MSAA_ERROR_TEST if (enable_msaa_) { ScreenMessage("MSAA ENABLED"); - Log("Ballistica MSAA Test: MSAA ENABLED", false, false); + Log(LogLevel::kInfo, "Ballistica MSAA Test: MSAA ENABLED"); } else { ScreenMessage("MSAA DISABLED"); - Log("Ballistica MSAA Test: MSAA DISABLED", false, false); + Log(LogLevel::kInfo, "Ballistica MSAA Test: MSAA DISABLED"); } #endif // MSAA_ERROR_TEST @@ -769,7 +769,6 @@ class RendererGL::FramebufferObjectGL : public Framebuffer { GLenum format = GL_UNSIGNED_BYTE; #endif // if (srgbTest) { - // Log("YOOOOOOO"); // glTexImage2D(GL_TEXTURE_2D, 0, alpha_?GL_SRGB8_ALPHA8:GL_SRGB8, // _width, _height, 0, alpha_?GL_RGBA:GL_RGB, format, nullptr); // } else { @@ -910,11 +909,11 @@ class RendererGL::FramebufferObjectGL : public Framebuffer { // glGetFramebufferAttachmentParameteriv(GL_FRAMEBUFFER, // GL_COLOR_ATTACHMENT0, GL_FRAMEBUFFER_ATTACHMENT_COLOR_ENCODING, &enc); if // (enc == GL_SRGB) { - // Log("GOT SRGB!!!!!!!!!!!"); + // Log(LogLevel::kInfo, "GOT SRGB!!!!!!!!!!!"); // } else if (enc == GL_LINEAR) { - // Log("GOT LINEAR..."); + // Log(LogLevel::kInfo, "GOT LINEAR..."); // } else { - // Log("GOT OTHER.."); + // Log(LogLevel::kInfo, "GOT OTHER.."); // } loaded_ = true; } @@ -1001,8 +1000,8 @@ class RendererGL::FramebufferObjectGL : public Framebuffer { // Base class for fragment/vertex shaders. class RendererGL::ShaderGL : public Object { public: - auto GetDefaultOwnerThread() const -> ThreadIdentifier override { - return ThreadIdentifier::kMain; + auto GetDefaultOwnerThread() const -> ThreadTag override { + return ThreadTag::kMain; } ShaderGL(GLenum type_in, const std::string& src_in) : type_(type_in) { @@ -1023,11 +1022,12 @@ class RendererGL::ShaderGL : public Object { const char* renderer = (const char*)glGetString(GL_RENDERER); // Let's not crash here. We have a better chance of calling home this way // and theres a chance the game will still be playable. - Log(std::string("Compile failed for ") + GetTypeName() - + " shader:\n------------SOURCE BEGIN-------------\n" + src_in - + "\n-----------SOURCE END-------------\n" + GetInfo() - + "\nrenderer: " + renderer + "\nvendor: " + vendor - + "\nversion:" + version); + Log(LogLevel::kError, + std::string("Compile failed for ") + GetTypeName() + + " shader:\n------------SOURCE BEGIN-------------\n" + src_in + + "\n-----------SOURCE END-------------\n" + GetInfo() + + "\nrenderer: " + renderer + "\nvendor: " + vendor + + "\nversion:" + version); } else { assert(compile_status == GL_TRUE); std::string info = GetInfo(); @@ -1038,10 +1038,12 @@ class RendererGL::ShaderGL : public Object { const char* version = (const char*)glGetString(GL_VERSION); const char* vendor = (const char*)glGetString(GL_VENDOR); const char* renderer = (const char*)glGetString(GL_RENDERER); - Log(std::string("WARNING: info returned for ") + GetTypeName() - + " shader:\n------------SOURCE BEGIN-------------\n" + src_in - + "\n-----------SOURCE END-------------\n" + info + "\nrenderer: " - + renderer + "\nvendor: " + vendor + "\nversion:" + version); + Log(LogLevel::kError, + std::string("WARNING: info returned for ") + GetTypeName() + + " shader:\n------------SOURCE BEGIN-------------\n" + src_in + + "\n-----------SOURCE END-------------\n" + info + + "\nrenderer: " + renderer + "\nvendor: " + vendor + + "\nversion:" + version); } } DEBUG_CHECK_GL_ERROR; @@ -1131,7 +1133,8 @@ class RendererGL::ProgramGL { GLint linkStatus; glGetProgramiv(program_, GL_LINK_STATUS, &linkStatus); if (linkStatus == GL_FALSE) { - Log("Link failed for program '" + name_ + "':\n" + GetInfo()); + Log(LogLevel::kError, + "Link failed for program '" + name_ + "':\n" + GetInfo()); } else { assert(linkStatus == GL_TRUE); @@ -1140,8 +1143,8 @@ class RendererGL::ProgramGL { && (strstr(info.c_str(), "error:") || strstr(info.c_str(), "warning:") || strstr(info.c_str(), "Error:") || strstr(info.c_str(), "Warning:"))) { - Log("WARNING: program using frag shader '" + name_ - + "' returned info:\n" + info); + Log(LogLevel::kError, "WARNING: program using frag shader '" + name_ + + "' returned info:\n" + info); } } @@ -1278,8 +1281,9 @@ class RendererGL::ProgramGL { int c = glGetUniformLocation(program_, tex_name); if (c == -1) { #if !MSAA_ERROR_TEST - Log("Error: ShaderGL: " + name_ + ": Can't set texture unit for texture '" - + tex_name + "'"); + Log(LogLevel::kError, "ShaderGL: " + name_ + + ": Can't set texture unit for texture '" + + tex_name + "'"); DEBUG_CHECK_GL_ERROR; #endif } else { @@ -1511,7 +1515,8 @@ class RendererGL::SimpleProgramGL : public RendererGL::ProgramGL { "}"; if (flags & SHD_DEBUG_PRINT) - Log("\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); + Log(LogLevel::kInfo, + "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); return s; } auto GetFragmentCode(int flags) -> std::string { @@ -1613,7 +1618,8 @@ class RendererGL::SimpleProgramGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) - Log("\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); + Log(LogLevel::kInfo, + "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); return s; } float r_{}, g_{}, b_{}, a_{}; @@ -1846,7 +1852,8 @@ class RendererGL::ObjectProgramGL : public RendererGL::ProgramGL { } s += "}"; if (flags & SHD_DEBUG_PRINT) - Log("\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); + Log(LogLevel::kInfo, + "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); return s; } auto GetFragmentCode(int flags) -> std::string { @@ -1918,7 +1925,8 @@ class RendererGL::ObjectProgramGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) - Log("\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); + Log(LogLevel::kInfo, + "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); return s; } float r_, g_, b_, a_; @@ -2035,7 +2043,8 @@ class RendererGL::SmokeProgramGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) - Log("\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); + Log(LogLevel::kInfo, + "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); return s; } auto GetFragmentCode(int flags) -> std::string { @@ -2077,7 +2086,8 @@ class RendererGL::SmokeProgramGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) - Log("\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); + Log(LogLevel::kInfo, + "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); return s; } float r_, g_, b_, a_; @@ -2164,7 +2174,8 @@ class RendererGL::BlurProgramGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) - Log("\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); + Log(LogLevel::kInfo, + "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); return s; } auto GetFragmentCode(int flags) -> std::string { @@ -2198,7 +2209,8 @@ class RendererGL::BlurProgramGL : public RendererGL::ProgramGL { " + texture2D(colorTex,vUV8));\n" "}"; if (flags & SHD_DEBUG_PRINT) { - Log("\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); + Log(LogLevel::kInfo, + "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); } return s; } @@ -2248,7 +2260,8 @@ class RendererGL::ShieldProgramGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) - Log("\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); + Log(LogLevel::kInfo, + "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); return s; } auto GetFragmentCode(int flags) -> std::string { @@ -2301,7 +2314,8 @@ class RendererGL::ShieldProgramGL : public RendererGL::ProgramGL { //" gl_FragColor = vec4(vec3(depth),1);\n" if (flags & SHD_DEBUG_PRINT) - Log("\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); + Log(LogLevel::kInfo, + "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); return s; } @@ -2449,7 +2463,8 @@ class RendererGL::PostProcessProgramGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) - Log("\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); + Log(LogLevel::kInfo, + "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); return s; } string GetFragmentCode(int flags) { @@ -2463,7 +2478,8 @@ class RendererGL::PostProcessProgramGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) - Log("\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); + Log(LogLevel::kInfo, + "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); return s; } @@ -2510,7 +2526,8 @@ class RendererGL::PostProcessProgramGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) - Log("\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); + Log(LogLevel::kInfo, + "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); return s; } auto GetFragmentCode(int flags) -> std::string { @@ -2636,7 +2653,8 @@ class RendererGL::PostProcessProgramGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) - Log("\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); + Log(LogLevel::kInfo, + "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); return s; } #endif // msaa bug test @@ -2751,7 +2769,8 @@ class RendererGL::SpriteProgramGL : public RendererGL::ProgramGL { s += "}"; if (flags & SHD_DEBUG_PRINT) - Log("\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); + Log(LogLevel::kInfo, + "\nVertex code for shader '" + GetName(flags) + "':\n\n" + s); return s; } auto GetFragmentCode(int flags) -> std::string { @@ -2787,7 +2806,8 @@ class RendererGL::SpriteProgramGL : public RendererGL::ProgramGL { } s += "}"; if (flags & SHD_DEBUG_PRINT) - Log("\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); + Log(LogLevel::kInfo, + "\nFragment code for shader '" + GetName(flags) + "':\n\n" + s); return s; } float r_, g_, b_, a_; @@ -2808,7 +2828,7 @@ class RendererGL::TextureDataGL : public TextureRendererData { ~TextureDataGL() override { if (!InGraphicsThread()) { - Log("Error: TextureDataGL dying outside of graphics thread."); + Log(LogLevel::kError, "TextureDataGL dying outside of graphics thread."); } else { // if we're currently bound as anything, clear that out // (otherwise a new texture with that same ID won't be bindable) @@ -3272,6 +3292,7 @@ class RendererGL::ModelDataGL : public ModelRendererData { } case 4: { BA_LOG_ONCE( + LogLevel::kWarning, "GL WARNING - USING 32 BIT INDICES WHICH WONT WORK IN ES2!!"); elem_count_ = static_cast(model.indices32().size()); index_type_ = GL_UNSIGNED_INT; @@ -3473,7 +3494,8 @@ class RendererGL::MeshDataGL : public MeshRendererData { dynamic_draw_ ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW); index_state_ = data->state; have_index_data_ = true; - BA_LOG_ONCE("GL WARNING - USING 32 BIT INDICES WHICH WONT WORK IN ES2!!"); + BA_LOG_ONCE(LogLevel::kWarning, + "GL WARNING - USING 32 BIT INDICES WHICH WONT WORK IN ES2!!"); index_type_ = GL_UNSIGNED_INT; } } @@ -3889,6 +3911,7 @@ class RendererGL::RenderTargetGL : public RenderTarget { // this needs to be on for glClear to work on depth. if (!renderer_->depth_writing_enabled_) { BA_LOG_ONCE( + LogLevel::kWarning, "RendererGL: depth-writing not enabled when clearing depth"); } clear_mask |= GL_DEPTH_BUFFER_BIT; @@ -5743,7 +5766,8 @@ void RendererGL::UpdateVignetteTex(bool force) { if (err != GL_NO_ERROR) { static bool reported = false; if (!reported) { - Log("Error: 32-bit vignette creation failed; falling back to 16."); + Log(LogLevel::kError, + "32-bit vignette creation failed; falling back to 16."); reported = true; } const int kVignetteTexWidth = 64; @@ -5800,14 +5824,15 @@ void RendererGL::UpdateVignetteTex(bool force) { auto RendererGL::GetFunkyDepthIssue() -> bool { if (!funky_depth_issue_set_) { - BA_LOG_ONCE("fetching funky depth issue but not set"); + BA_LOG_ONCE(LogLevel::kError, "fetching funky depth issue but not set"); } return funky_depth_issue_; } auto RendererGL::GetDrawsShieldsFunny() -> bool { if (!draws_shields_funny_set_) { - BA_LOG_ONCE("fetching draws-shields-funny value but not set"); + BA_LOG_ONCE(LogLevel::kError, + "fetching draws-shields-funny value but not set"); } return draws_shields_funny_; } diff --git a/src/ballistica/graphics/graphics.cc b/src/ballistica/graphics/graphics.cc index ef1949db..d2fd216d 100644 --- a/src/ballistica/graphics/graphics.cc +++ b/src/ballistica/graphics/graphics.cc @@ -5,10 +5,6 @@ #include "ballistica/app/app.h" #include "ballistica/app/app_flavor.h" #include "ballistica/dynamics/bg/bg_dynamics.h" -#include "ballistica/game/connection/connection_set.h" -#include "ballistica/game/connection/connection_to_client.h" -#include "ballistica/game/connection/connection_to_host.h" -#include "ballistica/game/session/session.h" #include "ballistica/generic/utils.h" #include "ballistica/graphics/camera.h" #include "ballistica/graphics/component/empty_component.h" @@ -22,6 +18,10 @@ #include "ballistica/graphics/net_graph.h" #include "ballistica/graphics/text/text_graphics.h" #include "ballistica/input/input.h" +#include "ballistica/logic/connection/connection_set.h" +#include "ballistica/logic/connection/connection_to_client.h" +#include "ballistica/logic/connection/connection_to_host.h" +#include "ballistica/logic/session/session.h" #include "ballistica/python/python.h" #include "ballistica/python/python_context_call.h" #include "ballistica/scene/node/globals_node.h" @@ -293,7 +293,7 @@ void Graphics::DrawMiscOverlays(RenderPass* pass) { // Add in/out data for any host connection. if (ConnectionToHost* connection_to_host = - g_game->connections()->connection_to_host()) { + g_logic->connections()->connection_to_host()) { if (connection_to_host->can_communicate()) show = true; in_size += connection_to_host->GetBytesInPerSecond(); in_size_compressed += connection_to_host->GetBytesInPerSecondCompressed(); @@ -307,7 +307,7 @@ void Graphics::DrawMiscOverlays(RenderPass* pass) { ping = connection_to_host->average_ping(); } else { int connected_count = 0; - for (auto&& i : g_game->connections()->connections_to_clients()) { + for (auto&& i : g_logic->connections()->connections_to_clients()) { ConnectionToClient* client = i.second.get(); if (client->can_communicate()) { show = true; @@ -449,7 +449,7 @@ void Graphics::DrawMiscOverlays(RenderPass* pass) { { SimpleComponent c(pass); c.SetTransparent(true); - c.SetTexture(g_media->GetTexture(SystemTextureID::kSoftRectVertical)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kSoftRectVertical)); float screen_width = g_graphics->screen_virtual_width(); @@ -533,7 +533,7 @@ void Graphics::DrawMiscOverlays(RenderPass* pass) { // Align our bottom with where we just scaled from. c.Translate(0, 0.5f, 0); } - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); v += scale * (36 + str_height); @@ -690,14 +690,14 @@ void Graphics::DrawMiscOverlays(RenderPass* pass) { c2.SetColorizeColor(i->tint.x, i->tint.y, i->tint.z); c2.SetColorizeColor2(i->tint2.x, i->tint2.y, i->tint2.z); c2.SetMaskTexture( - g_media->GetTexture(SystemTextureID::kCharacterIconMask)); + g_assets->GetTexture(SystemTextureID::kCharacterIconMask)); } c2.SetColor(1, 1, 1, a); c2.PushTransform(); c2.Translate(h - 14, v_base + 10 + i->v_smoothed, kScreenMessageZDepth); c2.Scale(22.0f * s_extra, 22.0f * s_extra); - c2.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c2.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c2.PopTransform(); c2.Submit(); } @@ -877,9 +877,10 @@ void Graphics::FadeScreen(bool to, millisecs_t time, PyObject* endcall) { // (otherwise, overlapping fades can cause things to get lost) if (fade_end_call_.exists()) { if (g_buildconfig.debug_build()) { - Log("WARNING: 2 fades overlapping; running first fade-end-call early"); + Log(LogLevel::kWarning, + "2 fades overlapping; running first fade-end-call early"); } - g_game->PushPythonCall(fade_end_call_); + g_logic->PushPythonCall(fade_end_call_); fade_end_call_.Clear(); } set_fade_start_on_next_draw_ = true; @@ -898,7 +899,7 @@ void Graphics::DrawLoadDot(RenderPass* pass) { // Draw red if we've got graphics stuff loading. Green if only other stuff // left. - if (g_media->GetGraphicalPendingLoadCount() > 0) { + if (g_assets->GetGraphicalPendingLoadCount() > 0) { c.SetColor(0.2f, 0, 0, 1); } else { c.SetColor(0, 0.2f, 0, 1); @@ -991,17 +992,17 @@ void Graphics::BuildAndPushFrameDef() { // We should not be building/pushing any frames until after // app-launch-commands have been run.. - BA_PRECONDITION_FATAL(g_game->ran_app_launch_commands()); + BA_PRECONDITION_FATAL(g_logic->ran_app_launch_commands()); // This should no longer be necessary.. WaitForRendererToExist(); - Session* session = g_game->GetForegroundSession(); + Session* session = g_logic->GetForegroundSession(); bool session_fills_screen = session ? session->DoesFillScreen() : false; millisecs_t real_time = GetRealTime(); // Store how much time this frame_def represents. - millisecs_t net_time = g_game->master_time(); + millisecs_t net_time = g_logic->master_time(); millisecs_t elapsed = std::min(millisecs_t{50}, net_time - last_create_frame_def_time_); last_create_frame_def_time_ = net_time; @@ -1010,7 +1011,7 @@ void Graphics::BuildAndPushFrameDef() { FrameDef* frame_def = GetEmptyFrameDef(); frame_def->set_real_time(real_time); - frame_def->set_base_time(g_game->master_time()); + frame_def->set_base_time(g_logic->master_time()); frame_def->set_base_time_elapsed(elapsed); frame_def->set_frame_number(frame_def_count_++); @@ -1070,13 +1071,14 @@ void Graphics::BuildAndPushFrameDef() { if (frame_def->GetOverlayFlatPass()->HasDrawCommands()) { if (!g_ui->IsWindowPresent()) { BA_LOG_ONCE( + LogLevel::kError, "Drawing in overlay pass in VR mode without UI; shouldn't " "happen!"); } } } - if (g_media->GetPendingLoadCount() > 0) { + if (g_assets->GetPendingLoadCount() > 0) { DrawLoadDot(overlay_pass); } @@ -1124,7 +1126,7 @@ void Graphics::DrawBoxingGlovesTest(FrameDef* frame_def) { c.Translate(0, 7, -3.3f); c.Scale(10, 10, 10); c.Rotate(a, 0, 0, 1); - c.DrawModel(g_media->GetModel(SystemModelID::kBoxingGlove)); + c.DrawModel(g_assets->GetModel(SystemModelID::kBoxingGlove)); c.PopTransform(); c.Submit(); } @@ -1132,14 +1134,14 @@ void Graphics::DrawBoxingGlovesTest(FrameDef* frame_def) { // Beauty. if (explicit_bool(false)) { ObjectComponent c(frame_def->beauty_pass()); - c.SetTexture(g_media->GetTexture(SystemTextureID::kBoxingGlove)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kBoxingGlove)); c.SetReflection(ReflectionType::kSoft); c.SetReflectionScale(0.4f, 0.4f, 0.4f); c.PushTransform(); c.Translate(0.0f, 3.7f, -3.3f); c.Scale(10.0f, 10.0f, 10.0f); c.Rotate(a, 0.0f, 0.0f, 1.0f); - c.DrawModel(g_media->GetModel(SystemModelID::kBoxingGlove)); + c.DrawModel(g_assets->GetModel(SystemModelID::kBoxingGlove)); c.PopTransform(); c.Submit(); } @@ -1153,7 +1155,7 @@ void Graphics::DrawBoxingGlovesTest(FrameDef* frame_def) { c.Translate(0.0f, 3.7f, -3.3f); c.Scale(10.0f, 10.0f, 10.0f); c.Rotate(a, 0.0f, 0.0f, 1.0f); - c.DrawModel(g_media->GetModel(SystemModelID::kBoxingGlove)); + c.DrawModel(g_assets->GetModel(SystemModelID::kBoxingGlove)); c.PopTransform(); c.Submit(); } @@ -1168,7 +1170,7 @@ void Graphics::DrawDebugBuffers(RenderPass* pass) { c.PushTransform(); c.Translate(70, 400, kDebugImgZDepth); c.Scale(csize, csize); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); c.Submit(); } @@ -1178,7 +1180,7 @@ void Graphics::DrawDebugBuffers(RenderPass* pass) { c.PushTransform(); c.Translate(70, 250, kDebugImgZDepth); c.Scale(csize, csize); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); c.Submit(); } @@ -1190,18 +1192,18 @@ void Graphics::UpdateAndDrawProgressBar(FrameDef* frame_def, RenderPass* pass = frame_def->overlay_pass(); UpdateProgressBarProgress( 1.0f - - static_cast(g_media->GetGraphicalPendingLoadCount()) + - static_cast(g_assets->GetGraphicalPendingLoadCount()) / static_cast(progress_bar_loads_)); DrawProgressBar(pass, 1.0f); // If we were drawing a progress bar, see if everything is now loaded.. if // so, start rendering normally next frame. - int count = g_media->GetGraphicalPendingLoadCount(); + int count = g_assets->GetGraphicalPendingLoadCount(); if (count <= 0) { progress_bar_ = false; progress_bar_end_time_ = real_time; } - if (g_media->GetPendingLoadCount() > 0) { + if (g_assets->GetPendingLoadCount() > 0) { DrawLoadDot(pass); } } @@ -1213,7 +1215,7 @@ void Graphics::DrawFades(FrameDef* frame_def, millisecs_t real_time) { if (fade_ <= 0.0f && fade_out_) { millisecs_t faded_time = real_time - (fade_start_ + fade_time_); if (faded_time > 15000) { - Log("FORCE-ENDING STUCK FADE"); + Log(LogLevel::kError, "FORCE-ENDING STUCK FADE"); fade_out_ = false; fade_ = 1.0f; fade_time_ = 1000; @@ -1238,7 +1240,7 @@ void Graphics::DrawFades(FrameDef* frame_def, millisecs_t real_time) { } else { fade_ = 0; if (!was_done && fade_end_call_.exists()) { - g_game->PushPythonCall(fade_end_call_); + g_logic->PushPythonCall(fade_end_call_); fade_end_call_.Clear(); } } @@ -1294,7 +1296,7 @@ void Graphics::DrawFades(FrameDef* frame_def, millisecs_t real_time) { float inv_a = 1.0f - a; float s = 100.0f * inv_a + 5.0f * a; c.Scale(s, s, s); - c.DrawModel(g_media->GetModel(SystemModelID::kVRFade)); + c.DrawModel(g_assets->GetModel(SystemModelID::kVRFade)); c.PopTransform(); c.Submit(); #else // BA_VR_BUILD @@ -1347,7 +1349,7 @@ void Graphics::DrawCursor(RenderPass* pass, millisecs_t real_time) { SimpleComponent c(pass); c.SetTransparent(true); float csize = 50.0f; - c.SetTexture(g_media->GetTexture(SystemTextureID::kCursor)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kCursor)); c.PushTransform(); // Note: we don't plug in known cursor position values here; we tell the @@ -1356,7 +1358,7 @@ void Graphics::DrawCursor(RenderPass* pass, millisecs_t real_time) { c.CursorTranslate(); c.Translate(csize * 0.44f, csize * -0.44f, kCursorZDepth); c.Scale(csize, csize); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); c.Submit(); } @@ -1372,7 +1374,7 @@ void Graphics::DrawBlotches(FrameDef* frame_def) { this->shadow_blotch_mesh_->SetData(Object::New>( this->blotch_verts_.size(), &this->blotch_verts_[0])); SpriteComponent c(frame_def->light_shadow_pass()); - c.SetTexture(g_media->GetTexture(SystemTextureID::kLight)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kLight)); c.DrawMesh(this->shadow_blotch_mesh_.get()); c.Submit(); } @@ -1385,7 +1387,7 @@ void Graphics::DrawBlotches(FrameDef* frame_def) { Object::New>(this->blotch_soft_verts_.size(), &this->blotch_soft_verts_[0])); SpriteComponent c(frame_def->light_shadow_pass()); - c.SetTexture(g_media->GetTexture(SystemTextureID::kLightSoft)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kLightSoft)); c.DrawMesh(this->shadow_blotch_soft_mesh_.get()); c.Submit(); } @@ -1401,7 +1403,7 @@ void Graphics::DrawBlotches(FrameDef* frame_def) { this->blotch_soft_obj_verts_.size(), &this->blotch_soft_obj_verts_[0])); SpriteComponent c(frame_def->light_pass()); - c.SetTexture(g_media->GetTexture(SystemTextureID::kLightSoft)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kLightSoft)); c.DrawMesh(this->shadow_blotch_soft_obj_mesh_.get()); c.Submit(); } @@ -1448,7 +1450,7 @@ void Graphics::AddMeshDataDestroy(MeshData* d) { void Graphics::EnableProgressBar(bool fade_in) { assert(InLogicThread()); - progress_bar_loads_ = g_media->GetGraphicalPendingLoadCount(); + progress_bar_loads_ = g_assets->GetGraphicalPendingLoadCount(); assert(progress_bar_loads_ >= 0); if (progress_bar_loads_ > 0) { progress_bar_ = true; @@ -1503,6 +1505,7 @@ void Graphics::WaitForRendererToExist() { while (g_graphics_server == nullptr || g_graphics_server->renderer() == nullptr) { BA_LOG_ONCE( + LogLevel::kWarning, "BuildAndPushFrameDef() called before renderer is up; spinning..."); Platform::SleepMS(100); sleep_count++; @@ -1801,7 +1804,7 @@ void Graphics::ScreenResize(float virtual_width, float virtual_height, void Graphics::ScreenMessageEntry::UpdateTranslation() { if (translation_dirty) { - s_translated = g_game->CompileResourceString( + s_translated = g_logic->CompileResourceString( s_raw, "Graphics::ScreenMessageEntry::UpdateTranslation"); translation_dirty = false; mesh_dirty = true; diff --git a/src/ballistica/graphics/graphics.h b/src/ballistica/graphics/graphics.h index 4c6b02cb..8d8c309f 100644 --- a/src/ballistica/graphics/graphics.h +++ b/src/ballistica/graphics/graphics.h @@ -43,7 +43,7 @@ const float kBackingDepth1 = 0.0f; const float kShadowNeutral = 0.5f; -// Client class for graphics operations (used from the game thread). +// Client class for graphics operations (used from the logic thread). class Graphics { public: Graphics(); diff --git a/src/ballistica/graphics/graphics_server.cc b/src/ballistica/graphics/graphics_server.cc index fe7b7862..f0abe83f 100644 --- a/src/ballistica/graphics/graphics_server.cc +++ b/src/ballistica/graphics/graphics_server.cc @@ -11,10 +11,10 @@ #include "ballistica/platform/sdl/sdl_app.h" #else #include "ballistica/app/app_flavor.h" +#include "ballistica/assets/assets.h" #include "ballistica/graphics/frame_def.h" #include "ballistica/graphics/mesh/mesh_data.h" #include "ballistica/graphics/renderer.h" -#include "ballistica/media/media.h" #include "ballistica/platform/platform.h" #endif @@ -30,10 +30,9 @@ void GraphicsServer::FullscreenCheck() { } #endif -GraphicsServer::GraphicsServer(Thread* thread) : thread_(thread) { - // We're a singleton. +GraphicsServer::GraphicsServer() : thread_(g_main_thread) { + // We're a singleton; make sure we don't already exist. assert(g_graphics_server == nullptr); - g_graphics_server = this; // For janky old non-event-push mode, just fall back on a timer for rendering. if (!g_platform->IsEventPushMode()) { @@ -51,7 +50,7 @@ void GraphicsServer::SetRenderHold() { void GraphicsServer::SetFrameDef(FrameDef* framedef) { // Note: we're just setting the framedef directly here - // even though this gets called from the game thread. + // even though this gets called from the logic thread. // Ideally it would seem we should push these to our thread // event list, but currently we spin-lock waiting for new // frames to appear which would prevent that from working; @@ -75,19 +74,19 @@ auto GraphicsServer::GetRenderFrameDef() -> FrameDef* { } // Do some incremental loading every time we try to render. - g_media->RunPendingGraphicsLoads(); + g_assets->RunPendingGraphicsLoads(); // Spin and wait for a short bit for a frame_def to appear. If it does, we - // grab it, render it, and also message the game thread to start generating + // grab it, render it, and also message the logic thread to start generating // another one. while (true) { if (frame_def_) { FrameDef* frame_def = frame_def_; frame_def_ = nullptr; - // Tell the game thread we're ready for the next frame_def so it can start - // building it while we render this one. - g_game->PushFrameDefRequest(); + // Tell the logic thread we're ready for the next frame_def so it can + // start building it while we render this one. + g_logic->PushFrameDefRequest(); return frame_def; } @@ -174,7 +173,7 @@ void GraphicsServer::TryRender() { FinishRenderFrameDef(frame_def); } - // Send this frame_def back to the game thread for deletion. + // Send this frame_def back to the logic thread for deletion. g_graphics->ReturnCompletedFrameDef(frame_def); } } @@ -185,7 +184,7 @@ void GraphicsServer::ReloadMedia() { // Immediately unload all renderer data here in this thread. if (renderer_) { - g_media->UnloadRendererBits(true, true); + g_assets->UnloadRendererBits(true, true); } // Set a render-hold so we ignore all frame_defs up until the point at which @@ -195,11 +194,11 @@ void GraphicsServer::ReloadMedia() { assert(g_graphics_server); SetRenderHold(); - // Now tell the game thread to kick off loads for everything, flip on + // Now tell the logic thread to kick off loads for everything, flip on // progress bar drawing, and then tell the graphics thread to stop ignoring // frame-defs. - g_game->thread()->PushCall([this] { - g_media->MarkAllMediaForLoad(); + g_logic->thread()->PushCall([this] { + g_assets->MarkAllAssetsForLoad(); g_graphics->EnableProgressBar(false); PushRemoveRenderHoldCall(); }); @@ -210,7 +209,7 @@ void GraphicsServer::RebuildLostContext() { assert(InGraphicsThread()); if (!renderer_) { - Log("Error: No renderer on GraphicsServer::_rebuildContext."); + Log(LogLevel::kError, "No renderer on GraphicsServer::_rebuildContext."); return; } @@ -219,7 +218,7 @@ void GraphicsServer::RebuildLostContext() { set_renderer_context_lost(true); // Unload all texture and model data here in the render thread. - g_media->UnloadRendererBits(true, true); + g_assets->UnloadRendererBits(true, true); // Also unload dynamic meshes. for (auto&& i : mesh_datas_) { @@ -247,10 +246,11 @@ void GraphicsServer::RebuildLostContext() { // we won't hitch if we actually render them.) SetRenderHold(); - // Now tell the game thread to kick off loads for everything, flip on progress - // bar drawing, and then tell the graphics thread to stop ignoring frame-defs. - g_game->thread()->PushCall([this] { - g_media->MarkAllMediaForLoad(); + // Now tell the logic thread to kick off loads for everything, flip on + // progress bar drawing, and then tell the graphics thread to stop ignoring + // frame-defs. + g_logic->thread()->PushCall([this] { + g_assets->MarkAllAssetsForLoad(); g_graphics->EnableProgressBar(false); PushRemoveRenderHoldCall(); }); @@ -352,12 +352,12 @@ void GraphicsServer::SetScreen(bool fullscreen, int width, int height, } // The first time we complete setting up our screen, we send a message - // back to the game thread to complete the init process.. (they can't start + // back to the logic thread to complete the init process.. (they can't start // loading graphics and things until we have our context set up so we know // what types of textures to load, etc) if (!initial_screen_created_) { initial_screen_created_ = true; - g_game->PushInitialScreenCreatedCall(); + g_logic->PushInitialScreenCreatedCall(); } } @@ -373,7 +373,7 @@ void GraphicsServer::HandleFullContextScreenRebuild( if (renderer_) { // Unload all textures and models.. these will be reloaded as-needed // automatically for the new context.. - g_media->UnloadRendererBits(true, true); + g_assets->UnloadRendererBits(true, true); // Also unload all dynamic meshes. for (auto&& i : mesh_datas_) { @@ -403,9 +403,9 @@ void GraphicsServer::HandleFullContextScreenRebuild( UpdateVirtualScreenRes(); - // Inform the game thread of the latest values. - g_game->PushScreenResizeCall(res_x_virtual_, res_y_virtual_, res_x_, - res_y_); + // Inform the logic thread of the latest values. + g_logic->PushScreenResizeCall(res_x_virtual_, res_y_virtual_, res_x_, + res_y_); } if (!renderer_) { @@ -460,11 +460,11 @@ void GraphicsServer::HandleFullContextScreenRebuild( // so we won't hitch if we actually render them.) SetRenderHold(); - // Now tell the game thread to kick off loads for everything, flip on + // Now tell the logic thread to kick off loads for everything, flip on // progress bar drawing, and then tell the graphics thread to stop ignoring // frame-defs. - g_game->thread()->PushCall([this] { - g_media->MarkAllMediaForLoad(); + g_logic->thread()->PushCall([this] { + g_assets->MarkAllAssetsForLoad(); g_graphics->set_internal_components_inited(false); g_graphics->EnableProgressBar(false); PushRemoveRenderHoldCall(); @@ -514,8 +514,8 @@ void GraphicsServer::VideoResize(float h, float v) { res_y_ = v; UpdateVirtualScreenRes(); - // Inform the game thread of the latest values. - g_game->PushScreenResizeCall(res_x_virtual_, res_y_virtual_, res_x_, res_y_); + // Inform the logic thread of the latest values. + g_logic->PushScreenResizeCall(res_x_virtual_, res_y_virtual_, res_x_, res_y_); if (renderer_) { renderer_->ScreenSizeChanged(); } @@ -759,7 +759,7 @@ void GraphicsServer::PushSetVSyncCall(bool sync, bool auto_sync) { gl_context_->SetVSync(v_sync_); } } else { - Log("Error: Got SetVSyncCall with no gl context."); + Log(LogLevel::kError, "Got SetVSyncCall with no gl context."); } } } @@ -768,15 +768,15 @@ void GraphicsServer::PushSetVSyncCall(bool sync, bool auto_sync) { } void GraphicsServer::PushComponentUnloadCall( - const std::vector*>& components) { + const std::vector*>& components) { thread()->PushCall([this, components] { // Unload all components we were passed. for (auto&& i : components) { (**i).Unload(); } - // ..and then ship these pointers back to the game thread so it can free the - // references. - g_game->PushFreeMediaComponentRefsCall(components); + // ..and then ship these pointers back to the logic thread so it can free + // the references. + g_logic->PushFreeAssetComponentRefsCall(components); }); } @@ -785,7 +785,7 @@ void GraphicsServer::PushRemoveRenderHoldCall() { assert(render_hold_); render_hold_--; if (render_hold_ < 0) { - Log("Error: RenderHold < 0"); + Log(LogLevel::kError, "RenderHold < 0"); render_hold_ = 0; } }); diff --git a/src/ballistica/graphics/graphics_server.h b/src/ballistica/graphics/graphics_server.h index 79cb989d..061da29c 100644 --- a/src/ballistica/graphics/graphics_server.h +++ b/src/ballistica/graphics/graphics_server.h @@ -18,7 +18,7 @@ namespace ballistica { // Graphics class GraphicsServer { public: - explicit GraphicsServer(Thread* thread); + GraphicsServer(); auto PushSetScreenGammaCall(float gamma) -> void; auto PushSetScreenPixelScaleCall(float pixel_scale) -> void; auto PushSetVSyncCall(bool sync, bool auto_sync) -> void; @@ -29,10 +29,10 @@ class GraphicsServer { auto PushReloadMediaCall() -> void; auto PushRemoveRenderHoldCall() -> void; auto PushComponentUnloadCall( - const std::vector*>& components) -> void; + const std::vector*>& components) -> void; auto SetRenderHold() -> void; - // Used by the game thread to pass frame-defs to the graphics server + // Used by the logic thread to pass frame-defs to the graphics server // for rendering. auto SetFrameDef(FrameDef* framedef) -> void; @@ -61,18 +61,22 @@ class GraphicsServer { // init the modelview matrix to look here auto SetCamera(const Vector3f& eye, const Vector3f& target, const Vector3f& up) -> void; + auto SetOrthoProjection(float left, float right, float bottom, float top, float near, float far) -> void; + auto ModelViewReset() -> void { model_view_matrix_ = kMatrix44fIdentity; model_view_projection_matrix_dirty_ = model_world_matrix_dirty_ = true; model_view_stack_.clear(); } + auto SetProjectionMatrix(const Matrix44f& p) -> void { projection_matrix_ = p; model_view_projection_matrix_dirty_ = true; projection_matrix_state_++; } + auto projection_matrix_state() -> uint32_t { return projection_matrix_state_; } @@ -85,9 +89,11 @@ class GraphicsServer { light_shadow_projection_matrix_state_++; } } + auto light_shadow_projection_matrix_state() const -> uint32_t { return light_shadow_projection_matrix_state_; } + auto light_shadow_projection_matrix() const -> const Matrix44f& { return light_shadow_projection_matrix_; } @@ -270,6 +276,13 @@ class GraphicsServer { model_world_matrix_dirty_ = false; } } + auto SetScreen(bool fullscreen, int width, int height, + TextureQuality texture_quality, + GraphicsQuality graphics_quality, + const std::string& android_res) -> void; +#if BA_OSTYPE_MACOS && BA_XCODE_BUILD + void FullscreenCheck(); +#endif #if BA_ENABLE_OPENGL std::unique_ptr gl_context_; #endif @@ -291,7 +304,6 @@ class GraphicsServer { bool fullscreen_enabled_{}; float target_res_x_{800.0f}; float target_res_y_{600.0f}; - Matrix44f model_view_matrix_{kMatrix44fIdentity}; Matrix44f view_world_matrix_{kMatrix44fIdentity}; Matrix44f projection_matrix_{kMatrix44fIdentity}; @@ -314,18 +326,11 @@ class GraphicsServer { std::list mesh_datas_; bool v_sync_{}; bool auto_vsync_{}; - auto SetScreen(bool fullscreen, int width, int height, - TextureQuality texture_quality, - GraphicsQuality graphics_quality, - const std::string& android_res) -> void; Timer* render_timer_{}; Renderer* renderer_{}; FrameDef* frame_def_{}; bool initial_screen_created_{}; int render_hold_{}; -#if BA_OSTYPE_MACOS && BA_XCODE_BUILD - void FullscreenCheck(); -#endif }; } // namespace ballistica diff --git a/src/ballistica/graphics/mesh/mesh_buffer_base.h b/src/ballistica/graphics/mesh/mesh_buffer_base.h index 0a498f49..04f264a6 100644 --- a/src/ballistica/graphics/mesh/mesh_buffer_base.h +++ b/src/ballistica/graphics/mesh/mesh_buffer_base.h @@ -7,10 +7,10 @@ namespace ballistica { -// Buffers used by the game thread to pass indices/vertices/etc. to meshes in +// Buffers used by the logic thread to pass indices/vertices/etc. to meshes in // the graphics thread. Note that it is safe to create these in other threads; // you just need to turn off thread-checks until you pass ownership to the game -// thread. (or just avoid creating references outside of the game thread) +// thread. (or just avoid creating references outside of the logic thread) class MeshBufferBase : public Object { public: uint32_t state; // which dynamicState value on the mesh this corresponds to diff --git a/src/ballistica/graphics/mesh/mesh_data.h b/src/ballistica/graphics/mesh/mesh_data.h index 1dd956c9..016b98c1 100644 --- a/src/ballistica/graphics/mesh/mesh_data.h +++ b/src/ballistica/graphics/mesh/mesh_data.h @@ -17,7 +17,7 @@ class MeshData { : type_(type), draw_type_(draw_type) {} virtual ~MeshData() { if (renderer_data_) { - Log("Error: MeshData going down with rendererData intact!"); + Log(LogLevel::kError, "MeshData going down with rendererData intact!"); } } std::list::iterator iterator_; diff --git a/src/ballistica/graphics/mesh/mesh_indexed_base.h b/src/ballistica/graphics/mesh/mesh_indexed_base.h index e8daed0c..5b372252 100644 --- a/src/ballistica/graphics/mesh/mesh_indexed_base.h +++ b/src/ballistica/graphics/mesh/mesh_indexed_base.h @@ -76,9 +76,10 @@ class MeshIndexedBase : public Mesh { // For use by subclasses in their IsValid() overrides auto IndexSizeIsValid(size_t data_size) const -> bool { if (index_data_size() == 2 && data_size > 65535) { - BA_LOG_ONCE("ERROR: got mesh data with > 65535 elems and 16 bit indices: " - + GetObjectDescription() - + ". This case requires 32 bit indices."); + BA_LOG_ONCE(LogLevel::kError, + "Got mesh data with > 65535 elems and 16 bit indices: " + + GetObjectDescription() + + ". This case requires 32 bit indices."); return false; } return true; diff --git a/src/ballistica/graphics/mesh/mesh_indexed_static_dynamic.h b/src/ballistica/graphics/mesh/mesh_indexed_static_dynamic.h index a1aeff0a..eb792a64 100644 --- a/src/ballistica/graphics/mesh/mesh_indexed_static_dynamic.h +++ b/src/ballistica/graphics/mesh/mesh_indexed_static_dynamic.h @@ -32,7 +32,8 @@ class MeshIndexedStaticDynamic : public MeshIndexedBase { // Static and dynamic data sizes should always match, right? if (static_data_->elements.size() != dynamic_data_->elements.size()) { - BA_LOG_ONCE("ERROR: mesh static and dynamic data sizes do not match"); + BA_LOG_ONCE(LogLevel::kError, + "Mesh static and dynamic data sizes do not match"); return false; } diff --git a/src/ballistica/graphics/render_command_buffer.h b/src/ballistica/graphics/render_command_buffer.h index 27f8af98..691100f7 100644 --- a/src/ballistica/graphics/render_command_buffer.h +++ b/src/ballistica/graphics/render_command_buffer.h @@ -5,12 +5,12 @@ #include +#include "ballistica/assets/component/model.h" +#include "ballistica/assets/component/texture.h" #include "ballistica/ballistica.h" #include "ballistica/graphics/frame_def.h" #include "ballistica/graphics/mesh/mesh.h" #include "ballistica/math/matrix44f.h" -#include "ballistica/media/component/model.h" -#include "ballistica/media/component/texture.h" namespace ballistica { @@ -258,14 +258,14 @@ class RenderCommandBuffer { void PutModel(ModelData* model) { assert(frame_def_); assert(!finalized_); - frame_def_->AddComponent(Object::Ref(model)); + frame_def_->AddComponent(Object::Ref(model)); models_.push_back(model); } void PutTexture(TextureData* texture) { assert(frame_def_); assert(!finalized_); - frame_def_->AddComponent(Object::Ref(texture)); + frame_def_->AddComponent(Object::Ref(texture)); textures_.push_back(texture); } @@ -277,7 +277,7 @@ class RenderCommandBuffer { void PutCubeMapTexture(TextureData* texture) { assert(frame_def_); assert(!finalized_); - frame_def_->AddComponent(Object::Ref(texture)); + frame_def_->AddComponent(Object::Ref(texture)); textures_.push_back(texture); } diff --git a/src/ballistica/graphics/render_target.h b/src/ballistica/graphics/render_target.h index ef88585e..721a446e 100644 --- a/src/ballistica/graphics/render_target.h +++ b/src/ballistica/graphics/render_target.h @@ -11,8 +11,8 @@ namespace ballistica { // Encapsulates framebuffers, main windows, etc. class RenderTarget : public Object { public: - auto GetDefaultOwnerThread() const -> ThreadIdentifier override { - return ThreadIdentifier::kMain; + auto GetDefaultOwnerThread() const -> ThreadTag override { + return ThreadTag::kMain; } enum class Type { kScreen, kFramebuffer }; explicit RenderTarget(Type type); diff --git a/src/ballistica/graphics/renderer.cc b/src/ballistica/graphics/renderer.cc index f5fe3ed4..43c9ddb1 100644 --- a/src/ballistica/graphics/renderer.cc +++ b/src/ballistica/graphics/renderer.cc @@ -650,7 +650,7 @@ void Renderer::UpdatePixelScaleAndBackingBuffer(FrameDef* frame_def) { void Renderer::LoadMedia(FrameDef* frame_def) { millisecs_t t = GetRealTime(); for (auto&& i : frame_def->media_components()) { - MediaComponentData* mc = i.get(); + AssetComponentData* mc = i.get(); assert(mc); mc->Load(); diff --git a/src/ballistica/graphics/renderer.h b/src/ballistica/graphics/renderer.h index b0ff2946..ccf7a257 100644 --- a/src/ballistica/graphics/renderer.h +++ b/src/ballistica/graphics/renderer.h @@ -7,6 +7,9 @@ #include #include +#include "ballistica/assets/assets.h" +#include "ballistica/assets/component/texture.h" +#include "ballistica/assets/data/model_data.h" #include "ballistica/core/object.h" #include "ballistica/graphics/frame_def.h" #include "ballistica/graphics/framebuffer.h" @@ -38,9 +41,6 @@ #include "ballistica/graphics/text/text_group.h" #include "ballistica/math/matrix44f.h" #include "ballistica/math/vector3f.h" -#include "ballistica/media/component/texture.h" -#include "ballistica/media/data/model_data.h" -#include "ballistica/media/media.h" namespace ballistica { diff --git a/src/ballistica/graphics/text/text_graphics.cc b/src/ballistica/graphics/text/text_graphics.cc index fb8de9fb..5e045591 100644 --- a/src/ballistica/graphics/text/text_graphics.cc +++ b/src/ballistica/graphics/text/text_graphics.cc @@ -19,12 +19,6 @@ class TextGraphics::TextSpanBoundsCacheEntry : public Object { std::list>::iterator list_iterator_; }; -void TextGraphics::Init() { - assert(InLogicThread()); - assert(g_text_graphics == nullptr); - g_text_graphics = new TextGraphics(); -} - TextGraphics::TextGraphics() { // Init glyph values for our custom font pages // (just a 5x5 array currently). @@ -337,7 +331,7 @@ TextGraphics::TextGraphics() { if (g.tex_max_x > 1.0f || g.tex_max_x < 0.0f || g.tex_min_x > 1.0 || g.tex_min_x < 0.0f || g.tex_max_y > 1.0f || g.tex_max_y < 0.0 || g.tex_min_y > 1.0f || g.tex_min_y < 0.0f) { - BA_LOG_ONCE("Warning: glyph bounds error"); + BA_LOG_ONCE(LogLevel::kWarning, "glyph bounds error"); } } } @@ -1027,6 +1021,7 @@ void TextGraphics::GetOSTextSpanBoundsAndWidth(const std::string& s, Rect* r, g_platform->GetTextBoundsAndWidth(s, &entry->r, &entry->width); } else { BA_LOG_ONCE( + LogLevel::kError, "FIXME: GetOSTextSpanBoundsAndWidth unimplemented on this platform"); r->l = 0.0f; r->r = 1.0f; diff --git a/src/ballistica/graphics/text/text_graphics.h b/src/ballistica/graphics/text/text_graphics.h index b041f2e2..c293da30 100644 --- a/src/ballistica/graphics/text/text_graphics.h +++ b/src/ballistica/graphics/text/text_graphics.h @@ -19,11 +19,9 @@ namespace ballistica { const int kTextMaxUnicodeVal = 999999; const float kTextRowHeight = 32.0f; -// Encapsulates text-display functionality used by the game thread. +// Encapsulates text-display functionality used by the logic thread. class TextGraphics { public: - static void Init(); - TextGraphics(); enum class FontPage { diff --git a/src/ballistica/graphics/text/text_group.cc b/src/ballistica/graphics/text/text_group.cc index 43aee897..63e4e619 100644 --- a/src/ballistica/graphics/text/text_group.cc +++ b/src/ballistica/graphics/text/text_group.cc @@ -33,7 +33,7 @@ void TextGroup::SetText(const std::string& text, TextMesh::HAlign alignment_h, entry->max_flatness = 1.0f; entry->mesh.SetText(text, alignment_h, alignment_v, true, 0, 65535, TextMeshEntryType::kRegular, nullptr); - entry->tex = g_media->GetTexture(SystemTextureID::kFontBig); + entry->tex = g_assets->GetTexture(SystemTextureID::kFontBig); entries_.push_back(std::move(entry)); } else { @@ -109,8 +109,8 @@ void TextGroup::SetText(const std::string& text, TextMesh::HAlign alignment_h, // There should only ever be one of these. assert(!os_texture_.exists()); { - Media::MediaListsLock lock; - os_texture_ = g_media->GetTextureData(packer.get()); + Assets::AssetListLock lock; + os_texture_ = g_assets->GetTextureData(packer.get()); } // We also need to know what uv-scales to use for shadows/etc. @@ -122,43 +122,43 @@ void TextGroup::SetText(const std::string& text, TextMesh::HAlign alignment_h, } switch (*i) { case 0: - entry->tex = g_media->GetTexture(SystemTextureID::kFontSmall0); + entry->tex = g_assets->GetTexture(SystemTextureID::kFontSmall0); break; case 1: - entry->tex = g_media->GetTexture(SystemTextureID::kFontSmall1); + entry->tex = g_assets->GetTexture(SystemTextureID::kFontSmall1); break; case 2: - entry->tex = g_media->GetTexture(SystemTextureID::kFontSmall2); + entry->tex = g_assets->GetTexture(SystemTextureID::kFontSmall2); break; case 3: - entry->tex = g_media->GetTexture(SystemTextureID::kFontSmall3); + entry->tex = g_assets->GetTexture(SystemTextureID::kFontSmall3); break; case 4: - entry->tex = g_media->GetTexture(SystemTextureID::kFontSmall4); + entry->tex = g_assets->GetTexture(SystemTextureID::kFontSmall4); break; case 5: - entry->tex = g_media->GetTexture(SystemTextureID::kFontSmall5); + entry->tex = g_assets->GetTexture(SystemTextureID::kFontSmall5); break; case 6: - entry->tex = g_media->GetTexture(SystemTextureID::kFontSmall6); + entry->tex = g_assets->GetTexture(SystemTextureID::kFontSmall6); break; case 7: - entry->tex = g_media->GetTexture(SystemTextureID::kFontSmall7); + entry->tex = g_assets->GetTexture(SystemTextureID::kFontSmall7); break; case static_cast(TextGraphics::FontPage::kOSRendered): entry->tex = os_texture_; break; case static_cast(TextGraphics::FontPage::kExtras1): - entry->tex = g_media->GetTexture(SystemTextureID::kFontExtras); + entry->tex = g_assets->GetTexture(SystemTextureID::kFontExtras); break; case static_cast(TextGraphics::FontPage::kExtras2): - entry->tex = g_media->GetTexture(SystemTextureID::kFontExtras2); + entry->tex = g_assets->GetTexture(SystemTextureID::kFontExtras2); break; case static_cast(TextGraphics::FontPage::kExtras3): - entry->tex = g_media->GetTexture(SystemTextureID::kFontExtras3); + entry->tex = g_assets->GetTexture(SystemTextureID::kFontExtras3); break; case static_cast(TextGraphics::FontPage::kExtras4): - entry->tex = g_media->GetTexture(SystemTextureID::kFontExtras4); + entry->tex = g_assets->GetTexture(SystemTextureID::kFontExtras4); break; default: throw Exception(); diff --git a/src/ballistica/graphics/text/text_group.h b/src/ballistica/graphics/text/text_group.h index a9756e25..36081d75 100644 --- a/src/ballistica/graphics/text/text_group.h +++ b/src/ballistica/graphics/text/text_group.h @@ -7,10 +7,10 @@ #include #include +#include "ballistica/assets/assets.h" +#include "ballistica/assets/data/texture_data.h" #include "ballistica/core/object.h" #include "ballistica/graphics/mesh/text_mesh.h" -#include "ballistica/media/data/texture_data.h" -#include "ballistica/media/media.h" namespace ballistica { @@ -50,10 +50,10 @@ class TextGroup : public Object { } auto GetElementMaskUV2Texture(int index) const -> TextureData* { assert(index < static_cast(entries_.size())); - return g_media->GetTexture(entries_[index]->type - == TextMeshEntryType::kOSRendered - ? SystemTextureID::kSoftRect2 - : SystemTextureID::kSoftRect); + return g_assets->GetTexture(entries_[index]->type + == TextMeshEntryType::kOSRendered + ? SystemTextureID::kSoftRect2 + : SystemTextureID::kSoftRect); } void SetText(const std::string& text, TextMesh::HAlign alignment_h = TextMesh::HAlign::kLeft, diff --git a/src/ballistica/graphics/vr_graphics.cc b/src/ballistica/graphics/vr_graphics.cc index ebb6a81e..30aa4514 100644 --- a/src/ballistica/graphics/vr_graphics.cc +++ b/src/ballistica/graphics/vr_graphics.cc @@ -4,13 +4,13 @@ #include "ballistica/graphics/vr_graphics.h" #include "ballistica/app/app.h" -#include "ballistica/game/game.h" #include "ballistica/graphics/camera.h" #include "ballistica/graphics/component/object_component.h" #include "ballistica/graphics/component/simple_component.h" #include "ballistica/graphics/component/special_component.h" #include "ballistica/graphics/frame_def.h" #include "ballistica/graphics/render_pass.h" +#include "ballistica/logic/logic.h" #include "ballistica/scene/node/globals_node.h" namespace ballistica { @@ -241,7 +241,7 @@ void VRGraphics::DrawVROverlay(FrameDef* frame_def) { c.Scale(kBaseVirtualResX * (1.0f + kVRBorder), kBaseVirtualResY * (1.0f + kVRBorder), kBaseVirtualResX * (1.0f + kVRBorder)); - c.DrawModel(g_media->GetModel(SystemModelID::kVROverlay)); + c.DrawModel(g_assets->GetModel(SystemModelID::kVROverlay)); c.PopTransform(); c.Submit(); } @@ -258,7 +258,7 @@ void VRGraphics::DrawOverlayBounds(RenderPass* pass) { // Slight offset in z to reduce z fighting. c.Translate(0.5f * width, 0.5f * height, 1.0f); c.Scale(width, height, 100.0f); - c.DrawModel(g_media->GetModel(SystemModelID::kOverlayGuide)); + c.DrawModel(g_assets->GetModel(SystemModelID::kOverlayGuide)); c.PopTransform(); c.Submit(); } @@ -277,33 +277,33 @@ void VRGraphics::DrawVRControllers(FrameDef* frame_def) { if (false) { ObjectComponent c(frame_def->beauty_pass()); c.SetColor(1, 0, 0); - c.SetTexture(g_media->GetTexture(SystemTextureID::kBoxingGlove)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kBoxingGlove)); c.SetReflection(ReflectionType::kSoft); c.SetReflectionScale(0.4f, 0.4f, 0.4f); c.PushTransform(); c.VRTransformToHead(); c.Translate(0, 0, 5); c.Scale(2, 2, 2); - c.DrawModel(g_media->GetModel(SystemModelID::kBoxingGlove)); + c.DrawModel(g_assets->GetModel(SystemModelID::kBoxingGlove)); c.PopTransform(); c.Submit(); } // test right hand - const VRHandsState& s(g_game->vr_hands_state()); + const VRHandsState& s(g_logic->vr_hands_state()); switch (s.r.type) { case VRHandType::kOculusTouchR: case VRHandType::kDaydreamRemote: { ObjectComponent c(frame_def->beauty_pass()); c.SetColor(0, 1, 0); - c.SetTexture(g_media->GetTexture(SystemTextureID::kBoxingGlove)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kBoxingGlove)); c.SetReflection(ReflectionType::kSoft); c.SetReflectionScale(0.4f, 0.4f, 0.4f); c.PushTransform(); c.VRTransformToRightHand(); c.Scale(10, 10, 10); - c.DrawModel(g_media->GetModel(SystemModelID::kBoxingGlove)); + c.DrawModel(g_assets->GetModel(SystemModelID::kBoxingGlove)); c.PopTransform(); c.Submit(); break; @@ -316,13 +316,13 @@ void VRGraphics::DrawVRControllers(FrameDef* frame_def) { case VRHandType::kOculusTouchL: { ObjectComponent c(frame_def->beauty_pass()); c.SetColor(0, 0, 1); - c.SetTexture(g_media->GetTexture(SystemTextureID::kBoxingGlove)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kBoxingGlove)); c.SetReflection(ReflectionType::kSoft); c.SetReflectionScale(0.4f, 0.4f, 0.4f); c.PushTransform(); c.VRTransformToLeftHand(); c.Scale(10, 10, 10); - c.DrawModel(g_media->GetModel(SystemModelID::kBoxingGlove)); + c.DrawModel(g_assets->GetModel(SystemModelID::kBoxingGlove)); c.PopTransform(); c.Submit(); break; diff --git a/src/ballistica/input/device/client_input_device.cc b/src/ballistica/input/device/client_input_device.cc index 7cd9de43..d146c1a1 100644 --- a/src/ballistica/input/device/client_input_device.cc +++ b/src/ballistica/input/device/client_input_device.cc @@ -2,8 +2,8 @@ #include "ballistica/input/device/client_input_device.h" -#include "ballistica/game/connection/connection_to_client.h" -#include "ballistica/game/player.h" +#include "ballistica/logic/connection/connection_to_client.h" +#include "ballistica/logic/player.h" #include "ballistica/networking/networking.h" namespace ballistica { @@ -27,7 +27,8 @@ auto ClientInputDevice::GetClientID() const -> int { if (ConnectionToClient* c = connection_to_client_.get()) { return c->id(); } else { - Log("ClientInputDevice::get_client_id(): connection_to_client no longer " + Log(LogLevel::kError, + "ClientInputDevice::get_client_id(): connection_to_client no longer " "exists; returning -1.."); return -1; } diff --git a/src/ballistica/input/device/input_device.cc b/src/ballistica/input/device/input_device.cc index 16926d6f..9c7f4859 100644 --- a/src/ballistica/input/device/input_device.cc +++ b/src/ballistica/input/device/input_device.cc @@ -6,11 +6,11 @@ #include #include "ballistica/app/app.h" -#include "ballistica/game/connection/connection_to_host.h" -#include "ballistica/game/player.h" -#include "ballistica/game/session/host_session.h" -#include "ballistica/game/session/net_client_session.h" #include "ballistica/internal/app_internal.h" +#include "ballistica/logic/connection/connection_to_host.h" +#include "ballistica/logic/player.h" +#include "ballistica/logic/session/host_session.h" +#include "ballistica/logic/session/net_client_session.h" #include "ballistica/networking/networking.h" #include "ballistica/python/class/python_class_input_device.h" #include "ballistica/python/python.h" @@ -109,13 +109,13 @@ auto InputDevice::GetDefaultPlayerName() -> std::string { auto InputDevice::GetButtonName(int id) -> std::string { // By default just say 'button 1' or whatnot. // FIXME: should return this in Lstr json form. - return g_game->GetResourceString("buttonText") + " " + std::to_string(id); + return g_logic->GetResourceString("buttonText") + " " + std::to_string(id); } auto InputDevice::GetAxisName(int id) -> std::string { // By default just return 'axis 5' or whatnot. // FIXME: should return this in Lstr json form. - return g_game->GetResourceString("axisText") + " " + std::to_string(id); + return g_logic->GetResourceString("axisText") + " " + std::to_string(id); } auto InputDevice::HasMeaningfulButtonNames() -> bool { return false; } @@ -139,13 +139,15 @@ InputDevice::~InputDevice() { // when the host-session tells us to attach to a player void InputDevice::AttachToLocalPlayer(Player* player) { if (player_.exists()) { - Log("Error: InputDevice::AttachToLocalPlayer() called with already " + Log(LogLevel::kError, + "InputDevice::AttachToLocalPlayer() called with already " "existing " "player"); return; } if (remote_player_.exists()) { - Log("Error: InputDevice::AttachToLocalPlayer() called with already " + Log(LogLevel::kError, + "InputDevice::AttachToLocalPlayer() called with already " "existing " "remote-player"); return; @@ -158,13 +160,15 @@ void InputDevice::AttachToRemotePlayer(ConnectionToHost* connection_to_host, int remote_player_id) { assert(connection_to_host); if (player_.exists()) { - Log("Error: InputDevice::AttachToRemotePlayer()" + Log(LogLevel::kError, + "InputDevice::AttachToRemotePlayer()" " called with already existing " "player"); return; } if (remote_player_.exists()) { - Log("Error: InputDevice::AttachToRemotePlayer()" + Log(LogLevel::kError, + "InputDevice::AttachToRemotePlayer()" " called with already existing " "remote-player"); return; @@ -180,7 +184,8 @@ void InputDevice::RemoveRemotePlayerFromGame() { data[1] = static_cast_check_fit(index()); connection_to_host->SendReliableMessage(data); } else { - Log("Error: RemoveRemotePlayerFromGame called without remote player"); + Log(LogLevel::kError, + "RemoveRemotePlayerFromGame called without remote player"); } } @@ -207,29 +212,31 @@ void InputDevice::RequestPlayer() { assert(InLogicThread()); // Make note that we're being used in some way. - last_input_time_ = g_game->master_time(); + last_input_time_ = g_logic->master_time(); if (player_.exists()) { - Log("Error: InputDevice::RequestPlayer()" + Log(LogLevel::kError, + "InputDevice::RequestPlayer()" " called with already-existing player"); return; } if (remote_player_.exists()) { - Log("Error: InputDevice::RequestPlayer() called with already-existing " + Log(LogLevel::kError, + "InputDevice::RequestPlayer() called with already-existing " "remote-player"); return; } // If we have a local host-session, ask it for a player.. otherwise if we have // a client-session, ask it for a player. - assert(g_game); - if (auto* hs = dynamic_cast(g_game->GetForegroundSession())) { + assert(g_logic); + if (auto* hs = dynamic_cast(g_logic->GetForegroundSession())) { { Python::ScopedCallLabel label("requestPlayer"); hs->RequestPlayer(this); } } else if (auto* client_session = dynamic_cast( - g_game->GetForegroundSession())) { + g_logic->GetForegroundSession())) { if (ConnectionToHost* connection_to_host = client_session->connection_to_host()) { std::vector data(2); @@ -270,7 +277,7 @@ void InputDevice::Update() { void InputDevice::UpdateLastInputTime() { // Keep our own individual time, and also let // the overall input system know something happened. - last_input_time_ = g_game->master_time(); + last_input_time_ = g_logic->master_time(); g_input->mark_input_active(); } diff --git a/src/ballistica/input/device/input_device.h b/src/ballistica/input/device/input_device.h index b9559fd2..012efa0c 100644 --- a/src/ballistica/input/device/input_device.h +++ b/src/ballistica/input/device/input_device.h @@ -13,8 +13,8 @@ namespace ballistica { /// Base class for game input devices (keyboard, joystick, etc). /// InputDevices can be allocated in any thread (generally on the main /// thread in response to some system event). An AddInputDevice() call -/// should then be pushed to the game thread to inform it of the new device. -/// Deletion of the input-device is then handled by the game thread +/// should then be pushed to the logic thread to inform it of the new device. +/// Deletion of the input-device is then handled by the logic thread /// and can be triggered by pushing a RemoveInputDevice() call to it. class InputDevice : public Object { public: diff --git a/src/ballistica/input/device/joystick.cc b/src/ballistica/input/device/joystick.cc index 292c1c01..2af36549 100644 --- a/src/ballistica/input/device/joystick.cc +++ b/src/ballistica/input/device/joystick.cc @@ -6,9 +6,9 @@ #include "ballistica/app/app_flavor.h" #include "ballistica/audio/audio.h" #include "ballistica/core/thread.h" -#include "ballistica/game/connection/connection_set.h" -#include "ballistica/game/player.h" #include "ballistica/graphics/renderer.h" +#include "ballistica/logic/connection/connection_set.h" +#include "ballistica/logic/player.h" #include "ballistica/python/python.h" #include "ballistica/python/python_command.h" #include "ballistica/ui/root_ui.h" @@ -183,13 +183,13 @@ auto Joystick::GetButtonName(int index) -> std::string { if (strstr(GetDeviceName().c_str(), "Samsung Game Pad EI")) { switch (index) { case 101: - return g_game->CharStr(SpecialChar::kDiceButton4); // Y + return g_logic->CharStr(SpecialChar::kDiceButton4); // Y case 100: - return g_game->CharStr(SpecialChar::kDiceButton3); // X + return g_logic->CharStr(SpecialChar::kDiceButton3); // X case 98: - return g_game->CharStr(SpecialChar::kDiceButton2); // B + return g_logic->CharStr(SpecialChar::kDiceButton2); // B case 97: - return g_game->CharStr(SpecialChar::kDiceButton1); // A + return g_logic->CharStr(SpecialChar::kDiceButton1); // A default: break; } @@ -272,13 +272,13 @@ auto Joystick::GetButtonName(int index) -> std::string { case 204: return "B16"; case 90: - return g_game->CharStr(SpecialChar::kRewindButton); + return g_logic->CharStr(SpecialChar::kRewindButton); case 91: - return g_game->CharStr(SpecialChar::kFastForwardButton); + return g_logic->CharStr(SpecialChar::kFastForwardButton); case 24: - return g_game->CharStr(SpecialChar::kDpadCenterButton); + return g_logic->CharStr(SpecialChar::kDpadCenterButton); case 86: - return g_game->CharStr(SpecialChar::kPlayPauseButton); + return g_logic->CharStr(SpecialChar::kPlayPauseButton); default: break; } @@ -288,7 +288,7 @@ auto Joystick::GetButtonName(int index) -> std::string { Joystick::~Joystick() { if (!InLogicThread()) { - Log("Error: Joystick dying in wrong thread."); + Log(LogLevel::kError, "Joystick dying in wrong thread."); } // Kill our child if need be. @@ -301,7 +301,7 @@ Joystick::~Joystick() { // Send a message back to the main thread to close this SDL Joystick. // HMMM - can we just have the main thread close the joystick immediately // before informing us its dead?.. i don't think we actually use it at all - // here in the game thread.. + // here in the logic thread.. if (sdl_joystick_) { #if BA_ENABLE_SDL_JOYSTICKS assert(g_app_flavor); @@ -310,7 +310,8 @@ Joystick::~Joystick() { [joystick] { SDL_JoystickClose(joystick); }); sdl_joystick_ = nullptr; #else - Log("sdl_joystick_ set in non-sdl-joystick build destructor."); + Log(LogLevel::kError, + "sdl_joystick_ set in non-sdl-joystick build destructor."); #endif // BA_ENABLE_SDL_JOYSTICKS } } @@ -536,7 +537,7 @@ void Joystick::HandleSDLEvent(const SDL_Event* e) { // If we've got a child joystick, send them any events they're set to handle. if (child_joy_stick_) { - assert(g_game); + assert(g_logic); bool send = false; switch (e->type) { @@ -692,8 +693,9 @@ void Joystick::HandleSDLEvent(const SDL_Event* e) { hat_held_ = true; break; default: - BA_LOG_ONCE("Error: Invalid hat value: " - + std::to_string(static_cast(e->jhat.value))); + BA_LOG_ONCE(LogLevel::kError, + "Invalid hat value: " + + std::to_string(static_cast(e->jhat.value))); break; } } @@ -762,14 +764,14 @@ void Joystick::HandleSDLEvent(const SDL_Event* e) { } else { // If there's no menu up, // tell the game to pop it up and snag menu ownership for ourself. - g_game->PushMainMenuPressCall(this); + g_logic->PushMainMenuPressCall(this); return; } } // On our oculus build, select presses reset the orientation. if (e->jbutton.button == vr_reorient_button_ && IsVRMode()) { - ScreenMessage(g_game->GetResourceString("vrOrientationResetText"), + ScreenMessage(g_logic->GetResourceString("vrOrientationResetText"), {0, 1, 0}); g_app->reset_vr_orientation = true; return; @@ -991,8 +993,8 @@ void Joystick::HandleSDLEvent(const SDL_Event* e) { } else { // FIXME: Need a call we can make for this. bool do_party_button = false; - int party_size = g_game->GetPartySize(); - if (party_size > 1 || g_game->connections()->connection_to_host() + int party_size = g_logic->GetPartySize(); + if (party_size > 1 || g_logic->connections()->connection_to_host() || g_ui->root_ui()->always_draw_party_icon()) { do_party_button = true; } @@ -1533,7 +1535,7 @@ auto Joystick::GetPartyButtonName() const -> std::string { if (g_buildconfig.iircade_build()) { return "X"; } - return g_game->CharStr(SpecialChar::kTopButton); + return g_logic->CharStr(SpecialChar::kTopButton); } } // namespace ballistica diff --git a/src/ballistica/input/device/keyboard_input.cc b/src/ballistica/input/device/keyboard_input.cc index 415578eb..b76c5ee6 100644 --- a/src/ballistica/input/device/keyboard_input.cc +++ b/src/ballistica/input/device/keyboard_input.cc @@ -2,7 +2,7 @@ #include "ballistica/input/device/keyboard_input.h" -#include "ballistica/game/player.h" +#include "ballistica/logic/player.h" #include "ballistica/platform/platform.h" #include "ballistica/python/python.h" #include "ballistica/ui/ui.h" @@ -139,7 +139,7 @@ auto KeyboardInput::HandleKey(const SDL_Keysym* keysym, bool repeat, bool down) // Bring up menu if start is pressed. if (keysym->sym == start_key_ && !repeat && g_ui && g_ui->screen_root_widget() && g_ui->screen_root_widget()->GetChildCount() == 0) { - g_game->PushMainMenuPressCall(this); + g_logic->PushMainMenuPressCall(this); return true; } diff --git a/src/ballistica/input/device/test_input.cc b/src/ballistica/input/device/test_input.cc index d3ad9fc2..40272ee4 100644 --- a/src/ballistica/input/device/test_input.cc +++ b/src/ballistica/input/device/test_input.cc @@ -2,9 +2,9 @@ #include "ballistica/input/device/test_input.h" -#include "ballistica/game/game.h" #include "ballistica/input/device/joystick.h" #include "ballistica/input/input.h" +#include "ballistica/logic/logic.h" #include "ballistica/platform/min_sdl.h" namespace ballistica { diff --git a/src/ballistica/input/device/touch_input.cc b/src/ballistica/input/device/touch_input.cc index 500b699e..a76bfa0c 100644 --- a/src/ballistica/input/device/touch_input.cc +++ b/src/ballistica/input/device/touch_input.cc @@ -4,10 +4,10 @@ #include "ballistica/app/app.h" #include "ballistica/app/app_config.h" -#include "ballistica/game/host_activity.h" -#include "ballistica/game/player.h" #include "ballistica/graphics/camera.h" #include "ballistica/graphics/component/simple_component.h" +#include "ballistica/logic/host_activity.h" +#include "ballistica/logic/player.h" #include "ballistica/python/python.h" #include "ballistica/scene/node/player_node.h" #include "ballistica/ui/ui.h" @@ -429,7 +429,7 @@ void TouchInput::Draw(FrameDef* frame_def) { if (movement_control_type_ == MovementControlType::kSwipe) sc2 *= 0.6f; if (movement_control_type_ == MovementControlType::kSwipe) { - c.SetTexture(g_media->GetTexture(SystemTextureID::kTouchArrows)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kTouchArrows)); if (editing_) { float val = 1.5f + sinf(static_cast(real_time) * 0.02f); c.SetColor(val, val, 1.0f, 1.0f); @@ -442,7 +442,7 @@ void TouchInput::Draw(FrameDef* frame_def) { val = 0.35f; } c.SetColor(0.5f, 0.3f, 0.8f, val); - c.SetTexture(g_media->GetTexture(SystemTextureID::kCircle)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kCircle)); } float x_offs = @@ -453,7 +453,7 @@ void TouchInput::Draw(FrameDef* frame_def) { c.PushTransform(); c.Translate(d_pad_base_x_ + x_offs, d_pad_base_y_ + y_offs, kDrawDepth); c.Scale(sc2, sc2); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); if (movement_control_type_ == MovementControlType::kJoystick) { @@ -467,7 +467,7 @@ void TouchInput::Draw(FrameDef* frame_def) { c.PushTransform(); c.Translate(d_pad_x_ + x_offs, d_pad_y_ + y_offs, kDrawDepth); c.Scale(sc_move * 0.5f, sc_move * 0.5f); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); } } @@ -475,7 +475,7 @@ void TouchInput::Draw(FrameDef* frame_def) { if (!buttons_touch_ && action_control_type_ == ActionControlType::kSwipe && !swipe_controls_hidden_) { float sc2{sc_actions * 0.6f}; - c.SetTexture(g_media->GetTexture(SystemTextureID::kTouchArrowsActions)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kTouchArrowsActions)); if (editing_) { float val = 1.5f + sinf(static_cast(real_time) * 0.02f); c.SetColor(val, val, 1.0f, 1.0f); @@ -489,7 +489,7 @@ void TouchInput::Draw(FrameDef* frame_def) { height * (-0.1f - buttons_default_frac_y_) * (1.0f - presence_); c.Translate(buttons_x_ + x_offs, buttons_y_ + y_offs, kDrawDepth); c.Scale(sc2, sc2); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); } c.Submit(); @@ -504,12 +504,12 @@ void TouchInput::Draw(FrameDef* frame_def) { // to pull a node for the player we're attached to. if (HostActivity* host_activity = - g_game->GetForegroundContext().GetHostActivity()) { + g_logic->GetForegroundContext().GetHostActivity()) { if (Player* player = GetPlayer()) { player_node = host_activity->scene()->GetPlayerNode(player->id()); } } else { - if (Scene* scene = g_game->GetForegroundScene()) { + if (Scene* scene = g_logic->GetForegroundScene()) { player_node = scene->GetPlayerNode(remote_player_id()); } } @@ -539,7 +539,7 @@ void TouchInput::Draw(FrameDef* frame_def) { base_fade = 0.25f; } else { base_fade = 0.8f; - c.SetTexture(g_media->GetTexture(SystemTextureID::kActionButtons)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kActionButtons)); } float x_offs; @@ -632,7 +632,7 @@ void TouchInput::Draw(FrameDef* frame_def) { } else { c.Scale(b_width, b_width); } - c.DrawModel(g_media->GetModel(SystemModelID::kActionButtonRight)); + c.DrawModel(g_assets->GetModel(SystemModelID::kActionButtonRight)); c.PopTransform(); } @@ -659,7 +659,7 @@ void TouchInput::Draw(FrameDef* frame_def) { } else { c.Scale(b_width, b_width); } - c.DrawModel(g_media->GetModel(SystemModelID::kActionButtonLeft)); + c.DrawModel(g_assets->GetModel(SystemModelID::kActionButtonLeft)); c.PopTransform(); } @@ -685,7 +685,7 @@ void TouchInput::Draw(FrameDef* frame_def) { } else { c.Scale(b_width, b_width); } - c.DrawModel(g_media->GetModel(SystemModelID::kActionButtonBottom)); + c.DrawModel(g_assets->GetModel(SystemModelID::kActionButtonBottom)); c.PopTransform(); } @@ -713,13 +713,13 @@ void TouchInput::Draw(FrameDef* frame_def) { } else { c.Scale(b_width, b_width); } - c.DrawModel(g_media->GetModel(SystemModelID::kActionButtonTop)); + c.DrawModel(g_assets->GetModel(SystemModelID::kActionButtonTop)); c.PopTransform(); } // Center point. if (buttons_touch_ && action_control_type_ == ActionControlType::kSwipe) { - c.SetTexture(g_media->GetTexture(SystemTextureID::kCircle)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kCircle)); c.SetColor(1.0f, 1.0f, 0.0f, 0.8f); c.PushTransform(); @@ -744,7 +744,7 @@ void TouchInput::Draw(FrameDef* frame_def) { kDrawDepth); } c.Scale(b_width * 0.3f, b_width * 0.3f); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); } c.PopTransform(); @@ -784,7 +784,7 @@ void TouchInput::Draw(FrameDef* frame_def) { dist = 0.05f; } - c2.SetTexture(g_media->GetTexture(SystemTextureID::kArrow)); + c2.SetTexture(g_assets->GetTexture(SystemTextureID::kArrow)); Matrix44f orient = Matrix44fOrient(d_pad_draw_dir_, Vector3f(0.0f, 1.0f, 0.0f)); c2.PushTransform(); @@ -816,13 +816,13 @@ void TouchInput::Draw(FrameDef* frame_def) { c2.PushTransform(); c2.Translate(0.0f, dist * -0.5f, 0.0f); c2.Scale(0.15f, dist, 0.2f); - c2.DrawModel(g_media->GetModel(SystemModelID::kArrowBack)); + c2.DrawModel(g_assets->GetModel(SystemModelID::kArrowBack)); c2.PopTransform(); c2.PushTransform(); c2.Translate(0.0f, dist * -1.0f - 0.15f, 0.0f); c2.Scale(0.45f, 0.3f, 0.3f); - c2.DrawModel(g_media->GetModel(SystemModelID::kArrowFront)); + c2.DrawModel(g_assets->GetModel(SystemModelID::kArrowFront)); c2.PopTransform(); c2.PopTransform(); @@ -840,7 +840,8 @@ void TouchInput::UpdateMapping() { } else if (touch_movement_type == "joystick") { movement_control_type_ = TouchInput::MovementControlType::kJoystick; } else { - Log("Error: Invalid touch-movement-type: " + touch_movement_type); + Log(LogLevel::kError, + "Invalid touch-movement-type: " + touch_movement_type); movement_control_type_ = TouchInput::MovementControlType::kSwipe; } std::string touch_action_type = @@ -850,7 +851,7 @@ void TouchInput::UpdateMapping() { } else if (touch_action_type == "buttons") { action_control_type_ = TouchInput::ActionControlType::kButtons; } else { - Log("Error: Invalid touch-action-type: " + touch_action_type); + Log(LogLevel::kError, "Invalid touch-action-type: " + touch_action_type); action_control_type_ = TouchInput::ActionControlType::kSwipe; } @@ -944,8 +945,9 @@ auto TouchInput::HandleTouchDown(void* touch, float x, float y) -> bool { // ..so lets issue a warning to that effect if there's already // controllers active.. (only if we got a player though). if (attached_to_player() && g_input->HaveControllerWithPlayer()) { - ScreenMessage(g_game->GetResourceString("touchScreenJoinWarningText"), - {1.0f, 1.0f, 0.0f}); + ScreenMessage( + g_logic->GetResourceString("touchScreenJoinWarningText"), + {1.0f, 1.0f, 0.0f}); } } } else { diff --git a/src/ballistica/input/input.cc b/src/ballistica/input/input.cc index 9af81231..b0745074 100644 --- a/src/ballistica/input/input.cc +++ b/src/ballistica/input/input.cc @@ -6,12 +6,12 @@ #include "ballistica/app/app_config.h" #include "ballistica/audio/audio.h" #include "ballistica/core/thread.h" -#include "ballistica/game/player.h" #include "ballistica/graphics/camera.h" #include "ballistica/input/device/joystick.h" #include "ballistica/input/device/keyboard_input.h" #include "ballistica/input/device/test_input.h" #include "ballistica/input/device/touch_input.h" +#include "ballistica/logic/player.h" #include "ballistica/python/python.h" #include "ballistica/ui/console.h" #include "ballistica/ui/root_ui.h" @@ -20,7 +20,7 @@ namespace ballistica { -// Though it seems strange, input is actually owned by the game thread, not the +// Though it seems strange, input is actually owned by the logic thread, not the // app thread. This keeps things simple for game logic interacting with input // stuff (controller names, counts, etc) but means we need to be prudent about // properly passing stuff between the game and app thread as needed. @@ -318,26 +318,17 @@ static const char* const scancode_names[SDL_NUM_SCANCODES] = { }; #endif // BA_SDL2_BUILD || BA_MINSDL_BUILD -Input::Input() { - // We're a singleton. - // assert(g_input == nullptr); - // g_input = this; - - assert(InLogicThread()); - - // Config should have always been read by this point; right? - // assert(g_python); - // UpdateEnabledControllerSubsystems(); -} +Input::Input() {} void Input::PushCreateKeyboardInputDevices() { - g_game->thread()->PushCall([this] { CreateKeyboardInputDevices(); }); + g_logic->thread()->PushCall([this] { CreateKeyboardInputDevices(); }); } void Input::CreateKeyboardInputDevices() { assert(InLogicThread()); if (keyboard_input_ != nullptr || keyboard_input_2_ != nullptr) { - Log("Error: CreateKeyboardInputDevices called with existing kbs."); + Log(LogLevel::kError, + "CreateKeyboardInputDevices called with existing kbs."); return; } keyboard_input_ = Object::NewDeferred(nullptr); @@ -347,13 +338,14 @@ void Input::CreateKeyboardInputDevices() { } void Input::PushDestroyKeyboardInputDevices() { - g_game->thread()->PushCall([this] { DestroyKeyboardInputDevices(); }); + g_logic->thread()->PushCall([this] { DestroyKeyboardInputDevices(); }); } void Input::DestroyKeyboardInputDevices() { assert(InLogicThread()); if (keyboard_input_ == nullptr || keyboard_input_2_ == nullptr) { - Log("Error: DestroyKeyboardInputDevices called with null kb(s)."); + Log(LogLevel::kError, + "DestroyKeyboardInputDevices called with null kb(s)."); return; } RemoveInputDevice(keyboard_input_, false); @@ -362,8 +354,6 @@ void Input::DestroyKeyboardInputDevices() { keyboard_input_2_ = nullptr; } -Input::~Input() = default; - auto Input::GetInputDevice(int id) -> InputDevice* { if (id < 0 || id >= static_cast(input_devices_.size())) { return nullptr; @@ -472,30 +462,30 @@ void Input::AnnounceConnects() { // If there's been several connected, just give a number. if (explicit_bool(do_print)) { if (newly_connected_controllers_.size() > 1) { - std::string s = g_game->GetResourceString("controllersDetectedText"); + std::string s = g_logic->GetResourceString("controllersDetectedText"); Utils::StringReplaceOne( &s, "${COUNT}", std::to_string(newly_connected_controllers_.size())); ScreenMessage(s); } else { - ScreenMessage(g_game->GetResourceString("controllerDetectedText")); + ScreenMessage(g_logic->GetResourceString("controllerDetectedText")); } } } else { // If there's been several connected, just give a number. if (newly_connected_controllers_.size() > 1) { - std::string s = g_game->GetResourceString("controllersConnectedText"); + std::string s = g_logic->GetResourceString("controllersConnectedText"); Utils::StringReplaceOne( &s, "${COUNT}", std::to_string(newly_connected_controllers_.size())); ScreenMessage(s); } else { // If its just one, name it. - std::string s = g_game->GetResourceString("controllerConnectedText"); + std::string s = g_logic->GetResourceString("controllerConnectedText"); Utils::StringReplaceOne(&s, "${CONTROLLER}", newly_connected_controllers_.front()); ScreenMessage(s); } - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kGunCock)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kGunCock)); } newly_connected_controllers_.clear(); @@ -504,18 +494,18 @@ void Input::AnnounceConnects() { void Input::AnnounceDisconnects() { // If there's been several connected, just give a number. if (newly_disconnected_controllers_.size() > 1) { - std::string s = g_game->GetResourceString("controllersDisconnectedText"); + std::string s = g_logic->GetResourceString("controllersDisconnectedText"); Utils::StringReplaceOne( &s, "${COUNT}", std::to_string(newly_disconnected_controllers_.size())); ScreenMessage(s); } else { // If its just one, name it. - std::string s = g_game->GetResourceString("controllerDisconnectedText"); + std::string s = g_logic->GetResourceString("controllerDisconnectedText"); Utils::StringReplaceOne(&s, "${CONTROLLER}", newly_disconnected_controllers_.front()); ScreenMessage(s); } - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kCorkPop)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kCorkPop)); newly_disconnected_controllers_.clear(); } @@ -532,9 +522,9 @@ void Input::ShowStandardInputDeviceConnectedMessage(InputDevice* j) { // Set a timer to go off and announce the accumulated additions. if (connect_print_timer_id_ != 0) { - g_game->DeleteRealTimer(connect_print_timer_id_); + g_logic->DeleteRealTimer(connect_print_timer_id_); } - connect_print_timer_id_ = g_game->NewRealTimer( + connect_print_timer_id_ = g_logic->NewRealTimer( 250, false, NewLambdaRunnable([this] { AnnounceConnects(); })); } @@ -547,15 +537,15 @@ void Input::ShowStandardInputDeviceDisconnectedMessage(InputDevice* j) { // Set a timer to go off and announce the accumulated additions. if (disconnect_print_timer_id_ != 0) { - g_game->DeleteRealTimer(disconnect_print_timer_id_); + g_logic->DeleteRealTimer(disconnect_print_timer_id_); } - disconnect_print_timer_id_ = g_game->NewRealTimer( + disconnect_print_timer_id_ = g_logic->NewRealTimer( 250, false, NewLambdaRunnable([this] { AnnounceDisconnects(); })); } void Input::PushAddInputDeviceCall(InputDevice* input_device, bool standard_message) { - g_game->thread()->PushCall([this, input_device, standard_message] { + g_logic->thread()->PushCall([this, input_device, standard_message] { AddInputDevice(input_device, standard_message); }); } @@ -617,7 +607,7 @@ void Input::AddInputDevice(InputDevice* input, bool standard_message) { void Input::PushRemoveInputDeviceCall(InputDevice* input_device, bool standard_message) { - g_game->thread()->PushCall([this, input_device, standard_message] { + g_logic->thread()->PushCall([this, input_device, standard_message] { RemoveInputDevice(input_device, standard_message); }); } @@ -648,8 +638,8 @@ void Input::RemoveInputDevice(InputDevice* input, bool standard_message) { // a call to do it; otherwise its possible that someone tries // to access the player's inputdevice before the call goes // through which would lead to an exception. - g_game->RemovePlayer(input->GetPlayer()); - // g_game->PushRemovePlayerCall(input->GetPlayer()); + g_logic->RemovePlayer(input->GetPlayer()); + // g_logic->PushRemovePlayerCall(input->GetPlayer()); } if (input->GetRemotePlayer() != nullptr) { input->RemoveRemotePlayerFromGame(); @@ -684,7 +674,7 @@ void Input::UpdateInputDeviceCounts() { if (input_device.exists() && ((*input_device).IsTouchScreen() || (*input_device).IsKeyboard() || ((*input_device).last_input_time() != 0 - && g_game->master_time() - (*input_device).last_input_time() + && g_logic->master_time() - (*input_device).last_input_time() < 60000))) { total++; if (!(*input_device).IsTouchScreen()) { @@ -715,7 +705,7 @@ auto Input::GetLocalActiveInputDeviceCount() -> int { assert(InLogicThread()); // This can get called alot so lets cache the value. - millisecs_t current_time = g_game->master_time(); + millisecs_t current_time = g_logic->master_time(); if (current_time != last_get_local_active_input_device_count_check_time_) { last_get_local_active_input_device_count_check_time_ = current_time; @@ -727,7 +717,7 @@ auto Input::GetLocalActiveInputDeviceCount() -> int { && !input_device->IsTouchScreen() && !input_device->IsUIOnly() && input_device->IsLocal() && (input_device->last_input_time() != 0 - && g_game->master_time() - input_device->last_input_time() + && g_logic->master_time() - input_device->last_input_time() < 60000)) { count++; } @@ -824,7 +814,8 @@ void Input::UpdateEnabledControllerSubsystems() { ignore_mfi_controllers_ = false; ignore_sdl_controllers_ = false; } else { - BA_LOG_ONCE("Invalid mac-controller-subsystem value: '" + sys + "'"); + BA_LOG_ONCE(LogLevel::kError, + "Invalid mac-controller-subsystem value: '" + sys + "'"); } } } @@ -854,7 +845,8 @@ void Input::Update() { // If input has been locked an excessively long amount of time, unlock it. if (input_lock_count_temp_) { if (real_time - last_input_temp_lock_time_ > 10000) { - Log("Error: Input has been temp-locked for 10 seconds; unlocking."); + Log(LogLevel::kError, + "Input has been temp-locked for 10 seconds; unlocking."); input_lock_count_temp_ = 0; PrintLockLabels(); input_lock_temp_labels_.clear(); @@ -950,8 +942,9 @@ void Input::UnlockAllInput(bool permanent, const std::string& label) { input_lock_count_temp_--; input_unlock_temp_labels_.push_back(label); if (input_lock_count_temp_ < 0) { - Log("WARNING: temp input unlock at time " + std::to_string(GetRealTime()) - + " with no active lock: '" + label + "'"); + Log(LogLevel::kWarning, "temp input unlock at time " + + std::to_string(GetRealTime()) + + " with no active lock: '" + label + "'"); // This is to be expected since we can reset this to 0. input_lock_count_temp_ = 0; } @@ -1003,7 +996,7 @@ void Input::PrintLockLabels() { s += "\n " + std::to_string(num++) + ": " + recent_input_locks_unlock; } - Log(s); + Log(LogLevel::kError, s); } void Input::ProcessStressTesting(int player_count) { @@ -1065,7 +1058,7 @@ void Input::HandleBackPress(bool from_toolbar) { if (g_ui == nullptr || g_ui->screen_root_widget() == nullptr || g_ui->overlay_root_widget() == nullptr || g_ui->root_widget() == nullptr) { - // Log("HandleBackPress() called without main UI"); + // Log(LogLevel::kError, "HandleBackPress() called without main UI"); return; } @@ -1073,7 +1066,7 @@ void Input::HandleBackPress(bool from_toolbar) { // if available). if (g_ui->screen_root_widget()->GetChildCount() == 0 && g_ui->overlay_root_widget()->GetChildCount() == 0) { - g_game->PushMainMenuPressCall(touch_input_); + g_logic->PushMainMenuPressCall(touch_input_); } else { if (from_toolbar) { // NOTE - this means the toolbar back button can never apply to overlay @@ -1088,7 +1081,7 @@ void Input::HandleBackPress(bool from_toolbar) { } void Input::PushTextInputEvent(const std::string& text) { - g_game->thread()->PushCall([this, text] { + g_logic->thread()->PushCall([this, text] { mark_input_active(); // Ignore if input is locked. @@ -1105,7 +1098,7 @@ void Input::PushTextInputEvent(const std::string& text) { auto Input::PushJoystickEvent(const SDL_Event& event, InputDevice* input_device) -> void { - g_game->thread()->PushCall([this, event, input_device] { + g_logic->thread()->PushCall([this, event, input_device] { HandleJoystickEvent(event, input_device); }); } @@ -1137,11 +1130,11 @@ void Input::HandleJoystickEvent(const SDL_Event& event, } void Input::PushKeyPressEvent(const SDL_Keysym& keysym) { - g_game->thread()->PushCall([this, keysym] { HandleKeyPress(&keysym); }); + g_logic->thread()->PushCall([this, keysym] { HandleKeyPress(&keysym); }); } void Input::PushKeyReleaseEvent(const SDL_Keysym& keysym) { - g_game->thread()->PushCall([this, keysym] { HandleKeyRelease(&keysym); }); + g_logic->thread()->PushCall([this, keysym] { HandleKeyRelease(&keysym); }); } void Input::HandleKeyPress(const SDL_Keysym* keysym) { @@ -1206,7 +1199,7 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) { // Command-Q or Control-Q quits. if (!repeat_press && keysym->sym == SDLK_q && ((keysym->mod & KMOD_CTRL) || (keysym->mod & KMOD_GUI))) { // NOLINT - g_game->PushConfirmQuitCall(); + g_logic->PushConfirmQuitCall(); return; } } @@ -1235,7 +1228,7 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) { // If there's no dialogs/windows up, ask for a menu (owned by the // touch-screen if available). if (g_ui->screen_root_widget()->GetChildCount() == 0) { - g_game->PushMainMenuPressCall(touch_input_); + g_logic->PushMainMenuPressCall(touch_input_); } } handled = true; @@ -1249,12 +1242,12 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) { case SDLK_EQUALS: case SDLK_PLUS: - g_game->ChangeGameSpeed(1); + g_logic->ChangeGameSpeed(1); handled = true; break; case SDLK_MINUS: - g_game->ChangeGameSpeed(-1); + g_logic->ChangeGameSpeed(-1); handled = true; break; @@ -1265,12 +1258,12 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) { } case SDLK_F7: - g_game->PushToggleManualCameraCall(); + g_logic->PushToggleManualCameraCall(); handled = true; break; case SDLK_F8: - g_game->PushToggleDebugInfoDisplayCall(); + g_logic->PushToggleDebugInfoDisplayCall(); handled = true; break; @@ -1280,7 +1273,7 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) { break; case SDLK_F10: - g_game->PushToggleCollisionGeometryDisplayCall(); + g_logic->PushToggleCollisionGeometryDisplayCall(); handled = true; break; @@ -1293,7 +1286,7 @@ void Input::HandleKeyPress(const SDL_Keysym* keysym) { if (g_ui->screen_root_widget()->GetChildCount() == 0 && g_ui->overlay_root_widget()->GetChildCount() == 0) { if (keyboard_input_) { - g_game->PushMainMenuPressCall(keyboard_input_); + g_logic->PushMainMenuPressCall(keyboard_input_); } } else { // Ok there's a UI up.. send along a cancel message. @@ -1390,7 +1383,7 @@ auto Input::UpdateModKeyStates(const SDL_Keysym* keysym, bool press) -> void { } auto Input::PushMouseScrollEvent(const Vector2f& amount) -> void { - g_game->thread()->PushCall([this, amount] { HandleMouseScroll(amount); }); + g_logic->thread()->PushCall([this, amount] { HandleMouseScroll(amount); }); } auto Input::HandleMouseScroll(const Vector2f& amount) -> void { @@ -1423,7 +1416,7 @@ auto Input::HandleMouseScroll(const Vector2f& amount) -> void { auto Input::PushSmoothMouseScrollEvent(const Vector2f& velocity, bool momentum) -> void { - g_game->thread()->PushCall([this, velocity, momentum] { + g_logic->thread()->PushCall([this, velocity, momentum] { HandleSmoothMouseScroll(velocity, momentum); }); } @@ -1458,7 +1451,8 @@ auto Input::HandleSmoothMouseScroll(const Vector2f& velocity, bool momentum) } auto Input::PushMouseMotionEvent(const Vector2f& position) -> void { - g_game->thread()->PushCall([this, position] { HandleMouseMotion(position); }); + g_logic->thread()->PushCall( + [this, position] { HandleMouseMotion(position); }); } auto Input::HandleMouseMotion(const Vector2f& position) -> void { @@ -1509,7 +1503,7 @@ auto Input::HandleMouseMotion(const Vector2f& position) -> void { } auto Input::PushMouseDownEvent(int button, const Vector2f& position) -> void { - g_game->thread()->PushCall( + g_logic->thread()->PushCall( [this, button, position] { HandleMouseDown(button, position); }); } @@ -1586,7 +1580,7 @@ auto Input::HandleMouseDown(int button, const Vector2f& position) -> void { } auto Input::PushMouseUpEvent(int button, const Vector2f& position) -> void { - g_game->thread()->PushCall( + g_logic->thread()->PushCall( [this, button, position] { HandleMouseUp(button, position); }); } @@ -1635,7 +1629,7 @@ auto Input::HandleMouseUp(int button, const Vector2f& position) -> void { } void Input::PushTouchEvent(const TouchEvent& e) { - g_game->thread()->PushCall([e, this] { HandleTouchEvent(e); }); + g_logic->thread()->PushCall([e, this] { HandleTouchEvent(e); }); } void Input::HandleTouchEvent(const TouchEvent& e) { @@ -1664,7 +1658,8 @@ void Input::HandleTouchEvent(const TouchEvent& e) { // overall multitouch gesture, it should always be winding up as our // single_touch_. if (e.type == TouchEvent::Type::kDown && single_touch_ != nullptr) { - BA_LOG_ONCE("Got touch labeled first but will not be our single."); + BA_LOG_ONCE(LogLevel::kError, + "Got touch labeled first but will not be our single."); } // Also: if the OS tells us that this is the end of an overall multi-touch @@ -1672,7 +1667,8 @@ void Input::HandleTouchEvent(const TouchEvent& e) { if ((e.type == TouchEvent::Type::kUp || e.type == TouchEvent::Type::kCanceled) && single_touch_ != nullptr && single_touch_ != e.touch) { - BA_LOG_ONCE("Last touch coming up is not single touch!"); + BA_LOG_ONCE(LogLevel::kError, + "Last touch coming up is not single touch!"); } } @@ -1803,8 +1799,9 @@ const char* GetScancodeName(SDL_Scancode scancode) { const char* name; if (static_cast(scancode) < SDL_SCANCODE_UNKNOWN || scancode >= SDL_NUM_SCANCODES) { - BA_LOG_ONCE("GetScancodeName passed invalid scancode " - + std::to_string(static_cast(scancode))); + BA_LOG_ONCE(LogLevel::kError, + "GetScancodeName passed invalid scancode " + + std::to_string(static_cast(scancode))); return ""; } diff --git a/src/ballistica/input/input.h b/src/ballistica/input/input.h index b2750784..8f1d7838 100644 --- a/src/ballistica/input/input.h +++ b/src/ballistica/input/input.h @@ -14,18 +14,17 @@ namespace ballistica { /// Class for managing input. -/// Should only be used in the game thread unless otherwise specified. +/// Should only be used in the logic thread unless otherwise specified. class Input { public: Input(); - virtual ~Input(); - // Add an input device. Must be called from the game thread; otherwise use + // Add an input device. Must be called from the logic thread; otherwise use // PushAddInputDeviceCall. auto AddInputDevice(InputDevice* input, bool standard_message) -> void; // Removes a previously-added input-device. Must be called from the - // game thread; otherwise use PushRemoveInputDeviceCall. + // logic thread; otherwise use PushRemoveInputDeviceCall. auto RemoveInputDevice(InputDevice* input, bool standard_message) -> void; // Given a device name and persistent identifier for it, returns a device or diff --git a/src/ballistica/input/remote_app.cc b/src/ballistica/input/remote_app.cc index b76a7fac..43c3549a 100644 --- a/src/ballistica/input/remote_app.cc +++ b/src/ballistica/input/remote_app.cc @@ -10,11 +10,11 @@ #endif #include "ballistica/app/app.h" -#include "ballistica/game/game.h" +#include "ballistica/assets/assets.h" #include "ballistica/generic/utils.h" #include "ballistica/input/input.h" +#include "ballistica/logic/logic.h" #include "ballistica/math/vector3f.h" -#include "ballistica/media/media.h" #include "ballistica/networking/network_reader.h" #include "ballistica/platform/min_sdl.h" #include "ballistica/platform/platform.h" @@ -75,9 +75,9 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt, } case BA_PACKET_REMOTE_ID_REQUEST: { if (amt < 5 || amt > 127) { - BA_LOG_ONCE( - "Error: received invalid BA_PACKET_REMOTE_ID_REQUEST of length " - + std::to_string(amt)); + BA_LOG_ONCE(LogLevel::kError, + "Received invalid BA_PACKET_REMOTE_ID_REQUEST of length " + + std::to_string(amt)); break; } @@ -165,10 +165,10 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt, // Replace ${CONTROLLER} with it in our message. std::string s = - g_game->GetResourceString("controllerDisconnectedText"); + g_logic->GetResourceString("controllerDisconnectedText"); Utils::StringReplaceOne(&s, "${CONTROLLER}", m); - g_game->PushScreenMessage(s, Vector3f(1, 1, 1)); - g_game->PushPlaySoundCall(SystemSoundID::kCorkPop); + g_logic->PushScreenMessage(s, Vector3f(1, 1, 1)); + g_logic->PushPlaySoundCall(SystemSoundID::kCorkPop); g_input->PushRemoveInputDeviceCall(client->joystick_, false); client->joystick_ = nullptr; client->in_use = false; @@ -212,7 +212,7 @@ void RemoteAppServer::HandleData(int socket, uint8_t* buffer, size_t amt, // Each state is 2 bytes. So make sure our length adds up. if (amt != 4 + state_count * 3) { - BA_LOG_ONCE("Error: Invalid state packet"); + BA_LOG_ONCE(LogLevel::kError, "Invalid state packet"); return; } RemoteAppClient* client = clients_ + joystick_id; @@ -367,10 +367,10 @@ auto RemoteAppServer::GetClient(int request_id, struct sockaddr* addr, snprintf(m, sizeof(m), "%s", clients_[i].display_name); // Replace ${CONTROLLER} with it in our message. - std::string s = g_game->GetResourceString("controllerReconnectedText"); + std::string s = g_logic->GetResourceString("controllerReconnectedText"); Utils::StringReplaceOne(&s, "${CONTROLLER}", m); - g_game->PushScreenMessage(s, Vector3f(1, 1, 1)); - g_game->PushPlaySoundCall(SystemSoundID::kGunCock); + g_logic->PushScreenMessage(s, Vector3f(1, 1, 1)); + g_logic->PushPlaySoundCall(SystemSoundID::kGunCock); } clients_[i].in_use = true; return i; @@ -407,10 +407,10 @@ auto RemoteAppServer::GetClient(int request_id, struct sockaddr* addr, snprintf(m, sizeof(m), "%s", clients_[i].display_name); // Replace ${CONTROLLER} with it in our message. - std::string s = g_game->GetResourceString("controllerConnectedText"); + std::string s = g_logic->GetResourceString("controllerConnectedText"); Utils::StringReplaceOne(&s, "${CONTROLLER}", m); - g_game->PushScreenMessage(s, Vector3f(1, 1, 1)); - g_game->PushPlaySoundCall(SystemSoundID::kGunCock); + g_logic->PushScreenMessage(s, Vector3f(1, 1, 1)); + g_logic->PushPlaySoundCall(SystemSoundID::kGunCock); std::string utf8 = Utils::GetValidUTF8(clients_[i].display_name, "rsgc1"); clients_[i].joystick_ = Object::NewDeferred( -1, // not an sdl joystick @@ -425,7 +425,7 @@ auto RemoteAppServer::GetClient(int request_id, struct sockaddr* addr, if (Utils::UTF8StringLength(utf8.c_str()) <= 10) { clients_[i].joystick_->set_custom_default_player_name(utf8); } - assert(g_game); + assert(g_logic); g_input->PushAddInputDeviceCall(clients_[i].joystick_, false); return i; } @@ -512,7 +512,7 @@ void RemoteAppServer::HandleRemoteEvent(RemoteAppClient* client, break; } if (send) { - assert(g_game); + assert(g_logic); g_input->PushJoystickEvent(e, client->joystick_); } #pragma clang diagnostic pop @@ -542,7 +542,7 @@ void RemoteAppServer::HandleRemoteFloatEvent(RemoteAppClient* client, break; } if (send) { - assert(g_game); + assert(g_logic); g_input->PushJoystickEvent(e, client->joystick_); } #pragma clang diagnostic pop diff --git a/src/ballistica/internal/app_internal.h b/src/ballistica/internal/app_internal.h index e1cf5d08..6421c66d 100644 --- a/src/ballistica/internal/app_internal.h +++ b/src/ballistica/internal/app_internal.h @@ -10,13 +10,13 @@ namespace ballistica { -auto GetAppInternal() -> AppInternal*; +auto CreateAppInternal() -> AppInternal*; class AppInternal { public: virtual ~AppInternal() {} - virtual auto PyInitialize(void* pyconfig) -> void = 0; + virtual auto DefineInternalModule() -> void = 0; virtual auto PythonPostInit() -> void = 0; virtual auto HasBlessingHash() -> bool = 0; virtual auto PutLog(bool fatal) -> bool = 0; @@ -41,9 +41,9 @@ class AppInternal { bool user_initiated) -> void = 0; virtual auto GetPublicV1AccountID() -> std::string = 0; virtual auto OnLogicThreadPause() -> void = 0; - virtual auto DirectSendLogs(const std::string& prefix, - const std::string& suffix, bool instant, - int* result = nullptr) -> void = 0; + virtual auto DirectSendV1CloudLogs(const std::string& prefix, + const std::string& suffix, bool instant, + int* result = nullptr) -> void = 0; virtual auto ClientInfoQuery(const std::string& val1, const std::string& val2, const std::string& val3, int build_number) -> void = 0; diff --git a/src/ballistica/game/client_controller_interface.h b/src/ballistica/logic/client_controller_interface.h similarity index 78% rename from src/ballistica/game/client_controller_interface.h rename to src/ballistica/logic/client_controller_interface.h index 0b686329..3f1a2cf4 100644 --- a/src/ballistica/game/client_controller_interface.h +++ b/src/ballistica/logic/client_controller_interface.h @@ -1,7 +1,7 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_GAME_CLIENT_CONTROLLER_INTERFACE_H_ -#define BALLISTICA_GAME_CLIENT_CONTROLLER_INTERFACE_H_ +#ifndef BALLISTICA_LOGIC_CLIENT_CONTROLLER_INTERFACE_H_ +#define BALLISTICA_LOGIC_CLIENT_CONTROLLER_INTERFACE_H_ #include "ballistica/ballistica.h" @@ -19,4 +19,4 @@ class ClientControllerInterface { } // namespace ballistica -#endif // BALLISTICA_GAME_CLIENT_CONTROLLER_INTERFACE_H_ +#endif // BALLISTICA_LOGIC_CLIENT_CONTROLLER_INTERFACE_H_ diff --git a/src/ballistica/game/connection/connection.cc b/src/ballistica/logic/connection/connection.cc similarity index 93% rename from src/ballistica/game/connection/connection.cc rename to src/ballistica/logic/connection/connection.cc index 75e87f96..ec901c52 100644 --- a/src/ballistica/game/connection/connection.cc +++ b/src/ballistica/logic/connection/connection.cc @@ -1,6 +1,6 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/game/connection/connection.h" +#include "ballistica/logic/connection/connection.h" #include "ballistica/generic/huffman.h" #include "ballistica/generic/json.h" @@ -149,7 +149,8 @@ void Connection::HandleGamePacketCompressed(const std::vector& data) { try { data_decompressed = g_utils->huffman()->decompress(data); } catch (const std::exception& e) { - Log(std::string("EXC in huffman decompression for packet: ") + e.what()); + Log(LogLevel::kError, + std::string("Error in huffman decompression for packet: ") + e.what()); // Hmmm i guess lets just ignore this packet and keep on trucking?.. or // should we kill the connection? @@ -168,7 +169,8 @@ void Connection::HandleGamePacket(const std::vector& data) { switch (data[0]) { case BA_GAMEPACKET_KEEPALIVE: { if (data.size() != 4) { - BA_LOG_ONCE("Error: got invalid BA_GAMEPACKET_KEEPALIVE packet."); + BA_LOG_ONCE(LogLevel::kError, + "Error: got invalid BA_GAMEPACKET_KEEPALIVE packet."); return; } millisecs_t real_time = GetRealTime(); @@ -181,7 +183,7 @@ void Connection::HandleGamePacket(const std::vector& data) { // Expect 1 byte type, 2 byte num, 3 byte acks, at least 1 byte payload. if (data.size() < 7) { - Log("Error: Got invalid BA_PACKET_STATE packet."); + Log(LogLevel::kError, "Got invalid BA_PACKET_STATE packet."); return; } uint16_t num; @@ -212,7 +214,7 @@ void Connection::HandleGamePacket(const std::vector& data) { // Expect 1 byte type, 2 byte num, 2 byte unreliable-num, 3 byte acks, // at least 1 byte payload. if (data.size() < 9) { - Log("Error: Got invalid BA_PACKET_STATE_UNRELIABLE packet."); + Log(LogLevel::kError, "Got invalid BA_PACKET_STATE_UNRELIABLE packet."); return; } uint16_t num, num_unreliable; @@ -233,8 +235,8 @@ void Connection::HandleGamePacket(const std::vector& data) { } default: - Log("Connection got unknown packet type: " - + std::to_string(static_cast(data[0]))); + Log(LogLevel::kError, "Connection got unknown packet type: " + + std::to_string(static_cast(data[0]))); break; } } @@ -316,8 +318,9 @@ void Connection::SendReliableMessage(const std::vector& data) { void Connection::SendUnreliableMessage(const std::vector& data) { // For now we just silently drop anything bigger than our max packet size. if (data.size() + 8 > kMaxPacketSize) { - BA_LOG_ONCE("Error: Dropping outgoing unreliable packet of size " - + std::to_string(data.size()) + "."); + BA_LOG_ONCE(LogLevel::kError, + "Error: Dropping outgoing unreliable packet of size " + + std::to_string(data.size()) + "."); return; } @@ -428,7 +431,7 @@ void Connection::HandleMessagePacket(const std::vector& buffer) { multipart_buffer_.resize(old_size + (buffer.size() - 1)); memcpy(&(multipart_buffer_[old_size]), &(buffer[1]), buffer.size() - 1); } else { - Log("got invalid BA_MESSAGE_MULTIPART"); + Log(LogLevel::kError, "got invalid BA_MESSAGE_MULTIPART"); } if (buffer[0] == BA_MESSAGE_MULTIPART_END) { HandleMessagePacket(multipart_buffer_); @@ -468,10 +471,11 @@ void Connection::SendGamePacket(const std::vector& data) { if (!can_send && data[0] != BA_GAMEPACKET_HANDSHAKE && data[0] != BA_GAMEPACKET_HANDSHAKE_RESPONSE) { if (explicit_bool(false)) { - BA_LOG_ONCE("SendGamePacket() called before can_communicate set (" - + g_platform->DemangleCXXSymbol(typeid(*this).name()) - + " ptype " + std::to_string(static_cast(data[0])) - + ")"); + BA_LOG_ONCE(LogLevel::kError, + "SendGamePacket() called before can_communicate set (" + + g_platform->DemangleCXXSymbol(typeid(*this).name()) + + " ptype " + std::to_string(static_cast(data[0])) + + ")"); } return; } diff --git a/src/ballistica/game/connection/connection.h b/src/ballistica/logic/connection/connection.h similarity index 96% rename from src/ballistica/game/connection/connection.h rename to src/ballistica/logic/connection/connection.h index d4565f66..b130ed21 100644 --- a/src/ballistica/game/connection/connection.h +++ b/src/ballistica/logic/connection/connection.h @@ -1,14 +1,14 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_GAME_CONNECTION_CONNECTION_H_ -#define BALLISTICA_GAME_CONNECTION_CONNECTION_H_ +#ifndef BALLISTICA_LOGIC_CONNECTION_CONNECTION_H_ +#define BALLISTICA_LOGIC_CONNECTION_CONNECTION_H_ #include #include #include #include "ballistica/core/object.h" -#include "ballistica/game/player_spec.h" +#include "ballistica/logic/player_spec.h" #include "ballistica/python/python_ref.h" namespace ballistica { @@ -142,4 +142,4 @@ class Connection : public Object { } // namespace ballistica -#endif // BALLISTICA_GAME_CONNECTION_CONNECTION_H_ +#endif // BALLISTICA_LOGIC_CONNECTION_CONNECTION_H_ diff --git a/src/ballistica/game/connection/connection_set.cc b/src/ballistica/logic/connection/connection_set.cc similarity index 89% rename from src/ballistica/game/connection/connection_set.cc rename to src/ballistica/logic/connection/connection_set.cc index e9e164b7..23797bf6 100644 --- a/src/ballistica/game/connection/connection_set.cc +++ b/src/ballistica/logic/connection/connection_set.cc @@ -1,15 +1,15 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/game/connection/connection_set.h" +#include "ballistica/logic/connection/connection_set.h" #include "ballistica/core/thread.h" -#include "ballistica/game/connection/connection_to_client_udp.h" -#include "ballistica/game/connection/connection_to_host_udp.h" -#include "ballistica/game/game.h" -#include "ballistica/game/player.h" -#include "ballistica/game/session/host_session.h" #include "ballistica/input/device/input_device.h" -#include "ballistica/networking/network_write_module.h" +#include "ballistica/logic/connection/connection_to_client_udp.h" +#include "ballistica/logic/connection/connection_to_host_udp.h" +#include "ballistica/logic/logic.h" +#include "ballistica/logic/player.h" +#include "ballistica/logic/session/host_session.h" +#include "ballistica/networking/network_writer.h" #include "ballistica/networking/sockaddr.h" #include "ballistica/python/python.h" #include "ballistica/python/python_sys.h" @@ -26,7 +26,8 @@ void ConnectionSet::RegisterClientController(ClientControllerInterface* c) { // This shouldn't happen, but if there's already a controller registered, // detach all clients from it. if (client_controller_) { - Log("RegisterClientController() called " + Log(LogLevel::kError, + "RegisterClientController() called " "but already have a controller; bad."); for (auto&& i : connections_to_clients_) { assert(i.second.exists()); @@ -113,7 +114,7 @@ void ConnectionSet::SendChatMessage(const std::string& message, // spec out of their name(s). std::string p_name_combined; if (auto* hs = - dynamic_cast(g_game->GetForegroundSession())) { + dynamic_cast(g_logic->GetForegroundSession())) { for (auto&& p : hs->players()) { InputDevice* input_device = p->GetInputDevice(); if (p->accepted() && p->name_is_real() && input_device != nullptr @@ -192,7 +193,7 @@ void ConnectionSet::SendChatMessage(const std::string& message, // And display locally if the message is addressed to all. if (clients == nullptr) { - g_game->LocalDisplayChatMessage(msg_out); + g_logic->LocalDisplayChatMessage(msg_out); } } } @@ -206,7 +207,8 @@ auto ConnectionSet::GetConnectionsToClients() if (connections_to_client.second.exists()) { connections.push_back(connections_to_client.second.get()); } else { - Log("HAVE NONEXISTENT CONNECTION_TO_CLIENT IN LIST; UNEXPECTED"); + Log(LogLevel::kError, + "HAVE NONEXISTENT CONNECTION_TO_CLIENT IN LIST; UNEXPECTED"); } } return connections; @@ -216,14 +218,15 @@ void ConnectionSet::PushUDPConnectionPacketCall( const std::vector& data, const SockAddr& addr) { // Avoid buffer-full errors if something is causing us to write too often; // these are unreliable messages so its ok to just drop them. - if (!g_game->thread()->CheckPushSafety()) { + if (!g_logic->thread()->CheckPushSafety()) { BA_LOG_ONCE( + LogLevel::kError, "Ignoring excessive udp-connection input packets; (could this be a " "flood attack?)."); return; } - g_game->thread()->PushCall( + g_logic->thread()->PushCall( [this, data, addr] { UDPConnectionPacket(data, addr); }); } @@ -280,11 +283,12 @@ void ConnectionSet::SendScreenMessageToAll(const std::string& s, float r, auto ConnectionSet::PrepareForLaunchHostSession() -> void { // If for some reason we're still attached to a host, kill the connection. if (connection_to_host_.exists()) { - Log("Had host-connection during LaunchHostSession(); shouldn't happen."); + Log(LogLevel::kError, + "Had host-connection during LaunchHostSession(); shouldn't happen."); connection_to_host_->RequestDisconnect(); connection_to_host_.Clear(); has_connection_to_host_ = false; - g_game->UpdateGameRoster(); + g_logic->UpdateGameRoster(); } } @@ -303,9 +307,9 @@ auto ConnectionSet::HandleClientDisconnected(int id) -> void { // gone. Also inform everyone who just left so they can announce it // (technically could consolidate these messages but whatever...). if (was_connected) { - g_game->UpdateGameRoster(); + g_logic->UpdateGameRoster(); for (auto&& connection : connections_to_clients_) { - if (g_game->ShouldAnnouncePartyJoinsAndLeaves()) { + if (g_logic->ShouldAnnouncePartyJoinsAndLeaves()) { connection.second->SendReliableMessage(leave_msg); } } @@ -322,8 +326,8 @@ auto ConnectionSet::DisconnectClient(int client_id, int ban_seconds) -> bool { return false; } if (client_id > 255) { - Log("DisconnectClient got client_id > 255 (" + std::to_string(client_id) - + ")"); + Log(LogLevel::kError, "DisconnectClient got client_id > 255 (" + + std::to_string(client_id) + ")"); } else { std::vector msg_out(2); msg_out[0] = BA_MESSAGE_KICK_VOTE; @@ -339,7 +343,7 @@ auto ConnectionSet::DisconnectClient(int client_id, int ban_seconds) -> bool { // If this is considered a kick, add an entry to our banned list so we // know not to let them back in for a while. if (ban_seconds > 0) { - g_game->BanPlayer(i->second->peer_spec(), 1000 * ban_seconds); + g_logic->BanPlayer(i->second->peer_spec(), 1000 * ban_seconds); } i->second->RequestDisconnect(); @@ -354,24 +358,24 @@ auto ConnectionSet::DisconnectClient(int client_id, int ban_seconds) -> bool { } void ConnectionSet::PushClientDisconnectedCall(int id) { - g_game->thread()->PushCall([this, id] { HandleClientDisconnected(id); }); + g_logic->thread()->PushCall([this, id] { HandleClientDisconnected(id); }); } void ConnectionSet::PushDisconnectedFromHostCall() { - g_game->thread()->PushCall([this] { + g_logic->thread()->PushCall([this] { if (connection_to_host_.exists()) { bool was_connected = connection_to_host_->can_communicate(); connection_to_host_.Clear(); has_connection_to_host_ = false; // Clear out our party roster. - g_game->UpdateGameRoster(); + g_logic->UpdateGameRoster(); // Go back to main menu *if* the connection was fully connected. // Otherwise we're still probably sitting at the main menu // so no need to reset it. if (was_connected) { - g_game->RunMainMenu(); + g_logic->RunMainMenu(); } } }); @@ -379,10 +383,10 @@ void ConnectionSet::PushDisconnectedFromHostCall() { void ConnectionSet::PushHostConnectedUDPCall(const SockAddr& addr, bool print_connect_progress) { - g_game->thread()->PushCall([this, addr, print_connect_progress] { + g_logic->thread()->PushCall([this, addr, print_connect_progress] { // Attempt to disconnect any clients we have, turn off public-party // advertising, etc. - g_game->CleanUpBeforeConnectingToHost(); + g_logic->CleanUpBeforeConnectingToHost(); print_udp_connect_progress_ = print_connect_progress; connection_to_host_ = Object::New(addr); has_connection_to_host_ = true; @@ -391,7 +395,7 @@ void ConnectionSet::PushHostConnectedUDPCall(const SockAddr& addr, } void ConnectionSet::PushDisconnectFromHostCall() { - g_game->thread()->PushCall([this] { + g_logic->thread()->PushCall([this] { if (connection_to_host_.exists()) { connection_to_host_->RequestDisconnect(); } @@ -404,7 +408,8 @@ auto ConnectionSet::UnregisterClientController(ClientControllerInterface* c) // This shouldn't happen. if (client_controller_ != c) { - Log("UnregisterClientController() called with a non-registered " + Log(LogLevel::kError, + "UnregisterClientController() called with a non-registered " "controller"); return; } @@ -463,7 +468,7 @@ auto ConnectionSet::UDPConnectionPacket(const std::vector& data_in, PushClientDisconnectedCall(client_id); // Now send an ack so they know it's been taken care of. - g_network_write_module->PushSendToCall( + g_network_writer->PushSendToCall( {BA_PACKET_DISCONNECT_FROM_CLIENT_ACK, client_id}, addr); } break; @@ -490,7 +495,7 @@ auto ConnectionSet::UDPConnectionPacket(const std::vector& data_in, } // Now send an ack so they know it's been taken care of. - g_network_write_module->PushSendToCall( + g_network_writer->PushSendToCall( {BA_PACKET_DISCONNECT_FROM_HOST_ACK, client_id}, addr); } break; @@ -516,7 +521,7 @@ auto ConnectionSet::UDPConnectionPacket(const std::vector& data_in, return; } else { // Send a disconnect request aimed at them. - g_network_write_module->PushSendToCall( + g_network_writer->PushSendToCall( {BA_PACKET_DISCONNECT_FROM_HOST_REQUEST, client_id}, addr); } } @@ -562,7 +567,7 @@ auto ConnectionSet::UDPConnectionPacket(const std::vector& data_in, keep_trying = hc->SwitchProtocol(); if (!keep_trying) { if (!printed_host_disconnect_) { - ScreenMessage(g_game->GetResourceString( + ScreenMessage(g_logic->GetResourceString( "connectionFailedVersionMismatchText"), {1, 0, 0}); printed_host_disconnect_ = true; @@ -572,22 +577,23 @@ auto ConnectionSet::UDPConnectionPacket(const std::vector& data_in, if (!printed_host_disconnect_) { if (print_udp_connect_progress_) { ScreenMessage( - g_game->GetResourceString("connectionFailedPartyFullText"), + g_logic->GetResourceString("connectionFailedPartyFullText"), {1, 0, 0}); } printed_host_disconnect_ = true; } } else if (data[0] == BA_PACKET_CLIENT_DENY_ALREADY_IN_PARTY) { if (!printed_host_disconnect_) { - ScreenMessage(g_game->GetResourceString( + ScreenMessage(g_logic->GetResourceString( "connectionFailedHostAlreadyInPartyText"), {1, 0, 0}); printed_host_disconnect_ = true; } } else { if (!printed_host_disconnect_) { - ScreenMessage(g_game->GetResourceString("connectionRejectedText"), - {1, 0, 0}); + ScreenMessage( + g_logic->GetResourceString("connectionRejectedText"), + {1, 0, 0}); printed_host_disconnect_ = true; } } @@ -615,21 +621,21 @@ auto ConnectionSet::UDPConnectionPacket(const std::vector& data_in, std::string client_instance_uuid = &(client_instance_buffer[0]); if (static_cast(connections_to_clients_.size() + 1) - >= g_game->public_party_max_size()) { + >= g_logic->public_party_max_size()) { // If we've reached our party size limit (including ourself in that // count), reject. // Newer version have a specific party-full message; send that first // but also follow up with a generic deny message for older clients. - g_network_write_module->PushSendToCall( + g_network_writer->PushSendToCall( {BA_PACKET_CLIENT_DENY_PARTY_FULL, request_id}, addr); - g_network_write_module->PushSendToCall( - {BA_PACKET_CLIENT_DENY, request_id}, addr); + g_network_writer->PushSendToCall({BA_PACKET_CLIENT_DENY, request_id}, + addr); } else if (connection_to_host_.exists()) { // If we're connected to someone else, we can't have clients. - g_network_write_module->PushSendToCall( + g_network_writer->PushSendToCall( {BA_PACKET_CLIENT_DENY_ALREADY_IN_PARTY, request_id}, addr); } else { // Otherwise go ahead and make them a new client connection. @@ -666,8 +672,8 @@ auto ConnectionSet::UDPConnectionPacket(const std::vector& data_in, std::vector msg_out(2); msg_out[0] = BA_PACKET_CLIENT_DENY; msg_out[1] = request_id; - g_network_write_module->PushSendToCall(msg_out, addr); - Log("All client slots full; really?.."); + g_network_writer->PushSendToCall(msg_out, addr); + Log(LogLevel::kError, "All client slots full; really?.."); break; } connection_to_client = Object::New( @@ -684,7 +690,7 @@ auto ConnectionSet::UDPConnectionPacket(const std::vector& data_in, msg_out[1] = static_cast_check_fit(connection_to_client->id()); msg_out[2] = request_id; - g_network_write_module->PushSendToCall(msg_out, addr); + g_network_writer->PushSendToCall(msg_out, addr); } } break; @@ -709,8 +715,9 @@ auto ConnectionSet::VerifyClientAddr(uint8_t client_id, const SockAddr& addr) if (addr == connection_to_client_udp->addr()) { return true; } - BA_LOG_ONCE("VerifyClientAddr() found mismatch for client " - + std::to_string(client_id) + "."); + BA_LOG_ONCE(LogLevel::kError, + "VerifyClientAddr() found mismatch for client " + + std::to_string(client_id) + "."); return false; } @@ -721,8 +728,9 @@ void ConnectionSet::SetClientInfoFromMasterServer( const std::string& client_token, PyObject* info_obj) { // NOLINTNEXTLINE (python doing bitwise math on signed int) if (!PyDict_Check(info_obj)) { - Log("got non-dict for master-server client info for token " + client_token - + ": " + Python::ObjToString(info_obj)); + Log(LogLevel::kError, + "got non-dict for master-server client info for token " + client_token + + ": " + Python::ObjToString(info_obj)); return; } for (ConnectionToClient* client : GetConnectionsToClients()) { @@ -730,7 +738,7 @@ void ConnectionSet::SetClientInfoFromMasterServer( client->HandleMasterServerClientInfo(info_obj); // Roster will now include account-id... - g_game->mark_game_roster_dirty(); + g_logic->mark_game_roster_dirty(); break; } } diff --git a/src/ballistica/game/connection/connection_set.h b/src/ballistica/logic/connection/connection_set.h similarity index 96% rename from src/ballistica/game/connection/connection_set.h rename to src/ballistica/logic/connection/connection_set.h index c3b1888d..2d44e1da 100644 --- a/src/ballistica/game/connection/connection_set.h +++ b/src/ballistica/logic/connection/connection_set.h @@ -1,7 +1,7 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_GAME_CONNECTION_CONNECTION_SET_H_ -#define BALLISTICA_GAME_CONNECTION_CONNECTION_SET_H_ +#ifndef BALLISTICA_LOGIC_CONNECTION_CONNECTION_SET_H_ +#define BALLISTICA_LOGIC_CONNECTION_CONNECTION_SET_H_ #include #include @@ -118,4 +118,4 @@ class ConnectionSet { } // namespace ballistica -#endif // BALLISTICA_GAME_CONNECTION_CONNECTION_SET_H_ +#endif // BALLISTICA_LOGIC_CONNECTION_CONNECTION_SET_H_ diff --git a/src/ballistica/game/connection/connection_to_client.cc b/src/ballistica/logic/connection/connection_to_client.cc similarity index 86% rename from src/ballistica/game/connection/connection_to_client.cc rename to src/ballistica/logic/connection/connection_to_client.cc index 05fb2b42..10d52a4c 100644 --- a/src/ballistica/game/connection/connection_to_client.cc +++ b/src/ballistica/logic/connection/connection_to_client.cc @@ -1,17 +1,17 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/game/connection/connection_to_client.h" +#include "ballistica/logic/connection/connection_to_client.h" +#include "ballistica/assets/assets.h" #include "ballistica/audio/audio.h" -#include "ballistica/game/client_controller_interface.h" -#include "ballistica/game/connection/connection_set.h" -#include "ballistica/game/game.h" -#include "ballistica/game/player.h" -#include "ballistica/game/session/host_session.h" #include "ballistica/generic/json.h" #include "ballistica/input/device/client_input_device.h" #include "ballistica/internal/app_internal.h" -#include "ballistica/media/media.h" +#include "ballistica/logic/client_controller_interface.h" +#include "ballistica/logic/connection/connection_set.h" +#include "ballistica/logic/logic.h" +#include "ballistica/logic/player.h" +#include "ballistica/logic/session/host_session.h" #include "ballistica/networking/networking.h" #include "ballistica/python/python.h" #include "ballistica/python/python_sys.h" @@ -69,11 +69,11 @@ ConnectionToClient::~ConnectionToClient() { } // If they had been announced as connected, announce their departure. - if (can_communicate() && g_game->ShouldAnnouncePartyJoinsAndLeaves()) { - std::string s = g_game->GetResourceString("playerLeftPartyText"); + if (can_communicate() && g_logic->ShouldAnnouncePartyJoinsAndLeaves()) { + std::string s = g_logic->GetResourceString("playerLeftPartyText"); Utils::StringReplaceOne(&s, "${NAME}", peer_spec().GetDisplayString()); ScreenMessage(s, {1, 0.5f, 0.0f}); - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kCorkPop)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kCorkPop)); } } @@ -129,14 +129,14 @@ void ConnectionToClient::HandleGamePacket(const std::vector& data) { } if (data.empty()) { - Log("Error: ConnectionToClient got data size 0."); + Log(LogLevel::kError, "ConnectionToClient got data size 0."); return; } switch (data[0]) { case BA_GAMEPACKET_HANDSHAKE_RESPONSE: { // We sent the client a handshake and they're responding. if (data.size() < 3) { - Log("got invalid BA_GAMEPACKET_HANDSHAKE_RESPONSE"); + Log(LogLevel::kError, "got invalid BA_GAMEPACKET_HANDSHAKE_RESPONSE"); return; } @@ -174,7 +174,7 @@ void ConnectionToClient::HandleGamePacket(const std::vector& data) { // Compare this against our blocked specs.. if there's a match, reject // them. - if (g_game->IsPlayerBanned(peer_spec())) { + if (g_logic->IsPlayerBanned(peer_spec())) { Error(""); return; } @@ -193,7 +193,7 @@ void ConnectionToClient::HandleGamePacket(const std::vector& data) { // connection attempt so this will only apply to things like Google // Play invites where we probably want to be more verbose as // to why the game just died. - s = g_game->GetResourceString("incompatibleVersionPlayerText"); + s = g_logic->GetResourceString("incompatibleVersionPlayerText"); Utils::StringReplaceOne(&s, "${NAME}", peer_spec().GetDisplayString()); } @@ -210,17 +210,17 @@ void ConnectionToClient::HandleGamePacket(const std::vector& data) { next_kick_vote_allow_time_ = GetRealTime() + kNewClientKickVoteDelay; // At this point we have their name, so lets announce their arrival. - if (g_game->ShouldAnnouncePartyJoinsAndLeaves()) { - std::string s = g_game->GetResourceString("playerJoinedPartyText"); + if (g_logic->ShouldAnnouncePartyJoinsAndLeaves()) { + std::string s = g_logic->GetResourceString("playerJoinedPartyText"); Utils::StringReplaceOne(&s, "${NAME}", peer_spec().GetDisplayString()); ScreenMessage(s, {0.5f, 1, 0.5f}); - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kGunCock)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kGunCock)); } // Also mark the time for flashing the 'someone just joined your // party' message in the corner. - g_game->set_last_connection_to_client_join_time(GetRealTime()); + g_logic->set_last_connection_to_client_join_time(GetRealTime()); // Added midway through protocol 29: // We now send a json dict of info about ourself first thing. This @@ -234,10 +234,10 @@ void ConnectionToClient::HandleGamePacket(const std::vector& data) { cJSON_CreateNumber(kAppBuildNumber)); // Add a name entry if we've got a public party name set. - if (!g_game->public_party_name().empty()) { + if (!g_logic->public_party_name().empty()) { cJSON_AddItemToObject( info_dict, "n", - cJSON_CreateString(g_game->public_party_name().c_str())); + cJSON_CreateString(g_logic->public_party_name().c_str())); } std::string info = cJSON_PrintUnformatted(info_dict); cJSON_Delete(info_dict); @@ -253,23 +253,23 @@ void ConnectionToClient::HandleGamePacket(const std::vector& data) { join_msg[0] = BA_MESSAGE_PARTY_MEMBER_JOINED; memcpy(&(join_msg[1]), joiner_spec.c_str(), joiner_spec.size()); - for (auto&& i : g_game->connections()->connections_to_clients()) { + for (auto&& i : g_logic->connections()->connections_to_clients()) { // Also send a 'party-member-joined' notification to all clients // *except* the new one. if (i.second.exists() && i.second.get() != this - && g_game->ShouldAnnouncePartyJoinsAndLeaves()) { + && g_logic->ShouldAnnouncePartyJoinsAndLeaves()) { i.second->SendReliableMessage(join_msg); } } // Update the game party roster and send it to all clients (including // this new one). - g_game->UpdateGameRoster(); + g_logic->UpdateGameRoster(); // Lastly, we hand this connection over to whoever is currently // feeding client connections. - if (g_game->connections()->client_controller()) { - SetController(g_game->connections()->client_controller()); + if (g_logic->connections()->client_controller()) { + SetController(g_logic->connections()->client_controller()); } } break; @@ -295,7 +295,7 @@ void ConnectionToClient::SendScreenMessage(const std::string& s, float r, // Older clients don't support the screen-message message, so in that case // we just send it as a chat-message from . if (build_number() < 14248) { - std::string value = g_game->CompileResourceString(s, "sendScreenMessage"); + std::string value = g_logic->CompileResourceString(s, "sendScreenMessage"); std::string our_spec_string = PlayerSpec::GetDummyPlayerSpec("").GetSpecString(); std::vector msg_out(1 + 1 + our_spec_string.size() + value.size()); @@ -322,7 +322,7 @@ void ConnectionToClient::SendScreenMessage(const std::string& s, float r, void ConnectionToClient::HandleMessagePacket( const std::vector& buffer) { if (buffer.empty()) { - Log("Error: Got invalid HandleMessagePacket."); + Log(LogLevel::kError, "Got invalid HandleMessagePacket."); return; } @@ -347,10 +347,10 @@ void ConnectionToClient::HandleMessagePacket( case BA_MESSAGE_KICK_VOTE: { if (buffer.size() == 2) { - for (auto&& i : g_game->connections()->connections_to_clients()) { + for (auto&& i : g_logic->connections()->connections_to_clients()) { ConnectionToClient* client = i.second.get(); if (client->id() == static_cast(buffer[1])) { - g_game->StartKickVote(this, client); + g_logic->StartKickVote(this, client); break; } } @@ -369,7 +369,7 @@ void ConnectionToClient::HandleMessagePacket( if (b) { build_number_ = b->valueint; } else { - Log("no buildnumber in clientinfo msg"); + Log(LogLevel::kError, "no buildnumber in clientinfo msg"); } // Grab their token (we use this to ask the @@ -378,7 +378,7 @@ void ConnectionToClient::HandleMessagePacket( if (t) { token_ = t->valuestring; } else { - Log("no token in clientinfo msg"); + Log(LogLevel::kError, "no token in clientinfo msg"); } // Newer clients also pass a peer-hash, which @@ -397,8 +397,10 @@ void ConnectionToClient::HandleMessagePacket( } cJSON_Delete(info); } else { - Log("got invalid json in clientinfo message: '" - + std::string(reinterpret_cast(&(buffer[1]))) + "'"); + Log(LogLevel::kError, + "got invalid json in clientinfo message: '" + + std::string(reinterpret_cast(&(buffer[1]))) + + "'"); } } got_client_info_ = true; @@ -409,7 +411,7 @@ void ConnectionToClient::HandleMessagePacket( // Newer type using json. // Only accept peer info if we've not gotten official info from // the master server (and if we're allowing it in general). - if (!g_game->require_client_authentication() + if (!g_logic->require_client_authentication() && !got_info_from_master_server_) { std::vector b2(buffer.size()); memcpy(&(b2[0]), &(buffer[1]), buffer.size() - 1); @@ -435,7 +437,8 @@ void ConnectionToClient::HandleMessagePacket( // we support for game streams vs client-connections. We could disallow // connections to/from these older peers while still allowing old replays // to play back. - BA_LOG_ONCE("Received old pre-json player profiles msg; ignoring."); + BA_LOG_ONCE(LogLevel::kError, + "Received old pre-json player profiles msg; ignoring."); break; } @@ -460,13 +463,14 @@ void ConnectionToClient::HandleMessagePacket( // If we require client-info and don't have it from this guy yet, // ignore their chat messages (prevent bots from jumping in and // spamming before we can verify their identities) - if (g_game->require_client_authentication() + if (g_logic->require_client_authentication() && !got_info_from_master_server_) { - Log("Ignoring chat message from peer with no client info."); + Log(LogLevel::kError, + "Ignoring chat message from peer with no client info."); SendScreenMessage(R"({"r":"loadingTryAgainText"})", 1, 0, 0); } else if (last_chat_times_.size() >= 5) { chat_block_time_ = now + next_chat_block_seconds_ * 1000; - g_game->connections()->SendScreenMessageToAll( + g_logic->connections()->SendScreenMessageToAll( R"({"r":"internal.chatBlockedText","s":[["${NAME}",)" + Utils::GetJSONString( GetCombinedSpec().GetDisplayString().c_str()) @@ -498,7 +502,7 @@ void ConnectionToClient::HandleMessagePacket( "{\"t\":[\"serverResponses\"," "\"Message is too long.\"]}", 1, 0, 0); - } else if (g_game->kick_vote_in_progress() + } else if (g_logic->kick_vote_in_progress() && (!strcmp(b2.data(), "1") || !strcmp(b2.data(), "2"))) { // Special case - if there's a kick vote going on, take '1' or @@ -535,14 +539,14 @@ void ConnectionToClient::HandleMessagePacket( // Send it out to all clients. for (auto&& i : - g_game->connections()->connections_to_clients()) { + g_logic->connections()->connections_to_clients()) { if (i.second->can_communicate()) { i.second->SendReliableMessage(msg_out); } } // Display it locally. - g_game->LocalDisplayChatMessage(msg_out); + g_logic->LocalDisplayChatMessage(msg_out); } } } @@ -556,7 +560,7 @@ void ConnectionToClient::HandleMessagePacket( GetClientInputDevice(buffer[1])) { int count = static_cast((buffer.size() - 2) / 5); if ((buffer.size() - 2) % 5 != 0) { - Log("Error: invalid player-input-commands packet"); + Log(LogLevel::kError, "Error: invalid player-input-commands packet"); break; } int index = 2; @@ -574,7 +578,7 @@ void ConnectionToClient::HandleMessagePacket( case BA_MESSAGE_REMOVE_REMOTE_PLAYER: { last_remove_player_time_ = GetRealTime(); if (buffer.size() != 2) { - Log("Error: invalid remove-remote-player packet"); + Log(LogLevel::kError, "Error: invalid remove-remote-player packet"); break; } if (ClientInputDevice* cid = GetClientInputDevice(buffer[1])) { @@ -589,7 +593,7 @@ void ConnectionToClient::HandleMessagePacket( case BA_MESSAGE_REQUEST_REMOTE_PLAYER: { if (buffer.size() != 2) { - Log("Error: invalid remote-player-request packet"); + Log(LogLevel::kError, "Error: invalid remote-player-request packet"); break; } @@ -598,7 +602,7 @@ void ConnectionToClient::HandleMessagePacket( ClientInputDevice* cid = GetClientInputDevice(buffer[1]); if (auto* hs = - dynamic_cast(g_game->GetForegroundSession())) { + dynamic_cast(g_logic->GetForegroundSession())) { if (!cid->attached_to_player()) { millisecs_t seconds_since_last_left = (GetRealTime() - last_remove_player_time_) / 1000; @@ -615,7 +619,7 @@ void ConnectionToClient::HandleMessagePacket( + "\"]]}", 1, 1, 0); } else { - bool still_waiting = (g_game->require_client_authentication() + bool still_waiting = (g_logic->require_client_authentication() && !got_info_from_master_server_); // If we're not allowing peer client-info and have yet to get // master-server info for this client, delay their join (we'll @@ -628,16 +632,18 @@ void ConnectionToClient::HandleMessagePacket( } else { // Either timed out or have info; let the request go through. if (still_waiting) { - Log("Allowing player-request without client\'s master-server " + Log(LogLevel::kError, + "Allowing player-request without client\'s master-server " "info (build " - + std::to_string(build_number_) + ")"); + + std::to_string(build_number_) + ")"); } hs->RequestPlayer(cid); } } } } else { - Log("Error: ConnectionToClient got remote player" + Log(LogLevel::kError, + "ConnectionToClient got remote player" " request but have no host session"); } break; @@ -650,9 +656,10 @@ void ConnectionToClient::HandleMessagePacket( if (multipart_buffer_size() > 50000) { // Its not actually unknown but shhh don't tell the hackers... SendScreenMessage(R"({"r":"errorUnknownText"})", 1, 0, 0); - Log("Client data limit exceeded by '" + peer_spec().GetShortName() - + "'; kicking."); - g_game->BanPlayer(peer_spec(), 1000 * 60); + Log(LogLevel::kError, "Client data limit exceeded by '" + + peer_spec().GetShortName() + + "'; kicking."); + g_logic->BanPlayer(peer_spec(), 1000 * 60); Error(""); return; } @@ -666,7 +673,7 @@ void ConnectionToClient::HandleMessagePacket( auto ConnectionToClient::GetCombinedSpec() -> PlayerSpec { // Look for players coming from this client-connection. // If we find any, make a spec out of their name(s). - if (auto* hs = dynamic_cast(g_game->GetForegroundSession())) { + if (auto* hs = dynamic_cast(g_logic->GetForegroundSession())) { std::string p_name_combined; for (auto&& p : hs->players()) { InputDevice* input_device = p->GetInputDevice(); @@ -733,18 +740,18 @@ void ConnectionToClient::HandleMasterServerClientInfo(PyObject* info_obj) { // If the server returned no valid account info for them // and we're not trusting peers, kick this fella right out // and ban him for a short bit (to hopefully limit rejoin spam). - if (g_game->require_client_authentication()) { + if (g_logic->require_client_authentication()) { SendScreenMessage( "{\"t\":[\"serverResponses\"," "\"Your account was rejected. Are you signed in?\"]}", 1, 0, 0); - Log("Master server found no valid account for '" - + peer_spec().GetShortName() + "'; kicking."); + Log(LogLevel::kError, "Master server found no valid account for '" + + peer_spec().GetShortName() + "'; kicking."); // Not benning anymore. People were exploiting this by impersonating // other players using their public ids to get them banned from // their own servers/etc. - // g_game->BanPlayer(peer_spec(), 1000 * 60); + // g_logic->BanPlayer(peer_spec(), 1000 * 60); Error(""); } } @@ -755,8 +762,8 @@ auto ConnectionToClient::IsAdmin() const -> bool { if (peer_public_account_id_.empty()) { return false; } - return (g_game->admin_public_ids().find(peer_public_account_id_) - != g_game->admin_public_ids().end()); + return (g_logic->admin_public_ids().find(peer_public_account_id_) + != g_logic->admin_public_ids().end()); } } // namespace ballistica diff --git a/src/ballistica/game/connection/connection_to_client.h b/src/ballistica/logic/connection/connection_to_client.h similarity index 92% rename from src/ballistica/game/connection/connection_to_client.h rename to src/ballistica/logic/connection/connection_to_client.h index 5269e1c5..be548a94 100644 --- a/src/ballistica/game/connection/connection_to_client.h +++ b/src/ballistica/logic/connection/connection_to_client.h @@ -1,13 +1,13 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_GAME_CONNECTION_CONNECTION_TO_CLIENT_H_ -#define BALLISTICA_GAME_CONNECTION_CONNECTION_TO_CLIENT_H_ +#ifndef BALLISTICA_LOGIC_CONNECTION_CONNECTION_TO_CLIENT_H_ +#define BALLISTICA_LOGIC_CONNECTION_CONNECTION_TO_CLIENT_H_ #include #include #include -#include "ballistica/game/connection/connection.h" +#include "ballistica/logic/connection/connection.h" namespace ballistica { @@ -83,4 +83,4 @@ class ConnectionToClient : public Connection { } // namespace ballistica -#endif // BALLISTICA_GAME_CONNECTION_CONNECTION_TO_CLIENT_H_ +#endif // BALLISTICA_LOGIC_CONNECTION_CONNECTION_TO_CLIENT_H_ diff --git a/src/ballistica/game/connection/connection_to_client_udp.cc b/src/ballistica/logic/connection/connection_to_client_udp.cc similarity index 80% rename from src/ballistica/game/connection/connection_to_client_udp.cc rename to src/ballistica/logic/connection/connection_to_client_udp.cc index b75ad69a..4dbbf839 100644 --- a/src/ballistica/game/connection/connection_to_client_udp.cc +++ b/src/ballistica/logic/connection/connection_to_client_udp.cc @@ -1,10 +1,10 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/game/connection/connection_to_client_udp.h" +#include "ballistica/logic/connection/connection_to_client_udp.h" -#include "ballistica/game/connection/connection_set.h" -#include "ballistica/game/game.h" -#include "ballistica/networking/network_write_module.h" +#include "ballistica/logic/connection/connection_set.h" +#include "ballistica/logic/logic.h" +#include "ballistica/networking/network_writer.h" #include "ballistica/networking/sockaddr.h" namespace ballistica { @@ -16,7 +16,7 @@ ConnectionToClientUDP::ConnectionToClientUDP(const SockAddr& addr, request_id_(request_id), addr_(new SockAddr(addr)), client_instance_uuid_(std::move(client_name)), - last_client_response_time_(g_game->master_time()), + last_client_response_time_(g_logic->master_time()), did_die_(false) {} ConnectionToClientUDP::~ConnectionToClientUDP() { @@ -39,14 +39,14 @@ void ConnectionToClientUDP::SendGamePacketCompressed( // Ship this off to the net-out thread to send; at this point we don't know // or case what happens to it. - assert(g_network_write_module); - g_network_write_module->PushSendToCall(data_full, *addr_); + assert(g_network_writer); + g_network_writer->PushSendToCall(data_full, *addr_); } void ConnectionToClientUDP::Update() { ConnectionToClient::Update(); - millisecs_t current_time = g_game->master_time(); + millisecs_t current_time = g_logic->master_time(); // if its been long enough since we've heard anything from the host, error. if (current_time - last_client_response_time_ @@ -60,17 +60,17 @@ void ConnectionToClientUDP::Update() { void ConnectionToClientUDP::HandleGamePacket( const std::vector& buffer) { // keep track of when we last heard from the host for disconnect purposes - last_client_response_time_ = g_game->master_time(); + last_client_response_time_ = g_logic->master_time(); ConnectionToClient::HandleGamePacket(buffer); } void ConnectionToClientUDP::Die() { if (did_die_) { - Log("Error: Posting multiple die messages; probably not good."); + Log(LogLevel::kError, "Posting multiple die messages; probably not good."); return; } // this will actually clear the object.. - g_game->connections()->PushClientDisconnectedCall(id()); + g_logic->connections()->PushClientDisconnectedCall(id()); did_die_ = true; } @@ -89,7 +89,7 @@ void ConnectionToClientUDP::SendDisconnectRequest() { std::vector data(2); data[0] = BA_PACKET_DISCONNECT_FROM_HOST_REQUEST; data[1] = static_cast(id()); - g_network_write_module->PushSendToCall(data, *addr_); + g_network_writer->PushSendToCall(data, *addr_); } } // namespace ballistica diff --git a/src/ballistica/game/connection/connection_to_client_udp.h b/src/ballistica/logic/connection/connection_to_client_udp.h similarity index 81% rename from src/ballistica/game/connection/connection_to_client_udp.h rename to src/ballistica/logic/connection/connection_to_client_udp.h index cac1b30a..c6a99237 100644 --- a/src/ballistica/game/connection/connection_to_client_udp.h +++ b/src/ballistica/logic/connection/connection_to_client_udp.h @@ -1,13 +1,13 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_GAME_CONNECTION_CONNECTION_TO_CLIENT_UDP_H_ -#define BALLISTICA_GAME_CONNECTION_CONNECTION_TO_CLIENT_UDP_H_ +#ifndef BALLISTICA_LOGIC_CONNECTION_CONNECTION_TO_CLIENT_UDP_H_ +#define BALLISTICA_LOGIC_CONNECTION_CONNECTION_TO_CLIENT_UDP_H_ #include #include #include -#include "ballistica/game/connection/connection_to_client.h" +#include "ballistica/logic/connection/connection_to_client.h" #include "ballistica/networking/networking.h" #include "ballistica/networking/sockaddr.h" @@ -40,4 +40,4 @@ class ConnectionToClientUDP : public ConnectionToClient { } // namespace ballistica -#endif // BALLISTICA_GAME_CONNECTION_CONNECTION_TO_CLIENT_UDP_H_ +#endif // BALLISTICA_LOGIC_CONNECTION_CONNECTION_TO_CLIENT_UDP_H_ diff --git a/src/ballistica/game/connection/connection_to_host.cc b/src/ballistica/logic/connection/connection_to_host.cc similarity index 88% rename from src/ballistica/game/connection/connection_to_host.cc rename to src/ballistica/logic/connection/connection_to_host.cc index ac401990..e1500402 100644 --- a/src/ballistica/game/connection/connection_to_host.cc +++ b/src/ballistica/logic/connection/connection_to_host.cc @@ -1,16 +1,16 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/game/connection/connection_to_host.h" +#include "ballistica/logic/connection/connection_to_host.h" +#include "ballistica/assets/assets.h" #include "ballistica/audio/audio.h" -#include "ballistica/game/game.h" -#include "ballistica/game/session/net_client_session.h" #include "ballistica/generic/json.h" #include "ballistica/input/device/input_device.h" #include "ballistica/input/input.h" #include "ballistica/internal/app_internal.h" +#include "ballistica/logic/logic.h" +#include "ballistica/logic/session/net_client_session.h" #include "ballistica/math/vector3f.h" -#include "ballistica/media/media.h" #include "ballistica/networking/networking.h" #include "ballistica/platform/platform.h" #include "ballistica/python/python.h" @@ -32,16 +32,16 @@ ConnectionToHost::~ConnectionToHost() { // '${PEER-NAME}'s party'. std::string s; if (!party_name_.empty()) { - s = g_game->GetResourceString("leftGameText"); + s = g_logic->GetResourceString("leftGameText"); Utils::StringReplaceOne(&s, "${NAME}", party_name_); } else { - s = g_game->GetResourceString("leftPartyText"); + s = g_logic->GetResourceString("leftPartyText"); Utils::StringReplaceOne(&s, "${NAME}", peer_spec().GetDisplayString()); } ScreenMessage(s, {1, 0.5f, 0.0f}); - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kCorkPop)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kCorkPop)); } else { - ScreenMessage(g_game->GetResourceString("connectionRejectedText"), + ScreenMessage(g_logic->GetResourceString("connectionRejectedText"), {1, 0, 0}); } } @@ -126,9 +126,9 @@ void ConnectionToHost::HandleGamePacket(const std::vector& data) { if (!compatible) { if (their_protocol_version > kProtocolVersion) { - Error(g_game->GetResourceString("incompatibleNewerVersionHostText")); + Error(g_logic->GetResourceString("incompatibleNewerVersionHostText")); } else { - Error(g_game->GetResourceString("incompatibleVersionHostText")); + Error(g_logic->GetResourceString("incompatibleVersionHostText")); } return; } @@ -175,7 +175,7 @@ void ConnectionToHost::HandleGamePacket(const std::vector& data) { peer_hash_ = g_app_internal->CalcV1PeerHash(peer_hash_input_); set_can_communicate(true); - g_game->LaunchClientSession(); + g_logic->LaunchClientSession(); // NOTE: // we don't actually print a 'connected' message until after @@ -185,7 +185,7 @@ void ConnectionToHost::HandleGamePacket(const std::vector& data) { // Wire ourselves up to drive the client-session we're in. auto* cs = - dynamic_cast(g_game->GetForegroundSession()); + dynamic_cast(g_logic->GetForegroundSession()); assert(cs); assert(!cs->connection_to_host()); client_session_ = cs; @@ -219,7 +219,8 @@ void ConnectionToHost::HandleGamePacket(const std::vector& data) { PyObject* profiles = g_python->GetRawConfigValue("Player Profiles"); PythonRef empty_dict; if (!profiles) { - Log("No profiles found; sending empty list to host"); + Log(LogLevel::kError, + "No profiles found; sending empty list to host"); empty_dict.Steal(PyDict_New()); profiles = empty_dict.get(); } @@ -231,7 +232,8 @@ void ConnectionToHost::HandleGamePacket(const std::vector& data) { PythonRef results = g_python->obj(Python::ObjID::kJsonDumpsCall).Call(args, keywds); if (!results.exists()) { - Log("Error getting json dump of local profiles"); + Log(LogLevel::kError, + "Error getting json dump of local profiles"); } else { try { // Pull the string as utf8 and send. @@ -241,13 +243,15 @@ void ConnectionToHost::HandleGamePacket(const std::vector& data) { memcpy(&(msg[1]), &s[0], s.size()); SendReliableMessage(msg); } catch (const std::exception& e) { - Log(std::string("exc sending player profiles to host: ") - + e.what()); + Log(LogLevel::kError, + std::string("exc sending player profiles to host: ") + + e.what()); } } } } else { - Log("Connected to old protocol; can't send player profiles"); + Log(LogLevel::kError, + "Connected to old protocol; can't send player profiles"); } } break; @@ -274,7 +278,7 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) { assert(InLogicThread()); if (buffer.empty()) { - Log("Error: got invalid HandleMessagePacket"); + Log(LogLevel::kError, "Got invalid HandleMessagePacket"); return; } @@ -298,7 +302,7 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) { if (b) { build_number_ = b->valueint; } else { - Log("no buildnumber in hostinfo msg"); + Log(LogLevel::kError, "no buildnumber in hostinfo msg"); } // Party name. cJSON* n = cJSON_GetObjectItem(info, "n"); @@ -307,7 +311,7 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) { } cJSON_Delete(info); } else { - Log("got invalid json in hostinfo message"); + Log(LogLevel::kError, "got invalid json in hostinfo message"); } } got_host_info_ = true; @@ -321,7 +325,7 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) { cJSON* new_roster = cJSON_Parse(reinterpret_cast(&(buffer[1]))); if (new_roster) { - g_game->SetGameRoster(new_roster); + g_logic->SetGameRoster(new_roster); } } break; @@ -372,11 +376,11 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) { std::vector str_buffer(buffer.size()); memcpy(&(str_buffer[0]), &(buffer[1]), buffer.size() - 1); str_buffer[str_buffer.size() - 1] = 0; - std::string s = g_game->GetResourceString("playerJoinedPartyText"); + std::string s = g_logic->GetResourceString("playerJoinedPartyText"); Utils::StringReplaceOne( &s, "${NAME}", PlayerSpec(str_buffer.data()).GetDisplayString()); ScreenMessage(s, {0.5f, 1.0f, 0.5f}); - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kGunCock)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kGunCock)); } break; } @@ -387,11 +391,11 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) { std::vector str_buffer(buffer.size()); memcpy(&(str_buffer[0]), &(buffer[1]), buffer.size() - 1); str_buffer[str_buffer.size() - 1] = 0; - std::string s = g_game->GetResourceString("playerLeftPartyText"); + std::string s = g_logic->GetResourceString("playerLeftPartyText"); Utils::StringReplaceOne( &s, "${NAME}", PlayerSpec(&(str_buffer[0])).GetDisplayString()); ScreenMessage(s, {1, 0.5f, 0.0f}); - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kCorkPop)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kCorkPop)); } break; } @@ -399,7 +403,7 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) { case BA_MESSAGE_ATTACH_REMOTE_PLAYER_2: { // New-style packet which includes a 32-bit player_id. if (buffer.size() != 6) { - Log("Error: invalid attach-remote-player-2 msg"); + Log(LogLevel::kError, "Invalid attach-remote-player-2 msg"); break; } @@ -426,7 +430,7 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) { // public servers. // TODO(ericf): can remove this once back-compat-protocol > 29. if (buffer.size() != 3) { - Log("Error: Invalid attach-remote-player msg."); + Log(LogLevel::kError, "Invalid attach-remote-player msg."); break; } @@ -441,13 +445,13 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) { } case BA_MESSAGE_CHAT: { - g_game->LocalDisplayChatMessage(buffer); + g_logic->LocalDisplayChatMessage(buffer); break; } case BA_MESSAGE_DETACH_REMOTE_PLAYER: { if (buffer.size() != 2) { - Log("Error: Invalid detach-remote-player msg"); + Log(LogLevel::kError, "Invalid detach-remote-player msg"); break; } InputDevice* input_device = g_input->GetInputDevice(buffer[1]); @@ -479,14 +483,14 @@ void ConnectionToHost::HandleMessagePacket(const std::vector& buffer) { // If we've got a name for their party, use it; otherwise call it // '${NAME}'s party'. if (!party_name_.empty()) { - s = g_game->GetResourceString("connectedToGameText"); + s = g_logic->GetResourceString("connectedToGameText"); Utils::StringReplaceOne(&s, "${NAME}", party_name_); } else { - s = g_game->GetResourceString("connectedToPartyText"); + s = g_logic->GetResourceString("connectedToPartyText"); Utils::StringReplaceOne(&s, "${NAME}", peer_spec().GetDisplayString()); } ScreenMessage(s, {0.5f, 1, 0.5f}); - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kGunCock)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kGunCock)); printed_connect_message_ = true; } diff --git a/src/ballistica/game/connection/connection_to_host.h b/src/ballistica/logic/connection/connection_to_host.h similarity index 85% rename from src/ballistica/game/connection/connection_to_host.h rename to src/ballistica/logic/connection/connection_to_host.h index 40b0e74b..0f1b5c54 100644 --- a/src/ballistica/game/connection/connection_to_host.h +++ b/src/ballistica/logic/connection/connection_to_host.h @@ -1,12 +1,12 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_GAME_CONNECTION_CONNECTION_TO_HOST_H_ -#define BALLISTICA_GAME_CONNECTION_CONNECTION_TO_HOST_H_ +#ifndef BALLISTICA_LOGIC_CONNECTION_CONNECTION_TO_HOST_H_ +#define BALLISTICA_LOGIC_CONNECTION_CONNECTION_TO_HOST_H_ #include #include -#include "ballistica/game/connection/connection.h" +#include "ballistica/logic/connection/connection.h" namespace ballistica { @@ -44,4 +44,4 @@ class ConnectionToHost : public Connection { } // namespace ballistica -#endif // BALLISTICA_GAME_CONNECTION_CONNECTION_TO_HOST_H_ +#endif // BALLISTICA_LOGIC_CONNECTION_CONNECTION_TO_HOST_H_ diff --git a/src/ballistica/game/connection/connection_to_host_udp.cc b/src/ballistica/logic/connection/connection_to_host_udp.cc similarity index 82% rename from src/ballistica/game/connection/connection_to_host_udp.cc rename to src/ballistica/logic/connection/connection_to_host_udp.cc index 79d3f906..8e332355 100644 --- a/src/ballistica/game/connection/connection_to_host_udp.cc +++ b/src/ballistica/logic/connection/connection_to_host_udp.cc @@ -1,11 +1,11 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/game/connection/connection_to_host_udp.h" +#include "ballistica/logic/connection/connection_to_host_udp.h" -#include "ballistica/game/connection/connection_set.h" -#include "ballistica/game/game.h" +#include "ballistica/logic/connection/connection_set.h" +#include "ballistica/logic/logic.h" #include "ballistica/math/vector3f.h" -#include "ballistica/networking/network_write_module.h" +#include "ballistica/networking/network_writer.h" #include "ballistica/networking/sockaddr.h" namespace ballistica { @@ -28,10 +28,10 @@ ConnectionToHostUDP::ConnectionToHostUDP(const SockAddr& addr) last_client_id_request_time_(0), last_disconnect_request_time_(0), did_die_(false), - last_host_response_time_(g_game->master_time()) { + last_host_response_time_(g_logic->master_time()) { GetRequestID(); - if (g_game->connections()->GetPrintUDPConnectProgress()) { - ScreenMessage(g_game->GetResourceString("connectingToPartyText")); + if (g_logic->connections()->GetPrintUDPConnectProgress()) { + ScreenMessage(g_logic->GetResourceString("connectingToPartyText")); } } @@ -54,7 +54,7 @@ void ConnectionToHostUDP::GetRequestID() { void ConnectionToHostUDP::Update() { ConnectionToHost::Update(); - millisecs_t current_time = g_game->master_time(); + millisecs_t current_time = g_logic->master_time(); // If we've not gotten a client_id from the host yet, keep pestering it. if (!errored()) { @@ -71,7 +71,7 @@ void ConnectionToHostUDP::Update() { memcpy(&(msg[1]), &p_version, 2); msg[3] = request_id_; memcpy(&(msg[4]), uuid.c_str(), uuid.size()); - g_network_write_module->PushSendToCall(msg, *addr_); + g_network_writer->PushSendToCall(msg, *addr_); } } @@ -80,7 +80,7 @@ void ConnectionToHostUDP::Update() { > (can_communicate() ? 10000u : 5000u)) { // If the connection never got established, announce it failed. if (!can_communicate()) { - ScreenMessage(g_game->GetResourceString("connectionFailedText"), + ScreenMessage(g_logic->GetResourceString("connectionFailedText"), {1, 0, 0}); } @@ -110,14 +110,15 @@ void ConnectionToHostUDP::Update() { // departure before doing this when possible. void ConnectionToHostUDP::Die() { if (did_die_) { - Log("Error: posting multiple die messages; probably not good."); + Log(LogLevel::kError, "Posting multiple die messages; probably not good."); return; } - if (g_game->connections()->connection_to_host() == this) { - g_game->connections()->PushDisconnectedFromHostCall(); + if (g_logic->connections()->connection_to_host() == this) { + g_logic->connections()->PushDisconnectedFromHostCall(); did_die_ = true; } else { - Log("Error: Running update for non-current host-connection; shouldn't " + Log(LogLevel::kError, + "Running update for non-current host-connection; shouldn't " "happen."); } } @@ -128,13 +129,13 @@ void ConnectionToHostUDP::SendDisconnectRequest() { std::vector data(2); data[0] = BA_PACKET_DISCONNECT_FROM_CLIENT_REQUEST; data[1] = static_cast_check_fit(client_id_); - g_network_write_module->PushSendToCall(data, *addr_); + g_network_writer->PushSendToCall(data, *addr_); } } void ConnectionToHostUDP::HandleGamePacket(const std::vector& buffer) { // Keep track of when we last heard from the host for time-out purposes. - last_host_response_time_ = g_game->master_time(); + last_host_response_time_ = g_logic->master_time(); ConnectionToHost::HandleGamePacket(buffer); } @@ -152,8 +153,8 @@ void ConnectionToHostUDP::SendGamePacketCompressed( // Ship this off to the net-out thread to send; at this point we don't know // or care what happens to it. - assert(g_network_write_module); - g_network_write_module->PushSendToCall(data_full, *addr_); + assert(g_network_writer); + g_network_writer->PushSendToCall(data_full, *addr_); } void ConnectionToHostUDP::Error(const std::string& msg) { diff --git a/src/ballistica/game/connection/connection_to_host_udp.h b/src/ballistica/logic/connection/connection_to_host_udp.h similarity index 83% rename from src/ballistica/game/connection/connection_to_host_udp.h rename to src/ballistica/logic/connection/connection_to_host_udp.h index d3fe9cbe..40de91e0 100644 --- a/src/ballistica/game/connection/connection_to_host_udp.h +++ b/src/ballistica/logic/connection/connection_to_host_udp.h @@ -1,13 +1,13 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_GAME_CONNECTION_CONNECTION_TO_HOST_UDP_H_ -#define BALLISTICA_GAME_CONNECTION_CONNECTION_TO_HOST_UDP_H_ +#ifndef BALLISTICA_LOGIC_CONNECTION_CONNECTION_TO_HOST_UDP_H_ +#define BALLISTICA_LOGIC_CONNECTION_CONNECTION_TO_HOST_UDP_H_ #include #include #include -#include "ballistica/game/connection/connection_to_host.h" +#include "ballistica/logic/connection/connection_to_host.h" #include "ballistica/networking/networking.h" namespace ballistica { @@ -46,4 +46,4 @@ class ConnectionToHostUDP : public ConnectionToHost { } // namespace ballistica -#endif // BALLISTICA_GAME_CONNECTION_CONNECTION_TO_HOST_UDP_H_ +#endif // BALLISTICA_LOGIC_CONNECTION_CONNECTION_TO_HOST_UDP_H_ diff --git a/src/ballistica/game/friend_score_set.h b/src/ballistica/logic/friend_score_set.h similarity index 81% rename from src/ballistica/game/friend_score_set.h rename to src/ballistica/logic/friend_score_set.h index a4108c8d..e90dd619 100644 --- a/src/ballistica/game/friend_score_set.h +++ b/src/ballistica/logic/friend_score_set.h @@ -1,7 +1,7 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_GAME_FRIEND_SCORE_SET_H_ -#define BALLISTICA_GAME_FRIEND_SCORE_SET_H_ +#ifndef BALLISTICA_LOGIC_FRIEND_SCORE_SET_H_ +#define BALLISTICA_LOGIC_FRIEND_SCORE_SET_H_ #include #include @@ -27,4 +27,4 @@ struct FriendScoreSet { } // namespace ballistica -#endif // BALLISTICA_GAME_FRIEND_SCORE_SET_H_ +#endif // BALLISTICA_LOGIC_FRIEND_SCORE_SET_H_ diff --git a/src/ballistica/game/host_activity.cc b/src/ballistica/logic/host_activity.cc similarity index 89% rename from src/ballistica/game/host_activity.cc rename to src/ballistica/logic/host_activity.cc index 3d097d7e..be77634f 100644 --- a/src/ballistica/game/host_activity.cc +++ b/src/ballistica/logic/host_activity.cc @@ -1,24 +1,24 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/game/host_activity.h" +#include "ballistica/logic/host_activity.h" +#include "ballistica/assets/component/collide_model.h" +#include "ballistica/assets/component/data.h" +#include "ballistica/assets/component/model.h" +#include "ballistica/assets/component/sound.h" +#include "ballistica/assets/component/texture.h" #include "ballistica/dynamics/material/material.h" -#include "ballistica/game/game_stream.h" -#include "ballistica/game/player.h" -#include "ballistica/game/session/host_session.h" #include "ballistica/generic/lambda_runnable.h" #include "ballistica/generic/timer.h" #include "ballistica/input/device/input_device.h" -#include "ballistica/media/component/collide_model.h" -#include "ballistica/media/component/data.h" -#include "ballistica/media/component/model.h" -#include "ballistica/media/component/sound.h" -#include "ballistica/media/component/texture.h" +#include "ballistica/logic/player.h" +#include "ballistica/logic/session/host_session.h" #include "ballistica/python/python.h" #include "ballistica/python/python_context_call.h" #include "ballistica/python/python_sys.h" #include "ballistica/scene/node/globals_node.h" #include "ballistica/scene/node/node_type.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { @@ -36,7 +36,7 @@ HostActivity::HostActivity(HostSession* host_session) { scene_ = Object::New(0); // If there's an output stream, add to it. - if (GameStream* out = host_session->GetGameStream()) { + if (SceneStream* out = host_session->GetSceneStream()) { out->AddScene(scene_.get()); } } @@ -98,21 +98,21 @@ HostActivity::~HostActivity() { if (g_buildconfig.debug_build()) { PruneDeadRefs(&python_calls_); if (python_calls_.size() > 1) { - std::string s = "WARNING: " + std::to_string(python_calls_.size()) + std::string s = std::to_string(python_calls_.size()) + " live PythonContextCalls at shutdown for " + "HostActivity" + " (1 call is expected):"; int count = 1; for (auto& python_call : python_calls_) s += "\n " + std::to_string(count++) + ": " + (*python_call).GetObjectDescription(); - Log(s); + Log(LogLevel::kWarning, s); } } } -auto HostActivity::GetGameStream() const -> GameStream* { +auto HostActivity::GetSceneStream() const -> SceneStream* { if (!host_session_.exists()) return nullptr; - return host_session_->GetGameStream(); + return host_session_->GetSceneStream(); } auto HostActivity::SetGlobalsNode(GlobalsNode* node) -> void { @@ -153,15 +153,16 @@ void HostActivity::RegisterCall(PythonContextCall* call) { // If we're shutting down, just kill the call immediately. // (we turn all of our calls to no-ops as we shut down) if (shutting_down_) { - Log("WARNING: adding call to expired activity; call will not function: " - + call->GetObjectDescription()); + Log(LogLevel::kWarning, + "Adding call to expired activity; call will not function: " + + call->GetObjectDescription()); call->MarkDead(); } } void HostActivity::start() { if (_started) { - Log("Error: Start called twice for activity."); + Log(LogLevel::kError, "Start called twice for activity."); } _started = true; } @@ -183,28 +184,28 @@ auto HostActivity::GetTexture(const std::string& name) -> Object::Ref { if (shutting_down_) { throw Exception("can't load assets during activity shutdown"); } - return Media::GetMedia(&textures_, name, scene()); + return Assets::GetAsset(&textures_, name, scene()); } auto HostActivity::GetSound(const std::string& name) -> Object::Ref { if (shutting_down_) { throw Exception("can't load assets during activity shutdown"); } - return Media::GetMedia(&sounds_, name, scene()); + return Assets::GetAsset(&sounds_, name, scene()); } auto HostActivity::GetData(const std::string& name) -> Object::Ref { if (shutting_down_) { throw Exception("can't load assets during activity shutdown"); } - return Media::GetMedia(&datas_, name, scene()); + return Assets::GetAsset(&datas_, name, scene()); } auto HostActivity::GetModel(const std::string& name) -> Object::Ref { if (shutting_down_) { throw Exception("can't load assets during activity shutdown"); } - return Media::GetMedia(&models_, name, scene()); + return Assets::GetAsset(&models_, name, scene()); } auto HostActivity::GetCollideModel(const std::string& name) @@ -212,7 +213,7 @@ auto HostActivity::GetCollideModel(const std::string& name) if (shutting_down_) { throw Exception("can't load assets during activity shutdown"); } - return Media::GetMedia(&collide_models_, name, scene()); + return Assets::GetAsset(&collide_models_, name, scene()); } void HostActivity::SetPaused(bool val) { @@ -239,7 +240,7 @@ void HostActivity::UpdateStepTimerLength() { step_scene_timer_->SetLength( std::max(1, static_cast( round(static_cast(kGameStepMilliseconds) - / (game_speed_ * g_game->debug_speed_mult())))), + / (game_speed_ * g_logic->debug_speed_mult())))), true, base_time_); } } @@ -253,7 +254,8 @@ void HostActivity::HandleOutOfBoundsNodes() { // Make sure someone's handling our out-of-bounds messages. out_of_bounds_in_a_row_++; if (out_of_bounds_in_a_row_ > 100) { - Log("Warning: 100 consecutive out-of-bounds messages sent." + Log(LogLevel::kWarning, + "100 consecutive out-of-bounds messages sent." " They are probably not being handled properly"); int j = 0; for (auto&& i : scene()->out_of_bounds_nodes()) { @@ -265,9 +267,10 @@ void HostActivity::HandleOutOfBoundsNodes() { if (delegate) { dstr = PythonRef(delegate, PythonRef::kAcquire).Str(); } - Log(" node #" + std::to_string(j) + ": type='" + n->type()->name() - + "' addr=" + Utils::PtrToString(i.get()) + " name='" + n->label() - + "' delegate=" + dstr); + Log(LogLevel::kWarning, + " node #" + std::to_string(j) + ": type='" + n->type()->name() + + "' addr=" + Utils::PtrToString(i.get()) + " name='" + + n->label() + "' delegate=" + dstr); } } out_of_bounds_in_a_row_ = 0; @@ -311,10 +314,10 @@ void HostActivity::SetIsForeground(bool val) { Scene* sg = scene(); if (val && sg) { // Set it locally. - g_game->SetForegroundScene(sg); + g_logic->SetForegroundScene(sg); // Also push it to clients. - if (GameStream* out = GetGameStream()) { + if (SceneStream* out = GetSceneStream()) { out->SetForegroundScene(scene_.get()); } } @@ -433,7 +436,7 @@ void HostActivity::Draw(FrameDef* frame_def) { scene()->Draw(frame_def); } -void HostActivity::DumpFullState(GameStream* out) { +void HostActivity::DumpFullState(SceneStream* out) { // Add our scene. if (scene_.exists()) { scene_->Dump(out); diff --git a/src/ballistica/game/host_activity.h b/src/ballistica/logic/host_activity.h similarity index 89% rename from src/ballistica/game/host_activity.h rename to src/ballistica/logic/host_activity.h index 4aebc6a2..f9dfca16 100644 --- a/src/ballistica/game/host_activity.h +++ b/src/ballistica/logic/host_activity.h @@ -1,7 +1,7 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_GAME_HOST_ACTIVITY_H_ -#define BALLISTICA_GAME_HOST_ACTIVITY_H_ +#ifndef BALLISTICA_LOGIC_HOST_ACTIVITY_H_ +#define BALLISTICA_LOGIC_HOST_ACTIVITY_H_ #include #include @@ -69,8 +69,8 @@ class HostActivity : public ContextTarget { auto getAllowKickIdlePlayers() const -> bool { return allow_kick_idle_players_; } - auto GetGameStream() const -> GameStream*; - auto DumpFullState(GameStream* out) -> void; + auto GetSceneStream() const -> SceneStream*; + auto DumpFullState(SceneStream* out) -> void; auto SetGlobalsNode(GlobalsNode* node) -> void; auto SetIsForeground(bool val) -> void; auto RegisterPyActivity(PyObject* pyActivity) -> void; @@ -87,8 +87,8 @@ class HostActivity : public ContextTarget { auto StepScene() -> void; Object::WeakRef globals_node_; - bool allow_kick_idle_players_ = false; - Timer* step_scene_timer_ = nullptr; + bool allow_kick_idle_players_{}; + Timer* step_scene_timer_{}; std::unordered_map > textures_; std::unordered_map > sounds_; std::unordered_map > datas_; @@ -96,18 +96,18 @@ class HostActivity : public ContextTarget { collide_models_; std::unordered_map > models_; std::list > materials_; - bool shutting_down_ = false; + bool shutting_down_{}; // Our list of python calls created in the context of this activity; // we clear them as we are shutting down and ensure nothing runs after // that point. std::list > python_calls_; - millisecs_t next_prune_time_ = 0; - bool _started = false; - int out_of_bounds_in_a_row_ = 0; - bool paused_ = false; - float game_speed_ = 0.0f; - millisecs_t base_time_ = 0; + millisecs_t next_prune_time_{}; + bool _started{}; + int out_of_bounds_in_a_row_{}; + bool paused_{}; + float game_speed_{}; + millisecs_t base_time_{}; Object::Ref scene_; Object::WeakRef host_session_; PythonRef py_activity_weak_ref_; @@ -120,4 +120,4 @@ class HostActivity : public ContextTarget { } // namespace ballistica -#endif // BALLISTICA_GAME_HOST_ACTIVITY_H_ +#endif // BALLISTICA_LOGIC_HOST_ACTIVITY_H_ diff --git a/src/ballistica/game/game.cc b/src/ballistica/logic/logic.cc similarity index 85% rename from src/ballistica/game/game.cc rename to src/ballistica/logic/logic.cc index 31399a63..ab03be30 100644 --- a/src/ballistica/game/game.cc +++ b/src/ballistica/logic/logic.cc @@ -1,24 +1,12 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/game/game.h" +#include "ballistica/logic/logic.h" #include "ballistica/app/app_config.h" #include "ballistica/app/app_flavor.h" #include "ballistica/audio/audio.h" #include "ballistica/core/thread.h" #include "ballistica/dynamics/bg/bg_dynamics.h" -#include "ballistica/game/account.h" -#include "ballistica/game/connection/connection_set.h" -#include "ballistica/game/connection/connection_to_client_udp.h" -#include "ballistica/game/connection/connection_to_host_udp.h" -#include "ballistica/game/friend_score_set.h" -#include "ballistica/game/host_activity.h" -#include "ballistica/game/player.h" -#include "ballistica/game/score_to_beat.h" -#include "ballistica/game/session/client_session.h" -#include "ballistica/game/session/host_session.h" -#include "ballistica/game/session/net_client_session.h" -#include "ballistica/game/session/replay_client_session.h" #include "ballistica/generic/json.h" #include "ballistica/generic/timer.h" #include "ballistica/graphics/graphics.h" @@ -28,7 +16,18 @@ #include "ballistica/input/device/keyboard_input.h" #include "ballistica/input/device/touch_input.h" #include "ballistica/internal/app_internal.h" -#include "ballistica/networking/network_write_module.h" +#include "ballistica/logic/connection/connection_set.h" +#include "ballistica/logic/connection/connection_to_client_udp.h" +#include "ballistica/logic/connection/connection_to_host_udp.h" +#include "ballistica/logic/friend_score_set.h" +#include "ballistica/logic/host_activity.h" +#include "ballistica/logic/player.h" +#include "ballistica/logic/session/client_session.h" +#include "ballistica/logic/session/host_session.h" +#include "ballistica/logic/session/net_client_session.h" +#include "ballistica/logic/session/replay_client_session.h" +#include "ballistica/logic/v1_account.h" +#include "ballistica/networking/network_writer.h" #include "ballistica/networking/sockaddr.h" #include "ballistica/networking/telnet_server.h" #include "ballistica/python/python.h" @@ -65,55 +64,51 @@ const int kMaxChatMessages = 40; // Go with 5 minute ban. const int kKickBanSeconds = 5 * 60; -Game::Game(Thread* thread) - : thread_(thread), - game_roster_(cJSON_CreateArray()), +Logic::Logic() + : game_roster_(cJSON_CreateArray()), realtimers_(new TimerList()), connections_(std::make_unique()) { - assert(g_game == nullptr); - g_game = this; + // We're a singleton; make sure we don't already exist. + assert(g_logic == nullptr); + InitSpecialChars(); + + // Spin up our thread. + thread_ = new Thread(ThreadTag::kLogic); + g_app->pausable_threads.push_back(thread_); +} +auto Logic::OnAppStart() -> void { + thread_->PushCallSynchronous([this] { OnAppStartInThread(); }); +} + +auto Logic::OnAppStartInThread() -> void { try { - // Spin up some other game-thread-based stuff. - AppConfig::Init(); - assert(g_graphics == nullptr); - g_graphics = g_platform->CreateGraphics(); - TextGraphics::Init(); - Media::Init(); - Audio::Init(); - if (!HeadlessMode()) { - BGDynamics::Init(); - } + // Our thread should not be holding the GIL here at the start (and + // probably not have any Python state at all). So here we set both + // of those up. + assert(!PyGILState_Check()); + PyGILState_Ensure(); - InitSpecialChars(); - - Context::Init(); + // Tell our thread that it should grab the Python GIL any time it + // is running something and release it when done. + // TODO(ericf): It could be good to explore the idea of having + // individual runnables grab the GIL instead of doing it here at the + // thread level. Though its a bit freeing to know that we can run + // Python code at any time in the logic thread without worry. We can + // also consider explicitly releasing the GIL in the logic thread + // during long operations where we know no Python will occur. + thread_->SetAcquiresPythonGIL(); // We want to be informed when our thread is pausing. - thread->AddPauseCallback(NewLambdaRunnableRaw([this] { OnThreadPause(); })); + thread()->AddPauseCallback( + NewLambdaRunnableRaw([this] { OnThreadPause(); })); - // Waaah does UI need to be a bs::Object? - // Update: yes it does in order to be a context target. - // (need to be able to create weak-refs to it). - assert(g_ui == nullptr); - g_ui = Object::NewUnmanaged(); - g_ui->PostInit(); - - assert(g_networking == nullptr); - g_networking = new Networking(); - - assert(g_input == nullptr); - g_input = new Input(); - - g_app_internal = GetAppInternal(); + g_ui->OnAppStart(); // Init python and apply our settings immediately. // This way we can get started loading stuff in the background // and it'll come in with the correct texture quality etc. - g_python->Reset(true); - - // We're the thread that 'owns' python so we need to wrangle the GIL. - thread->SetOwnsPython(); + g_python->InitBallisticaPython(); } catch (const std::exception& e) { // If anything went wrong, trigger a deferred error. // This way it is more likely we can show a fatal error dialog @@ -128,7 +123,7 @@ Game::Game(Thread* thread) } } -auto Game::OnThreadPause() -> void { +auto Logic::OnThreadPause() -> void { ScopedSetContext cp(GetUIContextTarget()); // Let Python and internal layers do their thing. @@ -136,7 +131,7 @@ auto Game::OnThreadPause() -> void { g_app_internal->OnLogicThreadPause(); } -void Game::InitSpecialChars() { +void Logic::InitSpecialChars() { std::scoped_lock lock(special_char_mutex_); special_char_strings_[SpecialChar::kDownArrow] = "\xee\x80\x84"; @@ -240,55 +235,56 @@ void Game::InitSpecialChars() { special_char_strings_[SpecialChar::kV2Logo] = "\xee\x81\xA3"; } -void Game::SetGameRoster(cJSON* r) { +void Logic::SetGameRoster(cJSON* r) { if (game_roster_ != nullptr) { cJSON_Delete(game_roster_); } game_roster_ = r; } -void Game::ResetActivityTracking() { +void Logic::ResetActivityTracking() { largest_draw_time_increment_since_last_reset_ = 0; first_draw_real_time_ = last_draw_real_time_ = g_platform->GetTicks(); } #if BA_VR_BUILD -void Game::PushVRHandsState(const VRHandsState& state) { +void Logic::PushVRHandsState(const VRHandsState& state) { thread()->PushCall([this, state] { vr_hands_state_ = state; }); } #endif // BA_VR_BUILD -void Game::PushMediaPruneCall(int level) { +void Logic::PushMediaPruneCall(int level) { thread()->PushCall([level] { assert(InLogicThread()); - g_media->Prune(level); + g_assets->Prune(level); }); } -void Game::PushSetV1LoginCall(V1AccountType account_type, - V1LoginState account_state, - const std::string& account_name, - const std::string& account_id) { - thread()->PushCall([this, account_type, account_state, account_name, - account_id] { - g_account->SetLogin(account_type, account_state, account_name, account_id); - }); +void Logic::PushSetV1LoginCall(V1AccountType account_type, + V1LoginState account_state, + const std::string& account_name, + const std::string& account_id) { + thread()->PushCall( + [this, account_type, account_state, account_name, account_id] { + g_v1_account->SetLogin(account_type, account_state, account_name, + account_id); + }); } -void Game::PushInitialScreenCreatedCall() { +void Logic::PushInitialScreenCreatedCall() { thread()->PushCall([this] { InitialScreenCreated(); }); } -void Game::InitialScreenCreated() { +void Logic::InitialScreenCreated() { assert(InLogicThread()); // Ok; graphics-server is telling us we've got a screen. // We can now let the media thread go to town pre-loading system media // while we wait. - g_media->LoadSystemMedia(); + g_assets->LoadSystemAssets(); // FIXME: ideally we should create this as part of bootstrapping, but // we need it to be possible to load textures/etc. before the renderer @@ -315,10 +311,10 @@ void Game::InitialScreenCreated() { RunAppLaunchCommands(); } -void Game::PruneMedia() { g_media->Prune(); } +void Logic::PruneMedia() { g_assets->Prune(); } // Launch into main menu or whatever else. -void Game::RunAppLaunchCommands() { +void Logic::RunAppLaunchCommands() { assert(InLogicThread()); assert(!ran_app_launch_commands_); @@ -347,7 +343,7 @@ void Game::RunAppLaunchCommands() { } // Set up our sleeping based on what we're doing. -void Game::UpdateProcessTimer() { +void Logic::UpdateProcessTimer() { assert(InLogicThread()); // This might get called before we set up our timer in some cases. (such as @@ -368,7 +364,7 @@ void Game::UpdateProcessTimer() { } } -void Game::PruneSessions() { +void Logic::PruneSessions() { bool have_dead_session = false; for (auto&& i : sessions_) { if (i.exists()) { @@ -377,7 +373,8 @@ void Game::PruneSessions() { try { i.Clear(); } catch (const std::exception& e) { - Log("Exception killing Session: " + std::string(e.what())); + Log(LogLevel::kError, + "Exception killing Session: " + std::string(e.what())); } have_dead_session = true; } @@ -396,7 +393,7 @@ void Game::PruneSessions() { } } -void Game::UpdateKickVote() { +void Logic::UpdateKickVote() { if (!kick_vote_in_progress_) { return; } @@ -501,7 +498,7 @@ void Game::UpdateKickVote() { } } -void Game::HandleQuitOnIdle() { +void Logic::HandleQuitOnIdle() { if (idle_exit_minutes_) { float idle_seconds{g_input->input_idle_time() * 0.001f}; if (!idle_exiting_ && idle_seconds > (idle_exit_minutes_.value() * 60.0f)) { @@ -519,7 +516,7 @@ void Game::HandleQuitOnIdle() { } // Bring our scenes, real-time timers, etc up to date. -void Game::Update() { +void Logic::Update() { auto startms{Platform::GetCurrentMilliseconds()}; assert(InLogicThread()); millisecs_t real_time = GetRealTime(); @@ -622,7 +619,7 @@ void Game::Update() { } else { // If we've gone too far already, bail. if (master_time_ >= max_target_master_time) { - // Log("BAILING EARLY"); + // Log(LogLevel::kError, "BAILING EARLY"); // On rift if this is a 2-step and we bailed after 1, aim for 2 again // next time (otherwise we'll always get 3 singles in a row when this // happens). @@ -667,8 +664,8 @@ void Game::Update() { // Complain when our full update takes longer than 1/60th second. if (duration > (1000 / 60)) { - Log("Game update took too long (" + std::to_string(duration) + " ms).", - true, false); + Log(LogLevel::kInfo, + "Logic update took too long (" + std::to_string(duration) + " ms)."); // Limit these if we want (not doing so for now). next_long_update_report_time_ = real_time; @@ -677,7 +674,7 @@ void Game::Update() { } // Reset the game to a blank slate. -void Game::Reset() { +void Logic::Reset() { assert(InLogicThread()); // Tear down any existing setup. @@ -690,8 +687,9 @@ void Game::Reset() { // If all is well our sessions should all be dead. if (g_app->session_count != 0) { - Log("Error: session-count is non-zero (" - + std::to_string(g_app->session_count) + ") on Game::Reset."); + Log(LogLevel::kError, "Session-count is non-zero (" + + std::to_string(g_app->session_count) + + ") on Logic::Reset."); } // Note: we don't clear real-time timers anymore. Should we?.. @@ -712,11 +710,11 @@ void Game::Reset() { } } -auto Game::IsInUIContext() const -> bool { +auto Logic::IsInUIContext() const -> bool { return (g_ui && Context::current().target.get() == g_ui); } -void Game::PushShowURLCall(const std::string& url) { +void Logic::PushShowURLCall(const std::string& url) { thread()->PushCall([url] { assert(InLogicThread()); assert(g_python); @@ -724,7 +722,7 @@ void Game::PushShowURLCall(const std::string& url) { }); } -auto Game::GetForegroundContext() -> Context { +auto Logic::GetForegroundContext() -> Context { Session* s = GetForegroundSession(); if (s) { return s->GetForegroundContext(); @@ -733,7 +731,7 @@ auto Game::GetForegroundContext() -> Context { } } -void Game::PushBackButtonCall(InputDevice* input_device) { +void Logic::PushBackButtonCall(InputDevice* input_device) { thread()->PushCall([this, input_device] { assert(InLogicThread()); @@ -755,10 +753,10 @@ void Game::PushBackButtonCall(InputDevice* input_device) { }); } -void Game::PushStringEditSetCall(const std::string& value) { +void Logic::PushStringEditSetCall(const std::string& value) { thread()->PushCall([value] { if (!g_ui) { - Log("Error: No ui on StringEditSetEvent."); + Log(LogLevel::kError, "No ui on StringEditSetEvent."); return; } #if BA_OSTYPE_ANDROID @@ -772,10 +770,10 @@ void Game::PushStringEditSetCall(const std::string& value) { }); } -void Game::PushStringEditCancelCall() { +void Logic::PushStringEditCancelCall() { thread()->PushCall([] { if (!g_ui) { - Log("Error: No ui in PushStringEditCancelCall."); + Log(LogLevel::kError, "No ui in PushStringEditCancelCall."); return; } }); @@ -783,12 +781,12 @@ void Game::PushStringEditCancelCall() { // Called by a newly made Session instance to set itself as the current // session. -void Game::SetForegroundSession(Session* s) { +void Logic::SetForegroundSession(Session* s) { assert(InLogicThread()); foreground_session_ = s; } -void Game::SetForegroundScene(Scene* sg) { +void Logic::SetForegroundScene(Scene* sg) { assert(InLogicThread()); if (foreground_scene_.get() != sg) { foreground_scene_ = sg; @@ -800,7 +798,7 @@ void Game::SetForegroundScene(Scene* sg) { } } -void Game::LaunchClientSession() { +void Logic::LaunchClientSession() { if (in_update_) { throw Exception( "can't launch a session from within a session update; use " @@ -829,7 +827,7 @@ void Game::LaunchClientSession() { } } -void Game::LaunchReplaySession(const std::string& file_name) { +void Logic::LaunchReplaySession(const std::string& file_name) { if (in_update_) throw Exception( "can't launch a session from within a session update; use " @@ -859,8 +857,8 @@ void Game::LaunchReplaySession(const std::string& file_name) { } } -void Game::LaunchHostSession(PyObject* session_type_obj, - BenchmarkType benchmark_type) { +void Logic::LaunchHostSession(PyObject* session_type_obj, + BenchmarkType benchmark_type) { if (in_update_) { throw Exception( "can't call host_session() from within session update; use " @@ -894,7 +892,7 @@ void Game::LaunchHostSession(PyObject* session_type_obj, } } -void Game::RunMainMenu() { +void Logic::RunMainMenu() { assert(InLogicThread()); if (g_app->shutting_down) { return; @@ -911,11 +909,11 @@ void Game::RunMainMenu() { // Commands run via the in-game console. These are a bit more 'casual' and run // in the current visible context. -void Game::PushInGameConsoleScriptCommand(const std::string& command) { +void Logic::PushInGameConsoleScriptCommand(const std::string& command) { thread()->PushCall([this, command] { // These are always run in whichever context is 'visible'. ScopedSetContext cp(GetForegroundContext()); - PythonCommand cmd(command, ""); + PythonCommand cmd(command, ""); if (!g_app->user_ran_commands) { g_app->user_ran_commands = true; } @@ -940,7 +938,7 @@ void Game::PushInGameConsoleScriptCommand(const std::string& command) { } // Commands run via stdin. -void Game::PushStdinScriptCommand(const std::string& command) { +void Logic::PushStdinScriptCommand(const std::string& command) { thread()->PushCall([this, command] { // These are always run in whichever context is 'visible'. ScopedSetContext cp(GetForegroundContext()); @@ -974,7 +972,7 @@ void Game::PushStdinScriptCommand(const std::string& command) { }); } -void Game::PushInterruptSignalCall() { +void Logic::PushInterruptSignalCall() { thread()->PushCall([this] { assert(InLogicThread()); @@ -989,7 +987,7 @@ void Game::PushInterruptSignalCall() { }); } -void Game::PushAskUserForTelnetAccessCall() { +void Logic::PushAskUserForTelnetAccessCall() { thread()->PushCall([this] { assert(InLogicThread()); ScopedSetContext cp(GetUIContext()); @@ -997,8 +995,8 @@ void Game::PushAskUserForTelnetAccessCall() { }); } -void Game::PushPythonCall(const Object::Ref& call) { - // Since we're mucking with refs, need to limit to game thread. +void Logic::PushPythonCall(const Object::Ref& call) { + // Since we're mucking with refs, need to limit to logic thread. BA_PRECONDITION(InLogicThread()); BA_PRECONDITION(call->object_strong_ref_count() > 0); thread()->PushCall([call] { @@ -1007,9 +1005,9 @@ void Game::PushPythonCall(const Object::Ref& call) { }); } -void Game::PushPythonCallArgs(const Object::Ref& call, - const PythonRef& args) { - // Since we're mucking with refs, need to limit to game thread. +void Logic::PushPythonCallArgs(const Object::Ref& call, + const PythonRef& args) { + // Since we're mucking with refs, need to limit to logic thread. BA_PRECONDITION(InLogicThread()); BA_PRECONDITION(call->object_strong_ref_count() > 0); thread()->PushCall([call, args] { @@ -1018,8 +1016,8 @@ void Game::PushPythonCallArgs(const Object::Ref& call, }); } -void Game::PushPythonWeakCall(const Object::WeakRef& call) { - // Since we're mucking with refs, need to limit to game thread. +void Logic::PushPythonWeakCall(const Object::WeakRef& call) { + // Since we're mucking with refs, need to limit to logic thread. BA_PRECONDITION(InLogicThread()); // Even though we only hold a weak ref, we expect a valid strong-reffed @@ -1034,9 +1032,9 @@ void Game::PushPythonWeakCall(const Object::WeakRef& call) { }); } -void Game::PushPythonWeakCallArgs( +void Logic::PushPythonWeakCallArgs( const Object::WeakRef& call, const PythonRef& args) { - // Since we're mucking with refs, need to limit to game thread. + // Since we're mucking with refs, need to limit to logic thread. BA_PRECONDITION(InLogicThread()); // Even though we only hold a weak ref, we expect a valid strong-reffed @@ -1048,34 +1046,29 @@ void Game::PushPythonWeakCallArgs( }); } -void Game::PushPythonRawCallable(PyObject* callable) { - thread()->PushCall([this, callable] { +void Logic::PushPythonRawCallable(PyObject* callable, bool fg_context) { + thread()->PushCall([this, callable, fg_context] { assert(InLogicThread()); - // Lets run this in the UI context. - // (can add other options if we need later) - ScopedSetContext cp(GetUIContext()); + // Run this in the UI context by default, or foreground if requested. + ScopedSetContext cp(fg_context ? GetForegroundContext() : GetUIContext()); - // This event contains a raw python obj with an incremented ref-count. - auto call(Object::New(callable)); - Py_DECREF(callable); // now just held by call - - call->Run(); + PythonRef(callable, PythonRef::kSteal).Call(); }); } -void Game::PushScreenMessage(const std::string& message, - const Vector3f& color) { +void Logic::PushScreenMessage(const std::string& message, + const Vector3f& color) { thread()->PushCall( [message, color] { g_graphics->AddScreenMessage(message, color); }); } -void Game::SetReplaySpeedExponent(int val) { +void Logic::SetReplaySpeedExponent(int val) { replay_speed_exponent_ = std::min(3, std::max(-3, val)); replay_speed_mult_ = powf(2.0f, static_cast(replay_speed_exponent_)); } -void Game::SetDebugSpeedExponent(int val) { +void Logic::SetDebugSpeedExponent(int val) { debug_speed_exponent_ = val; debug_speed_mult_ = powf(2.0f, static_cast(debug_speed_exponent_)); @@ -1083,7 +1076,7 @@ void Game::SetDebugSpeedExponent(int val) { if (s) s->DebugSpeedMultChanged(); } -void Game::ChangeGameSpeed(int offs) { +void Logic::ChangeGameSpeed(int offs) { assert(InLogicThread()); // If we're in a replay session, adjust playback speed there. @@ -1110,40 +1103,40 @@ void Game::ChangeGameSpeed(int offs) { } } -auto Game::GetUIContext() const -> Context { +auto Logic::GetUIContext() const -> Context { return Context(GetUIContextTarget()); } -void Game::PushToggleManualCameraCall() { +void Logic::PushToggleManualCameraCall() { thread()->PushCall([] { g_graphics->ToggleManualCamera(); }); } -void Game::PushToggleDebugInfoDisplayCall() { +void Logic::PushToggleDebugInfoDisplayCall() { thread()->PushCall([] { g_graphics->ToggleNetworkDebugDisplay(); }); } -void Game::PushToggleCollisionGeometryDisplayCall() { +void Logic::PushToggleCollisionGeometryDisplayCall() { thread()->PushCall([] { g_graphics->ToggleDebugDraw(); }); } -void Game::PushMainMenuPressCall(InputDevice* device) { +void Logic::PushMainMenuPressCall(InputDevice* device) { thread()->PushCall([this, device] { MainMenuPress(device); }); } -void Game::MainMenuPress(InputDevice* device) { +void Logic::MainMenuPress(InputDevice* device) { assert(InLogicThread()); g_python->HandleDeviceMenuPress(device); } -void Game::PushScreenResizeCall(float virtual_width, float virtual_height, - float pixel_width, float pixel_height) { +void Logic::PushScreenResizeCall(float virtual_width, float virtual_height, + float pixel_width, float pixel_height) { thread()->PushCall([=] { ScreenResize(virtual_width, virtual_height, pixel_width, pixel_height); }); } -void Game::ScreenResize(float virtual_width, float virtual_height, - float pixel_width, float pixel_height) { +void Logic::ScreenResize(float virtual_width, float virtual_height, + float pixel_width, float pixel_height) { assert(InLogicThread()); assert(g_graphics != nullptr); if (g_graphics) { @@ -1158,49 +1151,34 @@ void Game::ScreenResize(float virtual_width, float virtual_height, } } -void Game::PushGameServiceAchievementListCall( +void Logic::PushGameServiceAchievementListCall( const std::set& achievements) { thread()->PushCall( [this, achievements] { GameServiceAchievementList(achievements); }); } -void Game::GameServiceAchievementList( +void Logic::GameServiceAchievementList( const std::set& achievements) { assert(g_python); assert(InLogicThread()); g_app_internal->DispatchRemoteAchievementList(achievements); } -void Game::PushScoresToBeatResponseCall(bool success, - const std::list& scores, - void* py_callback) { - thread()->PushCall([this, success, scores, py_callback] { - ScoresToBeatResponse(success, scores, py_callback); - }); +void Logic::PushPlaySoundCall(SystemSoundID sound) { + thread()->PushCall( + [sound] { g_audio->PlaySound(g_assets->GetSound(sound)); }); } -void Game::ScoresToBeatResponse(bool success, - const std::list& scores, - void* py_callback) { - assert(g_python); - assert(InLogicThread()); - g_python->DispatchScoresToBeatResponse(success, scores, py_callback); -} - -void Game::PushPlaySoundCall(SystemSoundID sound) { - thread()->PushCall([sound] { g_audio->PlaySound(g_media->GetSound(sound)); }); -} - -void Game::PushFriendScoreSetCall(const FriendScoreSet& score_set) { +void Logic::PushFriendScoreSetCall(const FriendScoreSet& score_set) { thread()->PushCall( [score_set] { g_python->HandleFriendScoresCB(score_set); }); } -void Game::PushConfirmQuitCall() { +void Logic::PushConfirmQuitCall() { thread()->PushCall([this] { assert(InLogicThread()); if (HeadlessMode()) { - Log("PushConfirmQuitCall() unhandled on headless."); + Log(LogLevel::kError, "PushConfirmQuitCall() unhandled on headless."); } else { // If input is locked, just quit immediately.. a confirm screen wouldn't // work anyway @@ -1214,7 +1192,7 @@ void Game::PushConfirmQuitCall() { // this needs to be run in the UI context ScopedSetContext cp(GetUIContextTarget()); - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kSwish)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kSwish)); g_python->obj(Python::ObjID::kQuitWindowCall).Call(); // if we have a keyboard, give it UI ownership @@ -1227,7 +1205,7 @@ void Game::PushConfirmQuitCall() { }); } -void Game::Draw() { +void Logic::Draw() { g_graphics->BuildAndPushFrameDef(); // Now bring the game up to date. @@ -1252,17 +1230,17 @@ void Game::Draw() { HostActivity* ha = GetForegroundContext().GetHostActivity(); if (ha) { int64_t step = ha->scene()->stepnum(); - Log(std::to_string(step - last_step)); + Log(LogLevel::kInfo, std::to_string(step - last_step)); last_step = step; } } } -void Game::PushFrameDefRequest() { +void Logic::PushFrameDefRequest() { thread()->PushCall([this] { Draw(); }); } -void Game::PushOnAppResumeCall() { +void Logic::PushOnAppResumeCall() { thread()->PushCall([] { // Wipe out whatever input device was in control of the UI. assert(g_ui); @@ -1270,8 +1248,12 @@ void Game::PushOnAppResumeCall() { }); } +void Logic::PushApplyConfigCall() { + thread()->PushCall([this] { ApplyConfig(); }); +} + // Look through everything in our config dict and act on it. -void Game::ApplyConfig() { +void Logic::ApplyConfig() { assert(InLogicThread()); // Not relevant for fullscreen anymore @@ -1293,7 +1275,8 @@ void Game::ApplyConfig() { } else if (texqualstr == "Low") { texture_quality_requested = TextureQuality::kLow; } else { - Log("Invalid texture quality: '" + texqualstr + "'; defaulting to low."); + Log(LogLevel::kError, + "Invalid texture quality: '" + texqualstr + "'; defaulting to low."); texture_quality_requested = TextureQuality::kLow; } @@ -1313,8 +1296,8 @@ void Game::ApplyConfig() { } else if (gqualstr == "Low") { graphics_quality_requested = GraphicsQuality::kLow; } else { - Log("Error: Invalid graphics quality: '" + gqualstr - + "'; defaulting to auto."); + Log(LogLevel::kError, + "Invalid graphics quality: '" + gqualstr + "'; defaulting to auto."); graphics_quality_requested = GraphicsQuality::kAuto; } @@ -1380,7 +1363,7 @@ void Game::ApplyConfig() { } else { do_v_sync = false; auto_v_sync = false; - Log("Error: Invalid 'Vertical Sync' value: '" + v_sync + "'"); + Log(LogLevel::kError, "Invalid 'Vertical Sync' value: '" + v_sync + "'"); } g_graphics_server->PushSetVSyncCall(do_v_sync, auto_v_sync); @@ -1429,11 +1412,7 @@ void Game::ApplyConfig() { g_platform->ApplyConfig(); } -void Game::PushApplyConfigCall() { - thread()->PushCall([this] { ApplyConfig(); }); -} - -void Game::PushRemoveGraphicsServerRenderHoldCall() { +void Logic::PushRemoveGraphicsServerRenderHoldCall() { thread()->PushCall([] { // This call acts as a flush of sorts; when it goes through, // we push a call to the graphics server saying its ok for it @@ -1443,8 +1422,8 @@ void Game::PushRemoveGraphicsServerRenderHoldCall() { }); } -void Game::PushFreeMediaComponentRefsCall( - const std::vector*>& components) { +void Logic::PushFreeAssetComponentRefsCall( + const std::vector*>& components) { thread()->PushCall([components] { for (auto&& i : components) { delete i; @@ -1452,18 +1431,18 @@ void Game::PushFreeMediaComponentRefsCall( }); } -void Game::PushHavePendingLoadsDoneCall() { - thread()->PushCall([] { g_media->ClearPendingLoadsDoneList(); }); +void Logic::PushHavePendingLoadsDoneCall() { + thread()->PushCall([] { g_assets->ClearPendingLoadsDoneList(); }); } -void Game::ToggleConsole() { +void Logic::ToggleConsole() { assert(InLogicThread()); if (auto console = g_app->console) { console->ToggleState(); } } -void Game::PushConsolePrintCall(const std::string& msg) { +void Logic::PushConsolePrintCall(const std::string& msg) { thread()->PushCall([msg] { // Send them to the console if its been created or store them // for when it is (unless we're headless in which case it never will). @@ -1475,18 +1454,18 @@ void Game::PushConsolePrintCall(const std::string& msg) { }); } -void Game::PushHavePendingLoadsCall() { +void Logic::PushHavePendingLoadsCall() { thread()->PushCall([this] { have_pending_loads_ = true; UpdateProcessTimer(); }); } -void Game::PushShutdownCall(bool soft) { +void Logic::PushShutdownCall(bool soft) { thread()->PushCall([this, soft] { Shutdown(soft); }); } -void Game::Shutdown(bool soft) { +void Logic::Shutdown(bool soft) { assert(InLogicThread()); if (!g_app->shutting_down) { @@ -1513,46 +1492,49 @@ void Game::Shutdown(bool soft) { } } -void Game::ResetInput() { +void Logic::ResetInput() { assert(InLogicThread()); g_input->ResetKeyboardHeldKeys(); g_input->ResetJoyStickHeldButtons(); } -auto Game::RemovePlayer(Player* player) -> void { +auto Logic::RemovePlayer(Player* player) -> void { assert(InLogicThread()); if (HostSession* host_session = player->GetHostSession()) { host_session->RemovePlayer(player); } else { - Log("Got RemovePlayer call but have no host_session"); + Log(LogLevel::kError, "Got RemovePlayer call but have no host_session"); } } -auto Game::NewRealTimer(millisecs_t length, bool repeat, - const Object::Ref& runnable) -> int { +auto Logic::NewRealTimer(millisecs_t length, bool repeat, + const Object::Ref& runnable) -> int { int offset = 0; Timer* t = realtimers_->NewTimer(GetRealTime(), length, offset, repeat ? -1 : 0, runnable); return t->id(); } -void Game::DeleteRealTimer(int timer_id) { realtimers_->DeleteTimer(timer_id); } +void Logic::DeleteRealTimer(int timer_id) { + realtimers_->DeleteTimer(timer_id); +} -void Game::SetRealTimerLength(int timer_id, millisecs_t length) { +void Logic::SetRealTimerLength(int timer_id, millisecs_t length) { Timer* t = realtimers_->GetTimer(timer_id); if (t) { t->SetLength(length); } else { - Log("Error: Game::SetRealTimerLength() called on nonexistent timer."); + Log(LogLevel::kError, + "Logic::SetRealTimerLength() called on nonexistent timer."); } } -void Game::Process() { - have_pending_loads_ = g_media->RunPendingLoadsLogicThread(); +void Logic::Process() { + have_pending_loads_ = g_assets->RunPendingLoadsLogicThread(); UpdateProcessTimer(); } -void Game::SetLanguageKeys( +void Logic::SetLanguageKeys( const std::unordered_map& language) { assert(InLogicThread()); { @@ -1594,8 +1576,9 @@ auto DoCompileResourceString(cJSON* obj) -> std::string { if (!printed) { printed = true; char* c = cJSON_Print(obj); - BA_LOG_ONCE("found long key 'resource' in raw lstr json: " - + std::string(c)); + BA_LOG_ONCE( + LogLevel::kError, + "found long key 'resource' in raw lstr json: " + std::string(c)); free(c); } } @@ -1613,8 +1596,9 @@ auto DoCompileResourceString(cJSON* obj) -> std::string { if (!printed) { printed = true; char* c = cJSON_Print(obj); - BA_LOG_ONCE("found long key 'fallback' in raw lstr json: " - + std::string(c)); + BA_LOG_ONCE( + LogLevel::kError, + "found long key 'fallback' in raw lstr json: " + std::string(c)); free(c); } } @@ -1637,8 +1621,9 @@ auto DoCompileResourceString(cJSON* obj) -> std::string { if (!printed) { printed = true; char* c = cJSON_Print(obj); - BA_LOG_ONCE("found long key 'translate' in raw lstr json: " - + std::string(c)); + BA_LOG_ONCE( + LogLevel::kError, + "found long key 'translate' in raw lstr json: " + std::string(c)); free(c); } } @@ -1675,8 +1660,9 @@ auto DoCompileResourceString(cJSON* obj) -> std::string { if (!printed) { printed = true; char* c = cJSON_Print(obj); - BA_LOG_ONCE("found long key 'value' in raw lstr json: " - + std::string(c)); + BA_LOG_ONCE( + LogLevel::kError, + "found long key 'value' in raw lstr json: " + std::string(c)); free(c); } } @@ -1706,8 +1692,8 @@ auto DoCompileResourceString(cJSON* obj) -> std::string { if (!printed) { printed = true; char* c = cJSON_Print(obj); - BA_LOG_ONCE("found long key 'subs' in raw lstr json: " - + std::string(c)); + BA_LOG_ONCE(LogLevel::kError, "found long key 'subs' in raw lstr json: " + + std::string(c)); free(c); } } @@ -1760,8 +1746,8 @@ auto DoCompileResourceString(cJSON* obj) -> std::string { return result; } -auto Game::CompileResourceString(const std::string& s, const std::string& loc, - bool* valid) -> std::string { +auto Logic::CompileResourceString(const std::string& s, const std::string& loc, + bool* valid) -> std::string { assert(g_python != nullptr); bool dummyvalid; @@ -1778,8 +1764,8 @@ auto Game::CompileResourceString(const std::string& s, const std::string& loc, cJSON* root = cJSON_Parse(s.c_str()); if (root == nullptr) { - Log("CompileResourceString failed (loc " + loc + "); invalid json: '" + s - + "'"); + Log(LogLevel::kError, "CompileResourceString failed (loc " + loc + + "); invalid json: '" + s + "'"); *valid = false; return ""; } @@ -1788,8 +1774,8 @@ auto Game::CompileResourceString(const std::string& s, const std::string& loc, result = DoCompileResourceString(root); *valid = true; } catch (const std::exception& e) { - Log("CompileResourceString failed (loc " + loc - + "): " + std::string(e.what()) + "; str='" + s + "'"); + Log(LogLevel::kError, "CompileResourceString failed (loc " + loc + "): " + + std::string(e.what()) + "; str='" + s + "'"); result = ""; *valid = false; } @@ -1797,7 +1783,7 @@ auto Game::CompileResourceString(const std::string& s, const std::string& loc, return result; } -auto Game::GetResourceString(const std::string& key) -> std::string { +auto Logic::GetResourceString(const std::string& key) -> std::string { std::string val; { std::scoped_lock lock(language_mutex_); @@ -1809,7 +1795,7 @@ auto Game::GetResourceString(const std::string& key) -> std::string { return val; } -auto Game::CharStr(SpecialChar id) -> std::string { +auto Logic::CharStr(SpecialChar id) -> std::string { std::scoped_lock lock(special_char_mutex_); std::string val; auto i = special_char_strings_.find(id); @@ -1823,7 +1809,7 @@ auto Game::CharStr(SpecialChar id) -> std::string { return val; } -auto Game::ShouldAnnouncePartyJoinsAndLeaves() -> bool { +auto Logic::ShouldAnnouncePartyJoinsAndLeaves() -> bool { assert(InLogicThread()); // At the moment we don't announce these for public internet parties.. (too @@ -1831,7 +1817,7 @@ auto Game::ShouldAnnouncePartyJoinsAndLeaves() -> bool { return !public_party_enabled(); } -void Game::CleanUpBeforeConnectingToHost() { +void Logic::CleanUpBeforeConnectingToHost() { // We can't have connected clients and a host-connection at the same time. // Make a minimal attempt to disconnect any client connections we have, but // get them off the list immediately. @@ -1845,13 +1831,13 @@ void Game::CleanUpBeforeConnectingToHost() { SetPublicPartyEnabled(false); } -auto Game::GetPartySize() const -> int { +auto Logic::GetPartySize() const -> int { assert(InLogicThread()); assert(game_roster_ != nullptr); return cJSON_GetArraySize(game_roster_); } -void Game::LocalDisplayChatMessage(const std::vector& buffer) { +void Logic::LocalDisplayChatMessage(const std::vector& buffer) { // 1 type byte, 1 spec-len byte, 1 or more spec chars, 0 or more msg chars. if (buffer.size() > 3) { size_t spec_len = buffer[1]; @@ -1886,13 +1872,13 @@ void Game::LocalDisplayChatMessage(const std::vector& buffer) { g_python->HandleLocalChatMessage(final_message); } if (!chat_muted_) { - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kTap)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kTap)); } } } } -auto Game::GetGameRosterMessage() -> std::vector { +auto Logic::GetGameRosterMessage() -> std::vector { // This message is simply a flattened json string of our roster (including // terminating char). char* s = cJSON_PrintUnformatted(game_roster_); @@ -1905,7 +1891,7 @@ auto Game::GetGameRosterMessage() -> std::vector { return msg; } -auto Game::IsPlayerBanned(const PlayerSpec& spec) -> bool { +auto Logic::IsPlayerBanned(const PlayerSpec& spec) -> bool { millisecs_t current_time = GetRealTime(); // Now is a good time to prune no-longer-banned specs. @@ -1922,8 +1908,8 @@ auto Game::IsPlayerBanned(const PlayerSpec& spec) -> bool { return false; } -void Game::StartKickVote(ConnectionToClient* starter, - ConnectionToClient* target) { +void Logic::StartKickVote(ConnectionToClient* starter, + ConnectionToClient* target) { // Restrict votes per client. millisecs_t current_time = GetRealTime(); @@ -2021,11 +2007,11 @@ void Game::StartKickVote(ConnectionToClient* starter, } } -void Game::BanPlayer(const PlayerSpec& spec, millisecs_t duration) { +void Logic::BanPlayer(const PlayerSpec& spec, millisecs_t duration) { banned_players_.emplace_back(GetRealTime() + duration, spec); } -void Game::UpdateGameRoster() { +void Logic::UpdateGameRoster() { assert(InLogicThread()); assert(game_roster_ != nullptr); @@ -2136,7 +2122,7 @@ void Game::UpdateGameRoster() { game_roster_dirty_ = true; } -void Game::SetPublicPartyEnabled(bool val) { +void Logic::SetPublicPartyEnabled(bool val) { assert(InLogicThread()); if (val == public_party_enabled_) { return; @@ -2145,7 +2131,7 @@ void Game::SetPublicPartyEnabled(bool val) { g_app_internal->PushPublicPartyState(); } -void Game::SetPublicPartySize(int count) { +void Logic::SetPublicPartySize(int count) { assert(InLogicThread()); if (count == public_party_size_) { return; @@ -2159,7 +2145,7 @@ void Game::SetPublicPartySize(int count) { } } -void Game::SetPublicPartyMaxSize(int count) { +void Logic::SetPublicPartyMaxSize(int count) { assert(InLogicThread()); if (count == public_party_max_size_) { return; @@ -2173,7 +2159,7 @@ void Game::SetPublicPartyMaxSize(int count) { } } -void Game::SetPublicPartyName(const std::string& name) { +void Logic::SetPublicPartyName(const std::string& name) { assert(InLogicThread()); if (name == public_party_name_) { return; @@ -2187,7 +2173,7 @@ void Game::SetPublicPartyName(const std::string& name) { } } -void Game::SetPublicPartyStatsURL(const std::string& url) { +void Logic::SetPublicPartyStatsURL(const std::string& url) { assert(InLogicThread()); if (url == public_party_stats_url_) { return; @@ -2201,7 +2187,7 @@ void Game::SetPublicPartyStatsURL(const std::string& url) { } } -void Game::SetPublicPartyPlayerCount(int count) { +void Logic::SetPublicPartyPlayerCount(int count) { assert(InLogicThread()); if (count == public_party_player_count_) { return; diff --git a/src/ballistica/game/game.h b/src/ballistica/logic/logic.h similarity index 91% rename from src/ballistica/game/game.h rename to src/ballistica/logic/logic.h index acd50155..4de58019 100644 --- a/src/ballistica/game/game.h +++ b/src/ballistica/logic/logic.h @@ -1,7 +1,7 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_GAME_GAME_H_ -#define BALLISTICA_GAME_GAME_H_ +#ifndef BALLISTICA_LOGIC_LOGIC_H_ +#define BALLISTICA_LOGIC_LOGIC_H_ #include #include @@ -19,12 +19,13 @@ namespace ballistica { const int kMaxPartyNameCombinedSize = 25; -/// The Game Module generally runs on a dedicated thread; it manages -/// all game logic, builds frame_defs to send to the graphics-server for -/// rendering, etc. -class Game { +/// The logic subsystem of the app. This runs on a dedicated thread +/// and is where high level app logic happens. Much app functionality +/// including UI calls must be run on the logic thread. +class Logic { public: - explicit Game(Thread* thread); + Logic(); + auto OnAppStart() -> void; auto LaunchHostSession(PyObject* session_type_obj, BenchmarkType benchmark_type = BenchmarkType::kNone) @@ -43,7 +44,7 @@ class Game { /// Push a generic 'menu press' event, optionally associated with an /// input device (nullptr to specify none). Note: caller must ensure - /// a RemoveInputDevice() call does not arrive at the game thread + /// a RemoveInputDevice() call does not arrive at the logic thread /// before this one. auto PushMainMenuPressCall(InputDevice* device) -> void; @@ -54,15 +55,12 @@ class Game { auto PushGameServiceAchievementListCall( const std::set& achievements) -> void; - auto PushScoresToBeatResponseCall(bool success, - const std::list& scores, - void* py_callback) -> void; auto PushToggleCollisionGeometryDisplayCall() -> void; auto PushToggleDebugInfoDisplayCall() -> void; auto PushToggleManualCameraCall() -> void; auto PushHavePendingLoadsDoneCall() -> void; - auto PushFreeMediaComponentRefsCall( - const std::vector*>& components) -> void; + auto PushFreeAssetComponentRefsCall( + const std::vector*>& components) -> void; auto PushHavePendingLoadsCall() -> void; auto PushShutdownCall(bool soft) -> void; @@ -73,20 +71,22 @@ class Game { auto PushMediaPruneCall(int level) -> void; auto PushAskUserForTelnetAccessCall() -> void; - // Push Python call and keep it alive; must be called from game thread. + // Push Python call and keep it alive; must be called from logic thread. auto PushPythonCall(const Object::Ref& call) -> void; auto PushPythonCallArgs(const Object::Ref& call, const PythonRef& args) -> void; - // Push Python call without keeping it alive; must be called from game thread. + // Push Python call without keeping it alive; must be called from logic + // thread. auto PushPythonWeakCall(const Object::WeakRef& call) -> void; auto PushPythonWeakCallArgs(const Object::WeakRef& call, const PythonRef& args) -> void; - // Push a raw Python call, decrements its refcount after running. - // Can be pushed from any thread. - auto PushPythonRawCallable(PyObject* callable) -> void; + // Push a raw Python call from any thread. Refcount should be incremented + // beforehand and will be decremented after running. + auto PushPythonRawCallable(PyObject* callable, bool fg_context = false) + -> void; auto PushScreenMessage(const std::string& message, const Vector3f& color) -> void; auto RemovePlayer(Player* player) -> void; @@ -247,6 +247,7 @@ class Game { auto thread() const -> Thread* { return thread_; } private: + auto OnAppStartInThread() -> void; auto HandleQuitOnIdle() -> void; auto InitSpecialChars() -> void; auto Draw() -> void; @@ -256,8 +257,6 @@ class Game { float pixel_width, float pixel_height) -> void; auto GameServiceAchievementList(const std::set& achievements) -> void; - auto ScoresToBeatResponse(bool success, const std::list& scores, - void* py_callback) -> void; auto PruneMedia() -> void; auto Update() -> void; @@ -329,7 +328,7 @@ class Game { int last_kick_votes_needed_{-1}; Object::WeakRef kick_vote_starter_; Object::WeakRef kick_vote_target_; - bool public_party_enabled_{false}; + bool public_party_enabled_{}; int public_party_size_{1}; // Always count ourself (is that what we want?). int public_party_max_size_{8}; int public_party_player_count_{0}; @@ -341,4 +340,4 @@ class Game { } // namespace ballistica -#endif // BALLISTICA_GAME_GAME_H_ +#endif // BALLISTICA_LOGIC_LOGIC_H_ diff --git a/src/ballistica/game/player.cc b/src/ballistica/logic/player.cc similarity index 97% rename from src/ballistica/game/player.cc rename to src/ballistica/logic/player.cc index 198d4ad1..4571e24b 100644 --- a/src/ballistica/game/player.cc +++ b/src/ballistica/logic/player.cc @@ -1,11 +1,11 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/game/player.h" +#include "ballistica/logic/player.h" -#include "ballistica/game/host_activity.h" -#include "ballistica/game/session/host_session.h" #include "ballistica/generic/utils.h" #include "ballistica/input/device/joystick.h" +#include "ballistica/logic/host_activity.h" +#include "ballistica/logic/session/host_session.h" #include "ballistica/python/class/python_class_session_player.h" #include "ballistica/python/python.h" #include "ballistica/python/python_context_call.h" @@ -367,7 +367,7 @@ void Player::SetName(const std::string& name, const std::string& full_name, // If we're already in the game and our name is changing, we need to update // the roster. if (accepted_) { - g_game->UpdateGameRoster(); + g_logic->UpdateGameRoster(); } } @@ -380,8 +380,8 @@ void Player::InputCommand(InputType type, float value) { RunInput(type, value); break; // case InputType::kReset: - // Log("Error: FIXME: player-input-reset command unimplemented"); - // break; + // Log(LogLevel::kError, "FIXME: player-input-reset command + // unimplemented"); break; default: RunInput(type); break; diff --git a/src/ballistica/game/player.h b/src/ballistica/logic/player.h similarity index 97% rename from src/ballistica/game/player.h rename to src/ballistica/logic/player.h index 1d416764..e9d6682c 100644 --- a/src/ballistica/game/player.h +++ b/src/ballistica/logic/player.h @@ -1,7 +1,7 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_GAME_PLAYER_H_ -#define BALLISTICA_GAME_PLAYER_H_ +#ifndef BALLISTICA_LOGIC_PLAYER_H_ +#define BALLISTICA_LOGIC_PLAYER_H_ #include #include @@ -147,7 +147,7 @@ class Player : public Object { // Player's position for use by input devices and whatnot for guides. // FIXME: This info should be acquired through the player node. - bool have_position_{false}; + bool have_position_{}; Vector3f position_{0.0f, 0.0f, 0.0f}; // These should be destructed before the rest of our class goes down, @@ -164,4 +164,4 @@ class Player : public Object { } // namespace ballistica -#endif // BALLISTICA_GAME_PLAYER_H_ +#endif // BALLISTICA_LOGIC_PLAYER_H_ diff --git a/src/ballistica/game/player_spec.cc b/src/ballistica/logic/player_spec.cc similarity index 77% rename from src/ballistica/game/player_spec.cc rename to src/ballistica/logic/player_spec.cc index c41c6f29..8f8f28c9 100644 --- a/src/ballistica/game/player_spec.cc +++ b/src/ballistica/logic/player_spec.cc @@ -1,12 +1,12 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/game/player_spec.h" +#include "ballistica/logic/player_spec.h" #include "ballistica/app/app.h" -#include "ballistica/game/account.h" -#include "ballistica/game/game.h" #include "ballistica/generic/json.h" #include "ballistica/generic/utils.h" +#include "ballistica/logic/logic.h" +#include "ballistica/logic/v1_account.h" #include "ballistica/platform/platform.h" namespace ballistica { @@ -26,13 +26,14 @@ PlayerSpec::PlayerSpec(const std::string& s) { // Account type may technically be something we don't recognize, // but that's ok.. it'll just be 'invalid' to us in that case - account_type_ = Account::AccountTypeFromString(account_obj->valuestring); + account_type_ = + V1Account::AccountTypeFromString(account_obj->valuestring); success = true; } cJSON_Delete(root_obj); } if (!success) { - Log("Error creating PlayerSpec from string: '" + s + "'"); + Log(LogLevel::kError, "Error creating PlayerSpec from string: '" + s + "'"); name_ = ""; short_name_ = ""; account_type_ = V1AccountType::kInvalid; @@ -40,7 +41,7 @@ PlayerSpec::PlayerSpec(const std::string& s) { } auto PlayerSpec::GetDisplayString() const -> std::string { - return Account::AccountTypeToIconString(account_type_) + name_; + return V1Account::AccountTypeToIconString(account_type_) + name_; } auto PlayerSpec::GetShortName() const -> std::string { @@ -60,8 +61,8 @@ auto PlayerSpec::GetSpecString() const -> std::string { cJSON* root; root = cJSON_CreateObject(); cJSON_AddStringToObject(root, "n", name_.c_str()); - cJSON_AddStringToObject(root, "a", - Account::AccountTypeToString(account_type_).c_str()); + cJSON_AddStringToObject( + root, "a", V1Account::AccountTypeToString(account_type_).c_str()); cJSON_AddStringToObject(root, "sn", short_name_.c_str()); char* out = cJSON_PrintUnformatted(root); std::string out_s = out; @@ -76,16 +77,16 @@ auto PlayerSpec::GetSpecString() const -> std::string { auto PlayerSpec::GetAccountPlayerSpec() -> PlayerSpec { PlayerSpec spec; - if (g_account->GetLoginState() == V1LoginState::kSignedIn) { + if (g_v1_account->GetLoginState() == V1LoginState::kSignedIn) { spec.account_type_ = g_app->account_type; spec.name_ = - Utils::GetValidUTF8(g_account->GetLoginName().c_str(), "bsgaps"); + Utils::GetValidUTF8(g_v1_account->GetLoginName().c_str(), "bsgaps"); } else { // Headless builds fall back to V1 public-party name if that's available. if (g_buildconfig.headless_build() - && !g_game->public_party_name().empty()) { + && !g_logic->public_party_name().empty()) { spec.name_ = - Utils::GetValidUTF8(g_game->public_party_name().c_str(), "bsgp3r"); + Utils::GetValidUTF8(g_logic->public_party_name().c_str(), "bsgp3r"); } else { // Or lastly fall back to device name. spec.name_ = @@ -94,7 +95,7 @@ auto PlayerSpec::GetAccountPlayerSpec() -> PlayerSpec { } if (spec.name_.size() > 100) { // FIXME should perhaps clamp this in unicode space - Log("account name size too long: '" + spec.name_ + "'"); + Log(LogLevel::kError, "account name size too long: '" + spec.name_ + "'"); spec.name_.resize(100); spec.name_ = Utils::GetValidUTF8(spec.name_.c_str(), "bsgaps3"); } @@ -106,7 +107,8 @@ auto PlayerSpec::GetDummyPlayerSpec(const std::string& name) -> PlayerSpec { spec.name_ = Utils::GetValidUTF8(name.c_str(), "bsgdps1"); if (spec.name_.size() > 100) { // FIXME should perhaps clamp this in unicode space - Log("dummy player spec name too long: '" + spec.name_ + "'"); + Log(LogLevel::kError, + "dummy player spec name too long: '" + spec.name_ + "'"); spec.name_.resize(100); spec.name_ = Utils::GetValidUTF8(spec.name_.c_str(), "bsgdps2"); } diff --git a/src/ballistica/game/player_spec.h b/src/ballistica/logic/player_spec.h similarity index 92% rename from src/ballistica/game/player_spec.h rename to src/ballistica/logic/player_spec.h index a93af89b..8d1bbc49 100644 --- a/src/ballistica/game/player_spec.h +++ b/src/ballistica/logic/player_spec.h @@ -1,7 +1,7 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_GAME_PLAYER_SPEC_H_ -#define BALLISTICA_GAME_PLAYER_SPEC_H_ +#ifndef BALLISTICA_LOGIC_PLAYER_SPEC_H_ +#define BALLISTICA_LOGIC_PLAYER_SPEC_H_ #include @@ -16,7 +16,7 @@ namespace ballistica { /// should not know or care about V2 accounts. class PlayerSpec { public: - /// Init an invalid player-spec + /// Create an invalid player-spec. PlayerSpec(); auto operator==(const PlayerSpec& spec) const -> bool; @@ -56,4 +56,4 @@ class PlayerSpec { } // namespace ballistica -#endif // BALLISTICA_GAME_PLAYER_SPEC_H_ +#endif // BALLISTICA_LOGIC_PLAYER_SPEC_H_ diff --git a/src/ballistica/game/session/client_session.cc b/src/ballistica/logic/session/client_session.cc similarity index 97% rename from src/ballistica/game/session/client_session.cc rename to src/ballistica/logic/session/client_session.cc index 30453ffe..73e5b67a 100644 --- a/src/ballistica/game/session/client_session.cc +++ b/src/ballistica/logic/session/client_session.cc @@ -1,8 +1,12 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/game/session/client_session.h" +#include "ballistica/logic/session/client_session.h" #include "ballistica/app/app.h" +#include "ballistica/assets/component/collide_model.h" +#include "ballistica/assets/component/model.h" +#include "ballistica/assets/component/sound.h" +#include "ballistica/assets/component/texture.h" #include "ballistica/audio/audio.h" #include "ballistica/dynamics/bg/bg_dynamics.h" #include "ballistica/dynamics/material/material.h" @@ -10,17 +14,13 @@ #include "ballistica/dynamics/material/material_component.h" #include "ballistica/dynamics/material/material_condition_node.h" #include "ballistica/dynamics/rigid_body.h" -#include "ballistica/game/game_stream.h" #include "ballistica/graphics/graphics.h" -#include "ballistica/media/component/collide_model.h" -#include "ballistica/media/component/model.h" -#include "ballistica/media/component/sound.h" -#include "ballistica/media/component/texture.h" #include "ballistica/networking/networking.h" #include "ballistica/python/python.h" #include "ballistica/scene/node/node_attribute.h" #include "ballistica/scene/node/node_type.h" #include "ballistica/scene/scene.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { @@ -196,10 +196,12 @@ void ClientSession::Update(int time_advance) { if (g_buildconfig.debug_build()) { if (current_cmd_ptr_ != nullptr) { if (current_cmd_ptr_ != &(current_cmd_[0]) + current_cmd_.size()) { - Log("SIZE ERROR FOR CMD " - + std::to_string(static_cast(current_cmd_[0])) - + " expected " + std::to_string(current_cmd_.size()) + " got " - + std::to_string(current_cmd_ptr_ - &(current_cmd_[0]))); + Log(LogLevel::kError, + "SIZE ERROR FOR CMD " + + std::to_string(static_cast(current_cmd_[0])) + + " expected " + std::to_string(current_cmd_.size()) + + " got " + + std::to_string(current_cmd_ptr_ - &(current_cmd_[0]))); } } assert(current_cmd_ptr_ == current_cmd_.data() + current_cmd_.size()); @@ -360,7 +362,7 @@ void ClientSession::Update(int time_advance) { } case SessionCommand::kSetForegroundSceneGraph: { Scene* scene = GetScene(ReadInt32()); - g_game->SetForegroundScene(scene); + g_logic->SetForegroundScene(scene); break; } case SessionCommand::kNodeMessage: { @@ -953,7 +955,7 @@ auto ClientSession::GetCollideModel(int id) const -> CollideModel* { } void ClientSession::Error(const std::string& description) { - Log("ERROR: client session error: " + description); + Log(LogLevel::kError, "Client session error: " + description); End(); } @@ -1063,7 +1065,7 @@ void ClientSession::GetCorrectionMessages( } } -void ClientSession::DumpFullState(GameStream* out) { +void ClientSession::DumpFullState(SceneStream* out) { // Add all scenes. for (auto&& i : scenes()) { if (Scene* sg = i.get()) { diff --git a/src/ballistica/game/session/client_session.h b/src/ballistica/logic/session/client_session.h similarity index 90% rename from src/ballistica/game/session/client_session.h rename to src/ballistica/logic/session/client_session.h index 6d01a8b3..2536848d 100644 --- a/src/ballistica/game/session/client_session.h +++ b/src/ballistica/logic/session/client_session.h @@ -1,14 +1,14 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_GAME_SESSION_CLIENT_SESSION_H_ -#define BALLISTICA_GAME_SESSION_CLIENT_SESSION_H_ +#ifndef BALLISTICA_LOGIC_SESSION_CLIENT_SESSION_H_ +#define BALLISTICA_LOGIC_SESSION_CLIENT_SESSION_H_ #include #include #include -#include "ballistica/game/client_controller_interface.h" -#include "ballistica/game/session/session.h" +#include "ballistica/logic/client_controller_interface.h" +#include "ballistica/logic/session/session.h" namespace ballistica { @@ -85,8 +85,8 @@ class ClientSession : public Session { virtual auto OnReset(bool rewind) -> void; virtual auto FetchMessages() -> void {} virtual void Error(const std::string& description); - void End(); - void DumpFullState(GameStream* out) override; + auto End() -> void; + auto DumpFullState(SceneStream* out) -> void override; /// Reset target base time to equal current. This can be used during command /// buffer underruns to cause playback to pause momentarily instead of @@ -95,8 +95,8 @@ class ClientSession : public Session { auto ResetTargetBaseTime() -> void { target_base_time_ = base_time_; } private: - void ClearSessionObjs(); - void AddCommand(const std::vector& command); + auto ClearSessionObjs() -> void; + auto AddCommand(const std::vector& command) -> void; auto ReadByte() -> uint8_t; auto ReadInt32() -> int32_t; @@ -135,4 +135,4 @@ class ClientSession : public Session { } // namespace ballistica -#endif // BALLISTICA_GAME_SESSION_CLIENT_SESSION_H_ +#endif // BALLISTICA_LOGIC_SESSION_CLIENT_SESSION_H_ diff --git a/src/ballistica/game/session/host_session.cc b/src/ballistica/logic/session/host_session.cc similarity index 90% rename from src/ballistica/game/session/host_session.cc rename to src/ballistica/logic/session/host_session.cc index b3dfa82b..57f4f2be 100644 --- a/src/ballistica/game/session/host_session.cc +++ b/src/ballistica/logic/session/host_session.cc @@ -1,28 +1,28 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/game/session/host_session.h" +#include "ballistica/logic/session/host_session.h" -#include "ballistica/game/game_stream.h" -#include "ballistica/game/host_activity.h" -#include "ballistica/game/player.h" +#include "ballistica/assets/component/data.h" +#include "ballistica/assets/component/model.h" +#include "ballistica/assets/component/sound.h" +#include "ballistica/assets/component/texture.h" #include "ballistica/generic/lambda_runnable.h" #include "ballistica/generic/timer.h" #include "ballistica/graphics/graphics.h" #include "ballistica/input/device/input_device.h" -#include "ballistica/media/component/data.h" -#include "ballistica/media/component/model.h" -#include "ballistica/media/component/sound.h" -#include "ballistica/media/component/texture.h" +#include "ballistica/logic/host_activity.h" +#include "ballistica/logic/player.h" #include "ballistica/python/python.h" #include "ballistica/python/python_command.h" #include "ballistica/python/python_context_call.h" #include "ballistica/python/python_sys.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { HostSession::HostSession(PyObject* session_type_obj) : last_kick_idle_players_decrement_time_(GetRealTime()) { - assert(g_game); + assert(g_logic); assert(InLogicThread()); assert(session_type_obj != nullptr); @@ -32,9 +32,9 @@ HostSession::HostSession(PyObject* session_type_obj) is_main_menu_ = static_cast(strstr(Python::ObjToString(session_type_obj).c_str(), "bastd.mainmenu.MainMenuSession")); - // Log("MAIN MENU? " + std::to_string(is_main_menu())); + // Log(LogLevel::kInfo, "MAIN MENU? " + std::to_string(is_main_menu())); - kick_idle_players_ = g_game->kick_idle_players(); + kick_idle_players_ = g_logic->kick_idle_players(); // Create a timer to step our session scene. step_scene_timer_ = @@ -51,7 +51,7 @@ HostSession::HostSession(PyObject* session_type_obj) do_replay = false; } - output_stream_ = Object::New(this, do_replay); + output_stream_ = Object::New(this, do_replay); // Make a scene for our session-level nodes, etc. scene_ = Object::New(0); @@ -90,7 +90,7 @@ HostSession::HostSession(PyObject* session_type_obj) // Lastly, keep the python layer fed with our latest player count in case // it is updating the master-server with our current/max player counts. - g_game->SetPublicPartyPlayerCount(static_cast(players_.size())); + g_logic->SetPublicPartyPlayerCount(static_cast(players_.size())); } auto HostSession::GetHostSession() -> HostSession* { return this; } @@ -206,27 +206,27 @@ auto HostSession::GetSound(const std::string& name) -> Object::Ref { if (shutting_down_) { throw Exception("can't load assets during session shutdown"); } - return Media::GetMedia(&sounds_, name, scene()); + return Assets::GetAsset(&sounds_, name, scene()); } auto HostSession::GetData(const std::string& name) -> Object::Ref { if (shutting_down_) { throw Exception("can't load assets during session shutdown"); } - return Media::GetMedia(&datas_, name, scene()); + return Assets::GetAsset(&datas_, name, scene()); } auto HostSession::GetTexture(const std::string& name) -> Object::Ref { if (shutting_down_) { throw Exception("can't load assets during session shutdown"); } - return Media::GetMedia(&textures_, name, scene()); + return Assets::GetAsset(&textures_, name, scene()); } auto HostSession::GetModel(const std::string& name) -> Object::Ref { if (shutting_down_) { throw Exception("can't load media during session shutdown"); } - return Media::GetMedia(&models_, name, scene()); + return Assets::GetAsset(&models_, name, scene()); } auto HostSession::GetForegroundContext() -> Context { @@ -242,7 +242,8 @@ void HostSession::RequestPlayer(InputDevice* device) { // Ignore if we have no Python session obj. if (!GetSessionPyObj()) { - Log("Error: HostSession::RequestPlayer() called w/no session_py_obj_."); + Log(LogLevel::kError, + "HostSession::RequestPlayer() called w/no session_py_obj_."); return; } @@ -272,12 +273,12 @@ void HostSession::RequestPlayer(InputDevice* device) { // If he was accepted, update our game roster with the new info. if (accept) { - g_game->UpdateGameRoster(); + g_logic->UpdateGameRoster(); } // Lastly, keep the python layer fed with our latest player count in case it // is updating the master-server with our current/max player counts. - g_game->SetPublicPartyPlayerCount(static_cast(players_.size())); + g_logic->SetPublicPartyPlayerCount(static_cast(players_.size())); } void HostSession::RemovePlayer(Player* player) { @@ -296,11 +297,11 @@ void HostSession::RemovePlayer(Player* player) { } // Update our game roster with the departure. - g_game->UpdateGameRoster(); + g_logic->UpdateGameRoster(); // Lastly, keep the python layer fed with our latest player count in case // it is updating the master-server with our current/max player counts. - g_game->SetPublicPartyPlayerCount(static_cast(players_.size())); + g_logic->SetPublicPartyPlayerCount(static_cast(players_.size())); return; } @@ -325,11 +326,13 @@ void HostSession::IssuePlayerLeft(Player* player) { BA_LOG_PYTHON_TRACE_ONCE("missing player on IssuePlayerLeft"); } } else { - Log("WARNING: HostSession: IssuePlayerLeft caled with no " + Log(LogLevel::kWarning, + "HostSession: IssuePlayerLeft caled with no " "session_py_obj_"); } } catch (const std::exception& e) { - Log(std::string("Error calling on_player_leave(): ") + e.what()); + Log(LogLevel::kError, + std::string("Error calling on_player_leave(): ") + e.what()); } } @@ -347,7 +350,8 @@ void HostSession::SetForegroundHostActivity(HostActivity* a) { assert(InLogicThread()); if (shutting_down_) { - Log("WARNING: SetForegroundHostActivity called during session shutdown; " + Log(LogLevel::kWarning, + "SetForegroundHostActivity called during session shutdown; " "ignoring."); return; } @@ -369,7 +373,7 @@ void HostSession::SetForegroundHostActivity(HostActivity* a) { // Now go through telling each host-activity whether it's foregrounded or not. // FIXME: Dying sessions never get told they're un-foregrounded.. could that // ever be a problem? - bool session_is_foreground = (g_game->GetForegroundSession() != nullptr); + bool session_is_foreground = (g_logic->GetForegroundSession() != nullptr); for (auto&& i : host_activities_) { i->SetIsForeground(session_is_foreground && (i == a)); } @@ -442,7 +446,7 @@ void HostSession::DecrementPlayerTimeOuts(millisecs_t millisecs) { assert(player); if (player->time_out() < millisecs) { std::string kick_str = - g_game->GetResourceString("kickIdlePlayersKickedText"); + g_logic->GetResourceString("kickIdlePlayersKickedText"); Utils::StringReplaceOne(&kick_str, "${NAME}", player->GetName()); ScreenMessage(kick_str); RemovePlayer(player); @@ -450,12 +454,12 @@ void HostSession::DecrementPlayerTimeOuts(millisecs_t millisecs) { } else if (player->time_out() > BA_PLAYER_TIME_OUT_WARN && (player->time_out() - millisecs <= BA_PLAYER_TIME_OUT_WARN)) { std::string kick_str_1 = - g_game->GetResourceString("kickIdlePlayersWarning1Text"); + g_logic->GetResourceString("kickIdlePlayersWarning1Text"); Utils::StringReplaceOne(&kick_str_1, "${NAME}", player->GetName()); Utils::StringReplaceOne(&kick_str_1, "${COUNT}", std::to_string(BA_PLAYER_TIME_OUT_WARN / 1000)); ScreenMessage(kick_str_1); - ScreenMessage(g_game->GetResourceString("kickIdlePlayersWarning2Text")); + ScreenMessage(g_logic->GetResourceString("kickIdlePlayersWarning2Text")); } player->set_time_out(player->time_out() - millisecs); } @@ -499,7 +503,7 @@ void HostSession::Update(int time_advance) { ProcessPlayerTimeOuts(); - GameStream* output_stream = GetGameStream(); + SceneStream* output_stream = GetSceneStream(); // Advance base time by the specified amount, // firing all timers along the way. @@ -603,7 +607,7 @@ HostSession::~HostSession() { if (g_buildconfig.debug_build()) { PruneDeadRefs(&python_calls_); if (python_calls_.size() > 1) { - std::string s = "WARNING: " + std::to_string(python_calls_.size()) + std::string s = std::to_string(python_calls_.size()) + " live PythonContextCalls at shutdown for " + "HostSession" + " (1 call is expected):"; int count = 1; @@ -611,11 +615,12 @@ HostSession::~HostSession() { s += ("\n " + std::to_string(count++) + ": " + i->GetObjectDescription()); } - Log(s); + Log(LogLevel::kWarning, s); } } } catch (const std::exception& e) { - Log("Exception in HostSession destructor: " + std::string(e.what())); + Log(LogLevel::kError, + "Exception in HostSession destructor: " + std::string(e.what())); } } @@ -626,8 +631,9 @@ void HostSession::RegisterCall(PythonContextCall* call) { // If we're shutting down, just kill the call immediately. // (we turn all of our calls to no-ops as we shut down). if (shutting_down_) { - Log("WARNING: adding call to expired session; call will not function: " - + call->GetObjectDescription()); + Log(LogLevel::kWarning, + "Adding call to expired session; call will not function: " + + call->GetObjectDescription()); call->MarkDead(); } } @@ -656,7 +662,7 @@ auto HostSession::GetUnusedPlayerName(Player* p, const std::string& base_name) return name_test; } -void HostSession::DumpFullState(GameStream* out) { +void HostSession::DumpFullState(SceneStream* out) { // Add session-scene. if (scene_.exists()) { scene_->Dump(out); diff --git a/src/ballistica/game/session/host_session.h b/src/ballistica/logic/session/host_session.h similarity index 92% rename from src/ballistica/game/session/host_session.h rename to src/ballistica/logic/session/host_session.h index e594e52d..20729dfa 100644 --- a/src/ballistica/game/session/host_session.h +++ b/src/ballistica/logic/session/host_session.h @@ -1,7 +1,7 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_GAME_SESSION_HOST_SESSION_H_ -#define BALLISTICA_GAME_SESSION_HOST_SESSION_H_ +#ifndef BALLISTICA_LOGIC_SESSION_HOST_SESSION_H_ +#define BALLISTICA_LOGIC_SESSION_HOST_SESSION_H_ #include #include @@ -9,8 +9,8 @@ #include #include "ballistica/core/context.h" -#include "ballistica/game/session/session.h" #include "ballistica/generic/timer_list.h" +#include "ballistica/logic/session/session.h" #include "ballistica/python/python_ref.h" namespace ballistica { @@ -64,11 +64,11 @@ class HostSession : public Session { return scene_.get(); } void RegisterCall(PythonContextCall* call); - auto GetGameStream() const -> GameStream* { return output_stream_.get(); } + auto GetSceneStream() const -> SceneStream* { return output_stream_.get(); } auto is_main_menu() const -> bool { return is_main_menu_; } // fixme remove this - void DumpFullState(GameStream* out) override; + void DumpFullState(SceneStream* out) override; void GetCorrectionMessages(bool blend, std::vector >* messages); auto base_time() const -> millisecs_t { return base_time_; } @@ -98,7 +98,7 @@ class HostSession : public Session { void IssuePlayerLeft(Player* player); bool is_main_menu_; // FIXME: Remove this. - Object::Ref output_stream_; + Object::Ref output_stream_; Timer* step_scene_timer_; millisecs_t base_time_ = 0; TimerList sim_timers_; @@ -128,4 +128,4 @@ class HostSession : public Session { } // namespace ballistica -#endif // BALLISTICA_GAME_SESSION_HOST_SESSION_H_ +#endif // BALLISTICA_LOGIC_SESSION_HOST_SESSION_H_ diff --git a/src/ballistica/game/session/net_client_session.cc b/src/ballistica/logic/session/net_client_session.cc similarity index 91% rename from src/ballistica/game/session/net_client_session.cc rename to src/ballistica/logic/session/net_client_session.cc index 477d1c89..a1f6b7b0 100644 --- a/src/ballistica/game/session/net_client_session.cc +++ b/src/ballistica/logic/session/net_client_session.cc @@ -1,22 +1,23 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/game/session/net_client_session.h" +#include "ballistica/logic/session/net_client_session.h" #include "ballistica/app/app.h" -#include "ballistica/game/connection/connection_to_host.h" +#include "ballistica/assets/assets_server.h" #include "ballistica/graphics/graphics.h" #include "ballistica/graphics/net_graph.h" -#include "ballistica/media/media_server.h" +#include "ballistica/logic/connection/connection_to_host.h" namespace ballistica { NetClientSession::NetClientSession() { // Sanity check: we should only ever be writing one replay at once. if (g_app->replay_open) { - Log("ERROR: g_replay_open true at netclient start; shouldn't happen."); + Log(LogLevel::kError, + "g_replay_open true at netclient start; shouldn't happen."); } - assert(g_media_server); - g_media_server->PushBeginWriteReplayCall(); + assert(g_assets_server); + g_assets_server->PushBeginWriteReplayCall(); writing_replay_ = true; g_app->replay_open = true; } @@ -25,11 +26,12 @@ NetClientSession::~NetClientSession() { if (writing_replay_) { // Sanity check: we should only ever be writing one replay at once. if (!g_app->replay_open) { - Log("ERROR: g_replay_open false at net-client close; shouldn't happen."); + Log(LogLevel::kError, + "g_replay_open false at net-client close; shouldn't happen."); } g_app->replay_open = false; - assert(g_media_server); - g_media_server->PushEndWriteReplayCall(); + assert(g_assets_server); + g_assets_server->PushEndWriteReplayCall(); writing_replay_ = false; } } @@ -192,8 +194,8 @@ void NetClientSession::HandleSessionMessage( ClientSession::HandleSessionMessage(message); if (writing_replay_) { - assert(g_media_server); - g_media_server->PushAddMessageToReplayCall(message); + assert(g_assets_server); + g_assets_server->PushAddMessageToReplayCall(message); } } diff --git a/src/ballistica/game/session/net_client_session.h b/src/ballistica/logic/session/net_client_session.h similarity index 88% rename from src/ballistica/game/session/net_client_session.h rename to src/ballistica/logic/session/net_client_session.h index de232896..c53e9b0f 100644 --- a/src/ballistica/game/session/net_client_session.h +++ b/src/ballistica/logic/session/net_client_session.h @@ -1,11 +1,11 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_GAME_SESSION_NET_CLIENT_SESSION_H_ -#define BALLISTICA_GAME_SESSION_NET_CLIENT_SESSION_H_ +#ifndef BALLISTICA_LOGIC_SESSION_NET_CLIENT_SESSION_H_ +#define BALLISTICA_LOGIC_SESSION_NET_CLIENT_SESSION_H_ #include -#include "ballistica/game/session/client_session.h" +#include "ballistica/logic/session/client_session.h" namespace ballistica { @@ -59,4 +59,4 @@ class NetClientSession : public ClientSession { } // namespace ballistica -#endif // BALLISTICA_GAME_SESSION_NET_CLIENT_SESSION_H_ +#endif // BALLISTICA_LOGIC_SESSION_NET_CLIENT_SESSION_H_ diff --git a/src/ballistica/game/session/replay_client_session.cc b/src/ballistica/logic/session/replay_client_session.cc similarity index 89% rename from src/ballistica/game/session/replay_client_session.cc rename to src/ballistica/logic/session/replay_client_session.cc index f924439f..d422cb4d 100644 --- a/src/ballistica/game/session/replay_client_session.cc +++ b/src/ballistica/logic/session/replay_client_session.cc @@ -1,29 +1,29 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/game/session/replay_client_session.h" +#include "ballistica/logic/session/replay_client_session.h" #include "ballistica/dynamics/material/material.h" -#include "ballistica/game/connection/connection_set.h" -#include "ballistica/game/connection/connection_to_client.h" -#include "ballistica/game/game_stream.h" #include "ballistica/generic/huffman.h" #include "ballistica/generic/utils.h" +#include "ballistica/logic/connection/connection_set.h" +#include "ballistica/logic/connection/connection_to_client.h" #include "ballistica/math/vector3f.h" #include "ballistica/networking/networking.h" #include "ballistica/platform/platform.h" #include "ballistica/scene/scene.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { auto ReplayClientSession::GetActualTimeAdvance(int advance_in) -> int { return static_cast( - round(advance_in * pow(2.0f, g_game->replay_speed_exponent()))); + round(advance_in * pow(2.0f, g_logic->replay_speed_exponent()))); } ReplayClientSession::ReplayClientSession(std::string filename) : file_name_(std::move(filename)) { // take responsibility for feeding all clients to this device.. - g_game->connections()->RegisterClientController(this); + g_logic->connections()->RegisterClientController(this); // go ahead and just do a reset here, which will get things going.. Reset(true); @@ -31,7 +31,7 @@ ReplayClientSession::ReplayClientSession(std::string filename) ReplayClientSession::~ReplayClientSession() { // we no longer are responsible for feeding clients to this device.. - g_game->connections()->UnregisterClientController(this); + g_logic->connections()->UnregisterClientController(this); if (file_) { fclose(file_); @@ -45,14 +45,16 @@ void ReplayClientSession::OnClientConnected(ConnectionToClient* c) { // sanity check - abort if its on either of our lists already for (ConnectionToClient* i : connections_to_clients_) { if (i == c) { - Log("Error: ReplayClientSession::OnClientConnected()" + Log(LogLevel::kError, + "ReplayClientSession::OnClientConnected()" " got duplicate connection"); return; } } for (ConnectionToClient* i : connections_to_clients_ignored_) { if (i == c) { - Log("Error: ReplayClientSession::OnClientConnected()" + Log(LogLevel::kError, + "ReplayClientSession::OnClientConnected()" " got duplicate connection"); return; } @@ -67,7 +69,7 @@ void ReplayClientSession::OnClientConnected(ConnectionToClient* c) { // we create a temporary output stream just for the purpose of building // a giant session-commands message that we can send to the client // to build its state up to where we are currently. - GameStream out(nullptr, false); + SceneStream out(nullptr, false); // go ahead and dump our full state.. DumpFullState(&out); @@ -113,7 +115,8 @@ void ReplayClientSession::OnClientDisconnected(ConnectionToClient* c) { return; } } - Log("Error: ReplayClientSession::OnClientDisconnected()" + Log(LogLevel::kError, + "ReplayClientSession::OnClientDisconnected()" " called for connection not on lists"); } @@ -198,7 +201,7 @@ void ReplayClientSession::FetchMessages() { void ReplayClientSession::Error(const std::string& description) { // Close the replay, announce something went wrong with it, and then do // standard error response.. - ScreenMessage(g_game->GetResourceString("replayReadErrorText"), {1, 0, 0}); + ScreenMessage(g_logic->GetResourceString("replayReadErrorText"), {1, 0, 0}); if (file_) { fclose(file_); file_ = nullptr; @@ -246,7 +249,7 @@ void ReplayClientSession::OnReset(bool rewind) { return; } if (version > kProtocolVersion || version < kProtocolVersionMin) { - ScreenMessage(g_game->GetResourceString("replayVersionErrorText"), + ScreenMessage(g_logic->GetResourceString("replayVersionErrorText"), {1, 0, 0}); End(); return; diff --git a/src/ballistica/game/session/replay_client_session.h b/src/ballistica/logic/session/replay_client_session.h similarity index 79% rename from src/ballistica/game/session/replay_client_session.h rename to src/ballistica/logic/session/replay_client_session.h index 411da3db..5e03044a 100644 --- a/src/ballistica/game/session/replay_client_session.h +++ b/src/ballistica/logic/session/replay_client_session.h @@ -1,13 +1,13 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_GAME_SESSION_REPLAY_CLIENT_SESSION_H_ -#define BALLISTICA_GAME_SESSION_REPLAY_CLIENT_SESSION_H_ +#ifndef BALLISTICA_LOGIC_SESSION_REPLAY_CLIENT_SESSION_H_ +#define BALLISTICA_LOGIC_SESSION_REPLAY_CLIENT_SESSION_H_ #include #include -#include "ballistica/game/client_controller_interface.h" -#include "ballistica/game/session/client_session.h" +#include "ballistica/logic/client_controller_interface.h" +#include "ballistica/logic/session/client_session.h" namespace ballistica { @@ -39,4 +39,4 @@ class ReplayClientSession : public ClientSession, } // namespace ballistica -#endif // BALLISTICA_GAME_SESSION_REPLAY_CLIENT_SESSION_H_ +#endif // BALLISTICA_LOGIC_SESSION_REPLAY_CLIENT_SESSION_H_ diff --git a/src/ballistica/game/session/session.cc b/src/ballistica/logic/session/session.cc similarity index 70% rename from src/ballistica/game/session/session.cc rename to src/ballistica/logic/session/session.cc index 6085642f..a785e291 100644 --- a/src/ballistica/game/session/session.cc +++ b/src/ballistica/logic/session/session.cc @@ -1,9 +1,9 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/game/session/session.h" +#include "ballistica/logic/session/session.h" #include "ballistica/app/app.h" -#include "ballistica/game/game.h" +#include "ballistica/logic/logic.h" namespace ballistica { @@ -11,7 +11,7 @@ Session::Session() { g_app->session_count++; // New sessions immediately become foreground. - g_game->SetForegroundSession(this); + g_logic->SetForegroundSession(this); } Session::~Session() { g_app->session_count--; } @@ -30,8 +30,9 @@ void Session::GraphicsQualityChanged(GraphicsQuality q) {} void Session::DebugSpeedMultChanged() {} -void Session::DumpFullState(GameStream* out) { - Log("Session::DumpFullState() being called; shouldn't happen."); +void Session::DumpFullState(SceneStream* out) { + Log(LogLevel::kError, + "Session::DumpFullState() being called; shouldn't happen."); } } // namespace ballistica diff --git a/src/ballistica/game/session/session.h b/src/ballistica/logic/session/session.h similarity index 86% rename from src/ballistica/game/session/session.h rename to src/ballistica/logic/session/session.h index a6e4f7ca..e212b79f 100644 --- a/src/ballistica/game/session/session.h +++ b/src/ballistica/logic/session/session.h @@ -1,7 +1,7 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_GAME_SESSION_SESSION_H_ -#define BALLISTICA_GAME_SESSION_SESSION_H_ +#ifndef BALLISTICA_LOGIC_SESSION_SESSION_H_ +#define BALLISTICA_LOGIC_SESSION_SESSION_H_ #include "ballistica/core/context.h" #include "ballistica/core/object.h" @@ -33,7 +33,7 @@ class Session : public ContextTarget { virtual void DebugSpeedMultChanged(); auto benchmark_type() const -> BenchmarkType { return benchmark_type_; } void set_benchmark_type(BenchmarkType val) { benchmark_type_ = val; } - virtual void DumpFullState(GameStream* s); + virtual void DumpFullState(SceneStream* s); private: BenchmarkType benchmark_type_ = BenchmarkType::kNone; @@ -41,4 +41,4 @@ class Session : public ContextTarget { } // namespace ballistica -#endif // BALLISTICA_GAME_SESSION_SESSION_H_ +#endif // BALLISTICA_LOGIC_SESSION_SESSION_H_ diff --git a/src/ballistica/game/account.cc b/src/ballistica/logic/v1_account.cc similarity index 70% rename from src/ballistica/game/account.cc rename to src/ballistica/logic/v1_account.cc index da5670c4..399211cc 100644 --- a/src/ballistica/game/account.cc +++ b/src/ballistica/logic/v1_account.cc @@ -1,16 +1,16 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/game/account.h" +#include "ballistica/logic/v1_account.h" #include "ballistica/app/app.h" -#include "ballistica/game/game.h" #include "ballistica/generic/utils.h" #include "ballistica/internal/app_internal.h" +#include "ballistica/logic/logic.h" #include "ballistica/platform/platform.h" namespace ballistica { -auto Account::AccountTypeFromString(const std::string& val) -> V1AccountType { +auto V1Account::AccountTypeFromString(const std::string& val) -> V1AccountType { if (val == "Game Center") { return V1AccountType::kGameCenter; } else if (val == "Game Circle") { @@ -36,7 +36,7 @@ auto Account::AccountTypeFromString(const std::string& val) -> V1AccountType { } } -auto Account::AccountTypeToString(V1AccountType type) -> std::string { +auto V1Account::AccountTypeToString(V1AccountType type) -> std::string { switch (type) { case V1AccountType::kGameCenter: return "Game Center"; @@ -63,60 +63,60 @@ auto Account::AccountTypeToString(V1AccountType type) -> std::string { } } -auto Account::AccountTypeToIconString(V1AccountType type) -> std::string { +auto V1Account::AccountTypeToIconString(V1AccountType type) -> std::string { switch (type) { case V1AccountType::kTest: - return g_game->CharStr(SpecialChar::kTestAccount); + return g_logic->CharStr(SpecialChar::kTestAccount); case V1AccountType::kNvidiaChina: - return g_game->CharStr(SpecialChar::kNvidiaLogo); + return g_logic->CharStr(SpecialChar::kNvidiaLogo); case V1AccountType::kGooglePlay: - return g_game->CharStr(SpecialChar::kGooglePlayGamesLogo); + return g_logic->CharStr(SpecialChar::kGooglePlayGamesLogo); case V1AccountType::kSteam: - return g_game->CharStr(SpecialChar::kSteamLogo); + return g_logic->CharStr(SpecialChar::kSteamLogo); case V1AccountType::kOculus: - return g_game->CharStr(SpecialChar::kOculusLogo); + return g_logic->CharStr(SpecialChar::kOculusLogo); case V1AccountType::kGameCenter: - return g_game->CharStr(SpecialChar::kGameCenterLogo); + return g_logic->CharStr(SpecialChar::kGameCenterLogo); case V1AccountType::kGameCircle: - return g_game->CharStr(SpecialChar::kGameCircleLogo); + return g_logic->CharStr(SpecialChar::kGameCircleLogo); case V1AccountType::kDevice: case V1AccountType::kServer: - return g_game->CharStr(SpecialChar::kLocalAccount); + return g_logic->CharStr(SpecialChar::kLocalAccount); case V1AccountType::kV2: - return g_game->CharStr(SpecialChar::kV2Logo); + return g_logic->CharStr(SpecialChar::kV2Logo); default: return ""; } } -Account::Account() = default; +V1Account::V1Account() = default; -auto Account::GetLoginName() -> std::string { +auto V1Account::GetLoginName() -> std::string { std::scoped_lock lock(mutex_); return login_name_; } -auto Account::GetLoginID() -> std::string { +auto V1Account::GetLoginID() -> std::string { std::scoped_lock lock(mutex_); return login_id_; } -auto Account::GetToken() -> std::string { +auto V1Account::GetToken() -> std::string { std::scoped_lock lock(mutex_); return token_; } -auto Account::GetExtra() -> std::string { +auto V1Account::GetExtra() -> std::string { std::scoped_lock lock(mutex_); return extra_; } -auto Account::GetExtra2() -> std::string { +auto V1Account::GetExtra2() -> std::string { std::scoped_lock lock(mutex_); return extra_2_; } -auto Account::GetLoginState(int* state_num) -> V1LoginState { +auto V1Account::GetLoginState(int* state_num) -> V1LoginState { std::scoped_lock lock(mutex_); if (state_num) { *state_num = login_state_num_; @@ -124,18 +124,18 @@ auto Account::GetLoginState(int* state_num) -> V1LoginState { return login_state_; } -void Account::SetExtra(const std::string& extra) { +void V1Account::SetExtra(const std::string& extra) { std::scoped_lock lock(mutex_); extra_ = extra; } -void Account::SetExtra2(const std::string& extra) { +void V1Account::SetExtra2(const std::string& extra) { std::scoped_lock lock(mutex_); extra_2_ = extra; } -void Account::SetToken(const std::string& account_id, - const std::string& token) { +void V1Account::SetToken(const std::string& account_id, + const std::string& token) { std::scoped_lock lock(mutex_); // Hmm, does this compare logic belong in here? if (login_id_ == account_id) { @@ -143,14 +143,14 @@ void Account::SetToken(const std::string& account_id, } } -void Account::SetLogin(V1AccountType account_type, V1LoginState login_state, - const std::string& login_name, - const std::string& login_id) { +void V1Account::SetLogin(V1AccountType account_type, V1LoginState login_state, + const std::string& login_name, + const std::string& login_id) { bool call_login_did_change = false; { std::scoped_lock lock(mutex_); - // We call out to Python so need to be in game thread. + // We call out to Python so need to be in logic thread. assert(InLogicThread()); if (login_state_ != login_state || g_app->account_type != account_type || login_id_ != login_id || login_name_ != login_name) { @@ -181,7 +181,7 @@ void Account::SetLogin(V1AccountType account_type, V1LoginState login_state, } } -void Account::SetProductsPurchased(const std::vector& products) { +void V1Account::SetProductsPurchased(const std::vector& products) { std::scoped_lock lock(mutex_); std::unordered_map purchases_old = product_purchases_; product_purchases_.clear(); @@ -193,7 +193,7 @@ void Account::SetProductsPurchased(const std::vector& products) { } } -auto Account::GetProductPurchased(const std::string& product) -> bool { +auto V1Account::GetProductPurchased(const std::string& product) -> bool { std::scoped_lock lock(mutex_); auto i = product_purchases_.find(product); if (i == product_purchases_.end()) { diff --git a/src/ballistica/game/account.h b/src/ballistica/logic/v1_account.h similarity index 92% rename from src/ballistica/game/account.h rename to src/ballistica/logic/v1_account.h index 099e2ca2..6b61243b 100644 --- a/src/ballistica/game/account.h +++ b/src/ballistica/logic/v1_account.h @@ -1,7 +1,7 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_GAME_ACCOUNT_H_ -#define BALLISTICA_GAME_ACCOUNT_H_ +#ifndef BALLISTICA_LOGIC_V1_ACCOUNT_H_ +#define BALLISTICA_LOGIC_V1_ACCOUNT_H_ #include #include @@ -13,9 +13,9 @@ namespace ballistica { /// Global account functionality. -class Account { +class V1Account { public: - Account(); + V1Account(); static auto AccountTypeFromString(const std::string& val) -> V1AccountType; static auto AccountTypeToString(V1AccountType type) -> std::string; static auto AccountTypeToIconString(V1AccountType type) -> std::string; @@ -63,4 +63,4 @@ class Account { } // namespace ballistica -#endif // BALLISTICA_GAME_ACCOUNT_H_ +#endif // BALLISTICA_LOGIC_V1_ACCOUNT_H_ diff --git a/src/ballistica/media/media_server.h b/src/ballistica/media/media_server.h deleted file mode 100644 index cff7007e..00000000 --- a/src/ballistica/media/media_server.h +++ /dev/null @@ -1,41 +0,0 @@ -// Released under the MIT License. See LICENSE for details. - -#ifndef BALLISTICA_MEDIA_MEDIA_SERVER_H_ -#define BALLISTICA_MEDIA_MEDIA_SERVER_H_ - -#include -#include - -#include "ballistica/core/object.h" - -namespace ballistica { - -class MediaServer { - public: - explicit MediaServer(Thread* thread); - ~MediaServer(); - void PushBeginWriteReplayCall(); - void PushEndWriteReplayCall(); - void PushAddMessageToReplayCall(const std::vector& data); - auto thread() const -> Thread* { return thread_; } - - private: - void Process(); - void WriteReplayMessages(); - Thread* thread_{}; - FILE* replay_out_file_{}; - size_t replay_bytes_written_{}; - bool writing_replay_{}; - bool replays_broken_{}; - std::list > replay_messages_; - size_t replay_message_bytes_{}; - Timer* process_timer_{}; - std::vector*> pending_preloads_; - std::vector*> pending_preloads_audio_; - friend struct PreloadRunnable; - friend class Media; -}; - -} // namespace ballistica - -#endif // BALLISTICA_MEDIA_MEDIA_SERVER_H_ diff --git a/src/ballistica/networking/network_reader.cc b/src/ballistica/networking/network_reader.cc index 8ca323ff..68097bf2 100644 --- a/src/ballistica/networking/network_reader.cc +++ b/src/ballistica/networking/network_reader.cc @@ -2,23 +2,32 @@ #include "ballistica/networking/network_reader.h" -#include "ballistica/game/connection/connection_set.h" -#include "ballistica/game/game.h" -#include "ballistica/game/player_spec.h" #include "ballistica/generic/json.h" #include "ballistica/input/remote_app.h" +#include "ballistica/logic/connection/connection_set.h" +#include "ballistica/logic/logic.h" +#include "ballistica/logic/player_spec.h" #include "ballistica/math/vector3f.h" -#include "ballistica/networking/network_write_module.h" +#include "ballistica/networking/network_writer.h" #include "ballistica/networking/sockaddr.h" #include "ballistica/platform/platform.h" #include "ballistica/python/python.h" namespace ballistica { -NetworkReader::NetworkReader(int port) : port4_(port), port6_(port) { - thread_ = new std::thread(RunThreadStatic, this); +NetworkReader::NetworkReader() { + // We're a singleton; make sure we don't exist. assert(g_network_reader == nullptr); - g_network_reader = this; +} + +auto NetworkReader::SetPort(int port) -> void { + assert(InMainThread()); + // Currently can't switch once this is set. + if (port4_ != -1) { + return; + } + port4_ = port6_ = port; + thread_ = new std::thread(RunThreadStatic, this); } auto NetworkReader::Pause() -> void { @@ -34,7 +43,7 @@ auto NetworkReader::Pause() -> void { if (port4_ != -1) { PokeSelf(); } else { - Log("Error: NetworkReader port is -1 on pause"); + Log(LogLevel::kError, "NetworkReader port is -1 on pause"); } } @@ -54,8 +63,8 @@ void NetworkReader::Resume() { void NetworkReader::PokeSelf() { int sd = socket(AF_INET, SOCK_DGRAM, 0); if (sd < 0) { - Log("ERROR: unable to create sleep ping socket; errno " - + g_platform->GetSocketErrorString()); + Log(LogLevel::kError, "Unable to create sleep ping socket; errno " + + g_platform->GetSocketErrorString()); } else { struct sockaddr_in serv_addr {}; memset(&serv_addr, 0, sizeof(serv_addr)); @@ -64,8 +73,8 @@ void NetworkReader::PokeSelf() { serv_addr.sin_port = 0; // any int bresult = ::bind(sd, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); if (bresult == 1) { - Log("ERROR: unable to bind sleep socket: " - + g_platform->GetSocketErrorString()); + Log(LogLevel::kError, + "Unable to bind sleep socket: " + g_platform->GetSocketErrorString()); } else { struct sockaddr_in t_addr {}; memset(&t_addr, 0, sizeof(t_addr)); @@ -76,8 +85,8 @@ void NetworkReader::PokeSelf() { ssize_t sresult = sendto(sd, b, 1, 0, (struct sockaddr*)(&t_addr), sizeof(t_addr)); if (sresult == -1) { - Log("Error on sleep self-sendto: " - + g_platform->GetSocketErrorString()); + Log(LogLevel::kError, "Error on sleep self-sendto: " + + g_platform->GetSocketErrorString()); } } g_platform->CloseSocket(sd); @@ -97,8 +106,8 @@ static auto HandleJSONPing(const std::string& data_str) -> std::string { int party_size = 0; int party_size_max = 10; if (g_python != nullptr) { - party_size = g_game->public_party_size(); - party_size_max = g_game->public_party_max_size(); + party_size = g_logic->public_party_size(); + party_size_max = g_logic->public_party_max_size(); } snprintf(buffer, sizeof(buffer), R"({"b":%d,"ps":%d,"psmx":%d})", kAppBuildNumber, party_size, party_size_max); @@ -110,7 +119,7 @@ static auto HandleGameQuery(const char* buffer, size_t size, if (size == 5) { // If we're already in a party, don't advertise since they // wouldn't be able to join us anyway. - if (g_game->connections()->has_connection_to_host()) { + if (g_logic->connections()->has_connection_to_host()) { return; } @@ -119,7 +128,7 @@ static auto HandleGameQuery(const char* buffer, size_t size, memcpy(&query_id, buffer + 1, 4); // Ship them a response packet containing the query id, - // our protocol version, our unique-session-id, and our + // our protocol version, our unique-app-instance-id, and our // player_spec. char msg[400]; @@ -135,7 +144,7 @@ static auto HandleGameQuery(const char* buffer, size_t size, BA_PRECONDITION_FATAL(!usid.empty()); if (usid.size() > 100) { - Log("had to truncate session-id; shouldn't happen"); + Log(LogLevel::kError, "had to truncate session-id; shouldn't happen"); usid.resize(100); } if (usid.empty()) { @@ -158,11 +167,11 @@ static auto HandleGameQuery(const char* buffer, size_t size, std::vector msg_buffer(msg_len); memcpy(msg_buffer.data(), msg, msg_len); - g_network_write_module->PushSendToCall(msg_buffer, SockAddr(*from)); + g_network_writer->PushSendToCall(msg_buffer, SockAddr(*from)); } else { - Log("Error: Got invalid game-query packet of len " + std::to_string(size) - + "; expected 5."); + Log(LogLevel::kError, "Got invalid game-query packet of len " + + std::to_string(size) + "; expected 5."); } } @@ -223,7 +232,8 @@ auto NetworkReader::RunThread() -> int { // Aint no thang. } else { // Let's complain for anything else though. - Log("Error on select: " + g_platform->GetSocketErrorString()); + Log(LogLevel::kError, + "Error on select: " + g_platform->GetSocketErrorString()); } } else { // Wait for any data on either of our sockets. @@ -235,7 +245,8 @@ auto NetworkReader::RunThread() -> int { recvfrom(sd, buffer, sizeof(buffer), 0, reinterpret_cast(&from), &from_size); if (rresult == 0) { - Log("ERROR: NetworkReader Recv got length 0; this shouldn't " + Log(LogLevel::kError, + "NetworkReader Recv got length 0; this shouldn't " "happen"); } else if (rresult == -1) { // This needs to be locked during any sd changes/writes. @@ -345,10 +356,10 @@ auto NetworkReader::RunThread() -> int { case BA_PACKET_CLIENT_GAMEPACKET_COMPRESSED: case BA_PACKET_HOST_GAMEPACKET_COMPRESSED: { // These messages are associated with udp host/client - // connections.. pass them to the game thread to wrangle. + // connections.. pass them to the logic thread to wrangle. std::vector msg_buffer(rresult2); memcpy(&(msg_buffer[0]), buffer, rresult2); - g_game->connections()->PushUDPConnectionPacketCall( + g_logic->connections()->PushUDPConnectionPacketCall( msg_buffer, SockAddr(from)); break; } @@ -386,8 +397,8 @@ auto NetworkReader::OpenSockets() -> void { sd4_ = socket(AF_INET, SOCK_DGRAM, 0); if (sd4_ < 0) { - Log("ERROR: Unable to open host socket; errno " - + g_platform->GetSocketErrorString()); + Log(LogLevel::kError, "Unable to open host socket; errno " + + g_platform->GetSocketErrorString()); } else { g_platform->SetSocketNonBlocking(sd4_); @@ -404,9 +415,8 @@ auto NetworkReader::OpenSockets() -> void { // If we're headless then we abort here; we're useless if we don't get // the port we wanted. if (HeadlessMode()) { - Log("FATAL ERROR: unable to bind to requested udp port " - + std::to_string(port4_) + " (ipv4)"); - exit(1); + FatalError("Unable to bind to requested udp port " + + std::to_string(port4_) + " (ipv4)"); } // Primary ipv4 bind failed; try on any port as a backup. @@ -441,8 +451,8 @@ auto NetworkReader::OpenSockets() -> void { // available everywhere (win XP, etc) so let's do this for now. sd6_ = socket(AF_INET6, SOCK_DGRAM, 0); if (sd6_ < 0) { - Log("ERROR: Unable to open ipv6 socket: " - + g_platform->GetSocketErrorString()); + Log(LogLevel::kError, + "Unable to open ipv6 socket: " + g_platform->GetSocketErrorString()); } else { // Since we're explicitly creating both a v4 and v6 socket, tell the v6 // to *not* do both itself (not sure if this is necessary; on mac it @@ -451,7 +461,7 @@ auto NetworkReader::OpenSockets() -> void { if (setsockopt(sd6_, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast(&on), sizeof(on)) == -1) { - Log("error setting socket as ipv6-only"); + Log(LogLevel::kError, "Error setting socket as ipv6-only"); } g_platform->SetSocketNonBlocking(sd6_); @@ -464,9 +474,8 @@ auto NetworkReader::OpenSockets() -> void { if (result != 0) { if (HeadlessMode()) { - Log("FATAL ERROR: unable to bind to requested udp port " - + std::to_string(port6_) + " (ipv6)"); - exit(1); + FatalError("Unable to bind to requested udp port " + + std::to_string(port6_) + " (ipv6)"); } // Primary ipv6 bind failed; try backup. @@ -499,12 +508,10 @@ auto NetworkReader::OpenSockets() -> void { + std::to_string(initial_requested_port) + "; some network functionality may fail.", {1, 0.5f, 0}); - Log("Unable to bind udp port " + std::to_string(initial_requested_port) - + "; some network functionality may fail.", - true, false); + Log(LogLevel::kWarning, "Unable to bind udp port " + + std::to_string(initial_requested_port) + + "; some network functionality may fail."); } } -NetworkReader::~NetworkReader() = default; - } // namespace ballistica diff --git a/src/ballistica/networking/network_reader.h b/src/ballistica/networking/network_reader.h index a21b1ab6..7d86249b 100644 --- a/src/ballistica/networking/network_reader.h +++ b/src/ballistica/networking/network_reader.h @@ -13,16 +13,16 @@ namespace ballistica { -// This is a special thread that manages the game's main network sockets; -// it handles creating/destroying them as well as listening for incoming +// A subsystem that manages the game's main network sockets. +// It handles creating/destroying them as well as listening for incoming // packets. it is not a normal BA thread so doesn't have the ability to receive // messages (it generally sits blocked in a select() call). Writing to these // sockets takes place in other threads; just make sure to lock the mutex and // ensure the sockets exist before doing the actual write. class NetworkReader { public: - explicit NetworkReader(int port); - ~NetworkReader(); + NetworkReader(); + auto SetPort(int port) -> void; auto Pause() -> void; auto Resume() -> void; auto port4() const { return port4_; } diff --git a/src/ballistica/networking/network_write_module.cc b/src/ballistica/networking/network_writer.cc similarity index 51% rename from src/ballistica/networking/network_write_module.cc rename to src/ballistica/networking/network_writer.cc index 93dbde63..3050e8b5 100644 --- a/src/ballistica/networking/network_write_module.cc +++ b/src/ballistica/networking/network_writer.cc @@ -1,6 +1,6 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/networking/network_write_module.h" +#include "ballistica/networking/network_writer.h" #include "ballistica/core/thread.h" #include "ballistica/networking/networking.h" @@ -8,18 +8,22 @@ namespace ballistica { -NetworkWriteModule::NetworkWriteModule(Thread* thread) : thread_(thread) { - // we're a singleton - assert(g_network_write_module == nullptr); - g_network_write_module = this; +NetworkWriter::NetworkWriter() { + // We're a singleton; make sure we don't already exist. + assert(g_network_writer == nullptr); + + // Spin up our thread. + thread_ = new Thread(ThreadTag::kNetworkWrite); + g_app->pausable_threads.push_back(thread_); } -void NetworkWriteModule::PushSendToCall(const std::vector& msg, - const SockAddr& addr) { +void NetworkWriter::PushSendToCall(const std::vector& msg, + const SockAddr& addr) { // Avoid buffer-full errors if something is causing us to write too often; // these are unreliable messages so its ok to just drop them. if (!thread()->CheckPushSafety()) { - BA_LOG_ONCE("Excessive send-to calls in net-write-module."); + BA_LOG_ONCE(LogLevel::kError, + "Excessive send-to calls in net-write-module."); return; } thread()->PushCall([this, msg, addr] { diff --git a/src/ballistica/networking/network_write_module.h b/src/ballistica/networking/network_writer.h similarity index 54% rename from src/ballistica/networking/network_write_module.h rename to src/ballistica/networking/network_writer.h index 20a01131..a67c336b 100644 --- a/src/ballistica/networking/network_write_module.h +++ b/src/ballistica/networking/network_writer.h @@ -1,7 +1,7 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_NETWORKING_NETWORK_WRITE_MODULE_H_ -#define BALLISTICA_NETWORKING_NETWORK_WRITE_MODULE_H_ +#ifndef BALLISTICA_NETWORKING_NETWORK_WRITER_H_ +#define BALLISTICA_NETWORKING_NETWORK_WRITER_H_ #include @@ -9,11 +9,11 @@ namespace ballistica { -// this thread handles network output and whatnot -class NetworkWriteModule { +// A subsystem handling outbound network traffic. +class NetworkWriter { public: + NetworkWriter(); void PushSendToCall(const std::vector& msg, const SockAddr& addr); - explicit NetworkWriteModule(Thread* thread); auto thread() const -> Thread* { return thread_; } private: @@ -22,4 +22,4 @@ class NetworkWriteModule { } // namespace ballistica -#endif // BALLISTICA_NETWORKING_NETWORK_WRITE_MODULE_H_ +#endif // BALLISTICA_NETWORKING_NETWORK_WRITER_H_ diff --git a/src/ballistica/networking/networking.cc b/src/ballistica/networking/networking.cc index 14304ecd..7af90cf3 100644 --- a/src/ballistica/networking/networking.cc +++ b/src/ballistica/networking/networking.cc @@ -3,7 +3,7 @@ #include "ballistica/networking/networking.h" #include "ballistica/app/app.h" -#include "ballistica/game/player_spec.h" +#include "ballistica/logic/player_spec.h" #include "ballistica/networking/network_reader.h" #include "ballistica/networking/sockaddr.h" #include "ballistica/platform/platform.h" @@ -17,12 +17,7 @@ struct Networking::ScanResultsEntryPriv { millisecs_t last_contact_time{}; }; -Networking::Networking() { - assert(InLogicThread()); - Resume(); -} - -Networking::~Networking() = default; +Networking::Networking() {} // Note: for now we're making our host-scan network calls directly from the game // thread. This is generally not a good idea since it appears that even in @@ -37,14 +32,14 @@ void Networking::HostScanCycle() { scan_socket_ = socket(AF_INET, SOCK_DGRAM, 0); if (scan_socket_ == -1) { - Log("Error opening scan socket: " + g_platform->GetSocketErrorString() - + "."); + Log(LogLevel::kError, "Error opening scan socket: " + + g_platform->GetSocketErrorString() + "."); return; } // Since this guy lives in the game-thread we need it to not block. if (!g_platform->SetSocketNonBlocking(scan_socket_)) { - Log("Error setting socket non-blocking."); + Log(LogLevel::kError, "Error setting socket non-blocking."); g_platform->CloseSocket(scan_socket_); scan_socket_ = -1; return; @@ -59,7 +54,8 @@ void Networking::HostScanCycle() { int result = ::bind(scan_socket_, (struct sockaddr*)&serv_addr, sizeof(serv_addr)); if (result == 1) { - Log("Error binding socket: " + g_platform->GetSocketErrorString() + "."); + Log(LogLevel::kError, + "Error binding socket: " + g_platform->GetSocketErrorString() + "."); g_platform->CloseSocket(scan_socket_); scan_socket_ = -1; return; @@ -71,8 +67,8 @@ void Networking::HostScanCycle() { sizeof(op_val)); if (result != 0) { - Log("Error enabling broadcast for scan-socket: " - + g_platform->GetSocketErrorString() + "."); + Log(LogLevel::kError, "Error enabling broadcast for scan-socket: " + + g_platform->GetSocketErrorString() + "."); g_platform->CloseSocket(scan_socket_); scan_socket_ = -1; return; @@ -104,8 +100,8 @@ void Networking::HostScanCycle() { case ENETUNREACH: break; default: - Log("Error on scanSocket sendto: " - + g_platform->GetSocketErrorString()); + Log(LogLevel::kError, "Error on scanSocket sendto: " + + g_platform->GetSocketErrorString()); } } } @@ -127,7 +123,8 @@ void Networking::HostScanCycle() { case EWOULDBLOCK: break; default: - Log("Error: recvfrom error: " + g_platform->GetSocketErrorString()); + Log(LogLevel::kError, + "Error: recvfrom error: " + g_platform->GetSocketErrorString()); break; } break; @@ -184,10 +181,12 @@ void Networking::HostScanCycle() { PruneScanResults(); } } else { - Log("Error: Got invalid BA_PACKET_GAME_QUERY_RESPONSE packet"); + Log(LogLevel::kError, + "Got invalid BA_PACKET_GAME_QUERY_RESPONSE packet"); } } else { - Log("Error: Got invalid BA_PACKET_GAME_QUERY_RESPONSE packet"); + Log(LogLevel::kError, + "Got invalid BA_PACKET_GAME_QUERY_RESPONSE packet"); } } } @@ -232,7 +231,10 @@ void Networking::EndHostScanning() { } void Networking::Pause() { - if (!running_) Log("Networking::pause() called with running_ already false"); + if (!running_) { + Log(LogLevel::kError, + "Networking::pause() called with running_ already false"); + } running_ = false; // Game is going into background or whatnot. Kill any sockets/etc. @@ -241,7 +243,8 @@ void Networking::Pause() { void Networking::Resume() { if (running_) { - Log("Networking::resume() called with running_ already true"); + Log(LogLevel::kError, + "Networking::resume() called with running_ already true"); } running_ = true; } diff --git a/src/ballistica/networking/networking.h b/src/ballistica/networking/networking.h index 430c6198..6554d5c5 100644 --- a/src/ballistica/networking/networking.h +++ b/src/ballistica/networking/networking.h @@ -112,7 +112,7 @@ namespace ballistica { #define HUFFMAN_TRAINING_MODE 0 #endif -// Bits used by the game thread for network communication. +// Bits used by the logic thread for network communication. class Networking { public: // Send a message to an address. This may block for a brief moment, so it can @@ -120,7 +120,6 @@ class Networking { // will do this there. static void SendTo(const std::vector& buffer, const SockAddr& addr); Networking(); - ~Networking(); // Run a cycle of host scanning (basically sending out a broadcast packet to // see who's out there). @@ -147,7 +146,7 @@ class Networking { std::mutex scan_results_mutex_; uint32_t next_scan_query_id_{}; int scan_socket_{-1}; - bool running_{}; + bool running_{true}; }; } // namespace ballistica diff --git a/src/ballistica/networking/telnet_server.cc b/src/ballistica/networking/telnet_server.cc index 224afaca..7a75d1f9 100644 --- a/src/ballistica/networking/telnet_server.cc +++ b/src/ballistica/networking/telnet_server.cc @@ -5,7 +5,7 @@ #include "ballistica/app/app.h" #include "ballistica/core/context.h" #include "ballistica/core/thread.h" -#include "ballistica/game/game.h" +#include "ballistica/logic/logic.h" #include "ballistica/networking/networking.h" #include "ballistica/networking/networking_sys.h" #include "ballistica/platform/platform.h" @@ -69,7 +69,8 @@ auto TelnetServer::RunThread() -> int { sd_ = socket(AF_INET, SOCK_STREAM, 0); if (sd_ < 0) { - Log("Error: Unable to open host socket; errno " + std::to_string(errno)); + Log(LogLevel::kError, + "Unable to open host socket; errno " + std::to_string(errno)); return 1; } @@ -78,7 +79,7 @@ auto TelnetServer::RunThread() -> int { int status = setsockopt(sd_, SOL_SOCKET, SO_REUSEADDR, (const char*)&on, sizeof(on)); if (-1 == status) { - Log("Error setting SO_REUSEADDR on telnet server"); + Log(LogLevel::kError, "Error setting SO_REUSEADDR on telnet server"); } // Bind to local server port. @@ -106,16 +107,16 @@ auto TelnetServer::RunThread() -> int { } // If we dont have access and havnt asked the user for it yet, ask them. - if (!user_has_granted_access_ && g_game + if (!user_has_granted_access_ && g_logic && !have_asked_user_for_access_) { - g_game->PushAskUserForTelnetAccessCall(); + g_logic->PushAskUserForTelnetAccessCall(); have_asked_user_for_access_ = true; } // Require password for each connection if we have one reading_password_ = require_password_; - if (g_game) { + if (g_logic) { if (reading_password_) { PushPrint(password_prompt); } else { @@ -144,7 +145,7 @@ auto TelnetServer::RunThread() -> int { if (result > 1 && (buffer[result - 2] == '\r')) buffer[result - 2] = 0; } - if (g_game) { + if (g_logic) { if (user_has_granted_access_) { if (reading_password_) { if (GetRealTime() - last_try_time_ < 2000) { @@ -163,7 +164,7 @@ auto TelnetServer::RunThread() -> int { PushTelnetScriptCommand(buffer); } } else { - PushPrint(g_game->GetResourceString("telnetAccessDeniedText")); + PushPrint(g_logic->GetResourceString("telnetAccessDeniedText")); } } } @@ -185,13 +186,13 @@ auto TelnetServer::RunThread() -> int { #pragma clang diagnostic pop void TelnetServer::PushTelnetScriptCommand(const std::string& command) { - assert(g_game); - if (g_game == nullptr) { + assert(g_logic); + if (g_logic == nullptr) { return; } - g_game->thread()->PushCall([this, command] { + g_logic->thread()->PushCall([this, command] { // These are always run in whichever context is 'visible'. - ScopedSetContext cp(g_game->GetForegroundContext()); + ScopedSetContext cp(g_logic->GetForegroundContext()); if (!g_app->user_ran_commands) { g_app->user_ran_commands = true; } @@ -216,12 +217,12 @@ void TelnetServer::PushTelnetScriptCommand(const std::string& command) { } void TelnetServer::PushPrint(const std::string& s) { - assert(g_game); - g_game->thread()->PushCall([this, s] { Print(s); }); + assert(g_logic); + g_logic->thread()->PushCall([this, s] { Print(s); }); } void TelnetServer::Print(const std::string& s) { - // Currently we make the assumption that *only* the game thread writes to our + // Currently we make the assumption that *only* the logic thread writes to our // socket. assert(InLogicThread()); if (client_sd_ != -1) { diff --git a/src/ballistica/platform/apple/platform_apple.h b/src/ballistica/platform/apple/platform_apple.h index 1208489b..f9a31860 100644 --- a/src/ballistica/platform/apple/platform_apple.h +++ b/src/ballistica/platform/apple/platform_apple.h @@ -25,7 +25,8 @@ class PlatformApple : public Platform { auto DoHasTouchScreen() -> bool override; auto GetUIScale() -> UIScale override; auto IsRunningOnDesktop() -> bool override; - auto HandleLog(const std::string& msg) -> void override; + auto DisplayLog(const std::string& name, LogLevel level, + const std::string& msg) -> void override; auto SetupDataDirectory() -> void override; auto GetTextBoundsAndWidth(const std::string& text, Rect* r, float* width) -> void override; @@ -57,8 +58,6 @@ class PlatformApple : public Platform { auto IsOSPlayingMusic() -> bool override; auto SetHardwareCursorVisible(bool visible) -> void override; auto QuitApp() -> void override; - auto GetScoresToBeat(const std::string& level, const std::string& config, - void* py_callback) -> void override; auto OpenFileExternally(const std::string& path) -> void override; auto OpenDirExternally(const std::string& path) -> void override; auto MacMusicAppInit() -> void override; diff --git a/src/ballistica/platform/linux/platform_linux.cc b/src/ballistica/platform/linux/platform_linux.cc index de9f1ed9..58af4402 100644 --- a/src/ballistica/platform/linux/platform_linux.cc +++ b/src/ballistica/platform/linux/platform_linux.cc @@ -63,8 +63,8 @@ void PlatformLinux::OpenFileExternally(const std::string& path) { std::string cmd = std::string("xdg-open \"") + path + "\""; int result = system(cmd.c_str()); if (result != 0) { - Log("Error: Got return value " + std::to_string(result) - + " on xdg-open cmd '" + cmd + "'"); + Log(LogLevel::kError, "Got return value " + std::to_string(result) + + " on xdg-open cmd '" + cmd + "'"); } } @@ -72,8 +72,8 @@ void PlatformLinux::OpenDirExternally(const std::string& path) { std::string cmd = std::string("xdg-open \"") + path + "\""; int result = system(cmd.c_str()); if (result != 0) { - Log("Error: Got return value " + std::to_string(result) - + " on xdg-open cmd '" + cmd + "'"); + Log(LogLevel::kError, "Got return value " + std::to_string(result) + + " on xdg-open cmd '" + cmd + "'"); } } diff --git a/src/ballistica/platform/platform.cc b/src/ballistica/platform/platform.cc index 0a074a7c..56d0c959 100644 --- a/src/ballistica/platform/platform.cc +++ b/src/ballistica/platform/platform.cc @@ -22,18 +22,17 @@ #include "ballistica/app/app_flavor.h" #include "ballistica/core/thread.h" #include "ballistica/dynamics/bg/bg_dynamics_server.h" -#include "ballistica/game/friend_score_set.h" -#include "ballistica/game/game.h" -#include "ballistica/game/score_to_beat.h" #include "ballistica/generic/utils.h" #include "ballistica/graphics/camera.h" #include "ballistica/graphics/graphics.h" #include "ballistica/graphics/mesh/sprite_mesh.h" #include "ballistica/graphics/vr_graphics.h" #include "ballistica/input/input.h" -#include "ballistica/input/std_input_module.h" +#include "ballistica/logic/friend_score_set.h" +#include "ballistica/logic/logic.h" #include "ballistica/networking/networking_sys.h" #include "ballistica/platform/sdl/sdl_app.h" +#include "ballistica/platform/stdio_console.h" #include "ballistica/python/python.h" #if BA_HEADLESS_BUILD @@ -121,7 +120,7 @@ auto Platform::PostInit() -> void { ran_base_post_init_ = true; // Are we running in a terminal? - if (g_buildconfig.use_stdin_thread()) { + if (g_buildconfig.enable_stdio_console()) { is_stdin_a_terminal_ = GetIsStdinATerminal(); } else { is_stdin_a_terminal_ = false; @@ -170,10 +169,12 @@ auto Platform::GetLegacyDeviceUUID() -> const std::string& { legacy_device_uuid_ += val; if (FILE* f2 = FOpen(path.c_str(), "wb")) { size_t result = fwrite(val.c_str(), val.size(), 1, f2); - if (result != 1) Log("unable to write bsuuid file."); + if (result != 1) + Log(LogLevel::kError, "unable to write bsuuid file."); fclose(f2); } else { - Log("unable to open bsuuid file for writing: '" + path + "'"); + Log(LogLevel::kError, + "unable to open bsuuid file for writing: '" + path + "'"); } } } @@ -183,7 +184,7 @@ auto Platform::GetLegacyDeviceUUID() -> const std::string& { } auto Platform::GetDeviceV1AccountUUIDPrefix() -> std::string { - Log("GetDeviceV1AccountUUIDPrefix() unimplemented"); + Log(LogLevel::kError, "GetDeviceV1AccountUUIDPrefix() unimplemented"); return "u"; } @@ -262,10 +263,11 @@ void Platform::SetLowLevelConfigValue(const char* key, int value) { FILE* f = FOpen(path.c_str(), "w"); if (f) { size_t result = fwrite(out.c_str(), out.size(), 1, f); - if (result != 1) Log("unable to write low level config file."); + if (result != 1) + Log(LogLevel::kError, "unable to write low level config file."); fclose(f); } else { - Log("unable to open low level config file for writing."); + Log(LogLevel::kError, "unable to open low level config file for writing."); } } @@ -307,11 +309,10 @@ auto Platform::GetAppPythonDirectory() -> std::string { // Fall back to our default if that doesn't exist. if (FilePathExists(app_python_dir_)) { using_custom_app_python_dir_ = true; - Log("Using custom app Python path: '" - + (GetUserPythonDirectory() + BA_DIRSLASH + "sys" + BA_DIRSLASH - + kAppVersion) - + "'.", - true, false); + Log(LogLevel::kInfo, "Using custom app Python path: '" + + (GetUserPythonDirectory() + BA_DIRSLASH + "sys" + + BA_DIRSLASH + kAppVersion) + + "'."); } else { // Going with relative paths for cleaner tracebacks... @@ -485,7 +486,8 @@ auto Platform::GetLocale() -> std::string { return lang; } else { if (!g_buildconfig.headless_build()) { - BA_LOG_ONCE("No LANG value available; defaulting to en_US"); + BA_LOG_ONCE(LogLevel::kError, + "No LANG value available; defaulting to en_US"); } return "en_US"; } @@ -532,40 +534,6 @@ void Platform::SleepMS(millisecs_t ms) { std::this_thread::sleep_for(std::chrono::milliseconds(ms)); } -// General one-time initialization stuff -static void Init() { - // Sanity check: make sure asserts are stripped out of release builds - // (NDEBUG should do this). -#if !BA_DEBUG_BUILD -#ifndef NDEBUG -#error Expected NDEBUG to be defined for release builds. -#endif // NDEBUG - assert(true); -#endif // !BA_DEBUG_BUILD - - // If we're running in a terminal, print some info. - if (g_platform->is_stdin_a_terminal()) { - if (g_buildconfig.headless_build()) { - printf("BallisticaCore Headless %s build %d.\n", kAppVersion, - kAppBuildNumber); - fflush(stdout); - } else { - printf("BallisticaCore %s build %d.\n", kAppVersion, kAppBuildNumber); - fflush(stdout); - } - } - - g_app->user_agent_string = g_platform->GetUserAgentString(); - - // Figure out where our data is and chdir there. - g_platform->SetupDataDirectory(); - - // Run these just to make sure these dirs exist. - // (otherwise they might not get made if nothing writes to them). - g_platform->GetConfigDirectory(); - g_platform->GetUserPythonDirectory(); -} - #pragma clang diagnostic push #pragma ide diagnostic ignored "NullDereferences" @@ -575,7 +543,7 @@ static void HandleArgs(int argc, char** argv) { // If there's just one arg and it's "--version", return the version. if (argc == 2 && !strcmp(argv[1], "--version")) { - printf("Ballistica %s build %d\n", kAppVersion, kAppBuildNumber); + printf("BallisticaCore %s build %d\n", kAppVersion, kAppBuildNumber); fflush(stdout); exit(0); } @@ -623,7 +591,7 @@ static void HandleArgs(int argc, char** argv) { exit(-1); } } else { - Log("ERROR: expected arg after -cfgdir"); + Log(LogLevel::kError, "Expected arg after -cfgdir."); exit(-1); } } @@ -650,7 +618,6 @@ auto Platform::CreateAppFlavor() -> AppFlavor* { // Hmm do these belong here?... HandleArgs(g_app->argc, g_app->argv); - Init(); // TEMP - need to init sdl on our legacy mac build even though its not // technically an SDL app. Kill this once the old mac build is gone. @@ -658,26 +625,31 @@ auto Platform::CreateAppFlavor() -> AppFlavor* { SDLApp::InitSDL(); #endif + AppFlavor* app_flavor{}; + #if BA_HEADLESS_BUILD - return new AppFlavorHeadless(g_main_thread); + app_flavor = new AppFlavorHeadless(g_main_thread); #elif BA_RIFT_BUILD // Rift build can spin up in either VR or regular mode. if (g_app->vr_mode) { - return new AppFlavorVR(g_main_thread); + app_flavor = new AppFlavorVR(g_main_thread); } else { - return new SDLApp(g_main_thread); + app_flavor = new SDLApp(g_main_thread); } #elif BA_CARDBOARD_BUILD - return new AppFlavorVR(g_main_thread); + app_flavor = new AppFlavorVR(g_main_thread); #elif BA_SDL_BUILD - return new SDLApp(g_main_thread); + app_flavor = new SDLApp(g_main_thread); #else - return new AppFlavor(g_main_thread); + app_flavor = new AppFlavor(g_main_thread); #endif + + assert(app_flavor); + app_flavor->PostInit(); + return app_flavor; } auto Platform::CreateGraphics() -> Graphics* { - assert(InLogicThread()); #if BA_VR_BUILD return new VRGraphics(); #else @@ -696,27 +668,6 @@ auto Platform::GetKeyName(int keycode) -> std::string { #endif } -void Platform::CreateAuxiliaryModules() { -#if !BA_HEADLESS_BUILD - auto* bg_dynamics_thread = new Thread(ThreadIdentifier::kBGDynamics); - g_app->pausable_threads.push_back(bg_dynamics_thread); -#endif -#if !BA_HEADLESS_BUILD - bg_dynamics_thread->PushCallSynchronous( - [bg_dynamics_thread] { new BGDynamicsServer(bg_dynamics_thread); }); -#endif - - if (g_buildconfig.use_stdin_thread()) { - // Start listening for stdin commands (on platforms where that makes sense). - // Note: this thread blocks indefinitely for input so we don't add it to the - // pausable list. - auto* std_input_thread = new Thread(ThreadIdentifier::kStdin); - std_input_thread->PushCallSynchronous( - [std_input_thread] { new StdInputModule(std_input_thread); }); - g_std_input_module->PushBeginReadCall(); - } -} - void Platform::WillExitMain(bool errored) {} auto Platform::GetUIScale() -> UIScale { @@ -724,7 +675,8 @@ auto Platform::GetUIScale() -> UIScale { return UIScale::kLarge; } -void Platform::HandleLog(const std::string& msg) { +void Platform::DisplayLog(const std::string& name, LogLevel level, + const std::string& msg) { // Do nothing by default. } @@ -890,7 +842,7 @@ auto Platform::CreateTextTexture(int width, int height, auto Platform::GetTextTextureData(void* tex) -> uint8_t* { throw Exception(); } -void Platform::OnBootstrapComplete() {} +void Platform::OnAppStart() {} auto Platform::ConvertIncomingLeaderboardScore( const std::string& leaderboard_id, int score) -> int { @@ -900,13 +852,13 @@ auto Platform::ConvertIncomingLeaderboardScore( void Platform::GetFriendScores(const std::string& game, const std::string& game_version, void* data) { // As a default, just fail gracefully. - Log("FIXME: GetFriendScores unimplemented"); - g_game->PushFriendScoreSetCall(FriendScoreSet(false, data)); + Log(LogLevel::kError, "FIXME: GetFriendScores unimplemented"); + g_logic->PushFriendScoreSetCall(FriendScoreSet(false, data)); } void Platform::SubmitScore(const std::string& game, const std::string& version, int64_t score) { - Log("FIXME: SubmitScore() unimplemented"); + Log(LogLevel::kError, "FIXME: SubmitScore() unimplemented"); } void Platform::ReportAchievement(const std::string& achievement) {} @@ -918,13 +870,13 @@ auto Platform::HaveLeaderboard(const std::string& game, void Platform::EditText(const std::string& title, const std::string& value, int max_chars) { - Log("FIXME: EditText() unimplemented"); + Log(LogLevel::kError, "FIXME: EditText() unimplemented"); } void Platform::ShowOnlineScoreUI(const std::string& show, const std::string& game, const std::string& game_version) { - Log("FIXME: ShowOnlineScoreUI() unimplemented"); + Log(LogLevel::kError, "FIXME: ShowOnlineScoreUI() unimplemented"); } void Platform::Purchase(const std::string& item) { @@ -932,7 +884,9 @@ void Platform::Purchase(const std::string& item) { g_python->PushObjCall(Python::ObjID::kUnavailableMessageCall); } -void Platform::RestorePurchases() { Log("RestorePurchases() unimplemented"); } +void Platform::RestorePurchases() { + Log(LogLevel::kError, "RestorePurchases() unimplemented"); +} void Platform::AndroidSetResString(const std::string& res) { throw Exception(); @@ -941,11 +895,11 @@ void Platform::AndroidSetResString(const std::string& res) { void Platform::ApplyConfig() {} void Platform::AndroidSynthesizeBackPress() { - Log("AndroidSynthesizeBackPress() unimplemented"); + Log(LogLevel::kError, "AndroidSynthesizeBackPress() unimplemented"); } void Platform::AndroidQuitActivity() { - Log("AndroidQuitActivity() unimplemented"); + Log(LogLevel::kError, "AndroidQuitActivity() unimplemented"); } auto Platform::GetDeviceV1AccountID() -> std::string { @@ -972,7 +926,8 @@ auto Platform::DemangleCXXSymbol(const std::string& s) -> std::string { abi::__cxa_demangle(s.c_str(), nullptr, nullptr, &demangle_status); if (demangled_name != nullptr) { if (demangle_status != 0) { - BA_LOG_ONCE("__cxa_demangle got buffer but non-zero status; unexpected"); + BA_LOG_ONCE(LogLevel::kError, + "__cxa_demangle got buffer but non-zero status; unexpected"); } std::string retval = demangled_name; free(static_cast(demangled_name)); @@ -990,9 +945,9 @@ auto Platform::NewAutoReleasePool() -> void* { throw Exception(); } void Platform::DrainAutoReleasePool(void* pool) { throw Exception(); } void Platform::OpenURL(const std::string& url) { - // Can't open URLs in VR - just tell the game thread to show the url. + // Can't open URLs in VR - just tell the logic thread to show the url. if (IsVRMode()) { - g_game->PushShowURLCall(url); + g_logic->PushShowURLCall(url); return; } @@ -1001,16 +956,18 @@ void Platform::OpenURL(const std::string& url) { } void Platform::DoOpenURL(const std::string& url) { - Log("DoOpenURL unimplemented on this platform."); + Log(LogLevel::kError, "DoOpenURL unimplemented on this platform."); } -void Platform::ResetAchievements() { Log("ResetAchievements() unimplemented"); } +void Platform::ResetAchievements() { + Log(LogLevel::kError, "ResetAchievements() unimplemented"); +} void Platform::GameCenterLogin() { throw Exception(); } void Platform::PurchaseAck(const std::string& purchase, const std::string& order_id) { - Log("PurchaseAck() unimplemented"); + Log(LogLevel::kError, "PurchaseAck() unimplemented"); } void Platform::RunEvents() {} @@ -1021,17 +978,18 @@ void Platform::OnAppPause() {} void Platform::OnAppResume() {} void Platform::MusicPlayerPlay(PyObject* target) { - Log("MusicPlayerPlay() unimplemented on this platform"); + Log(LogLevel::kError, "MusicPlayerPlay() unimplemented on this platform"); } void Platform::MusicPlayerStop() { - Log("MusicPlayerStop() unimplemented on this platform"); + Log(LogLevel::kError, "MusicPlayerStop() unimplemented on this platform"); } void Platform::MusicPlayerShutdown() { - Log("MusicPlayerShutdown() unimplemented on this platform"); + Log(LogLevel::kError, "MusicPlayerShutdown() unimplemented on this platform"); } void Platform::MusicPlayerSetVolume(float volume) { - Log("MusicPlayerSetVolume() unimplemented on this platform"); + Log(LogLevel::kError, + "MusicPlayerSetVolume() unimplemented on this platform"); } auto Platform::IsOSPlayingMusic() -> bool { return false; } @@ -1039,7 +997,7 @@ auto Platform::IsOSPlayingMusic() -> bool { return false; } void Platform::AndroidShowAppInvite(const std::string& title, const std::string& message, const std::string& code) { - Log("AndroidShowAppInvite() unimplemented"); + Log(LogLevel::kError, "AndroidShowAppInvite() unimplemented"); } void Platform::IncrementAnalyticsCount(const std::string& name, int increment) { @@ -1058,11 +1016,11 @@ void Platform::SubmitAnalyticsCounts() {} void Platform::SetPlatformMiscReadVals(const std::string& vals) {} void Platform::AndroidRefreshFile(const std::string& file) { - Log("AndroidRefreshFile() unimplemented"); + Log(LogLevel::kError, "AndroidRefreshFile() unimplemented"); } void Platform::ShowAd(const std::string& purpose) { - Log("ShowAd() unimplemented"); + Log(LogLevel::kError, "ShowAd() unimplemented"); } auto Platform::GetHasAds() -> bool { return false; } @@ -1073,17 +1031,19 @@ auto Platform::GetHasVideoAds() -> bool { } void Platform::SignInV1(const std::string& account_type) { - Log("SignInV1() unimplemented"); + Log(LogLevel::kError, "SignInV1() unimplemented"); } void Platform::V1LoginDidChange() { // Default is no-op. } -void Platform::SignOutV1() { Log("SignOutV1() unimplemented"); } +void Platform::SignOutV1() { + Log(LogLevel::kError, "SignOutV1() unimplemented"); +} void Platform::AndroidShowWifiSettings() { - Log("AndroidShowWifiSettings() unimplemented"); + Log(LogLevel::kError, "AndroidShowWifiSettings() unimplemented"); } void Platform::SetHardwareCursorVisible(bool visible) { @@ -1093,50 +1053,47 @@ void Platform::SetHardwareCursorVisible(bool visible) { #endif } -void Platform::QuitApp() { exit(g_app->return_value); } +auto Platform::QuitApp() -> void { exit(g_app->return_value); } -void Platform::GetScoresToBeat(const std::string& level, - const std::string& config, void* py_callback) { - // By default, return nothing. - g_game->PushScoresToBeatResponseCall(false, std::list(), - py_callback); +auto Platform::OpenFileExternally(const std::string& path) -> void { + Log(LogLevel::kError, "OpenFileExternally() unimplemented"); } -void Platform::OpenFileExternally(const std::string& path) { - Log("OpenFileExternally() unimplemented"); +auto Platform::OpenDirExternally(const std::string& path) -> void { + Log(LogLevel::kError, "OpenDirExternally() unimplemented"); } -void Platform::OpenDirExternally(const std::string& path) { - Log("OpenDirExternally() unimplemented"); +auto Platform::MacMusicAppInit() -> void { + Log(LogLevel::kError, "MacMusicAppInit() unimplemented"); } -void Platform::MacMusicAppInit() { Log("MacMusicAppInit() unimplemented"); } - auto Platform::MacMusicAppGetVolume() -> int { - Log("MacMusicAppGetVolume() unimplemented"); + Log(LogLevel::kError, "MacMusicAppGetVolume() unimplemented"); return 0; } -void Platform::MacMusicAppSetVolume(int volume) { - Log("MacMusicAppSetVolume() unimplemented"); +auto Platform::MacMusicAppSetVolume(int volume) -> void { + Log(LogLevel::kError, "MacMusicAppSetVolume() unimplemented"); } -void Platform::MacMusicAppGetLibrarySource() { - Log("MacMusicAppGetLibrarySource() unimplemented"); +auto Platform::MacMusicAppGetLibrarySource() -> void { + Log(LogLevel::kError, "MacMusicAppGetLibrarySource() unimplemented"); } -void Platform::MacMusicAppStop() { Log("MacMusicAppStop() unimplemented"); } +auto Platform::MacMusicAppStop() -> void { + Log(LogLevel::kError, "MacMusicAppStop() unimplemented"); +} auto Platform::MacMusicAppPlayPlaylist(const std::string& playlist) -> bool { - Log("MacMusicAppPlayPlaylist() unimplemented"); + Log(LogLevel::kError, "MacMusicAppPlayPlaylist() unimplemented"); return false; } auto Platform::MacMusicAppGetPlaylists() -> std::list { - Log("MacMusicAppGetPlaylists() unimplemented"); + Log(LogLevel::kError, "MacMusicAppGetPlaylists() unimplemented"); return {}; } -void Platform::SetCurrentThreadName(const std::string& name) { +auto Platform::SetCurrentThreadName(const std::string& name) -> void { // Currently we leave the main thread alone, otherwise we show up as // "BallisticaMainThread" under "top" on linux (should check other platforms). if (InMainThread()) { @@ -1149,7 +1106,7 @@ void Platform::SetCurrentThreadName(const std::string& name) { #endif } -void Platform::Unlink(const char* path) { +auto Platform::Unlink(const char* path) -> void { // This default implementation covers non-windows platforms. #if BA_OSTYPE_WINDOWS throw Exception(); @@ -1186,7 +1143,7 @@ auto Platform::IsEventPushMode() -> bool { return false; } auto Platform::GetDisplayResolution(int* x, int* y) -> bool { return false; } -void Platform::CloseSocket(int socket) { +auto Platform::CloseSocket(int socket) -> void { // This default implementation covers non-windows platforms. #if BA_OSTYPE_WINDOWS throw Exception(); @@ -1236,8 +1193,8 @@ auto Platform::SetSocketNonBlocking(int sd) -> bool { #else int result = fcntl(sd, F_SETFL, O_NONBLOCK); if (result != 0) { - Log("Error setting non-blocking socket: " - + g_platform->GetSocketErrorString()); + Log(LogLevel::kError, "Error setting non-blocking socket: " + + g_platform->GetSocketErrorString()); return false; } return true; @@ -1332,10 +1289,10 @@ auto Platform::HavePermission(Permission p) -> bool { #if !BA_OSTYPE_WINDOWS static void HandleSIGINT(int s) { - if (g_game) { - g_game->PushInterruptSignalCall(); + if (g_logic) { + g_logic->PushInterruptSignalCall(); } else { - Log("SigInt handler called before g_game exists."); + Log(LogLevel::kError, "SigInt handler called before g_logic exists."); } } #endif diff --git a/src/ballistica/platform/platform.h b/src/ballistica/platform/platform.h index f1596019..77932c2b 100644 --- a/src/ballistica/platform/platform.h +++ b/src/ballistica/platform/platform.h @@ -54,12 +54,11 @@ class Platform { /// Create the appropriate Graphics subclass for the app. auto CreateGraphics() -> Graphics*; - 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 auto OnBootstrapComplete() -> void; + virtual auto OnAppStart() -> void; // Get/set values before standard game settings are available // (for values needed before SDL init/etc). @@ -127,10 +126,10 @@ class Platform { #pragma mark PRINTING/LOGGING -------------------------------------------------- - // Send a message to the default platform handler. - // IMPORTANT: No Object::Refs should be created or destroyed within this call, - // or deadlock can occur. - virtual auto HandleLog(const std::string& msg) -> void; + /// Display a message to any default log for the platform (android log, etc.) + /// Note that this can be called from any thread. + virtual auto DisplayLog(const std::string& name, LogLevel level, + const std::string& msg) -> void; #pragma mark ENVIRONMENT ------------------------------------------------------- @@ -428,11 +427,10 @@ class Platform { -> void; /// Print a log message to be included in crash logs or other debug - /// mechanisms. Standard log messages (at least with to_server=true) get - /// send here as well. It can be useful to call this directly to report - /// extra details that may help in debugging, as these calls are not - /// considered 'noteworthy' or presented to the user as standard Log() - /// calls are. + /// mechanisms (example: Crashlytics). V1-cloud-log messages get forwarded + /// to here as well. It can be useful to call this directly to report extra + /// details that may help in debugging, as these calls are not considered + /// 'noteworthy' or presented to the user as standard Log() calls are. virtual auto HandleDebugLog(const std::string& msg) -> void; static auto DebugLog(const std::string& msg) -> void { @@ -491,11 +489,6 @@ class Platform { /// Quit the app (can be immediate or via posting some high level event). virtual auto QuitApp() -> void; - // 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 auto OpenFileExternally(const std::string& path) -> void; diff --git a/src/ballistica/platform/sdl/sdl_app.cc b/src/ballistica/platform/sdl/sdl_app.cc index 589723ea..3f28f087 100644 --- a/src/ballistica/platform/sdl/sdl_app.cc +++ b/src/ballistica/platform/sdl/sdl_app.cc @@ -7,11 +7,11 @@ #include "ballistica/app/stress_test.h" #include "ballistica/core/thread.h" #include "ballistica/dynamics/bg/bg_dynamics.h" -#include "ballistica/game/game.h" #include "ballistica/graphics/gl/gl_sys.h" #include "ballistica/graphics/graphics_server.h" #include "ballistica/input/device/joystick.h" #include "ballistica/input/input.h" +#include "ballistica/logic/logic.h" #include "ballistica/platform/platform.h" #include "ballistica/python/python.h" @@ -43,8 +43,8 @@ void SDLApp::HandleSDLEvent(const SDL_Event& event) { g_input->PushJoystickEvent(event, js); } } else { - Log("Error: Unable to get SDL Joystick for event type " - + std::to_string(event.type)); + Log(LogLevel::kError, "Unable to get SDL Joystick for event type " + + std::to_string(event.type)); } break; } @@ -155,7 +155,7 @@ void SDLApp::HandleSDLEvent(const SDL_Event& event) { #endif case SDL_QUIT: - g_game->PushShutdownCall(false); + g_logic->PushShutdownCall(false); break; #if BA_OSTYPE_MACOS && BA_XCODE_BUILD && !BA_HEADLESS_BUILD @@ -245,8 +245,8 @@ auto FilterSDLEvent(const SDL_Event* event) -> int { return true; // sdl should keep this. } } catch (const std::exception& e) { - BA_LOG_ONCE(std::string("Exception in inline SDL-Event handling: ") - + e.what()); + BA_LOG_ONCE(LogLevel::kError, + std::string("Error in inline SDL-Event handling: ") + e.what()); throw; } } @@ -369,7 +369,7 @@ void SDLApp::DoSwap() { if (g_buildconfig.debug_build()) { millisecs_t diff = GetRealTime() - swap_start_time_; if (diff > 5) { - Log("WARNING: Swap handling delay of " + std::to_string(diff)); + Log(LogLevel::kWarning, "Swap handling delay of " + std::to_string(diff)); } } @@ -488,8 +488,8 @@ void SDLApp::SetAutoVSync(bool enable) { } } -void SDLApp::OnBootstrapComplete() { - AppFlavor::OnBootstrapComplete(); +void SDLApp::OnAppStart() { + AppFlavor::OnAppStart(); if (!HeadlessMode() && g_buildconfig.enable_sdl_joysticks()) { // Add initial sdl joysticks. any added/removed after this will be handled @@ -514,14 +514,15 @@ void SDLApp::SDLJoystickConnected(int device_index) { // We add all existing inputs when bootstrapping is complete; we should // never be getting these before that happens. if (g_input == nullptr || g_app_flavor == nullptr || !IsBootstrapped()) { - Log("Unexpected SDLJoystickConnected early in boot sequence."); + Log(LogLevel::kError, + "Unexpected SDLJoystickConnected early in boot sequence."); return; } // Create the joystick here in the main thread and then pass it over to the - // game thread to be added to the game. + // logic thread to be added to the game. if (g_buildconfig.ostype_ios_tvos()) { - BA_LOG_ONCE("WTF GOT SDL-JOY-CONNECTED ON IOS"); + BA_LOG_ONCE(LogLevel::kError, "WTF GOT SDL-JOY-CONNECTED ON IOS"); } else { auto* j = Object::NewDeferred(device_index); if (g_buildconfig.sdl2_build() && g_buildconfig.enable_sdl_joysticks()) { @@ -566,9 +567,9 @@ void SDLApp::RemoveSDLInputDevice(int index) { if (static_cast_check_fit(sdl_joysticks_.size()) > index) { sdl_joysticks_[index] = nullptr; } else { - Log("Error: Invalid index on RemoveSDLInputDevice: size is " - + std::to_string(sdl_joysticks_.size()) + "; index is " - + std::to_string(index)); + Log(LogLevel::kError, "Invalid index on RemoveSDLInputDevice: size is " + + std::to_string(sdl_joysticks_.size()) + + "; index is " + std::to_string(index)); } g_input->PushRemoveInputDeviceCall(j, true); } diff --git a/src/ballistica/platform/sdl/sdl_app.h b/src/ballistica/platform/sdl/sdl_app.h index 9215f266..367061e5 100644 --- a/src/ballistica/platform/sdl/sdl_app.h +++ b/src/ballistica/platform/sdl/sdl_app.h @@ -22,7 +22,7 @@ class SDLApp : public AppFlavor { auto SetAutoVSync(bool enable) -> void; static auto SDLJoystickConnected(int index) -> void; static auto SDLJoystickDisconnected(int index) -> void; - auto OnBootstrapComplete() -> void override; + auto OnAppStart() -> void override; /// Return g_app_flavor as a SDLApp. (assumes it actually is one). static SDLApp* get() { diff --git a/src/ballistica/input/std_input_module.cc b/src/ballistica/platform/stdio_console.cc similarity index 64% rename from src/ballistica/input/std_input_module.cc rename to src/ballistica/platform/stdio_console.cc index 8f662be0..fb1d9196 100644 --- a/src/ballistica/input/std_input_module.cc +++ b/src/ballistica/platform/stdio_console.cc @@ -1,6 +1,6 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/input/std_input_module.h" +#include "ballistica/platform/stdio_console.h" #if BA_OSTYPE_LINUX #include @@ -8,26 +8,31 @@ #include "ballistica/app/app.h" #include "ballistica/core/thread.h" -#include "ballistica/game/game.h" +#include "ballistica/logic/logic.h" #include "ballistica/platform/platform.h" namespace ballistica { -StdInputModule::StdInputModule(Thread* thread) : thread_(thread) { - assert(g_std_input_module == nullptr); - g_std_input_module = this; +StdioConsole::StdioConsole() { + // We're a singleton; make sure we don't already exist. + assert(g_stdio_console == nullptr); + + // Spin up our thread. + thread_ = new Thread(ThreadTag::kAssets); + g_app->pausable_threads.push_back(thread_); } -void StdInputModule::PushBeginReadCall() { +auto StdioConsole::OnAppStart() -> void { + // Tell our thread to start reading. thread()->PushCall([this] { bool stdin_is_terminal = g_platform->is_stdin_a_terminal(); while (true) { // Print a prompt if we're a tty. - // We send this to the game thread so it happens AFTER the + // We send this to the logic thread so it happens AFTER the // results of the last script-command message we may have just sent. if (stdin_is_terminal) { - g_game->thread()->PushCall([] { + g_logic->thread()->PushCall([] { if (!g_app->shutting_down) { printf(">>> "); fflush(stdout); @@ -42,13 +47,30 @@ void StdInputModule::PushBeginReadCall() { char buffer[4096]; char* val = fgets(buffer, sizeof(buffer), stdin); if (val) { + if (val == std::string("@clear\n")) { + int retval{-1}; +#if BA_OSTYPE_MACOS || BA_OSTYPE_LINUX + // Attempt to run actual clear command on unix-y systems to + // plop our prompt back at the top of the screen. + retval = system("clear"); +#endif + // As a fallback, just spit out a bunch of newlines. + if (retval != 0) { + std::string space; + for (int i = 0; i < 100; ++i) { + space += "\n"; + } + printf("%s", space.c_str()); + } + continue; + } pending_input_ += val; if (!pending_input_.empty() && pending_input_[pending_input_.size() - 1] == '\n') { // Get rid of the last newline and ship it to the game. pending_input_.pop_back(); - g_game->PushStdinScriptCommand(pending_input_); + g_logic->PushStdinScriptCommand(pending_input_); pending_input_.clear(); } } else { @@ -72,8 +94,8 @@ void StdInputModule::PushBeginReadCall() { } } } else { - Log("StdInputModule got non-eof error reading stdin: " - + std::to_string(ferror(stdin))); + Log(LogLevel::kError, "StdioConsole got non-eof error reading stdin: " + + std::to_string(ferror(stdin))); } break; } diff --git a/src/ballistica/input/std_input_module.h b/src/ballistica/platform/stdio_console.h similarity index 54% rename from src/ballistica/input/std_input_module.h rename to src/ballistica/platform/stdio_console.h index 7b8e04ee..1ff0f2d6 100644 --- a/src/ballistica/input/std_input_module.h +++ b/src/ballistica/platform/stdio_console.h @@ -1,16 +1,16 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_INPUT_STD_INPUT_MODULE_H_ -#define BALLISTICA_INPUT_STD_INPUT_MODULE_H_ +#ifndef BALLISTICA_PLATFORM_STDIO_CONSOLE_H_ +#define BALLISTICA_PLATFORM_STDIO_CONSOLE_H_ #include "ballistica/ballistica.h" namespace ballistica { -class StdInputModule { +class StdioConsole { public: - explicit StdInputModule(Thread* thread); - void PushBeginReadCall(); + StdioConsole(); + void OnAppStart(); auto thread() const -> Thread* { return thread_; } private: @@ -20,4 +20,4 @@ class StdInputModule { } // namespace ballistica -#endif // BALLISTICA_INPUT_STD_INPUT_MODULE_H_ +#endif // BALLISTICA_PLATFORM_STDIO_CONSOLE_H_ diff --git a/src/ballistica/platform/windows/platform_windows.cc b/src/ballistica/platform/windows/platform_windows.cc index 04fb6233..81376aa1 100644 --- a/src/ballistica/platform/windows/platform_windows.cc +++ b/src/ballistica/platform/windows/platform_windows.cc @@ -32,7 +32,7 @@ #pragma comment(lib, "SDL2main.lib") #endif -#include "ballistica/game/game.h" +#include "ballistica/logic/logic.h" #include "ballistica/networking/networking_sys.h" #include "ballistica/platform/min_sdl.h" @@ -108,10 +108,10 @@ PlatformWindows::PlatformWindows() { BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) { switch (fdwCtrlType) { case CTRL_C_EVENT: - if (g_game) { - g_game->PushInterruptSignalCall(); + if (g_logic) { + g_logic->PushInterruptSignalCall(); } else { - Log("SigInt handler called before g_game exists."); + Log(LogLevel::kError, "SigInt handler called before g_logic exists."); } return TRUE; @@ -123,7 +123,7 @@ BOOL WINAPI CtrlHandler(DWORD fdwCtrlType) { void PlatformWindows::SetupInterruptHandling() { // Set up Ctrl-C handling. if (!SetConsoleCtrlHandler(CtrlHandler, TRUE)) { - Log("Error on SetConsoleCtrlHandler()"); + Log(LogLevel::kError, "Error on SetConsoleCtrlHandler()"); } } @@ -719,10 +719,11 @@ std::string PlatformWindows::DoGetDeviceName() { bool PlatformWindows::DoHasTouchScreen() { return false; } -void PlatformWindows::HandleLog(const std::string& msg) { +void PlatformWindows::DisplayLog(const std::string& name, LogLevel level, + const std::string& msg) { // if (have_stdin_stdout_) { // // On headless builds we use default handler (simple stdout). - // return Platform::HandleLog(msg); + // return Platform::DisplayLog(msg); // } // Also spit this out as a debug-string for when running from msvc. @@ -822,7 +823,8 @@ void PlatformWindows::DoOpenURL(const std::string& url) { // This should return > 32 on success. if (r <= 32) { - Log("Error " + std::to_string(r) + " opening URL '" + url + "'"); + Log(LogLevel::kError, + "Error " + std::to_string(r) + " opening URL '" + url + "'"); } } @@ -831,8 +833,8 @@ void PlatformWindows::OpenFileExternally(const std::string& path) { ShellExecute(nullptr, _T("open"), _T("notepad.exe"), utf8_decode(path).c_str(), nullptr, SW_SHOWNORMAL)); if (r <= 32) { - Log("Error " + std::to_string(r) + " on open_file_externally for '" + path - + "'"); + Log(LogLevel::kError, "Error " + std::to_string(r) + + " on open_file_externally for '" + path + "'"); } } @@ -841,8 +843,8 @@ void PlatformWindows::OpenDirExternally(const std::string& path) { ShellExecute(nullptr, _T("open"), _T("explorer.exe"), utf8_decode(path).c_str(), nullptr, SW_SHOWNORMAL)); if (r <= 32) { - Log("Error " + std::to_string(r) + " on open_dir_externally for '" + path - + "'"); + Log(LogLevel::kError, "Error " + std::to_string(r) + + " on open_dir_externally for '" + path + "'"); } } @@ -873,15 +875,15 @@ std::vector PlatformWindows::GetBroadcastAddrs() { pIPAddrTable = static_cast(MALLOC(dwSize)); } if (pIPAddrTable == nullptr) { - Log("Error: Memory allocation failed for GetIpAddrTable\n"); + Log(LogLevel::kError, "Memory allocation failed for GetIpAddrTable\n"); err = true; } if (!err) { // Make a second call to GetIpAddrTable to get the actual data we want if ((dwRetVal = GetIpAddrTable(pIPAddrTable, &dwSize, 0)) != NO_ERROR) { - Log("Error: GetIpAddrTable failed with error " - + std::to_string(dwRetVal)); + Log(LogLevel::kError, + "GetIpAddrTable failed with error " + std::to_string(dwRetVal)); err = true; } } @@ -916,8 +918,8 @@ bool PlatformWindows::SetSocketNonBlocking(int sd) { unsigned long dataval = 1; // NOLINT (func signature wants long) int result = ioctlsocket(sd, FIONBIO, &dataval); if (result != 0) { - Log("Error setting non-blocking socket: " - + g_platform->GetSocketErrorString()); + Log(LogLevel::kError, "Error setting non-blocking socket: " + + g_platform->GetSocketErrorString()); return false; } return true; diff --git a/src/ballistica/platform/windows/platform_windows.h b/src/ballistica/platform/windows/platform_windows.h index ba7566a5..7857aca8 100644 --- a/src/ballistica/platform/windows/platform_windows.h +++ b/src/ballistica/platform/windows/platform_windows.h @@ -32,7 +32,8 @@ class PlatformWindows : public Platform { auto GetLocale() -> std::string override; auto DoGetDeviceName() -> std::string override; auto DoHasTouchScreen() -> bool override; - void HandleLog(const std::string& msg) override; + void DisplayLog(const std::string& name, LogLevel level, + const std::string& msg) override; void SetupDataDirectory() override; void SetEnv(const std::string& name, const std::string& value) override; auto GetIsStdinATerminal() -> bool override; diff --git a/src/ballistica/python/class/python_class_activity_data.cc b/src/ballistica/python/class/python_class_activity_data.cc index 016a1a8b..14bd3ffc 100644 --- a/src/ballistica/python/class/python_class_activity_data.cc +++ b/src/ballistica/python/class/python_class_activity_data.cc @@ -3,10 +3,10 @@ #include "ballistica/python/class/python_class_activity_data.h" #include "ballistica/core/thread.h" -#include "ballistica/game/game.h" -#include "ballistica/game/host_activity.h" -#include "ballistica/game/session/host_session.h" #include "ballistica/generic/utils.h" +#include "ballistica/logic/host_activity.h" +#include "ballistica/logic/logic.h" +#include "ballistica/logic/session/host_session.h" #include "ballistica/python/python.h" namespace ballistica { @@ -69,7 +69,7 @@ auto PythonClassActivityData::tp_new(PyTypeObject* type, PyObject* args, if (!InLogicThread()) { throw Exception( "ERROR: " + std::string(type_obj.tp_name) - + " objects must only be created in the game thread (current is (" + + " objects must only be created in the logic thread (current is (" + GetCurrentThreadName() + ")."); } self->host_activity_ = new Object::WeakRef(); @@ -81,11 +81,11 @@ auto PythonClassActivityData::tp_new(PyTypeObject* type, PyObject* args, void PythonClassActivityData::tp_dealloc(PythonClassActivityData* self) { BA_PYTHON_TRY; - // These have to be destructed in the game thread; send them along to + // These have to be destructed in the logic thread; send them along to // it if need be; otherwise do it immediately. if (!InLogicThread()) { Object::WeakRef* h = self->host_activity_; - g_game->thread()->PushCall([h] { delete h; }); + g_logic->thread()->PushCall([h] { delete h; }); } else { delete self->host_activity_; } diff --git a/src/ballistica/python/class/python_class_collide_model.cc b/src/ballistica/python/class/python_class_collide_model.cc index 15da88cc..086c3b35 100644 --- a/src/ballistica/python/class/python_class_collide_model.cc +++ b/src/ballistica/python/class/python_class_collide_model.cc @@ -2,9 +2,9 @@ #include "ballistica/python/class/python_class_collide_model.h" +#include "ballistica/assets/component/collide_model.h" #include "ballistica/core/thread.h" -#include "ballistica/game/game.h" -#include "ballistica/media/component/collide_model.h" +#include "ballistica/logic/logic.h" #include "ballistica/python/python.h" namespace ballistica { @@ -71,7 +71,7 @@ auto PythonClassCollideModel::tp_new(PyTypeObject* type, PyObject* args, if (!InLogicThread()) { throw Exception( "ERROR: " + std::string(type_obj.tp_name) - + " objects must only be created in the game thread (current is (" + + " objects must only be created in the logic thread (current is (" + GetCurrentThreadName() + ")."); } @@ -101,11 +101,11 @@ void PythonClassCollideModel::Delete(Object::Ref* ref) { void PythonClassCollideModel::tp_dealloc(PythonClassCollideModel* self) { BA_PYTHON_TRY; - // these have to be deleted in the game thread - send the ptr along if need + // these have to be deleted in the logic thread - send the ptr along if need // be; otherwise do it immediately if (!InLogicThread()) { Object::Ref* c = self->collide_model_; - g_game->thread()->PushCall([c] { Delete(c); }); + g_logic->thread()->PushCall([c] { Delete(c); }); } else { Delete(self->collide_model_); } diff --git a/src/ballistica/python/class/python_class_context.cc b/src/ballistica/python/class/python_class_context.cc index 09338be3..21915540 100644 --- a/src/ballistica/python/class/python_class_context.cc +++ b/src/ballistica/python/class/python_class_context.cc @@ -3,9 +3,9 @@ #include "ballistica/python/class/python_class_context.h" #include "ballistica/core/thread.h" -#include "ballistica/game/game.h" -#include "ballistica/game/host_activity.h" -#include "ballistica/game/session/host_session.h" +#include "ballistica/logic/host_activity.h" +#include "ballistica/logic/logic.h" +#include "ballistica/logic/session/host_session.h" #include "ballistica/python/python.h" #include "ballistica/ui/ui.h" @@ -136,7 +136,7 @@ auto PythonClassContext::tp_new(PyTypeObject* type, PyObject* args, if (!InLogicThread()) { throw Exception( "ERROR: " + std::string(type_obj.tp_name) - + " objects must only be created in the game thread (current is (" + + " objects must only be created in the logic thread (current is (" + GetCurrentThreadName() + ")."); } @@ -145,11 +145,12 @@ auto PythonClassContext::tp_new(PyTypeObject* type, PyObject* args, if (Python::IsPyString(source_obj)) { std::string source = Python::GetPyString(source_obj); if (source == "ui") { - cs = Context(g_game->GetUIContextTarget()); + cs = Context(g_logic->GetUIContextTarget()); } else if (source == "UI") { - BA_LOG_ONCE("'UI' context-target option is deprecated; please use 'ui'"); + BA_LOG_ONCE(LogLevel::kError, + "'UI' context-target option is deprecated; please use 'ui'"); Python::PrintStackTrace(); - cs = Context(g_game->GetUIContextTarget()); + cs = Context(g_logic->GetUIContextTarget()); } else if (source == "current") { cs = Context::current(); } else if (source == "empty") { @@ -183,12 +184,12 @@ auto PythonClassContext::tp_new(PyTypeObject* type, PyObject* args, void PythonClassContext::tp_dealloc(PythonClassContext* self) { BA_PYTHON_TRY; - // Contexts have to be deleted in the game thread; + // Contexts have to be deleted in the logic thread; // ship them to it for deletion if need be; otherwise do it immediately. if (!InLogicThread()) { Context* c = self->context_; Context* c2 = self->context_prev_; - g_game->thread()->PushCall([c, c2] { + g_logic->thread()->PushCall([c, c2] { delete c; delete c2; }); diff --git a/src/ballistica/python/class/python_class_context_call.cc b/src/ballistica/python/class/python_class_context_call.cc index 9e5eac31..01d7c50b 100644 --- a/src/ballistica/python/class/python_class_context_call.cc +++ b/src/ballistica/python/class/python_class_context_call.cc @@ -3,7 +3,7 @@ #include "ballistica/python/class/python_class_context_call.h" #include "ballistica/core/thread.h" -#include "ballistica/game/game.h" +#include "ballistica/logic/logic.h" #include "ballistica/python/python.h" #include "ballistica/python/python_context_call.h" @@ -102,8 +102,8 @@ auto PythonClassContextCall::tp_new(PyTypeObject* type, PyObject* args, if (!PyArg_ParseTuple(args, "O", &source_obj)) return nullptr; if (!InLogicThread()) { throw Exception( - "ERROR: " + std::string(type_obj.tp_name) - + " objects must only be created in the game thread (current is (" + std::string(type_obj.tp_name) + + " objects must only be created in the logic thread (current is (" + GetCurrentThreadName() + ")."); } self->context_call_ = new Object::Ref( @@ -115,11 +115,11 @@ auto PythonClassContextCall::tp_new(PyTypeObject* type, PyObject* args, void PythonClassContextCall::tp_dealloc(PythonClassContextCall* self) { BA_PYTHON_TRY; - // these have to be deleted in the game thread - send the ptr along if need + // these have to be deleted in the logic thread - send the ptr along if need // be; otherwise do it immediately if (!InLogicThread()) { Object::Ref* c = self->context_call_; - g_game->thread()->PushCall([c] { delete c; }); + g_logic->thread()->PushCall([c] { delete c; }); } else { delete self->context_call_; } diff --git a/src/ballistica/python/class/python_class_data.cc b/src/ballistica/python/class/python_class_data.cc index 88791137..eaebd5f9 100644 --- a/src/ballistica/python/class/python_class_data.cc +++ b/src/ballistica/python/class/python_class_data.cc @@ -2,9 +2,9 @@ #include "ballistica/python/class/python_class_data.h" +#include "ballistica/assets/component/data.h" #include "ballistica/core/thread.h" -#include "ballistica/game/game.h" -#include "ballistica/media/component/data.h" +#include "ballistica/logic/logic.h" #include "ballistica/python/python.h" namespace ballistica { @@ -68,7 +68,7 @@ auto PythonClassData::tp_new(PyTypeObject* type, PyObject* args, PyObject* kwds) if (!InLogicThread()) { throw Exception( "ERROR: " + std::string(type_obj.tp_name) - + " objects must only be created in the game thread (current is (" + + " objects must only be created in the logic thread (current is (" + GetCurrentThreadName() + ")."); } @@ -98,11 +98,11 @@ void PythonClassData::Delete(Object::Ref* ref) { void PythonClassData::tp_dealloc(PythonClassData* self) { BA_PYTHON_TRY; - // these have to be deleted in the game thread - send the ptr along if need + // these have to be deleted in the logic thread - send the ptr along if need // be; otherwise do it immediately if (!InLogicThread()) { Object::Ref* s = self->data_; - g_game->thread()->PushCall([s] { Delete(s); }); + g_logic->thread()->PushCall([s] { Delete(s); }); } else { Delete(self->data_); } diff --git a/src/ballistica/python/class/python_class_input_device.cc b/src/ballistica/python/class/python_class_input_device.cc index 38e04607..8f262fe3 100644 --- a/src/ballistica/python/class/python_class_input_device.cc +++ b/src/ballistica/python/class/python_class_input_device.cc @@ -3,8 +3,8 @@ #include "ballistica/python/class/python_class_input_device.h" #include "ballistica/core/thread.h" -#include "ballistica/game/player.h" #include "ballistica/input/device/input_device.h" +#include "ballistica/logic/player.h" #include "ballistica/python/python.h" namespace ballistica { @@ -126,7 +126,7 @@ auto PythonClassInputDevice::tp_new(PyTypeObject* type, PyObject* args, if (!InLogicThread()) { throw Exception( "ERROR: " + std::string(type_obj.tp_name) - + " objects must only be created in the game thread (current is (" + + " objects must only be created in the logic thread (current is (" + GetCurrentThreadName() + ")."); } self->input_device_ = new Object::WeakRef(); @@ -137,13 +137,13 @@ auto PythonClassInputDevice::tp_new(PyTypeObject* type, PyObject* args, void PythonClassInputDevice::tp_dealloc(PythonClassInputDevice* self) { BA_PYTHON_TRY; - // These have to be destructed in the game thread - send them along to it if + // These have to be destructed in the logic thread - send them along to it if // need be. // FIXME: Technically the main thread has a pointer to a dead PyObject // until the delete goes through; could that ever be a problem? if (!InLogicThread()) { Object::WeakRef* d = self->input_device_; - g_game->thread()->PushCall([d] { delete d; }); + g_logic->thread()->PushCall([d] { delete d; }); } else { delete self->input_device_; } @@ -391,7 +391,8 @@ auto PythonClassInputDevice::GetButtonName(PythonClassInputDevice* self, PythonRef results = g_python->obj(Python::ObjID::kLstrFromJsonCall).Call(args2); if (!results.exists()) { - Log("Error creating Lstr from raw button name: '" + bname + "'"); + Log(LogLevel::kError, + "Error creating Lstr from raw button name: '" + bname + "'"); PythonRef args3(Py_BuildValue("(s)", "?"), PythonRef::kSteal); results = g_python->obj(Python::ObjID::kLstrFromJsonCall).Call(args3); } diff --git a/src/ballistica/python/class/python_class_material.cc b/src/ballistica/python/class/python_class_material.cc index 2999279e..a1c6cfe1 100644 --- a/src/ballistica/python/class/python_class_material.cc +++ b/src/ballistica/python/class/python_class_material.cc @@ -15,8 +15,8 @@ #include "ballistica/dynamics/material/roll_sound_material_action.h" #include "ballistica/dynamics/material/skid_sound_material_action.h" #include "ballistica/dynamics/material/sound_material_action.h" -#include "ballistica/game/game.h" -#include "ballistica/game/host_activity.h" +#include "ballistica/logic/host_activity.h" +#include "ballistica/logic/logic.h" #include "ballistica/python/python.h" namespace ballistica { @@ -92,7 +92,7 @@ auto PythonClassMaterial::tp_new(PyTypeObject* type, PyObject* args, if (!InLogicThread()) { throw Exception( "ERROR: " + std::string(type_obj.tp_name) - + " objects must only be created in the game thread (current is (" + + " objects must only be created in the logic thread (current is (" + GetCurrentThreadName() + ")."); } PyObject* name_obj = Py_None; @@ -144,11 +144,11 @@ void PythonClassMaterial::Delete(Object::Ref* m) { void PythonClassMaterial::tp_dealloc(PythonClassMaterial* self) { BA_PYTHON_TRY; - // These have to be deleted in the game thread - push a call if + // These have to be deleted in the logic thread - push a call if // need be.. otherwise do it immediately. if (!InLogicThread()) { Object::Ref* ptr = self->material_; - g_game->thread()->PushCall([ptr] { Delete(ptr); }); + g_logic->thread()->PushCall([ptr] { Delete(ptr); }); } else { Delete(self->material_); } diff --git a/src/ballistica/python/class/python_class_model.cc b/src/ballistica/python/class/python_class_model.cc index 55e2b090..6da13d4e 100644 --- a/src/ballistica/python/class/python_class_model.cc +++ b/src/ballistica/python/class/python_class_model.cc @@ -2,9 +2,9 @@ #include "ballistica/python/class/python_class_model.h" +#include "ballistica/assets/component/model.h" #include "ballistica/core/thread.h" -#include "ballistica/game/game.h" -#include "ballistica/media/component/model.h" +#include "ballistica/logic/logic.h" #include "ballistica/python/python.h" namespace ballistica { @@ -69,7 +69,7 @@ auto PythonClassModel::tp_new(PyTypeObject* type, PyObject* args, if (!InLogicThread()) { throw Exception( "ERROR: " + std::string(type_obj.tp_name) - + " objects must only be created in the game thread (current is (" + + " objects must only be created in the logic thread (current is (" + GetCurrentThreadName() + ")."); } if (!s_create_empty_) { @@ -99,11 +99,11 @@ void PythonClassModel::Delete(Object::Ref* ref) { void PythonClassModel::tp_dealloc(PythonClassModel* self) { BA_PYTHON_TRY; - // these have to be deleted in the game thread - send the ptr along if need + // these have to be deleted in the logic thread - send the ptr along if need // be; otherwise do it immediately if (!InLogicThread()) { Object::Ref* m = self->model_; - g_game->thread()->PushCall([m] { Delete(m); }); + g_logic->thread()->PushCall([m] { Delete(m); }); } else { Delete(self->model_); } diff --git a/src/ballistica/python/class/python_class_node.cc b/src/ballistica/python/class/python_class_node.cc index a9e6b395..e8a31a32 100644 --- a/src/ballistica/python/class/python_class_node.cc +++ b/src/ballistica/python/class/python_class_node.cc @@ -5,9 +5,9 @@ #include #include "ballistica/core/thread.h" -#include "ballistica/game/game_stream.h" #include "ballistica/python/python.h" #include "ballistica/scene/scene.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { @@ -87,7 +87,7 @@ auto PythonClassNode::tp_new(PyTypeObject* type, PyObject* args, if (!InLogicThread()) { throw Exception( "ERROR: " + std::string(type_obj.tp_name) - + " objects must only be created in the game thread (current is (" + + " objects must only be created in the logic thread (current is (" + GetCurrentThreadName() + ")."); } // Clion incorrectly things s_create_empty will always be false. @@ -110,11 +110,11 @@ auto PythonClassNode::tp_new(PyTypeObject* type, PyObject* args, void PythonClassNode::tp_dealloc(PythonClassNode* self) { BA_PYTHON_TRY; - // These have to be deleted in the game thread; send the ptr along if need + // These have to be deleted in the logic thread; send the ptr along if need // be; otherwise do it immediately. if (!InLogicThread()) { Object::WeakRef* n = self->node_; - g_game->thread()->PushCall([n] { delete n; }); + g_logic->thread()->PushCall([n] { delete n; }); } else { delete self->node_; } @@ -277,7 +277,7 @@ auto PythonClassNode::HandleMessage(PythonClassNode* self, PyObject* args) if (user_message_obj) { node->DispatchUserMessage(user_message_obj, "Node User-Message dispatch"); } else { - if (GameStream* output_stream = node->scene()->GetGameStream()) { + if (SceneStream* output_stream = node->scene()->GetSceneStream()) { output_stream->NodeMessage(node, b.data(), b.size()); } node->DispatchNodeMessage(b.data()); @@ -335,7 +335,7 @@ auto PythonClassNode::ConnectAttr(PythonClassNode* self, PyObject* args) dst_node->type()->GetAttribute(std::string(dst_attr_name)); // Push to output_stream first to catch scene mismatch errors. - if (GameStream* output_stream = node->scene()->GetGameStream()) { + if (SceneStream* output_stream = node->scene()->GetSceneStream()) { output_stream->ConnectNodeAttribute(node, src_attr, dst_node, dst_attr); } diff --git a/src/ballistica/python/class/python_class_session_data.cc b/src/ballistica/python/class/python_class_session_data.cc index ec97ae43..1dc4c396 100644 --- a/src/ballistica/python/class/python_class_session_data.cc +++ b/src/ballistica/python/class/python_class_session_data.cc @@ -3,9 +3,9 @@ #include "ballistica/python/class/python_class_session_data.h" #include "ballistica/core/thread.h" -#include "ballistica/game/game.h" -#include "ballistica/game/session/session.h" #include "ballistica/generic/utils.h" +#include "ballistica/logic/logic.h" +#include "ballistica/logic/session/session.h" #include "ballistica/python/python.h" namespace ballistica { @@ -66,7 +66,7 @@ auto PythonClassSessionData::tp_new(PyTypeObject* type, PyObject* args, if (!InLogicThread()) { throw Exception( "ERROR: " + std::string(type_obj.tp_name) - + " objects must only be created in the game thread (current is (" + + " objects must only be created in the logic thread (current is (" + GetCurrentThreadName() + ")."); } self->session_ = new Object::WeakRef(); @@ -77,13 +77,13 @@ auto PythonClassSessionData::tp_new(PyTypeObject* type, PyObject* args, void PythonClassSessionData::tp_dealloc(PythonClassSessionData* self) { BA_PYTHON_TRY; - // These have to be deleted in the game thread; + // These have to be deleted in the logic thread; // ...send the ptr along if need be. // FIXME: technically the main thread has a pointer to a dead PyObject // until the delete goes through; could that ever be a problem? if (!InLogicThread()) { Object::WeakRef* s = self->session_; - g_game->thread()->PushCall([s] { delete s; }); + g_logic->thread()->PushCall([s] { delete s; }); } else { delete self->session_; } diff --git a/src/ballistica/python/class/python_class_session_player.cc b/src/ballistica/python/class/python_class_session_player.cc index 2c3a7ffc..66f6f36d 100644 --- a/src/ballistica/python/class/python_class_session_player.cc +++ b/src/ballistica/python/class/python_class_session_player.cc @@ -3,10 +3,10 @@ #include "ballistica/python/class/python_class_session_player.h" #include "ballistica/core/thread.h" -#include "ballistica/game/host_activity.h" -#include "ballistica/game/player.h" -#include "ballistica/game/session/host_session.h" #include "ballistica/input/device/input_device.h" +#include "ballistica/logic/host_activity.h" +#include "ballistica/logic/player.h" +#include "ballistica/logic/session/host_session.h" #include "ballistica/python/python.h" namespace ballistica { @@ -157,7 +157,7 @@ auto PythonClassSessionPlayer::tp_new(PyTypeObject* type, PyObject* args, if (!InLogicThread()) { throw Exception( "ERROR: " + std::string(type_obj.tp_name) - + " objects must only be created in the game thread (current is (" + + " objects must only be created in the logic thread (current is (" + GetCurrentThreadName() + ")."); } @@ -184,11 +184,11 @@ auto PythonClassSessionPlayer::tp_new(PyTypeObject* type, PyObject* args, void PythonClassSessionPlayer::tp_dealloc(PythonClassSessionPlayer* self) { BA_PYTHON_TRY; - // These have to be deleted in the game thread - send the ptr along if need + // These have to be deleted in the logic thread - send the ptr along if need // be; otherwise do it immediately. if (!InLogicThread()) { Object::WeakRef* p = self->player_; - g_game->thread()->PushCall([p] { delete p; }); + g_logic->thread()->PushCall([p] { delete p; }); } else { delete self->player_; } @@ -258,8 +258,9 @@ auto PythonClassSessionPlayer::tp_getattro(PythonClassSessionPlayer* self, throw Exception(PyExcType::kSessionPlayerNotFound); } if (!p->has_py_data()) { - BA_LOG_ONCE("Error: Calling getAttr for player attr '" + std::string(s) - + "' without data set."); + BA_LOG_ONCE(LogLevel::kError, "Calling getAttr for player attr '" + + std::string(s) + + "' without data set."); } PyObject* obj = p->GetPyCharacter(); Py_INCREF(obj); @@ -270,8 +271,9 @@ auto PythonClassSessionPlayer::tp_getattro(PythonClassSessionPlayer* self, throw Exception(PyExcType::kSessionPlayerNotFound); } if (!p->has_py_data()) { - BA_LOG_ONCE("Error: Calling getAttr for player attr '" + std::string(s) - + "' without data set."); + BA_LOG_ONCE(LogLevel::kError, "Calling getAttr for player attr '" + + std::string(s) + + "' without data set."); } PyObject* obj = p->GetPyColor(); Py_INCREF(obj); @@ -282,8 +284,9 @@ auto PythonClassSessionPlayer::tp_getattro(PythonClassSessionPlayer* self, throw Exception(PyExcType::kSessionPlayerNotFound); } if (!p->has_py_data()) { - BA_LOG_ONCE("Error: Calling getAttr for player attr '" + std::string(s) - + "' without data set."); + BA_LOG_ONCE(LogLevel::kError, "Calling getAttr for player attr '" + + std::string(s) + + "' without data set."); } PyObject* obj = p->GetPyHighlight(); Py_INCREF(obj); @@ -294,8 +297,9 @@ auto PythonClassSessionPlayer::tp_getattro(PythonClassSessionPlayer* self, throw Exception(PyExcType::kSessionPlayerNotFound); } if (!p->has_py_data()) { - BA_LOG_ONCE("Error: Calling getAttr for player attr '" + std::string(s) - + "' without data set."); + BA_LOG_ONCE(LogLevel::kError, "Calling getAttr for player attr '" + + std::string(s) + + "' without data set."); } PyObject* obj = p->GetPyActivityPlayer(); Py_INCREF(obj); diff --git a/src/ballistica/python/class/python_class_sound.cc b/src/ballistica/python/class/python_class_sound.cc index 4186668e..6a76d646 100644 --- a/src/ballistica/python/class/python_class_sound.cc +++ b/src/ballistica/python/class/python_class_sound.cc @@ -2,9 +2,9 @@ #include "ballistica/python/class/python_class_sound.h" +#include "ballistica/assets/component/sound.h" #include "ballistica/core/thread.h" -#include "ballistica/game/game.h" -#include "ballistica/media/component/sound.h" +#include "ballistica/logic/logic.h" #include "ballistica/python/python.h" namespace ballistica { @@ -68,7 +68,7 @@ auto PythonClassSound::tp_new(PyTypeObject* type, PyObject* args, if (!InLogicThread()) { throw Exception( "ERROR: " + std::string(type_obj.tp_name) - + " objects must only be created in the game thread (current is (" + + " objects must only be created in the logic thread (current is (" + GetCurrentThreadName() + ")."); } if (!s_create_empty_) { @@ -98,11 +98,11 @@ void PythonClassSound::Delete(Object::Ref* ref) { void PythonClassSound::tp_dealloc(PythonClassSound* self) { BA_PYTHON_TRY; - // these have to be deleted in the game thread - send the ptr along if need + // these have to be deleted in the logic thread - send the ptr along if need // be; otherwise do it immediately if (!InLogicThread()) { Object::Ref* s = self->sound_; - g_game->thread()->PushCall([s] { Delete(s); }); + g_logic->thread()->PushCall([s] { Delete(s); }); } else { Delete(self->sound_); } diff --git a/src/ballistica/python/class/python_class_texture.cc b/src/ballistica/python/class/python_class_texture.cc index 396440dc..adb4b4b5 100644 --- a/src/ballistica/python/class/python_class_texture.cc +++ b/src/ballistica/python/class/python_class_texture.cc @@ -2,9 +2,9 @@ #include "ballistica/python/class/python_class_texture.h" +#include "ballistica/assets/component/texture.h" #include "ballistica/core/thread.h" -#include "ballistica/game/game.h" -#include "ballistica/media/component/texture.h" +#include "ballistica/logic/logic.h" #include "ballistica/python/python.h" namespace ballistica { @@ -63,7 +63,7 @@ auto PythonClassTexture::tp_new(PyTypeObject* type, PyObject* args, if (!InLogicThread()) { throw Exception( "ERROR: " + std::string(type_obj.tp_name) - + " objects must only be created in the game thread (current is (" + + " objects must only be created in the logic thread (current is (" + GetCurrentThreadName() + ")."); } if (!s_create_empty_) { @@ -91,11 +91,11 @@ void PythonClassTexture::Delete(Object::Ref* ref) { void PythonClassTexture::tp_dealloc(PythonClassTexture* self) { BA_PYTHON_TRY; - // These have to be deleted in the game thread - send the ptr along if need + // These have to be deleted in the logic thread - send the ptr along if need // be; otherwise do it immediately. if (!InLogicThread()) { Object::Ref* t = self->texture_; - g_game->thread()->PushCall([t] { Delete(t); }); + g_logic->thread()->PushCall([t] { Delete(t); }); } else { Delete(self->texture_); } diff --git a/src/ballistica/python/class/python_class_timer.cc b/src/ballistica/python/class/python_class_timer.cc index 8a53687d..1895f793 100644 --- a/src/ballistica/python/class/python_class_timer.cc +++ b/src/ballistica/python/class/python_class_timer.cc @@ -3,7 +3,7 @@ #include "ballistica/python/class/python_class_timer.h" #include "ballistica/core/thread.h" -#include "ballistica/game/game.h" +#include "ballistica/logic/logic.h" #include "ballistica/python/python_context_call_runnable.h" namespace ballistica { @@ -76,7 +76,7 @@ auto PythonClassTimer::tp_new(PyTypeObject* type, PyObject* args, if (!InLogicThread()) { throw Exception( "ERROR: " + std::string(type_obj.tp_name) - + " objects must only be created in the game thread (current is (" + + " objects must only be created in the logic thread (current is (" + GetCurrentThreadName() + ")."); } @@ -161,13 +161,13 @@ void PythonClassTimer::DoDelete(bool have_timer, TimeType time_type, void PythonClassTimer::tp_dealloc(PythonClassTimer* self) { BA_PYTHON_TRY; - // These have to be deleted in the game thread. + // These have to be deleted in the logic thread. if (!InLogicThread()) { auto a0 = self->have_timer_; auto a1 = self->time_type_; auto a2 = self->timer_id_; auto a3 = self->context_; - g_game->thread()->PushCall( + g_logic->thread()->PushCall( [a0, a1, a2, a3] { PythonClassTimer::DoDelete(a0, a1, a2, a3); }); } else { DoDelete(self->have_timer_, self->time_type_, self->timer_id_, diff --git a/src/ballistica/python/class/python_class_widget.cc b/src/ballistica/python/class/python_class_widget.cc index cf8e0a07..684fa4c1 100644 --- a/src/ballistica/python/class/python_class_widget.cc +++ b/src/ballistica/python/class/python_class_widget.cc @@ -3,9 +3,9 @@ #include "ballistica/python/class/python_class_widget.h" #include "ballistica/core/thread.h" -#include "ballistica/game/game.h" #include "ballistica/generic/utils.h" #include "ballistica/graphics/graphics.h" +#include "ballistica/logic/logic.h" #include "ballistica/python/python.h" #include "ballistica/ui/widget/container_widget.h" @@ -78,7 +78,7 @@ auto PythonClassWidget::tp_new(PyTypeObject* type, PyObject* args, if (!InLogicThread()) { throw Exception( "ERROR: " + std::string(type_obj.tp_name) - + " objects must only be created in the game thread (current is (" + + " objects must only be created in the logic thread (current is (" + GetCurrentThreadName() + ")."); } self->widget_ = new Object::WeakRef(); @@ -89,11 +89,11 @@ auto PythonClassWidget::tp_new(PyTypeObject* type, PyObject* args, void PythonClassWidget::tp_dealloc(PythonClassWidget* self) { BA_PYTHON_TRY; - // these have to be destructed in the game thread - send them along to it if + // these have to be destructed in the logic thread - send them along to it if // need be if (!InLogicThread()) { Object::WeakRef* w = self->widget_; - g_game->thread()->PushCall([w] { delete w; }); + g_logic->thread()->PushCall([w] { delete w; }); } else { delete self->widget_; } @@ -222,7 +222,7 @@ auto PythonClassWidget::Delete(PythonClassWidget* self, PyObject* args, if (p) { p->DeleteWidget(w); } else { - Log("Error: Can't delete widget: no parent."); + Log(LogLevel::kError, "Can't delete widget: no parent."); } } Py_RETURN_NONE; diff --git a/src/ballistica/python/methods/python_methods_app.cc b/src/ballistica/python/methods/python_methods_app.cc index 2b2fa725..d2460ea9 100644 --- a/src/ballistica/python/methods/python_methods_app.cc +++ b/src/ballistica/python/methods/python_methods_app.cc @@ -4,19 +4,19 @@ #include "ballistica/app/app.h" #include "ballistica/app/app_flavor.h" +#include "ballistica/assets/component/texture.h" #include "ballistica/core/logging.h" -#include "ballistica/game/connection/connection_set.h" -#include "ballistica/game/game_stream.h" -#include "ballistica/game/host_activity.h" -#include "ballistica/game/session/host_session.h" -#include "ballistica/game/session/replay_client_session.h" #include "ballistica/graphics/graphics.h" -#include "ballistica/media/component/texture.h" +#include "ballistica/logic/connection/connection_set.h" +#include "ballistica/logic/host_activity.h" +#include "ballistica/logic/session/host_session.h" +#include "ballistica/logic/session/replay_client_session.h" #include "ballistica/python/class/python_class_activity_data.h" #include "ballistica/python/class/python_class_session_data.h" #include "ballistica/python/python.h" #include "ballistica/python/python_context_call_runnable.h" #include "ballistica/scene/scene.h" +#include "ballistica/scene/scene_stream.h" #include "ballistica/ui/ui.h" namespace ballistica { @@ -107,7 +107,7 @@ auto PyNewHostSession(PyObject* self, PyObject* args, PyObject* keywds) PyExcType::kValue); } } - g_game->LaunchHostSession(sessiontype_obj, benchmark_type); + g_logic->LaunchHostSession(sessiontype_obj, benchmark_type); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -123,7 +123,7 @@ auto PyNewReplaySession(PyObject* self, PyObject* args, PyObject* keywds) return nullptr; } file_name = Python::GetPyString(file_name_obj); - g_game->LaunchReplaySession(file_name); + g_logic->LaunchReplaySession(file_name); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -137,7 +137,7 @@ auto PyIsInReplay(PyObject* self, PyObject* args, PyObject* keywds) const_cast(kwlist))) { return nullptr; } - if (dynamic_cast(g_game->GetForegroundSession())) { + if (dynamic_cast(g_logic->GetForegroundSession())) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; @@ -145,6 +145,31 @@ auto PyIsInReplay(PyObject* self, PyObject* args, PyObject* keywds) BA_PYTHON_CATCH; } +auto PyAppInstanceUUID(PyObject* self, PyObject* args, PyObject* keywds) + -> PyObject* { + BA_PYTHON_TRY; + static const char* kwlist[] = {nullptr}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "", + const_cast(kwlist))) { + return nullptr; + } + return PyUnicode_FromString(GetAppInstanceUUID().c_str()); + BA_PYTHON_CATCH; +} + +auto PyUserRanCommands(PyObject* self, PyObject* args, PyObject* keywds) + -> PyObject* { + BA_PYTHON_TRY; + static const char* kwlist[] = {nullptr}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "", + const_cast(kwlist))) { + return nullptr; + } + g_app->user_ran_commands = true; + Py_RETURN_NONE; + BA_PYTHON_CATCH; +} + auto PyRegisterSession(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { BA_PYTHON_TRY; @@ -197,9 +222,9 @@ auto PyGetForegroundHostSession(PyObject* self, PyObject* args, return nullptr; } - // Note: we return None if not in the game thread. + // Note: we return None if not in the logic thread. HostSession* s = InLogicThread() - ? g_game->GetForegroundContext().GetHostSession() + ? g_logic->GetForegroundContext().GetHostSession() : nullptr; if (s != nullptr) { PyObject* obj = s->GetSessionPyObj(); @@ -259,7 +284,7 @@ auto PyGetActivity(PyObject* self, PyObject* args, PyObject* keywds) return nullptr; } - // Fail gracefully if called from outside the game thread. + // Fail gracefully if called from outside the logic thread. if (!InLogicThread()) { Py_RETURN_NONE; } @@ -282,17 +307,20 @@ auto PyPushCall(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { PyObject* call_obj; int from_other_thread{}; int suppress_warning{}; + int other_thread_use_fg_context{}; static const char* kwlist[] = {"call", "from_other_thread", - "suppress_other_thread_warning", nullptr}; - if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|ip", + "suppress_other_thread_warning", + "other_thread_use_fg_context", nullptr}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|ppp", const_cast(kwlist), &call_obj, - &from_other_thread, &suppress_warning)) { + &from_other_thread, &suppress_warning, + &other_thread_use_fg_context)) { return nullptr; } // The from-other-thread case is basically a different call. if (from_other_thread) { - // Warn the user not to use this from the game thread since it doesnt + // Warn the user not to use this from the logic thread since it doesnt // save/restore context. if (!suppress_warning && InLogicThread()) { g_python->IssueCallInLogicThreadWarning(call_obj); @@ -301,14 +329,14 @@ auto PyPushCall(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { // This gets called from other python threads so we can't construct // Objects and things here or we'll trip our thread-checks. Instead we // just increment the python object's refcount and pass it along raw; - // the game thread decrements it on the other end. + // the logic thread decrements it on the other end. Py_INCREF(call_obj); - g_game->PushPythonRawCallable(call_obj); + g_logic->PushPythonRawCallable(call_obj, other_thread_use_fg_context); } else { if (!InLogicThread()) { throw Exception("You must use from_other_thread mode."); } - g_game->PushPythonCall(Object::New(call_obj)); + g_logic->PushPythonCall(Object::New(call_obj)); } Py_RETURN_NONE; BA_PYTHON_CATCH; @@ -461,7 +489,7 @@ auto PyScreenMessage(PyObject* self, PyObject* args, PyObject* keywds) return nullptr; } if (log) { - Log(message); + Log(LogLevel::kInfo, message); } // Transient messages get sent to clients as high-level messages instead of @@ -483,11 +511,11 @@ auto PyScreenMessage(PyObject* self, PyObject* args, PyObject* keywds) std::vector client_ids; if (clients_obj != Py_None) { std::vector client_ids2 = Python::GetPyInts(clients_obj); - g_game->connections()->SendScreenMessageToSpecificClients( + g_logic->connections()->SendScreenMessageToSpecificClients( message, color.x, color.y, color.z, client_ids2); } else { - g_game->connections()->SendScreenMessageToAll(message, color.x, color.y, - color.z); + g_logic->connections()->SendScreenMessageToAll(message, color.x, color.y, + color.z); } } else { // Currently specifying client_ids only works for transient messages; we'd @@ -499,8 +527,8 @@ auto PyScreenMessage(PyObject* self, PyObject* args, PyObject* keywds) PyExcType::kValue); } Scene* context_scene = Context::current().GetMutableScene(); - GameStream* output_stream = - context_scene ? context_scene->GetGameStream() : nullptr; + SceneStream* output_stream = + context_scene ? context_scene->GetSceneStream() : nullptr; Texture* texture = nullptr; Texture* tint_texture = nullptr; @@ -552,7 +580,7 @@ auto PyScreenMessage(PyObject* self, PyObject* args, PyObject* keywds) tint_color.x, tint_color.y, tint_color.z, tint2_color.x, tint2_color.y, tint2_color.z); } else { - Log("Error: unhandled screenmessage output_stream case"); + Log(LogLevel::kError, "Unhandled screenmessage output_stream case."); } } @@ -581,7 +609,7 @@ auto PyQuit(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { if (g_buildconfig.ostype_ios_tvos()) { // This should never be called on iOS - Log("Error: Quit called."); + Log(LogLevel::kError, "Quit called."); } bool handled = false; @@ -610,7 +638,7 @@ auto PyQuit(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { } } if (!handled) { - g_game->PushShutdownCall(false); + g_logic->PushShutdownCall(false); } Py_RETURN_NONE; BA_PYTHON_CATCH; @@ -628,9 +656,9 @@ auto PyBless(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { auto PyApplyConfig(PyObject* self, PyObject* args) -> PyObject* { BA_PYTHON_TRY; - // Hmm; python runs in the game thread; technically we could just run + // Hmm; python runs in the logic thread; technically we could just run // ApplyConfig() immediately (though pushing is probably safer). - g_game->PushApplyConfigCall(); + g_logic->PushApplyConfigCall(); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -701,6 +729,7 @@ auto PyCommitConfig(PyObject* self, PyObject* args, PyObject* keywds) auto PyEnv(PyObject* self) -> PyObject* { BA_PYTHON_TRY; + assert(g_app->is_bootstrapped); static PyObject* env_obj = nullptr; @@ -806,42 +835,53 @@ auto PySetStressTesting(PyObject* self, PyObject* args) -> PyObject* { BA_PYTHON_CATCH; } -auto PyPrintStdout(PyObject* self, PyObject* args) -> PyObject* { +auto PyDisplayLog(PyObject* self, PyObject* args, PyObject* keywds) + -> PyObject* { BA_PYTHON_TRY; - const char* s; - if (!PyArg_ParseTuple(args, "s", &s)) { + static const char* kwlist[] = {"name", "level", "message", nullptr}; + const char* name; + const char* levelstr; + const char* message; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "sss", + const_cast(kwlist), &name, &levelstr, + &message)) { return nullptr; } - Logging::PrintStdout(s); + + // Calc LogLevel enum val from their string val. + LogLevel level; + if (levelstr == std::string("DEBUG")) { + level = LogLevel::kDebug; + } else if (levelstr == std::string("INFO")) { + level = LogLevel::kInfo; + } else if (levelstr == std::string("WARNING")) { + level = LogLevel::kWarning; + } else if (levelstr == std::string("ERROR")) { + level = LogLevel::kError; + } else if (levelstr == std::string("CRITICAL")) { + level = LogLevel::kCritical; + } else { + // Assume we should avoid Log() calls here since it could infinite loop. + fprintf(stderr, "Invalid log level to display_log(): %s\n", levelstr); + level = LogLevel::kInfo; + } + Logging::DisplayLog(name, level, message); + Py_RETURN_NONE; BA_PYTHON_CATCH; } -auto PyPrintStderr(PyObject* self, PyObject* args) -> PyObject* { +auto PyV1CloudLog(PyObject* self, PyObject* args, PyObject* keywds) + -> PyObject* { BA_PYTHON_TRY; - const char* s; - if (!PyArg_ParseTuple(args, "s", &s)) { + const char* message; + static const char* kwlist[] = {"message", nullptr}; + if (!PyArg_ParseTupleAndKeywords(args, keywds, "s", + const_cast(kwlist), &message)) { return nullptr; } - Logging::PrintStderr(s); - Py_RETURN_NONE; - BA_PYTHON_CATCH; -} + Logging::V1CloudLog(message); -auto PyLog(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { - BA_PYTHON_TRY; - static const char* kwlist[] = {"message", "to_stdout", "to_server", nullptr}; - int to_server = 1; - int to_stdout = 1; - std::string message; - PyObject* message_obj; - if (!PyArg_ParseTupleAndKeywords(args, keywds, "O|pp", - const_cast(kwlist), &message_obj, - &to_stdout, &to_server)) { - return nullptr; - } - message = Python::GetPyString(message_obj); - Log(message, static_cast(to_stdout), static_cast(to_server)); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -899,39 +939,22 @@ auto PythonMethodsApp::GetMethods() -> std::vector { "(for helping with the transition from milliseconds-based time calls\n" "to seconds-based ones)"}, - {"log", (PyCFunction)PyLog, METH_VARARGS | METH_KEYWORDS, - "log(message: str, to_stdout: bool = True,\n" - " to_server: bool = True) -> None\n" - "\n" - "Category: **General Utility Functions**\n" - "\n" - "Log a message. This goes to the default logging mechanism depending\n" - "on the platform (stdout on mac, android log on android, etc).\n" - "\n" - "Log messages also go to the in-game console unless 'to_console'\n" - "is False. They are also sent to the master-server for use in " - "analyzing\n" - "issues unless to_server is False.\n" - "\n" - "Python's standard print() is wired to call this (with default " - "values)\n" - "so in most cases you can just use that."}, - - {"print_stdout", PyPrintStdout, METH_VARARGS, - "print_stdout(message: str) -> None\n" - "\n" - "(internal)" - "\n" - "Print to system stdout.\n" - "Also forwards to the internal console, etc."}, - - {"print_stderr", PyPrintStderr, METH_VARARGS, - "print_stderr(message: str) -> None\n" + {"display_log", (PyCFunction)PyDisplayLog, METH_VARARGS | METH_KEYWORDS, + "display_log(name: str, level: str, message: str) -> None\n" "\n" "(internal)\n" "\n" - "Print to system stderr.\n" - "Also forwards to the internal console, etc."}, + "Sends a log message to the in-game console and any per-platform\n" + "log destinations (Android log, etc.). This generally is not called\n" + "directly and should instead be fed Python logging output."}, + + {"v1_cloud_log", (PyCFunction)PyV1CloudLog, + METH_VARARGS | METH_KEYWORDS, + "v1_cloud_log(message: str) -> None\n" + "\n" + "(internal)\n" + "\n" + "Push messages to the old v1 cloud log."}, {"set_stress_testing", PySetStressTesting, METH_VARARGS, "set_stress_testing(testing: bool, player_count: int) -> None\n" @@ -993,14 +1016,12 @@ auto PythonMethodsApp::GetMethods() -> std::vector { "Category: **General Utility Functions**\n" "\n" "If 'top' is True, the message will go to the top message area.\n" - "For 'top' messages, 'image' can be a texture to display alongside " - "the\n" - "message.\n" - "If 'log' is True, the message will also be printed to the output " - "log\n" - "'clients' can be a list of client-ids the message should be sent " - "to,\n" - "or None to specify that everyone should receive it.\n" + "For 'top' messages, 'image' must be a dict containing 'texture'\n" + "and 'tint_texture' textures and 'tint_color' and 'tint2_color'\n" + "colors. This defines an icon to display alongside the message.\n" + "If 'log' is True, the message will also be submitted to the log.\n" + "'clients' can be a list of client-ids the message should be sent\n" + "to, or None to specify that everyone should receive it.\n" "If 'transient' is True, the message will not be included in the\n" "game-stream and thus will not show up when viewing replays.\n" "Currently the 'clients' option only works for transient messages."}, @@ -1110,7 +1131,8 @@ auto PythonMethodsApp::GetMethods() -> std::vector { {"pushcall", (PyCFunction)PyPushCall, METH_VARARGS | METH_KEYWORDS, "pushcall(call: Callable, from_other_thread: bool = False,\n" - " suppress_other_thread_warning: bool = False ) -> None\n" + " suppress_other_thread_warning: bool = False,\n" + " other_thread_use_fg_context: bool = False) -> None\n" "\n" "Pushes a call onto the event loop to be run during the next cycle.\n" "\n" @@ -1119,13 +1141,15 @@ auto PythonMethodsApp::GetMethods() -> std::vector { "This can be handy for calls that are disallowed from within other\n" "callbacks, etc.\n" "\n" - "This call expects to be used in the game thread, and will " + "This call expects to be used in the logic thread, and will " "automatically\n" "save and restore the ba.Context to behave seamlessly.\n" "\n" - "If you want to push a call from outside of the game thread,\n" + "If you want to push a call from outside of the logic thread,\n" "however, you can pass 'from_other_thread' as True. In this case\n" - "the call will always run in the UI context on the game thread."}, + "the call will always run in the UI context on the logic thread\n" + "or whichever context is in the foreground if\n" + "other_thread_use_fg_context is True."}, {"getactivity", (PyCFunction)PyGetActivity, METH_VARARGS | METH_KEYWORDS, @@ -1182,6 +1206,18 @@ auto PythonMethodsApp::GetMethods() -> std::vector { "\n" "(internal)"}, + {"app_instance_uuid", (PyCFunction)PyAppInstanceUUID, + METH_VARARGS | METH_KEYWORDS, + "app_instance_uuid() -> str\n" + "\n" + "(internal)"}, + + {"user_ran_commands", (PyCFunction)PyUserRanCommands, + METH_VARARGS | METH_KEYWORDS, + "user_ran_commands() -> None\n" + "\n" + "(internal)"}, + {"new_replay_session", (PyCFunction)PyNewReplaySession, METH_VARARGS | METH_KEYWORDS, "new_replay_session(file_name: str) -> None\n" diff --git a/src/ballistica/python/methods/python_methods_media.cc b/src/ballistica/python/methods/python_methods_assets.cc similarity index 98% rename from src/ballistica/python/methods/python_methods_media.cc rename to src/ballistica/python/methods/python_methods_assets.cc index 98d73a24..0eb9ba88 100644 --- a/src/ballistica/python/methods/python_methods_media.cc +++ b/src/ballistica/python/methods/python_methods_assets.cc @@ -1,19 +1,19 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/python/methods/python_methods_media.h" +#include "ballistica/python/methods/python_methods_assets.h" #include #if 0 // Cpplint errs w/o this, CLion errs with it. Hard to please everybody. #include #endif -#include "ballistica/game/host_activity.h" +#include "ballistica/assets/component/collide_model.h" +#include "ballistica/assets/component/data.h" +#include "ballistica/assets/component/model.h" +#include "ballistica/assets/component/sound.h" +#include "ballistica/assets/component/texture.h" #include "ballistica/graphics/graphics_server.h" -#include "ballistica/media/component/collide_model.h" -#include "ballistica/media/component/data.h" -#include "ballistica/media/component/model.h" -#include "ballistica/media/component/sound.h" -#include "ballistica/media/component/texture.h" +#include "ballistica/logic/host_activity.h" #include "ballistica/python/python.h" #include "ballistica/python/python_sys.h" #include "ballistica/ui/ui.h" diff --git a/src/ballistica/python/methods/python_methods_media.h b/src/ballistica/python/methods/python_methods_assets.h similarity index 64% rename from src/ballistica/python/methods/python_methods_media.h rename to src/ballistica/python/methods/python_methods_assets.h index abf6713b..eb91a052 100644 --- a/src/ballistica/python/methods/python_methods_media.h +++ b/src/ballistica/python/methods/python_methods_assets.h @@ -1,7 +1,7 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_PYTHON_METHODS_PYTHON_METHODS_MEDIA_H_ -#define BALLISTICA_PYTHON_METHODS_PYTHON_METHODS_MEDIA_H_ +#ifndef BALLISTICA_PYTHON_METHODS_PYTHON_METHODS_ASSETS_H_ +#define BALLISTICA_PYTHON_METHODS_PYTHON_METHODS_ASSETS_H_ #include @@ -17,4 +17,4 @@ class PythonMethodsMedia { } // namespace ballistica -#endif // BALLISTICA_PYTHON_METHODS_PYTHON_METHODS_MEDIA_H_ +#endif // BALLISTICA_PYTHON_METHODS_PYTHON_METHODS_ASSETS_H_ diff --git a/src/ballistica/python/methods/python_methods_gameplay.cc b/src/ballistica/python/methods/python_methods_gameplay.cc index e1e9b397..46334d17 100644 --- a/src/ballistica/python/methods/python_methods_gameplay.cc +++ b/src/ballistica/python/methods/python_methods_gameplay.cc @@ -5,19 +5,18 @@ #include #include "ballistica/app/app_flavor.h" +#include "ballistica/assets/component/sound.h" #include "ballistica/dynamics/bg/bg_dynamics.h" #include "ballistica/dynamics/collision.h" #include "ballistica/dynamics/dynamics.h" #include "ballistica/dynamics/material/material_action.h" -#include "ballistica/game/connection/connection_set.h" -#include "ballistica/game/connection/connection_to_client.h" -#include "ballistica/game/game_stream.h" -#include "ballistica/game/host_activity.h" #include "ballistica/generic/json.h" #include "ballistica/graphics/graphics.h" #include "ballistica/input/device/input_device.h" #include "ballistica/internal/app_internal.h" -#include "ballistica/media/component/sound.h" +#include "ballistica/logic/connection/connection_set.h" +#include "ballistica/logic/connection/connection_to_client.h" +#include "ballistica/logic/host_activity.h" #include "ballistica/platform/platform.h" #include "ballistica/python/python.h" #include "ballistica/python/python_context_call_runnable.h" @@ -25,6 +24,7 @@ #include "ballistica/scene/node/node.h" #include "ballistica/scene/node/node_type.h" #include "ballistica/scene/scene.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { @@ -45,7 +45,7 @@ auto PyNewNode(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { auto PyPrintNodes(PyObject* self, PyObject* args) -> PyObject* { BA_PYTHON_TRY; HostActivity* host_activity = - g_game->GetForegroundContext().GetHostActivity(); + g_logic->GetForegroundContext().GetHostActivity(); if (!host_activity) { throw Exception(PyExcType::kContext); } @@ -57,7 +57,7 @@ auto PyPrintNodes(PyObject* self, PyObject* args) -> PyObject* { snprintf(buffer, sizeof(buffer), "#%d: type: %-14s desc: %s", count, i->type()->name().c_str(), i->label().c_str()); s += buffer; - Log(buffer); + Log(LogLevel::kInfo, buffer); count++; } Py_RETURN_NONE; @@ -343,7 +343,7 @@ auto PyEmitFx(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { e.spread = spread; e.chunk_type = chunk_type; e.tendril_type = tendril_type; - if (GameStream* output_stream = scene->GetGameStream()) { + if (SceneStream* output_stream = scene->GetSceneStream()) { output_stream->EmitBGDynamics(e); } #if !BA_HEADLESS_BUILD @@ -383,9 +383,9 @@ auto PyGetForegroundHostActivity(PyObject* self, PyObject* args, return nullptr; } - // Note: we return None if not in the game thread. + // Note: we return None if not in the logic thread. HostActivity* h = InLogicThread() - ? g_game->GetForegroundContext().GetHostActivity() + ? g_logic->GetForegroundContext().GetHostActivity() : nullptr; if (h != nullptr) { PyObject* obj = h->GetPyActivity(); @@ -406,7 +406,7 @@ auto PyGetGameRoster(PyObject* self, PyObject* args, PyObject* keywds) return nullptr; } PythonRef py_client_list(PyList_New(0), PythonRef::kSteal); - cJSON* party = g_game->game_roster(); + cJSON* party = g_logic->game_roster(); assert(party); int len = cJSON_GetArraySize(party); for (int i = 0; i < len; i++) { @@ -460,8 +460,8 @@ auto PyGetGameRoster(PyObject* self, PyObject* args, PyObject* keywds) account_id = g_app_internal->GetPublicV1AccountID(); } else { auto client2 = - g_game->connections()->connections_to_clients().find(clientid); - if (client2 != g_game->connections()->connections_to_clients().end()) { + g_logic->connections()->connections_to_clients().find(clientid); + if (client2 != g_logic->connections()->connections_to_clients().end()) { account_id = client2->second->peer_public_account_id(); } } @@ -491,27 +491,6 @@ auto PyGetGameRoster(PyObject* self, PyObject* args, PyObject* keywds) BA_PYTHON_CATCH; } -auto PyGetScoresToBeat(PyObject* self, PyObject* args, PyObject* keywds) - -> PyObject* { - BA_PYTHON_TRY; - const char* level; - const char* config; - PyObject* callback_obj = Py_None; - static const char* kwlist[] = {"level", "config", "callback", nullptr}; - if (!PyArg_ParseTupleAndKeywords(args, keywds, "ssO", - const_cast(kwlist), &level, &config, - &callback_obj)) { - return nullptr; - } - - // Allocate a Call object for this and pass its pointer to the main thread; - // we'll ref/de-ref it when it comes back. - auto* call = Object::NewDeferred(callback_obj); - g_app_flavor->PushGetScoresToBeatCall(level, config, call); - Py_RETURN_NONE; - BA_PYTHON_CATCH; -} - auto PySetDebugSpeedExponent(PyObject* self, PyObject* args) -> PyObject* { BA_PYTHON_TRY; int speed; @@ -523,7 +502,7 @@ auto PySetDebugSpeedExponent(PyObject* self, PyObject* args) -> PyObject* { throw Exception(PyExcType::kContext); } #if BA_DEBUG_BUILD - g_game->SetDebugSpeedExponent(speed); + g_logic->SetDebugSpeedExponent(speed); #else throw Exception("This call only functions in the debug build."); #endif @@ -533,8 +512,8 @@ auto PySetDebugSpeedExponent(PyObject* self, PyObject* args) -> PyObject* { auto PyGetReplaySpeedExponent(PyObject* self, PyObject* args) -> PyObject* { BA_PYTHON_TRY; - assert(g_game); - return PyLong_FromLong(g_game->replay_speed_exponent()); + assert(g_logic); + return PyLong_FromLong(g_logic->replay_speed_exponent()); BA_PYTHON_CATCH; } @@ -542,8 +521,8 @@ auto PySetReplaySpeedExponent(PyObject* self, PyObject* args) -> PyObject* { BA_PYTHON_TRY; int speed; if (!PyArg_ParseTuple(args, "i", &speed)) return nullptr; - assert(g_game); - g_game->SetReplaySpeedExponent(speed); + assert(g_logic); + g_logic->SetReplaySpeedExponent(speed); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -556,8 +535,8 @@ auto PyResetGameActivityTracking(PyObject* self, PyObject* args, const_cast(kwlist))) { return nullptr; } - if (g_game) { - g_game->ResetActivityTracking(); + if (g_logic) { + g_logic->ResetActivityTracking(); } Py_RETURN_NONE; BA_PYTHON_CATCH; @@ -629,13 +608,6 @@ auto PythonMethodsGameplay::GetMethods() -> std::vector { "Sets the debug speed scale for the game. Actual speed is " "pow(2,speed)."}, - {"get_scores_to_beat", (PyCFunction)PyGetScoresToBeat, - METH_VARARGS | METH_KEYWORDS, - "get_scores_to_beat(level: str, config: str, callback: Callable) -> " - "None\n" - "\n" - "(internal)"}, - {"get_game_roster", (PyCFunction)PyGetGameRoster, METH_VARARGS | METH_KEYWORDS, "get_game_roster() -> list[dict[str, Any]]\n" diff --git a/src/ballistica/python/methods/python_methods_graphics.cc b/src/ballistica/python/methods/python_methods_graphics.cc index 87f72ca9..0e59a559 100644 --- a/src/ballistica/python/methods/python_methods_graphics.cc +++ b/src/ballistica/python/methods/python_methods_graphics.cc @@ -2,10 +2,10 @@ #include "ballistica/python/methods/python_methods_graphics.h" -#include "ballistica/game/game.h" #include "ballistica/graphics/camera.h" #include "ballistica/graphics/graphics.h" #include "ballistica/graphics/text/text_graphics.h" +#include "ballistica/logic/logic.h" #include "ballistica/platform/platform.h" #include "ballistica/python/python.h" #include "ballistica/python/python_context_call_runnable.h" @@ -52,7 +52,7 @@ auto PySetCameraPosition(PyObject* self, PyObject* args, PyObject* keywds) const_cast(kwlist), &x, &y, &z)) { return nullptr; } - assert(g_game); + assert(g_logic); g_graphics->camera()->SetPosition(x, y, z); Py_RETURN_NONE; BA_PYTHON_CATCH; @@ -69,7 +69,7 @@ auto PySetCameraTarget(PyObject* self, PyObject* args, PyObject* keywds) const_cast(kwlist), &x, &y, &z)) { return nullptr; } - assert(g_game); + assert(g_logic); g_graphics->camera()->SetTarget(x, y, z); Py_RETURN_NONE; BA_PYTHON_CATCH; @@ -84,7 +84,7 @@ auto PySetCameraManual(PyObject* self, PyObject* args, PyObject* keywds) const_cast(kwlist), &value)) { return nullptr; } - assert(g_game); + assert(g_logic); g_graphics->camera()->SetManual(value); Py_RETURN_NONE; BA_PYTHON_CATCH; @@ -98,10 +98,10 @@ auto PyCharStr(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { const_cast(kwlist), &name_obj)) { return nullptr; } - assert(g_game); + assert(g_logic); auto id(Python::GetPyEnum_SpecialChar(name_obj)); - assert(Utils::IsValidUTF8(g_game->CharStr(id))); - return PyUnicode_FromString(g_game->CharStr(id).c_str()); + assert(Utils::IsValidUTF8(g_logic->CharStr(id))); + return PyUnicode_FromString(g_logic->CharStr(id).c_str()); BA_PYTHON_CATCH; } @@ -164,7 +164,7 @@ auto PyEvaluateLstr(PyObject* self, PyObject* args, PyObject* keywds) return nullptr; } return PyUnicode_FromString( - g_game->CompileResourceString(value, "evaluate_lstr").c_str()); + g_logic->CompileResourceString(value, "evaluate_lstr").c_str()); BA_PYTHON_CATCH; } @@ -188,7 +188,7 @@ auto PyGetStringHeight(PyObject* self, PyObject* args, PyObject* keywds) } s = Python::GetPyString(s_obj); #if BA_DEBUG_BUILD - if (g_game->CompileResourceString(s, "get_string_height test") != s) { + if (g_logic->CompileResourceString(s, "get_string_height test") != s) { BA_LOG_PYTHON_TRACE( "resource-string passed to get_string_height; this should be avoided"); } @@ -218,7 +218,7 @@ auto PyGetStringWidth(PyObject* self, PyObject* args, PyObject* keywds) } s = Python::GetPyString(s_obj); #if BA_DEBUG_BUILD - if (g_game->CompileResourceString(s, "get_string_width debug test") != s) { + if (g_logic->CompileResourceString(s, "get_string_width debug test") != s) { BA_LOG_PYTHON_TRACE( "resource-string passed to get_string_width; this should be avoided"); } diff --git a/src/ballistica/python/methods/python_methods_input.cc b/src/ballistica/python/methods/python_methods_input.cc index c03bf5b4..8f1cde34 100644 --- a/src/ballistica/python/methods/python_methods_input.cc +++ b/src/ballistica/python/methods/python_methods_input.cc @@ -3,10 +3,10 @@ #include "ballistica/python/methods/python_methods_input.h" #include "ballistica/app/app.h" -#include "ballistica/game/game.h" #include "ballistica/input/device/input_device.h" #include "ballistica/input/device/touch_input.h" #include "ballistica/input/input.h" +#include "ballistica/logic/logic.h" #include "ballistica/platform/platform.h" #include "ballistica/python/python.h" #include "ballistica/python/python_sys.h" diff --git a/src/ballistica/python/methods/python_methods_networking.cc b/src/ballistica/python/methods/python_methods_networking.cc index 8066dfd5..6c5a4d6e 100644 --- a/src/ballistica/python/methods/python_methods_networking.cc +++ b/src/ballistica/python/methods/python_methods_networking.cc @@ -3,10 +3,10 @@ #include "ballistica/python/methods/python_methods_networking.h" #include "ballistica/app/app.h" -#include "ballistica/game/connection/connection_set.h" -#include "ballistica/game/connection/connection_to_client.h" -#include "ballistica/game/connection/connection_to_host.h" -#include "ballistica/game/game.h" +#include "ballistica/logic/connection/connection_set.h" +#include "ballistica/logic/connection/connection_to_client.h" +#include "ballistica/logic/connection/connection_to_host.h" +#include "ballistica/logic/logic.h" #include "ballistica/math/vector3f.h" #include "ballistica/networking/network_reader.h" #include "ballistica/networking/networking.h" @@ -30,7 +30,7 @@ auto PyGetPublicPartyEnabled(PyObject* self, PyObject* args, PyObject* keywds) const_cast(kwlist))) return nullptr; assert(g_python); - if (g_game->public_party_enabled()) { + if (g_logic->public_party_enabled()) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; @@ -48,7 +48,7 @@ auto PySetPublicPartyEnabled(PyObject* self, PyObject* args, PyObject* keywds) return nullptr; } assert(g_python); - g_game->SetPublicPartyEnabled(static_cast(enable)); + g_logic->SetPublicPartyEnabled(static_cast(enable)); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -64,7 +64,7 @@ auto PySetPublicPartyName(PyObject* self, PyObject* args, PyObject* keywds) } std::string name = Python::GetPyString(name_obj); assert(g_python); - g_game->SetPublicPartyName(name); + g_logic->SetPublicPartyName(name); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -81,7 +81,7 @@ auto PySetPublicPartyStatsURL(PyObject* self, PyObject* args, PyObject* keywds) // The call expects an empty string for the no-url option. std::string url = (url_obj == Py_None) ? "" : Python::GetPyString(url_obj); assert(g_python); - g_game->SetPublicPartyStatsURL(url); + g_logic->SetPublicPartyStatsURL(url); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -95,7 +95,7 @@ auto PyGetPublicPartyMaxSize(PyObject* self, PyObject* args, PyObject* keywds) return nullptr; } assert(g_python); - return PyLong_FromLong(g_game->public_party_max_size()); + return PyLong_FromLong(g_logic->public_party_max_size()); BA_PYTHON_CATCH; } @@ -109,7 +109,7 @@ auto PySetPublicPartyMaxSize(PyObject* self, PyObject* args, PyObject* keywds) return nullptr; } assert(g_python); - g_game->SetPublicPartyMaxSize(max_size); + g_logic->SetPublicPartyMaxSize(max_size); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -123,8 +123,8 @@ auto PySetAuthenticateClients(PyObject* self, PyObject* args, PyObject* keywds) const_cast(kwlist), &enable)) { return nullptr; } - assert(g_game); - g_game->set_require_client_authentication(static_cast(enable)); + assert(g_logic); + g_logic->set_require_client_authentication(static_cast(enable)); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -138,14 +138,14 @@ auto PySetAdmins(PyObject* self, PyObject* args, PyObject* keywds) const_cast(kwlist), &admins_obj)) { return nullptr; } - assert(g_game); + assert(g_logic); auto admins = Python::GetPyStrings(admins_obj); std::set adminset; for (auto&& admin : admins) { adminset.insert(admin); } - g_game->set_admin_public_ids(adminset); + g_logic->set_admin_public_ids(adminset); Py_RETURN_NONE; BA_PYTHON_CATCH; @@ -160,8 +160,8 @@ auto PySetEnableDefaultKickVoting(PyObject* self, PyObject* args, const_cast(kwlist), &enable)) { return nullptr; } - assert(g_game); - g_game->set_kick_voting_enabled(static_cast(enable)); + assert(g_logic); + g_logic->set_kick_voting_enabled(static_cast(enable)); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -202,11 +202,11 @@ auto PyConnectToParty(PyObject* self, PyObject* args, PyObject* keywds) throw Exception(); } } catch (const std::exception&) { - ScreenMessage(g_game->GetResourceString("invalidAddressErrorText"), + ScreenMessage(g_logic->GetResourceString("invalidAddressErrorText"), {1, 0, 0}); Py_RETURN_NONE; } - g_game->connections()->PushHostConnectedUDPCall( + g_logic->connections()->PushHostConnectedUDPCall( s, static_cast(print_progress)); Py_RETURN_NONE; BA_PYTHON_CATCH; @@ -223,7 +223,7 @@ auto PyClientInfoQueryResponse(PyObject* self, PyObject* args, PyObject* keywds) &response_obj)) { return nullptr; } - g_game->connections()->SetClientInfoFromMasterServer(token, response_obj); + g_logic->connections()->SetClientInfoFromMasterServer(token, response_obj); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -236,7 +236,7 @@ auto PyGetConnectionToHostInfo(PyObject* self, PyObject* args, PyObject* keywds) const_cast(kwlist))) { return nullptr; } - ConnectionToHost* hc = g_game->connections()->connection_to_host(); + ConnectionToHost* hc = g_logic->connections()->connection_to_host(); if (hc) { return Py_BuildValue("{sssi}", "name", hc->party_name().c_str(), "build_number", hc->build_number()); @@ -255,7 +255,7 @@ auto PyDisconnectFromHost(PyObject* self, PyObject* args, PyObject* keywds) const_cast(kwlist))) { return nullptr; } - g_game->connections()->PushDisconnectFromHostCall(); + g_logic->connections()->PushDisconnectFromHostCall(); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -271,7 +271,7 @@ auto PyDisconnectClient(PyObject* self, PyObject* args, PyObject* keywds) &ban_time)) { return nullptr; } - bool kickable = g_game->connections()->DisconnectClient(client_id, ban_time); + bool kickable = g_logic->connections()->DisconnectClient(client_id, ban_time); if (kickable) { Py_RETURN_TRUE; } else { @@ -290,10 +290,10 @@ auto PyGetClientPublicDeviceUUID(PyObject* self, PyObject* args, return nullptr; } auto&& connection{ - g_game->connections()->connections_to_clients().find(client_id)}; + g_logic->connections()->connections_to_clients().find(client_id)}; // Does this connection exist? - if (connection == g_game->connections()->connections_to_clients().end()) { + if (connection == g_logic->connections()->connections_to_clients().end()) { Py_RETURN_NONE; } @@ -324,8 +324,8 @@ auto PySetMasterServerSource(PyObject* self, PyObject* args) -> PyObject* { int source; if (!PyArg_ParseTuple(args, "i", &source)) return nullptr; if (source != 0 && source != 1) { - BA_LOG_ONCE("Error: Invalid server source: " + std::to_string(source) - + "."); + BA_LOG_ONCE(LogLevel::kError, + "Invalid server source: " + std::to_string(source) + "."); source = 1; } g_app->master_server_source = source; @@ -379,7 +379,7 @@ auto PyEndHostScanning(PyObject* self, PyObject* args, PyObject* keywds) auto PyHaveConnectedClients(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { BA_PYTHON_TRY; - if (g_game->connections()->GetConnectedClientCount() > 0) { + if (g_logic->connections()->GetConnectedClientCount() > 0) { Py_RETURN_TRUE; } else { Py_RETURN_FALSE; diff --git a/src/ballistica/python/methods/python_methods_system.cc b/src/ballistica/python/methods/python_methods_system.cc index 4081c087..1cb80409 100644 --- a/src/ballistica/python/methods/python_methods_system.cc +++ b/src/ballistica/python/methods/python_methods_system.cc @@ -8,19 +8,19 @@ #include "ballistica/app/app.h" #include "ballistica/app/app_config.h" #include "ballistica/app/app_flavor.h" -#include "ballistica/game/game_stream.h" -#include "ballistica/game/host_activity.h" -#include "ballistica/game/session/host_session.h" -#include "ballistica/game/session/replay_client_session.h" +#include "ballistica/assets/assets.h" +#include "ballistica/assets/component/texture.h" #include "ballistica/graphics/camera.h" #include "ballistica/graphics/graphics.h" #include "ballistica/input/input.h" -#include "ballistica/media/component/texture.h" -#include "ballistica/media/media.h" +#include "ballistica/logic/host_activity.h" +#include "ballistica/logic/session/host_session.h" +#include "ballistica/logic/session/replay_client_session.h" #include "ballistica/python/python.h" #include "ballistica/python/python_context_call_runnable.h" #include "ballistica/python/python_sys.h" #include "ballistica/scene/scene.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { @@ -79,7 +79,7 @@ auto PySetUpSigInt(PyObject* self) -> PyObject* { if (g_app_flavor) { g_platform->SetupInterruptHandling(); } else { - Log("SigInt handler called before g_app_flavor exists."); + Log(LogLevel::kError, "SigInt handler called before g_app_flavor exists."); } Py_RETURN_NONE; BA_PYTHON_CATCH; @@ -354,7 +354,7 @@ auto PyPrintContext(PyObject* self, PyObject* args, PyObject* keywds) const_cast(kwlist))) { return nullptr; } - Python::LogContextAuto(); + Python::PrintContextAuto(); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -362,7 +362,7 @@ auto PyPrintContext(PyObject* self, PyObject* args, PyObject* keywds) auto PyPrintLoadInfo(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { BA_PYTHON_TRY; - g_media->PrintLoadInfo(); + g_assets->PrintLoadInfo(); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -521,19 +521,20 @@ auto PyGetVolatileDataDirectory(PyObject* self, PyObject* args) -> PyObject* { auto PyIsLogFull(PyObject* self, PyObject* args) -> PyObject* { BA_PYTHON_TRY; - if (g_app->log_full) { + if (g_app->v1_cloud_log_full) { Py_RETURN_TRUE; } Py_RETURN_FALSE; BA_PYTHON_CATCH; } -auto PyGetLog(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { +auto PyGetV1CloudLog(PyObject* self, PyObject* args, PyObject* keywds) + -> PyObject* { BA_PYTHON_TRY; std::string log_fin; { - std::scoped_lock lock(g_app->log_mutex); - log_fin = g_app->log; + std::scoped_lock lock(g_app->v1_cloud_log_mutex); + log_fin = g_app->v1_cloud_log; } // we want to use something with error handling here since the last // bit of this string could be truncated utf8 chars.. @@ -545,8 +546,8 @@ auto PyGetLog(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { auto PyMarkLogSent(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { BA_PYTHON_TRY; - // this way we won't try to send it at shutdown time and whatnot - g_app->put_log = true; + // This way we won't try to send it at shutdown time and whatnot + g_app->did_put_v1_cloud_log = true; Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -652,8 +653,8 @@ auto PySetInternalLanguageKeys(PyObject* self, PyObject* args) -> PyObject* { random_names.emplace_back(PyUnicode_AsUTF8(entry)); } Utils::SetRandomNameList(random_names); - assert(g_game); - g_game->SetLanguageKeys(language); + assert(g_logic); + g_logic->SetLanguageKeys(language); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -902,8 +903,9 @@ auto PythonMethodsSystem::GetMethods() -> std::vector { "\n" "(internal)"}, - {"getlog", (PyCFunction)PyGetLog, METH_VARARGS | METH_KEYWORDS, - "getlog() -> str\n" + {"get_v1_cloud_log", (PyCFunction)PyGetV1CloudLog, + METH_VARARGS | METH_KEYWORDS, + "get_v1_cloud_log() -> str\n" "\n" "(internal)"}, @@ -912,8 +914,8 @@ auto PythonMethodsSystem::GetMethods() -> std::vector { "\n" "(internal)"}, - {"get_log_file_path", PyGetLogFilePath, METH_VARARGS, - "get_log_file_path() -> str\n" + {"get_v1_cloud_log_file_path", PyGetLogFilePath, METH_VARARGS, + "get_v1_cloud_log_file_path() -> str\n" "\n" "(internal)\n" "\n" @@ -1055,7 +1057,7 @@ auto PythonMethodsSystem::GetMethods() -> std::vector { "\n" "(internal)\n" "\n" - "Returns whether or not the current thread is the game thread."}, + "Returns whether or not the current thread is the logic thread."}, {"request_permission", (PyCFunction)PyRequestPermission, METH_VARARGS | METH_KEYWORDS, diff --git a/src/ballistica/python/methods/python_methods_ui.cc b/src/ballistica/python/methods/python_methods_ui.cc index 71b2dedc..f5a7a6cc 100644 --- a/src/ballistica/python/methods/python_methods_ui.cc +++ b/src/ballistica/python/methods/python_methods_ui.cc @@ -4,11 +4,11 @@ #include "ballistica/app/app.h" #include "ballistica/app/app_flavor.h" -#include "ballistica/game/account.h" -#include "ballistica/game/connection/connection_set.h" -#include "ballistica/game/game.h" #include "ballistica/input/input.h" #include "ballistica/internal/app_internal.h" +#include "ballistica/logic/connection/connection_set.h" +#include "ballistica/logic/logic.h" +#include "ballistica/logic/v1_account.h" #include "ballistica/python/python.h" #include "ballistica/python/python_sys.h" #include "ballistica/ui/root_ui.h" @@ -130,12 +130,12 @@ auto PyButtonWidget(PyObject* self, PyObject* args, PyObject* keywds) &text_res_scale_obj, &enabled_obj)) return nullptr; - if (!g_game->IsInUIContext()) { + if (!g_logic->IsInUIContext()) { throw Exception( "This must be called within the UI context (see ba.Context docs)", PyExcType::kContext); } - ScopedSetContext cp(g_game->GetUIContextTarget()); + ScopedSetContext cp(g_logic->GetUIContextTarget()); // grab the edited widget or create a new one --------------------- Object::Ref b; @@ -381,12 +381,12 @@ auto PyCheckBoxWidget(PyObject* self, PyObject* args, PyObject* keywds) return nullptr; } - if (!g_game->IsInUIContext()) { + if (!g_logic->IsInUIContext()) { throw Exception( "This must be called within the UI context (see ba.Context docs).", PyExcType::kContext); } - ScopedSetContext cp(g_game->GetUIContextTarget()); + ScopedSetContext cp(g_logic->GetUIContextTarget()); // grab the edited widget or create a new one --------------------- Object::Ref widget; @@ -525,12 +525,12 @@ auto PyImageWidget(PyObject* self, PyObject* args, PyObject* keywds) &tilt_scale_obj, &mask_texture_obj, &radial_amount_obj)) return nullptr; - if (!g_game->IsInUIContext()) { + if (!g_logic->IsInUIContext()) { throw Exception( "This must be called within the UI context (see ba.Context docs).", PyExcType::kContext); } - ScopedSetContext cp(g_game->GetUIContextTarget()); + ScopedSetContext cp(g_logic->GetUIContextTarget()); // grab the edited widget or create a new one --------------------- Object::Ref b; @@ -681,15 +681,15 @@ auto PyColumnWidget(PyObject* self, PyObject* args, PyObject* keywds) &margin_obj, &claims_left_right_obj, &claims_tab_obj)) return nullptr; - if (!g_game->IsInUIContext()) { + if (!g_logic->IsInUIContext()) { throw Exception( "This must be called within the UI context (see ba.Context " "docs).", PyExcType::kContext); } - // if (!g_game->IsInUIContext()) { BA_LOG_PYTHON_TRACE("ERROR: This should be + // if (!g_logic->IsInUIContext()) { BA_LOG_PYTHON_TRACE("ERROR: This should be // called within the UI context (see ba.Context docs)");} - ScopedSetContext cp(g_game->GetUIContextTarget()); + ScopedSetContext cp(g_logic->GetUIContextTarget()); // grab the edited widget or create a new one --------------------- Object::Ref widget; @@ -859,11 +859,11 @@ auto PyContainerWidget(PyObject* self, PyObject* args, PyObject* keywds) return nullptr; } - if (!g_game->IsInUIContext()) + if (!g_logic->IsInUIContext()) throw Exception( "This must be called within the UI context (see ba.Context docs).", PyExcType::kContext); - ScopedSetContext cp(g_game->GetUIContextTarget()); + ScopedSetContext cp(g_logic->GetUIContextTarget()); // grab the edited widget or create a new one --------------------- Object::Ref widget; @@ -1080,13 +1080,13 @@ auto PyRowWidget(PyObject* /* self */, PyObject* args, PyObject* keywds) &claims_tab_obj, &selection_loops_to_parent_obj)) return nullptr; - if (!g_game->IsInUIContext()) + if (!g_logic->IsInUIContext()) throw Exception( "This must be called within the UI context (see ba.Context docs).", PyExcType::kContext); // Called within the UI context (see ba.Context docs)");} - ScopedSetContext cp(g_game->GetUIContextTarget()); + ScopedSetContext cp(g_logic->GetUIContextTarget()); // Grab the edited widget or create a new one. Object::Ref widget; @@ -1202,12 +1202,12 @@ auto PyScrollWidget(PyObject* self, PyObject* args, PyObject* keywds) &claims_up_down_obj, &claims_tab_obj, &autoselect_obj)) return nullptr; - if (!g_game->IsInUIContext()) { + if (!g_logic->IsInUIContext()) { throw Exception( "This must be called within the UI context (see ba.Context docs).", PyExcType::kContext); } - ScopedSetContext cp(g_game->GetUIContextTarget()); + ScopedSetContext cp(g_logic->GetUIContextTarget()); // Grab the edited widget or create a new one. --------------------- Object::Ref widget; @@ -1348,12 +1348,12 @@ auto PyHScrollWidget(PyObject* self, PyObject* args, PyObject* keywds) &claims_up_down_obj, &claims_tab_obj, &autoselect_obj)) return nullptr; - if (!g_game->IsInUIContext()) { + if (!g_logic->IsInUIContext()) { throw Exception( "This must be called within the UI context (see ba.Context docs).", PyExcType::kContext); } - ScopedSetContext cp(g_game->GetUIContextTarget()); + ScopedSetContext cp(g_logic->GetUIContextTarget()); // grab the edited widget or create a new one --------------------- Object::Ref widget; @@ -1528,13 +1528,13 @@ auto PyTextWidget(PyObject* self, PyObject* args, PyObject* keywds) &extra_touch_border_scale_obj, &res_scale_obj)) return nullptr; - if (!g_game->IsInUIContext()) + if (!g_logic->IsInUIContext()) throw Exception( "This must be called within the UI context (see ba.Context docs).", PyExcType::kContext); - // if (!g_game->IsInUIContext()) { BA_LOG_PYTHON_TRACE("ERROR: This should be + // if (!g_logic->IsInUIContext()) { BA_LOG_PYTHON_TRACE("ERROR: This should be // called within the UI context (see ba.Context docs)");} - ScopedSetContext cp(g_game->GetUIContextTarget()); + ScopedSetContext cp(g_logic->GetUIContextTarget()); // grab the edited widget or create a new one --------------------- Object::Ref widget; @@ -1578,7 +1578,7 @@ auto PyTextWidget(PyObject* self, PyObject* args, PyObject* keywds) // FIXME - compiling Lstr values to flat strings before passing them in; // we should probably extend TextWidget to handle this internally, but // punting on that for now.. - widget->set_description(g_game->CompileResourceString( + widget->set_description(g_logic->CompileResourceString( Python::GetPyString(description_obj), "textwidget set desc")); } if (autoselect_obj != Py_None) { @@ -1753,12 +1753,12 @@ auto PyWidgetCall(PyObject* self, PyObject* args, PyObject* keywds) &show_buffer_right_obj, &autoselect_obj)) return nullptr; - if (!g_game->IsInUIContext()) { + if (!g_logic->IsInUIContext()) { throw Exception( "This must be called within the UI context (see ba.Context docs).", PyExcType::kContext); } - ScopedSetContext cp(g_game->GetUIContextTarget()); + ScopedSetContext cp(g_logic->GetUIContextTarget()); Widget* widget = nullptr; if (edit_obj != Py_None) { @@ -2032,7 +2032,8 @@ auto PyChatMessage(PyObject* self, PyObject* args, PyObject* keywds) clients = Python::GetPyInts(clients_obj); clients_p = &clients; } - g_game->connections()->SendChatMessage(message, clients_p, sender_override_p); + g_logic->connections()->SendChatMessage(message, clients_p, + sender_override_p); Py_RETURN_NONE; BA_PYTHON_CATCH; } @@ -2048,7 +2049,7 @@ auto PyGetChatMessages(PyObject* self, PyObject* args, PyObject* keywds) return nullptr; } PyObject* py_list = PyList_New(0); - for (auto&& i : g_game->chat_messages()) { + for (auto&& i : g_logic->chat_messages()) { PyList_Append(py_list, PyUnicode_FromString(i.c_str())); } return py_list; @@ -2121,8 +2122,8 @@ auto PyCanShowAd(PyObject* self, PyObject* args, PyObject* keywds) // them or whatnot) also disallow ads if remote apps are connected; at least // on android ads pause our activity which disconnects the remote app.. (could // potentially still allow on other platforms; should verify..) - if (g_game->connections()->connection_to_host() - || g_game->connections()->has_connection_to_clients() + if (g_logic->connections()->connection_to_host() + || g_logic->connections()->has_connection_to_clients() || g_input->HaveRemoteAppController()) { Py_RETURN_FALSE; } @@ -2212,7 +2213,7 @@ auto PyConsolePrint(PyObject* self, PyObject* args) -> PyObject* { throw Exception(); } const char* c = PyUnicode_AsUTF8(str_obj); - g_game->PushConsolePrintCall(c); + g_logic->PushConsolePrintCall(c); Py_DECREF(str_obj); } #endif // !BA_HEADLESS_BUILD @@ -2224,8 +2225,8 @@ auto PyIsPartyIconVisible(PyObject* self, PyObject* args, PyObject* keywds) -> PyObject* { BA_PYTHON_TRY; bool party_button_active = - (g_game->connections()->GetConnectedClientCount() > 0 - || g_game->connections()->connection_to_host() + (g_logic->connections()->GetConnectedClientCount() > 0 + || g_logic->connections()->connection_to_host() || g_ui->root_ui()->always_draw_party_icon()); if (party_button_active) { Py_RETURN_TRUE; diff --git a/src/ballistica/python/python.cc b/src/ballistica/python/python.cc index a2b0f7ba..56eeb33e 100644 --- a/src/ballistica/python/python.cc +++ b/src/ballistica/python/python.cc @@ -3,23 +3,21 @@ #include "ballistica/python/python.h" #include "ballistica/app/app.h" +#include "ballistica/assets/component/collide_model.h" +#include "ballistica/assets/component/model.h" +#include "ballistica/assets/component/sound.h" +#include "ballistica/assets/component/texture.h" #include "ballistica/audio/audio.h" #include "ballistica/core/thread.h" #include "ballistica/dynamics/material/material.h" -#include "ballistica/game/account.h" -#include "ballistica/game/friend_score_set.h" -#include "ballistica/game/game_stream.h" -#include "ballistica/game/host_activity.h" -#include "ballistica/game/player.h" -#include "ballistica/game/score_to_beat.h" #include "ballistica/graphics/graphics.h" #include "ballistica/input/device/joystick.h" #include "ballistica/input/device/keyboard_input.h" #include "ballistica/internal/app_internal.h" -#include "ballistica/media/component/collide_model.h" -#include "ballistica/media/component/model.h" -#include "ballistica/media/component/sound.h" -#include "ballistica/media/component/texture.h" +#include "ballistica/logic/friend_score_set.h" +#include "ballistica/logic/host_activity.h" +#include "ballistica/logic/player.h" +#include "ballistica/logic/v1_account.h" #include "ballistica/python/class/python_class_activity_data.h" #include "ballistica/python/class/python_class_collide_model.h" #include "ballistica/python/class/python_class_context.h" @@ -37,16 +35,17 @@ #include "ballistica/python/class/python_class_vec3.h" #include "ballistica/python/class/python_class_widget.h" #include "ballistica/python/methods/python_methods_app.h" +#include "ballistica/python/methods/python_methods_assets.h" #include "ballistica/python/methods/python_methods_gameplay.h" #include "ballistica/python/methods/python_methods_graphics.h" #include "ballistica/python/methods/python_methods_input.h" -#include "ballistica/python/methods/python_methods_media.h" #include "ballistica/python/methods/python_methods_networking.h" #include "ballistica/python/methods/python_methods_system.h" #include "ballistica/python/methods/python_methods_ui.h" #include "ballistica/python/python_command.h" #include "ballistica/python/python_context_call_runnable.h" #include "ballistica/scene/node/node_attribute.h" +#include "ballistica/scene/scene_stream.h" #include "ballistica/ui/ui.h" #include "ballistica/ui/widget/text_widget.h" @@ -69,6 +68,46 @@ namespace ballistica { #pragma ide diagnostic ignored "hicpp-signed-bitwise" #pragma ide diagnostic ignored "RedundantCast" +auto Python::LoggingCall(LogLevel loglevel, const std::string& msg) -> void { + // If we've not yet captured our Python logging calls, stash this call away. + // We'll submit all accumulated entries after we bootstrap Python. + if (!objexists(ObjID::kLoggingCriticalCall)) { + std::scoped_lock lock(early_log_lock_); + early_logs_.emplace_back(std::make_pair(loglevel, msg)); + return; + } + + // Ok; seems we've got Python calls. Run the right one for our log level. + ObjID logcallobj; + switch (loglevel) { + case LogLevel::kDebug: + logcallobj = Python::ObjID::kLoggingDebugCall; + break; + case LogLevel::kInfo: + logcallobj = Python::ObjID::kLoggingInfoCall; + break; + case LogLevel::kWarning: + logcallobj = Python::ObjID::kLoggingWarningCall; + break; + case LogLevel::kError: + logcallobj = Python::ObjID::kLoggingErrorCall; + break; + case LogLevel::kCritical: + logcallobj = Python::ObjID::kLoggingCriticalCall; + break; + default: + logcallobj = Python::ObjID::kLoggingInfoCall; + fprintf(stderr, "Unexpected LogLevel %d\n", static_cast(loglevel)); + break; + } + + // Make sure we're good to go from any thread. + ScopedInterpreterLock lock; + + PythonRef args(Py_BuildValue("(s)", msg.c_str()), PythonRef::kSteal); + obj(logcallobj).Call(args); +} + void Python::SetPythonException(const Exception& exc) { PyExcType exctype{exc.python_type()}; const char* description{GetShortExceptionDescription(exc)}; @@ -131,7 +170,8 @@ void Python::PrintStackTrace() { if (g_python->objexists(objid)) { g_python->obj(objid).Call(); } else { - Log("Warning: Python::PrintStackTrace() called before bootstrap complete; " + Log(LogLevel::kWarning, + "Python::PrintStackTrace() called before bootstrap complete; " "not printing."); } } @@ -854,6 +894,19 @@ auto Python::GetPyVector3f(PyObject* o) -> Vector3f { Python::Python() = default; +auto Python::Create() -> Python* { + assert(InMainThread()); + + Python* python = new Python(); + python->InitCorePython(); + + // After we bootstrap Python here in the main thread we release the GIL. + // We'll explicitly reacquire it anytime we need it (mainly in the logic + // thread once that comes up later). + PyEval_SaveThread(); + return python; +} + static struct PyModuleDef ba_module_def = {PyModuleDef_HEAD_INIT}; static auto ba_exec(PyObject* module) -> int { @@ -886,161 +939,193 @@ static auto PyInit__ba() -> PyObject* { return module; } -void Python::Reset(bool do_init) { +auto Python::InitCorePython() -> void { + assert(!inited_); + assert(InMainThread()); + // Flip on some extra runtime debugging options in debug builds. + // https://docs.python.org/3/library/devmode.html#devmode + int dev_mode{g_buildconfig.debug_build()}; + + // Pre-config as isolated if we include our own Python and as standard + // otherwise. + PyPreConfig preconfig; + if (g_platform->ContainsPythonDist()) { + PyPreConfig_InitIsolatedConfig(&preconfig); + } else { + PyPreConfig_InitPythonConfig(&preconfig); + } + preconfig.dev_mode = dev_mode; + + // We want consistent utf-8 everywhere (Python used to default to + // windows-specific file encodings, etc.) + preconfig.utf8_mode = 1; + + PyStatus status = Py_PreInitialize(&preconfig); + BA_PRECONDITION(!PyStatus_Exception(status)); + + // Configure as isolated if we include our own Python and as standard + // otherwise. + PyConfig config; + if (g_platform->ContainsPythonDist()) { + PyConfig_InitIsolatedConfig(&config); + } else { + PyConfig_InitPythonConfig(&config); + } + config.dev_mode = dev_mode; + if (!g_buildconfig.debug_build()) { + config.optimization_level = 1; + } + + // In cases where we bundle Python, set up all paths explicitly. + // https://docs.python.org/3/c-api/init_config.html#path-configuration + if (g_platform->ContainsPythonDist()) { + PyConfig_SetBytesString(&config, &config.base_exec_prefix, ""); + PyConfig_SetBytesString(&config, &config.base_executable, ""); + PyConfig_SetBytesString(&config, &config.base_prefix, ""); + PyConfig_SetBytesString(&config, &config.exec_prefix, ""); + PyConfig_SetBytesString(&config, &config.executable, ""); + PyConfig_SetBytesString(&config, &config.prefix, ""); + + // Interesting note: it seems we can pass relative paths here but + // they wind up in sys.path as absolute paths (unlike entries we add + // to sys.path after things are up and running). + if (g_buildconfig.ostype_windows()) { + // Windows Python looks for Lib and DLLs dirs by default, along with + // some others, but we want to be more explicit in limiting to these. It + // also seems that windows Python's paths can be incorrect if we're in + // strange dirs such as \\wsl$\Ubuntu-18.04\ that we get with WSL build + // setups. + + // NOTE: Python for windows actually comes with 'Lib', not 'lib', but + // it seems the interpreter defaults point to ./lib (as of 3.8.5). + // Normally this doesn't matter since windows is case-insensitive but + // under WSL it does. + // So we currently bundle the dir as 'lib' and use that in our path so + // that everything is happy (both with us and with python.exe). + PyWideStringList_Append(&config.module_search_paths, + Py_DecodeLocale("lib", nullptr)); + PyWideStringList_Append(&config.module_search_paths, + Py_DecodeLocale("DLLs", nullptr)); + } else { + PyWideStringList_Append(&config.module_search_paths, + Py_DecodeLocale("pylib", nullptr)); + } + config.module_search_paths_set = 1; + } + + // Let Python know how to spin up our _ba module. + PyImport_AppendInittab("_ba", &PyInit__ba); + + // Let Python know how to spin up the _bainternal module. + g_app_internal->DefineInternalModule(); + + // Init Python. + status = Py_InitializeFromConfig(&config); + BA_PRECONDITION(!PyStatus_Exception(status)); + + // Create a dict for execing just this bootstrap code in so + // we don't pollute the __main__ namespace. + auto bootstrap_context{PythonRef(PyDict_New(), PythonRef::kSteal)}; + + // Do a bit of basic bootstrapping here in the main thread. +#include "ballistica/generated/python_embedded/bootstrap_monolithic.inc" + PyObject* result = + PyRun_String(bootstrap_monolithic_code, Py_file_input, + bootstrap_context.get(), bootstrap_context.get()); + if (result == nullptr) { + PyErr_PrintEx(0); + + // Throw a simple exception so we don't get a stack trace. + throw std::logic_error( + "Error in ba Python bootstrapping. See log for details."); + } + Py_DECREF(result); + + // Grab __main__ in case we need to use it later. + // FIXME: put this in objs_ with everything else. + PyObject* m; + BA_PRECONDITION(m = PyImport_AddModule("__main__")); + BA_PRECONDITION(main_dict_ = PyModule_GetDict(m)); + + // Make sure we're running the Python version we require. + const char* ver = Py_GetVersion(); + if (strncmp(ver, "3.10", 4) != 0) { + FatalError("We require Python 3.10.x; instead found " + std::string(ver)); + } +} + +auto Python::InitBallisticaPython() -> void { + assert(InLogicThread()); + + // Create a dict for execing just this bootstrap code in so + // we don't pollute the __main__ namespace. + auto bootstrap_context{PythonRef(PyDict_New(), PythonRef::kSteal)}; + + // Get the app up and running. + // Run a few core bootstrappy things first: + // - get stdout/stderr redirection up so we can intercept python output + // - add our user and system script dirs to python path + // - create the ba.app instance. + +#include "ballistica/generated/python_embedded/bootstrap.inc" + PyObject* result = + PyRun_String(bootstrap_code, Py_file_input, bootstrap_context.get(), + bootstrap_context.get()); + if (result == nullptr) { + PyErr_PrintEx(0); + + // Throw a simple exception so we don't get a stack trace. + throw std::logic_error( + "Error in ba Python bootstrapping. See log for details."); + } + Py_DECREF(result); + + // Import and grab all the Python stuff we use from C++. +#include "ballistica/generated/python_embedded/binding.inc" + + // If we've got any early log calls, it is now safe to push them along + // to Python. + assert(objexists(ObjID::kLoggingCriticalCall)); + { + std::scoped_lock lock(early_log_lock_); + for (auto&& entry : early_logs_) { + LoggingCall(entry.first, "DEFERRED-EARLY-LOG: " + entry.second); + } + early_logs_.clear(); + } + g_app_internal->PythonPostInit(); + + // Alright I guess let's pull ba in to main, since pretty + // much all interactive commands will be using it. + // If we ever build the game as a pure python module we should + // of course not do this. + BA_PRECONDITION(PyRun_SimpleString("import ba") == 0); + + // Read the config file and store the config dict for easy access. + obj(ObjID::kReadConfigCall).Call(); + StoreObj(ObjID::kConfig, obj(ObjID::kApp).GetAttr("config").get()); + assert(PyDict_Check(obj(ObjID::kConfig).get())); + + // Turn off fancy-pants cyclic garbage-collection. + // We run it only at explicit times to avoid random hitches and keep + // things more deterministic. + // Non-reference-looped objects will still get cleaned up + // immediately, so we should try to structure things to avoid + // reference loops (just like Swift, ObjC, etc). + // FIXME - move this to Python code. + g_python->obj(Python::ObjID::kGCDisableCall).Call(); + inited_ = true; +} + +auto Python::Reset() -> void { assert(InLogicThread()); assert(g_python); assert(g_platform); + assert(inited_); - bool was_inited = inited_; - - if (inited_) { - ReleaseGamePadInput(); - ReleaseKeyboardInput(); - g_graphics->ReleaseFadeEndCommand(); - inited_ = false; - } - - if (!was_inited && do_init) { - // Flip on some extra runtime debugging options in debug builds. - // https://docs.python.org/3.10/library/devmode.html#devmode - int dev_mode{g_buildconfig.debug_build()}; - - // Pre-config as isolated if we include our own Python and as standard - // otherwise. - PyPreConfig preconfig; - if (g_platform->ContainsPythonDist()) { - PyPreConfig_InitIsolatedConfig(&preconfig); - } else { - PyPreConfig_InitPythonConfig(&preconfig); - } - preconfig.dev_mode = dev_mode; - - // We want consistent utf-8 everywhere (Python used to default to - // windows-specific file encodings, etc.) - preconfig.utf8_mode = 1; - - PyStatus status = Py_PreInitialize(&preconfig); - BA_PRECONDITION(!PyStatus_Exception(status)); - - // Configure as isolated if we include our own Python and as standard - // otherwise. - PyConfig config; - if (g_platform->ContainsPythonDist()) { - PyConfig_InitIsolatedConfig(&config); - } else { - PyConfig_InitPythonConfig(&config); - } - config.dev_mode = dev_mode; - if (!g_buildconfig.debug_build()) { - config.optimization_level = 1; - } - - // In cases where we bundle Python, set up all paths explicitly. - // see https://docs.python.org/3.8/ - // c-api/init_config.html#path-configuration - if (g_platform->ContainsPythonDist()) { - PyConfig_SetBytesString(&config, &config.base_exec_prefix, ""); - PyConfig_SetBytesString(&config, &config.base_executable, ""); - PyConfig_SetBytesString(&config, &config.base_prefix, ""); - PyConfig_SetBytesString(&config, &config.exec_prefix, ""); - PyConfig_SetBytesString(&config, &config.executable, ""); - PyConfig_SetBytesString(&config, &config.prefix, ""); - - // Interesting note: it seems we can pass relative paths here but - // they wind up in sys.path as absolute paths (unlike entries we add - // to sys.path after things are up and running). - if (g_buildconfig.ostype_windows()) { - // Windows Python looks for Lib and DLLs dirs by default, along with - // some others, but we want to be more explicit in limiting to these. It - // also seems that windows Python's paths can be incorrect if we're in - // strange dirs such as \\wsl$\Ubuntu-18.04\ that we get with WSL build - // setups. - - // NOTE: Python for windows actually comes with 'Lib', not 'lib', but - // it seems the interpreter defaults point to ./lib (as of 3.8.5). - // Normally this doesn't matter since windows is case-insensitive but - // under WSL it does. - // So we currently bundle the dir as 'lib' and use that in our path so - // that everything is happy (both with us and with python.exe). - PyWideStringList_Append(&config.module_search_paths, - Py_DecodeLocale("lib", nullptr)); - PyWideStringList_Append(&config.module_search_paths, - Py_DecodeLocale("DLLs", nullptr)); - } else { - PyWideStringList_Append(&config.module_search_paths, - Py_DecodeLocale("pylib", nullptr)); - } - config.module_search_paths_set = 1; - } - - // Let Python know how to spin up our _ba module. - PyImport_AppendInittab("_ba", &PyInit__ba); - - // Inits our _ba module and runs Py_Initialize(). - g_app_internal->PyInitialize(&config); - - // Grab __main__ in case we need to use it later. - PyObject* m; - BA_PRECONDITION(m = PyImport_AddModule("__main__")); - BA_PRECONDITION(main_dict_ = PyModule_GetDict(m)); - - // Make sure we're running the Python version we require. - const char* ver = Py_GetVersion(); - if (strncmp(ver, "3.10", 4) != 0) { - throw Exception("We require Python 3.10.x; instead found " - + std::string(ver)); - } - - // Create a dict for execing our bootstrap code in so - // we don't pollute the __main__ namespace. - auto bootstrap_context{PythonRef(PyDict_New(), PythonRef::kSteal)}; - - // Get the app up and running. - // Run a few core bootstrappy things first: - // - get stdout/stderr redirection up so we can intercept python output - // - add our user and system script dirs to python path - // - create the ba.app instance. - -#include "ballistica/generated/python_embedded/bootstrap.inc" - PyObject* result = - PyRun_String(bootstrap_code, Py_file_input, bootstrap_context.get(), - bootstrap_context.get()); - if (result == nullptr) { - PyErr_PrintEx(0); - - // Throw a simple exception so we don't get a stack trace. - throw std::logic_error( - "Error in ba Python bootstrapping. See log for details."); - } - Py_DECREF(result); - - // Import and grab all the Python stuff we use from C++. -#include "ballistica/generated/python_embedded/binding.inc" - - g_app_internal->PythonPostInit(); - - // Alright I guess let's pull ba in to main, since pretty - // much all interactive commands will be using it. - // If we ever build the game as a pure python module we should - // of course not do this. - BA_PRECONDITION(PyRun_SimpleString("import ba") == 0); - - // Read the config file and store the config dict for easy access. - obj(ObjID::kReadConfigCall).Call(); - StoreObj(ObjID::kConfig, obj(ObjID::kApp).GetAttr("config").get()); - assert(PyDict_Check(obj(ObjID::kConfig).get())); - - // Turn off fancy-pants cyclic garbage-collection. - // We run it only at explicit times to avoid random hitches and keep - // things more deterministic. - // Non-reference-looped objects will still get cleaned up - // immediately, so we should try to structure things to avoid - // reference loops (just like Swift, ObjC, etc). - g_python->obj(Python::ObjID::kGCDisableCall).Call(); - } - if (do_init) { - inited_ = true; - } + ReleaseGamePadInput(); + ReleaseKeyboardInput(); + g_graphics->ReleaseFadeEndCommand(); } auto Python::GetModuleMethods() -> std::vector { @@ -1100,23 +1185,21 @@ auto Python::InitModuleClasses(PyObject* module) -> void { } void Python::PushObjCall(ObjID obj_id) { - g_game->thread()->PushCall([obj_id] { - ScopedSetContext cp(g_game->GetUIContext()); + g_logic->thread()->PushCall([obj_id] { + ScopedSetContext cp(g_logic->GetUIContext()); g_python->obj(obj_id).Call(); }); } void Python::PushObjCall(ObjID obj_id, const std::string& arg) { - g_game->thread()->PushCall([this, obj_id, arg] { - ScopedSetContext cp(g_game->GetUIContext()); + g_logic->thread()->PushCall([this, obj_id, arg] { + ScopedSetContext cp(g_logic->GetUIContext()); PythonRef args(Py_BuildValue("(s)", arg.c_str()), ballistica::PythonRef::kSteal); obj(obj_id).Call(args); }); } -Python::~Python() { Reset(false); } - auto Python::GetResource(const char* key, const char* fallback_resource, const char* fallback_value) -> std::string { assert(HaveGIL()); @@ -1155,14 +1238,15 @@ auto Python::GetResource(const char* key, const char* fallback_resource, try { return GetPyString(results.get()); } catch (const std::exception&) { - Log("GetResource failed for '" + std::string(key) + "'"); + Log(LogLevel::kError, + "GetResource failed for '" + std::string(key) + "'"); // Hmm; I guess let's just return the key to help identify/fix the // issue?.. return std::string(""; } } else { - Log("GetResource failed for '" + std::string(key) + "'"); + Log(LogLevel::kError, "GetResource failed for '" + std::string(key) + "'"); } // Hmm; I guess let's just return the key to help identify/fix the issue?.. @@ -1180,11 +1264,13 @@ auto Python::GetTranslation(const char* category, const char* s) try { return GetPyString(results.get()); } catch (const std::exception&) { - Log("GetTranslation failed for '" + std::string(category) + "'"); + Log(LogLevel::kError, + "GetTranslation failed for '" + std::string(category) + "'"); return ""; } } else { - Log("GetTranslation failed for category '" + std::string(category) + "'"); + Log(LogLevel::kError, + "GetTranslation failed for category '" + std::string(category) + "'"); } return ""; } @@ -1192,11 +1278,11 @@ auto Python::GetTranslation(const char* category, const char* s) void Python::RunDeepLink(const std::string& url) { assert(InLogicThread()); if (objexists(ObjID::kDeepLinkCall)) { - ScopedSetContext cp(g_game->GetUIContext()); + ScopedSetContext cp(g_logic->GetUIContext()); PythonRef args(Py_BuildValue("(s)", url.c_str()), PythonRef::kSteal); obj(ObjID::kDeepLinkCall).Call(args); } else { - Log("Error on deep-link call"); + Log(LogLevel::kError, "Error on deep-link call"); } } @@ -1217,11 +1303,11 @@ void Python::PlayMusic(const std::string& music_type, bool continuous) { void Python::ShowURL(const std::string& url) { if (objexists(ObjID::kShowURLWindowCall)) { - ScopedSetContext cp(g_game->GetUIContext()); + ScopedSetContext cp(g_logic->GetUIContext()); PythonRef args(Py_BuildValue("(s)", url.c_str()), PythonRef::kSteal); obj(ObjID::kShowURLWindowCall).Call(args); } else { - Log("Error: ShowURLWindowCall nonexistent."); + Log(LogLevel::kError, "ShowURLWindowCall nonexistent."); } } @@ -1245,7 +1331,7 @@ auto Python::SingleMemberTuple(const PythonRef& member) -> PythonRef { auto Python::FilterChatMessage(std::string* message, int client_id) -> bool { assert(message); - ScopedSetContext cp(g_game->GetUIContext()); + ScopedSetContext cp(g_logic->GetUIContext()); PythonRef args(Py_BuildValue("(si)", message->c_str(), client_id), PythonRef::kSteal); PythonRef result = obj(ObjID::kFilterChatMessageCall).Call(args); @@ -1264,44 +1350,18 @@ auto Python::FilterChatMessage(std::string* message, int client_id) -> bool { try { *message = Python::GetPyString(result.get()); } catch (const std::exception& e) { - Log("Error getting string from chat filter: " + std::string(e.what())); + Log(LogLevel::kError, + "Error getting string from chat filter: " + std::string(e.what())); } return true; } void Python::HandleLocalChatMessage(const std::string& message) { - ScopedSetContext cp(g_game->GetUIContext()); + ScopedSetContext cp(g_logic->GetUIContext()); PythonRef args(Py_BuildValue("(s)", message.c_str()), PythonRef::kSteal); obj(ObjID::kHandleLocalChatMessageCall).Call(args); } -void Python::DispatchScoresToBeatResponse( - bool success, const std::list& scores_to_beat, - void* callback_in) { - // callback_in was a newly allocated PythonContextCall. - // This will make it ref-counted so it'll die when we're done with it - auto callback( - Object::MakeRefCounted(static_cast(callback_in))); - - // Empty type denotes error. - if (!success) { - PythonRef args(Py_BuildValue("(O)", Py_None), PythonRef::kSteal); - callback->Run(args); - } else { - PyObject* py_list = PyList_New(0); - for (const auto& i : scores_to_beat) { - PyObject* val = Py_BuildValue("{sssssssd}", "player", i.player.c_str(), - "type", i.type.c_str(), "value", - i.value.c_str(), "time", i.time); - PyList_Append(py_list, val); - Py_DECREF(val); - } - PythonRef args(Py_BuildValue("(O)", py_list), PythonRef::kSteal); - Py_DECREF(py_list); - callback->Run(args); - } -} - // Put together a node message with all args on the provided tuple (starting // with arg_offset) returns false on failure, true on success. void Python::DoBuildNodeMessage(PyObject* args, int arg_offset, Buffer* b, @@ -1506,7 +1566,7 @@ auto Python::GetPythonFileLocation(bool pretty) -> std::string { void Python::SetNodeAttr(Node* node, const char* attr_name, PyObject* value_obj) { assert(node); - GameStream* out_stream = node->scene()->GetGameStream(); + SceneStream* out_stream = node->scene()->GetSceneStream(); NodeAttribute attr = node->GetAttribute(attr_name); switch (attr.type()) { case NodeAttributeType::kFloat: { @@ -1776,9 +1836,9 @@ auto Python::DoNewNode(PyObject* args, PyObject* keywds) -> Node* { attr_vals.emplace_back( t->GetAttribute(std::string(PyUnicode_AsUTF8(key))), value); } catch (const std::exception&) { - Log("ERROR: Attr not found on initial attr set: '" - + std::string(PyUnicode_AsUTF8(key)) + "' on " + type + " node '" - + name + "'"); + Log(LogLevel::kError, "Attr not found on initial attr set: '" + + std::string(PyUnicode_AsUTF8(key)) + "' on " + + type + " node '" + name + "'"); } } @@ -1788,8 +1848,9 @@ auto Python::DoNewNode(PyObject* args, PyObject* keywds) -> Node* { try { SetNodeAttr(node, i.first->name().c_str(), i.second); } catch (const std::exception& e) { - Log("ERROR: exception in initial attr set for attr '" + i.first->name() - + "' on " + type + " node '" + name + "':" + e.what()); + Log(LogLevel::kError, "Exception in initial attr set for attr '" + + i.first->name() + "' on " + type + " node '" + + name + "':" + e.what()); } } } @@ -1802,10 +1863,12 @@ auto Python::DoNewNode(PyObject* args, PyObject* keywds) -> Node* { if (PythonClassNode::Check(owner_obj)) { Node* owner_node = GetPyNode(owner_obj, true); if (owner_node == nullptr) { - Log("ERROR: empty node-ref passed for 'owner'; pass None if you want " + Log(LogLevel::kError, + "Empty node-ref passed for 'owner'; pass None if you want " "no owner."); } else if (owner_node->scene() != node->scene()) { - Log("ERROR: owner node is from a different scene; ignoring."); + Log(LogLevel::kError, + "Owner node is from a different scene; ignoring."); } else { owner_node->AddDependentNode(node); } @@ -1820,13 +1883,14 @@ auto Python::DoNewNode(PyObject* args, PyObject* keywds) -> Node* { // do. try { // Tell clients to do the same. - if (GameStream* output_stream = scene->GetGameStream()) { + if (SceneStream* output_stream = scene->GetSceneStream()) { output_stream->NodeOnCreate(node); } node->OnCreate(); } catch (const std::exception& e) { - Log("ERROR: exception in OnCreate() for node " - + ballistica::ObjToString(node) + "':" + e.what()); + Log(LogLevel::kError, "Exception in OnCreate() for node " + + ballistica::ObjToString(node) + + "':" + e.what()); } return node; @@ -2034,24 +2098,25 @@ auto Python::GetNodeAttr(Node* node, const char* attr_name) -> PyObject* { } void Python::IssueCallInLogicThreadWarning(PyObject* call_obj) { - Log("WARNING: ba.pushcall() called from the game thread with " + Log(LogLevel::kWarning, + "ba.pushcall() called from the logic thread with " "from_other_thread set to true (call " - + ObjToString(call_obj) + " at " + GetPythonFileLocation() - + "). That arg should only be used from other threads."); + + ObjToString(call_obj) + " at " + GetPythonFileLocation() + + "). That arg should only be used from other threads."); } void Python::LaunchStringEdit(TextWidget* w) { assert(InLogicThread()); BA_PRECONDITION(w); - ScopedSetContext cp(g_game->GetUIContext()); - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kSwish)); + ScopedSetContext cp(g_logic->GetUIContext()); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kSwish)); // Gotta run this in the next cycle. PythonRef args(Py_BuildValue("(Osi)", w->BorrowPyRef(), w->description().c_str(), w->max_chars()), PythonRef::kSteal); - g_game->PushPythonCallArgs( + g_logic->PushPythonCallArgs( Object::New(obj(ObjID::kOnScreenKeyboardClass).get()), args); } @@ -2094,11 +2159,11 @@ void Python::HandleFriendScoresCB(const FriendScoreSet& score_set) { PyObject* py_list = PyList_New(0); std::string icon_str; #if BA_USE_GOOGLE_PLAY_GAME_SERVICES - icon_str = g_game->CharStr(SpecialChar::kGooglePlayGamesLogo); + icon_str = g_logic->CharStr(SpecialChar::kGooglePlayGamesLogo); #elif BA_USE_GAME_CIRCLE - icon_str = g_game->CharStr(SpecialChar::kGameCircleLogo); + icon_str = g_logic->CharStr(SpecialChar::kGameCircleLogo); #elif BA_USE_GAME_CENTER - icon_str = g_game->CharStr(SpecialChar::kGameCenterLogo); + icon_str = g_logic->CharStr(SpecialChar::kGameCenterLogo); #endif for (auto&& i : score_set.entries) { PyObject* obj = @@ -2118,7 +2183,7 @@ auto Python::HandleKeyPressEvent(const SDL_Keysym& keysym) -> bool { if (!keyboard_call_.exists()) { return false; } - ScopedSetContext cp(g_game->GetUIContextTarget()); + ScopedSetContext cp(g_logic->GetUIContextTarget()); InputDevice* keyboard = g_input->keyboard_input(); PythonRef args( Py_BuildValue("({s:s,s:i,s:O})", "type", "BUTTONDOWN", "button", @@ -2133,7 +2198,7 @@ auto Python::HandleKeyReleaseEvent(const SDL_Keysym& keysym) -> bool { if (!keyboard_call_.exists()) { return false; } - ScopedSetContext cp(g_game->GetUIContextTarget()); + ScopedSetContext cp(g_logic->GetUIContextTarget()); InputDevice* keyboard = g_input->keyboard_input(); PythonRef args(Py_BuildValue("({s:s,s:i,s:O})", "type", "BUTTONUP", "button", static_cast(keysym.sym), "input_device", @@ -2150,7 +2215,7 @@ auto Python::HandleJoystickEvent(const SDL_Event& event, if (!game_pad_call_.exists()) { return false; } - ScopedSetContext cp(g_game->GetUIContextTarget()); + ScopedSetContext cp(g_logic->GetUIContextTarget()); InputDevice* device{}; device = input_device; @@ -2248,64 +2313,67 @@ auto Python::GetContextBaseString() -> std::string { return s; } -void Python::LogContextForCallableLabel(const char* label) { +void Python::PrintContextForCallableLabel(const char* label) { assert(InLogicThread()); assert(label); std::string s = std::string(" root call: ") + label; s += g_python->GetContextBaseString(); - Log(s); + PySys_WriteStderr("%s\n", s.c_str()); } -void Python::LogContextNonLogicThread() { +void Python::PrintContextNonLogicThread() { std::string s = - std::string(" root call: "); - Log(s); + std::string(" root call: "); + PySys_WriteStderr("%s\n", s.c_str()); } -void Python::LogContextEmpty() { +void Python::PrintContextEmpty() { assert(InLogicThread()); std::string s = std::string(" root call: "); s += g_python->GetContextBaseString(); - Log(s); + PySys_WriteStderr("%s\n", s.c_str()); } -void Python::LogContextAuto() { +void Python::PrintContextAuto() { // Lets print whatever context info is available. // FIXME: If we have recursive calls this may not print // the context we'd expect; we'd need a unified stack. if (!InLogicThread()) { - LogContextNonLogicThread(); + PrintContextNonLogicThread(); } else if (const char* label = ScopedCallLabel::current_label()) { - LogContextForCallableLabel(label); + PrintContextForCallableLabel(label); } else if (PythonCommand* cmd = PythonCommand::current_command()) { - cmd->LogContext(); + cmd->PrintContext(); } else if (PythonContextCall* call = PythonContextCall::current_call()) { - call->LogContext(); + call->PrintContext(); } else { - LogContextEmpty(); + PrintContextEmpty(); } } void Python::AcquireGIL() { + assert(InLogicThread()); auto debug_timing{g_app->debug_timing}; millisecs_t startms{debug_timing ? Platform::GetCurrentMilliseconds() : 0}; - if (thread_state_) { - PyEval_RestoreThread(thread_state_); - thread_state_ = nullptr; + if (logic_thread_state_) { + PyEval_RestoreThread(logic_thread_state_); + logic_thread_state_ = nullptr; } if (debug_timing) { auto duration{Platform::GetCurrentMilliseconds() - startms}; if (duration > (1000 / 120)) { - Log("GIL acquire took too long (" + std::to_string(duration) + " ms).", - true, false); + Log(LogLevel::kInfo, + "GIL acquire took too long (" + std::to_string(duration) + " ms)."); } } } + void Python::ReleaseGIL() { - assert(thread_state_ == nullptr); - thread_state_ = PyEval_SaveThread(); + assert(InLogicThread()); + assert(logic_thread_state_ == nullptr); + logic_thread_state_ = PyEval_SaveThread(); } void Python::AddCleanFrameCommand(const Object::Ref& c) { @@ -2363,7 +2431,7 @@ void Python::HandleDeviceMenuPress(InputDevice* input_device) { if (g_input->IsInputLocked()) { return; } - ScopedSetContext cp(g_game->GetUIContext()); + ScopedSetContext cp(g_logic->GetUIContext()); PythonRef args(Py_BuildValue("(O)", input_device ? input_device->BorrowPyRef() : Py_None), PythonRef::kSteal); @@ -2483,7 +2551,8 @@ auto Python::GetRawConfigValue(const char* name, float default_value) -> float { try { return GetPyFloat(value); } catch (const std::exception&) { - Log("expected a float for config value '" + std::string(name) + "'"); + Log(LogLevel::kError, + "expected a float for config value '" + std::string(name) + "'"); return default_value; } } @@ -2503,7 +2572,8 @@ auto Python::GetRawConfigValue(const char* name, } return GetPyFloat(value); } catch (const std::exception&) { - Log("expected a float for config value '" + std::string(name) + "'"); + Log(LogLevel::kError, + "expected a float for config value '" + std::string(name) + "'"); return default_value; } } @@ -2518,7 +2588,8 @@ auto Python::GetRawConfigValue(const char* name, int default_value) -> int { try { return static_cast_check_fit(GetPyInt64(value)); } catch (const std::exception&) { - Log("Expected an int value for config value '" + std::string(name) + "'."); + Log(LogLevel::kError, + "Expected an int value for config value '" + std::string(name) + "'."); return default_value; } } @@ -2533,7 +2604,8 @@ auto Python::GetRawConfigValue(const char* name, bool default_value) -> bool { try { return GetPyBool(value); } catch (const std::exception&) { - Log("Expected a bool value for config value '" + std::string(name) + "'."); + Log(LogLevel::kError, + "Expected a bool value for config value '" + std::string(name) + "'."); return default_value; } } @@ -2556,7 +2628,7 @@ void Python::TimeFormatCheck(TimeFormat time_format, PyObject* length_obj) { if (length >= 200.0) { static bool warned = false; if (!warned) { - Log("Warning: time value " + Log(LogLevel::kWarning, "Time value " +std::to_string(length)+" passed as seconds;" " did you mean milliseconds?" " (if so, pass suppress_format_warning=True to stop this warning)"); @@ -2570,7 +2642,7 @@ void Python::TimeFormatCheck(TimeFormat time_format, PyObject* length_obj) { if (length < 1.0 && length > 0.0000001) { static bool warned = false; if (!warned) { - Log("Warning: time value " + Log(LogLevel::kWarning, "Time value " + std::to_string(length) + " passed as milliseconds;" " did you mean seconds?" " (if so, pass suppress_format_warning=True to stop this warning)"); @@ -2581,8 +2653,9 @@ void Python::TimeFormatCheck(TimeFormat time_format, PyObject* length_obj) { } else { static bool warned = false; if (!warned) { - BA_LOG_ONCE("TimeFormatCheck got timeformat value: '" - + std::to_string(static_cast(time_format)) + "'"); + BA_LOG_ONCE(LogLevel::kError, + "TimeFormatCheck got timeformat value: '" + + std::to_string(static_cast(time_format)) + "'"); warned = true; } } diff --git a/src/ballistica/python/python.h b/src/ballistica/python/python.h index bd3e82a1..a48e847b 100644 --- a/src/ballistica/python/python.h +++ b/src/ballistica/python/python.h @@ -5,6 +5,7 @@ #include #include +#include #include #include #include @@ -42,7 +43,7 @@ class Python { /// Use this to protect Python code that may be run in cases where we don't /// hold the Global Interpreter Lock (GIL) (basically anything outside of the - /// game thread). + /// logic thread). class ScopedInterpreterLock { public: ScopedInterpreterLock(); @@ -75,14 +76,23 @@ class Python { auto ValidatedPackageAssetName(PyObject* package, const char* name) -> std::string; - static auto LogContextForCallableLabel(const char* label) -> void; - static auto LogContextEmpty() -> void; - static auto LogContextAuto() -> void; - static auto LogContextNonLogicThread() -> void; - Python(); - ~Python(); + /// Calls Python logging function (logging.error, logging.warning, etc.) + /// Can be called from any thread at any time. If called before Python + /// logging is available, logs locally using Logging::DisplayLog() + /// (with an added warning). + auto LoggingCall(LogLevel loglevel, const std::string& msg) -> void; - auto Reset(bool init = true) -> void; + // Print various context debugging bits to Python's sys.stderr. + static auto PrintContextForCallableLabel(const char* label) -> void; + static auto PrintContextEmpty() -> void; + static auto PrintContextAuto() -> void; + static auto PrintContextNonLogicThread() -> void; + Python(); + static auto Create() -> Python*; + + auto InitCorePython() -> void; + auto InitBallisticaPython() -> void; + auto Reset() -> void; /// Add classes to the newly created ba module. static auto InitModuleClasses(PyObject* module) -> void; @@ -111,7 +121,7 @@ class Python { /// results. static auto generic_dir(PyObject* self) -> PyObject*; - /// For use by g_game in passing events along to the python layer (for + /// For use by g_logic in passing events along to the python layer (for /// captured input, etc). auto HandleJoystickEvent(const SDL_Event& event, InputDevice* input_device = nullptr) -> bool; @@ -127,10 +137,6 @@ class Python { /// Pass a chat message along to the python UI layer for handling.. auto HandleLocalChatMessage(const std::string& message) -> void; - auto DispatchScoresToBeatResponse( - bool success, const std::list& scores_to_beat, - void* PyCallback) -> void; - /// Pop up an in-game window to show a url (NOT in a browser). auto ShowURL(const std::string& url) -> void; @@ -297,7 +303,7 @@ class Python { kVROrientationResetCBMessageCall, kVROrientationResetMessageCall, kHandleAppResumeCall, - kHandleLogCall, + kHandleV1CloudLogCall, kLaunchMainMenuSessionCall, kLanguageTestToggleCall, kAwardInControlAchievementCall, @@ -354,6 +360,11 @@ class Python { kUUIDStrCall, kHashStringsCall, kHaveAccountV2CredentialsCall, + kLoggingDebugCall, + kLoggingInfoCall, + kLoggingWarningCall, + kLoggingErrorCall, + kLoggingCriticalCall, kLast // Sentinel; must be at end. }; @@ -381,7 +392,7 @@ class Python { /// Create a Python single-member tuple. auto SingleMemberTuple(const PythonRef& member) -> PythonRef; - /// Push a call to a preset obj to the game thread + /// Push a call to a preset obj to the logic thread /// (will be run in the UI context). auto PushObjCall(ObjID obj) -> void; @@ -422,13 +433,15 @@ class Python { std::set do_once_locations_; PythonRef objs_[static_cast(ObjID::kLast)]; bool inited_{}; - std::list > clean_frame_commands_; + std::list> clean_frame_commands_; + std::mutex early_log_lock_; + std::list> early_logs_; PythonRef game_pad_call_; PythonRef keyboard_call_; PyObject* empty_dict_object_{}; PyObject* main_dict_{}; PyObject* env_{}; - PyThreadState* thread_state_{}; + PyThreadState* logic_thread_state_{}; }; } // namespace ballistica diff --git a/src/ballistica/python/python_command.cc b/src/ballistica/python/python_command.cc index 187bde06..54553315 100644 --- a/src/ballistica/python/python_command.cc +++ b/src/ballistica/python/python_command.cc @@ -85,7 +85,7 @@ auto PythonCommand::Run() -> bool { if (!g_python) { // This probably means the game is dying; let's not // throw an exception here so we don't mask the original error. - Log("PythonCommand: not running due to null g_python"); + Log(LogLevel::kError, "PythonCommand: not running due to null g_python."); return false; } assert(!dead_); @@ -99,14 +99,14 @@ auto PythonCommand::Run() -> bool { g_python->main_dict()); POP_PYCOMMAND(); - // Technically the python call could have killed us; + // Technically the Python call could have killed us; // make sure that didn't happen. assert(!dead_); if (v == nullptr) { // Save/restore error or it can mess with context print calls. BA_PYTHON_ERROR_SAVE; - Log("ERROR: exception in Python call:"); - LogContext(); + PySys_WriteStderr("Exception in Python call:\n"); + PrintContext(); BA_PYTHON_ERROR_RESTORE; // We pass zero here to avoid grabbing references to this exception @@ -158,8 +158,8 @@ auto PythonCommand::RunReturnObj(bool print_errors, PyObject* context) if (print_errors) { // Save/restore error or it can mess with context print calls. BA_PYTHON_ERROR_SAVE; - Log("ERROR: exception in Python call:"); - LogContext(); + PySys_WriteStderr("Exception in Python call:\n"); + PrintContext(); BA_PYTHON_ERROR_RESTORE; // We pass zero here to avoid grabbing references to this exception // which can cause objects to stick around and trip up our deletion checks @@ -181,8 +181,8 @@ auto PythonCommand::RunReturnObj(bool print_errors, PyObject* context) if (print_errors) { // save/restore error or it can mess with context print calls BA_PYTHON_ERROR_SAVE; - Log("ERROR: exception in Python call:"); - LogContext(); + PySys_WriteStderr("Exception in Python call:\n"); + PrintContext(); BA_PYTHON_ERROR_RESTORE; // we pass zero here to avoid grabbing references to this exception // which can cause objects to stick around and trip up our deletion checks @@ -199,11 +199,11 @@ auto PythonCommand::RunReturnObj(bool print_errors, PyObject* context) return v; } -void PythonCommand::LogContext() { +void PythonCommand::PrintContext() { assert(Python::HaveGIL()); std::string s = std::string(" call: ") + command(); s += g_python->GetContextBaseString(); - Log(s); + PySys_WriteStderr("%s\n", s.c_str()); } } // namespace ballistica diff --git a/src/ballistica/python/python_command.h b/src/ballistica/python/python_command.h index f91e729f..8926e2d5 100644 --- a/src/ballistica/python/python_command.h +++ b/src/ballistica/python/python_command.h @@ -51,7 +51,7 @@ class PythonCommand { /// Returns nullptr on errors, but Python error state will be cleared. auto RunReturnObj(bool print_errors, PyObject* context) -> PyObject*; - void LogContext(); + void PrintContext(); /// Return true if the command can be evaluated; otherwise it can only be /// executed diff --git a/src/ballistica/python/python_context_call.cc b/src/ballistica/python/python_context_call.cc index 958e599c..938e2dd1 100644 --- a/src/ballistica/python/python_context_call.cc +++ b/src/ballistica/python/python_context_call.cc @@ -2,8 +2,8 @@ #include "ballistica/python/python_context_call.h" -#include "ballistica/game/host_activity.h" -#include "ballistica/game/session/host_session.h" +#include "ballistica/logic/host_activity.h" +#include "ballistica/logic/session/host_session.h" #include "ballistica/python/python.h" #include "ballistica/python/python_sys.h" @@ -14,17 +14,17 @@ PythonContextCall* PythonContextCall::current_call_ = nullptr; PythonContextCall::PythonContextCall(PyObject* obj_in) { assert(InLogicThread()); - // as a sanity test, store the current context ptr just to make sure it - // hasn't changed when we run + // As a sanity test, store the current context ptr just to make sure it + // hasn't changed when we run. #if BA_DEBUG_BUILD context_target_sanity_test_ = context_.target.get(); #endif // BA_DEBUG_BUILD BA_PRECONDITION(PyCallable_Check(obj_in)); object_.Acquire(obj_in); GetTrace(); - // ok now we need to register this call with whatever the context is; + // Ok now we need to register this call with whatever the context is; // it can be stored in a host-activity, a host-session, or the UI context. - // whoever it is registered with will explicitly release its contents on + // Whoever it is registered with will explicitly release its contents on // shutdown and ensure that nothing gets run after that point. if (HostActivity* ha = context_.GetHostActivity()) { ha->RegisterCall(this); @@ -75,7 +75,7 @@ void PythonContextCall::Run(PyObject* args) { if (!g_python) { // This probably means the game is dying; let's not // throw an exception here so we don't mask the original error. - Log("PythonCommand: not running due to null g_python"); + Log(LogLevel::kError, "PythonCommand: not running due to null g_python"); return; } @@ -86,7 +86,8 @@ void PythonContextCall::Run(PyObject* args) { // Sanity test: make sure our context didn't go away. #if BA_DEBUG_BUILD if (context_.target.get() != context_target_sanity_test_) { - Log("WARNING: running Call after it's context has died: " + object_.Str()); + Log(LogLevel::kWarning, + "Running Call after it's context has died: " + object_.Str()); } #endif // BA_DEBUG_BUILD @@ -112,8 +113,8 @@ void PythonContextCall::Run(PyObject* args) { // Save/restore python error or it can mess with context print calls. BA_PYTHON_ERROR_SAVE; - Log("ERROR: exception in Python call:"); - LogContext(); + PySys_WriteStderr("Exception in Python call:\n"); + PrintContext(); BA_PYTHON_ERROR_RESTORE; // We pass zero here to avoid grabbing references to this exception @@ -124,12 +125,12 @@ void PythonContextCall::Run(PyObject* args) { } } -void PythonContextCall::LogContext() { +void PythonContextCall::PrintContext() { assert(InLogicThread()); std::string s = std::string(" root call: ") + object().Str(); s += ("\n root call origin: " + file_loc()); s += g_python->GetContextBaseString(); - Log(s); + PySys_WriteStderr("%s\n", s.c_str()); } } // namespace ballistica diff --git a/src/ballistica/python/python_context_call.h b/src/ballistica/python/python_context_call.h index f91100cb..9b04ff1a 100644 --- a/src/ballistica/python/python_context_call.h +++ b/src/ballistica/python/python_context_call.h @@ -31,7 +31,7 @@ class PythonContextCall : public Object { void MarkDead(); auto object() const -> const PythonRef& { return object_; } auto file_loc() const -> const std::string& { return file_loc_; } - void LogContext(); + auto PrintContext() -> void; private: void GetTrace(); // we try to grab basic trace info diff --git a/src/ballistica/python/python_ref.cc b/src/ballistica/python/python_ref.cc index a91b6ada..6121b029 100644 --- a/src/ballistica/python/python_ref.cc +++ b/src/ballistica/python/python_ref.cc @@ -13,7 +13,6 @@ namespace ballistica { #pragma ide diagnostic ignored "RedundantCast" PythonRef::PythonRef(PyObject* obj_in, ReferenceBehavior b) { - assert(g_python); assert(Python::HaveGIL()); switch (b) { case kSteal: @@ -54,7 +53,6 @@ void PythonRef::Acquire(PyObject* obj_in) { void PythonRef::Steal(PyObject* obj_in) { BA_PRECONDITION(obj_in); - assert(g_python); assert(Python::HaveGIL()); // Assign before decrementing the old @@ -67,7 +65,6 @@ void PythonRef::Steal(PyObject* obj_in) { } void PythonRef::Release() { - assert(g_python); assert(Python::HaveGIL()); // Py_CLEAR uses a temp variable and assigns o to nullptr first @@ -113,7 +110,6 @@ auto PythonRef::ValueAsInt() const -> int64_t { } auto PythonRef::GetAttr(const char* name) const -> PythonRef { - assert(g_python); assert(Python::HaveGIL()); BA_PRECONDITION(obj_); PyObject* val = PyObject_GetAttrString(get(), name); @@ -149,7 +145,6 @@ auto PythonRef::UnicodeCheck() const -> bool { auto PythonRef::Call(PyObject* args, PyObject* keywds, bool print_errors) const -> PythonRef { assert(obj_); - assert(g_python); assert(Python::HaveGIL()); assert(CallableCheck()); assert(args); @@ -160,8 +155,8 @@ auto PythonRef::Call(PyObject* args, PyObject* keywds, bool print_errors) const if (print_errors) { // Save/restore error or it can mess with context print calls. BA_PYTHON_ERROR_SAVE; - Log("ERROR: exception in Python call:"); - Python::LogContextAuto(); + PySys_WriteStderr("Exception in Python call:\n"); + Python::PrintContextAuto(); BA_PYTHON_ERROR_RESTORE; // We pass zero here to avoid grabbing references to this exception diff --git a/src/ballistica/python/python_sys.h b/src/ballistica/python/python_sys.h index dd4068bd..0e53b373 100644 --- a/src/ballistica/python/python_sys.h +++ b/src/ballistica/python/python_sys.h @@ -3,11 +3,11 @@ #ifndef BALLISTICA_PYTHON_PYTHON_SYS_H_ #define BALLISTICA_PYTHON_PYTHON_SYS_H_ -// Any code that actually runs any python logic should include this. -// This header pulls in the actual python includes and also defines some handy -// macros and functions for working with python objects. +// Any code that actually runs any Python logic should include this. +// This header pulls in the actual Python includes and also defines some handy +// macros and functions for working with Python objects. -// This is the ONE place we actually include python. +// This is the ONE place we actually include Python. #include #include #include @@ -60,12 +60,12 @@ ((void)0) // For use in tp_dealloc; simply prints the error. -#define BA_PYTHON_DEALLOC_CATCH \ - } \ - catch (const std::exception& e) { \ - Log(std::string("Error: tp_dealloc exception: ") \ - + GetShortExceptionDescription(e)); \ - } \ +#define BA_PYTHON_DEALLOC_CATCH \ + } \ + catch (const std::exception& e) { \ + Log(LogLevel::kError, std::string("tp_dealloc exception: ") \ + + GetShortExceptionDescription(e)); \ + } \ ((void)0) // Sets Python error and returns -1. diff --git a/src/ballistica/scene/node/anim_curve_node.cc b/src/ballistica/scene/node/anim_curve_node.cc index ab60be8a..94c796e8 100644 --- a/src/ballistica/scene/node/anim_curve_node.cc +++ b/src/ballistica/scene/node/anim_curve_node.cc @@ -2,6 +2,8 @@ #include "ballistica/scene/node/anim_curve_node.h" +#include + #include "ballistica/scene/node/node_attribute.h" #include "ballistica/scene/node/node_type.h" diff --git a/src/ballistica/scene/node/bomb_node.cc b/src/ballistica/scene/node/bomb_node.cc index 6620a487..844267c1 100644 --- a/src/ballistica/scene/node/bomb_node.cc +++ b/src/ballistica/scene/node/bomb_node.cc @@ -2,8 +2,8 @@ #include "ballistica/scene/node/bomb_node.h" +#include "ballistica/assets/component/collide_model.h" #include "ballistica/graphics/graphics.h" -#include "ballistica/media/component/collide_model.h" #include "ballistica/scene/scene.h" namespace ballistica { diff --git a/src/ballistica/scene/node/combine_node.cc b/src/ballistica/scene/node/combine_node.cc index e4948921..0d94572d 100644 --- a/src/ballistica/scene/node/combine_node.cc +++ b/src/ballistica/scene/node/combine_node.cc @@ -41,7 +41,7 @@ auto CombineNode::GetOutput() -> std::vector { if (dirty_) { if (do_size_unset_warning_) { do_size_unset_warning_ = false; - BA_LOG_ONCE("ERROR: CombineNode size unset for " + label()); + BA_LOG_ONCE(LogLevel::kError, "CombineNode size unset for " + label()); } int actual_size = std::min(4, std::max(0, size_)); output_.resize(static_cast(actual_size)); diff --git a/src/ballistica/scene/node/explosion_node.cc b/src/ballistica/scene/node/explosion_node.cc index 60fbcb38..27f6367e 100644 --- a/src/ballistica/scene/node/explosion_node.cc +++ b/src/ballistica/scene/node/explosion_node.cc @@ -143,7 +143,7 @@ void ExplosionNode::Draw(FrameDef* frame_def) { 1.0f + s * 0.8f * 0.0015f * age, 1.0f + s * 0.8f * 0.025f * age); c.Scale(0.7f, 0.7f, 0.7f); - c.DrawModel(g_media->GetModel(SystemModelID::kShockWave), + c.DrawModel(g_assets->GetModel(SystemModelID::kShockWave), kModelDrawFlagNoReflection); c.PopTransform(); c.Submit(); @@ -161,7 +161,7 @@ void ExplosionNode::Draw(FrameDef* frame_def) { 1.0f + s * 0.8f * 0.0015f * age, 1.0f + s * 0.8f * 0.025f * age); c.Scale(0.7f, 0.7f, 0.7f); - c.DrawModel(g_media->GetModel(SystemModelID::kShockWave), + c.DrawModel(g_assets->GetModel(SystemModelID::kShockWave), kModelDrawFlagNoReflection); c.PopTransform(); c.Submit(); @@ -198,7 +198,7 @@ void ExplosionNode::Draw(FrameDef* frame_def) { c.SetTransparent(true); c.SetLightShadow(LightShadowType::kNone); c.SetPremultiplied(true); - c.SetTexture(g_media->GetTexture(SystemTextureID::kExplosion)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kExplosion)); c.SetColor(1.3f * o * color_[0] * b, o * color_[1] * b, o * color_[2] * b, 0.0f); c.PushTransform(); @@ -211,13 +211,13 @@ void ExplosionNode::Draw(FrameDef* frame_def) { Matrix44f om = Matrix44fOrient(right, to_cam, up); c.MultMatrix((om * m).m); c.Scale(0.9f * s, 0.9f * s, 0.9f * s); - c.DrawModel(g_media->GetModel(SystemModelID::kShield), + c.DrawModel(g_assets->GetModel(SystemModelID::kShield), kModelDrawFlagNoReflection); c.Scale(0.6f, 0.6f, 0.6f); c.Rotate(33, 0, 1, 0); c.SetColor(o * 7.0f * color_[0], o * 7.0f * color_[1], o * 7.0f * color_[2], 0); - c.DrawModel(g_media->GetModel(SystemModelID::kShield), + c.DrawModel(g_assets->GetModel(SystemModelID::kShield), kModelDrawFlagNoReflection); c.PopTransform(); c.Submit(); diff --git a/src/ballistica/scene/node/flag_node.cc b/src/ballistica/scene/node/flag_node.cc index 20c6b4cd..b67367a4 100644 --- a/src/ballistica/scene/node/flag_node.cc +++ b/src/ballistica/scene/node/flag_node.cc @@ -307,7 +307,7 @@ void FlagNode::Draw(FrameDef* frame_def) { float s_scale, s_density; SimpleComponent c(frame_def->light_shadow_pass()); - c.SetTexture(g_media->GetTexture(SystemTextureID::kShadow)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kShadow)); c.SetTransparent(true); FullShadowSet* full_shadows = full_shadow_set_.get(); @@ -353,12 +353,12 @@ void FlagNode::Draw(FrameDef* frame_def) { // Flag pole. { ObjectComponent c(frame_def->beauty_pass()); - c.SetTexture(g_media->GetTexture(SystemTextureID::kFlagPole)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kFlagPole)); c.SetReflection(ReflectionType::kSharp); c.SetReflectionScale(0.1f, 0.1f, 0.1f); c.PushTransform(); c.TransformToBody(*body_); - c.DrawModel(g_media->GetModel(SystemModelID::kFlagPole)); + c.DrawModel(g_assets->GetModel(SystemModelID::kFlagPole)); c.PopTransform(); c.Submit(); } diff --git a/src/ballistica/scene/node/flash_node.cc b/src/ballistica/scene/node/flash_node.cc index 326b4692..dd1bf589 100644 --- a/src/ballistica/scene/node/flash_node.cc +++ b/src/ballistica/scene/node/flash_node.cc @@ -50,7 +50,7 @@ void FlashNode::Draw(FrameDef* frame_def) { c.Translate(position_[0], position_[1], position_[2]); c.Scale(size_, size_, size_); c.Rotate(RandomFloat() * 360.0f, 1, 1, 0); - c.DrawModel(g_media->GetModel(SystemModelID::kFlash)); + c.DrawModel(g_assets->GetModel(SystemModelID::kFlash)); c.PopTransform(); c.Submit(); } diff --git a/src/ballistica/scene/node/globals_node.cc b/src/ballistica/scene/node/globals_node.cc index 9cc39704..be43714b 100644 --- a/src/ballistica/scene/node/globals_node.cc +++ b/src/ballistica/scene/node/globals_node.cc @@ -4,9 +4,9 @@ #include "ballistica/audio/audio.h" #include "ballistica/dynamics/bg/bg_dynamics.h" -#include "ballistica/game/host_activity.h" #include "ballistica/graphics/camera.h" #include "ballistica/graphics/graphics.h" +#include "ballistica/logic/host_activity.h" #include "ballistica/python/python.h" #include "ballistica/scene/node/node_attribute.h" #include "ballistica/scene/node/node_type.h" @@ -106,7 +106,8 @@ GlobalsNode::GlobalsNode(Scene* scene) : Node(scene, node_type) { // FIXME: Need to update this for non-host activities at some point. if (HostActivity* ha = context().GetHostActivity()) { if (ha->globals_node()) { - Log("WARNING: more than one globals node created in HostActivity; this " + Log(LogLevel::kWarning, + "More than one globals node created in HostActivity; this " "shouldn't happen"); } ha->SetGlobalsNode(this); @@ -121,7 +122,7 @@ GlobalsNode::GlobalsNode(Scene* scene) : Node(scene, node_type) { // If our scene is currently the game's foreground one, go ahead and // push our values globally. - if (g_game->GetForegroundScene() == this->scene()) { + if (g_logic->GetForegroundScene() == this->scene()) { SetAsForeground(); } } @@ -154,7 +155,7 @@ auto GlobalsNode::IsCurrentGlobals() const -> bool { // node for our scene. Scene* scene = this->scene(); assert(scene); - return (g_game->GetForegroundScene() == this->scene() + return (g_logic->GetForegroundScene() == this->scene() && scene->globals_node() == this); } diff --git a/src/ballistica/scene/node/image_node.cc b/src/ballistica/scene/node/image_node.cc index c9e1d723..fd312a88 100644 --- a/src/ballistica/scene/node/image_node.cc +++ b/src/ballistica/scene/node/image_node.cc @@ -331,7 +331,7 @@ void ImageNode::Draw(FrameDef* frame_def) { if (vr && fill_screen_) { #if BA_VR_BUILD model_opaque_used = - g_media->GetModel(SystemModelID::kImage1x1VRFullScreen); + g_assets->GetModel(SystemModelID::kImage1x1VRFullScreen); #else throw Exception(); #endif // BA_VR_BUILD @@ -339,9 +339,9 @@ void ImageNode::Draw(FrameDef* frame_def) { SystemModelID m = fill_screen_ ? SystemModelID::kImage1x1FullScreen : SystemModelID::kImage1x1; if (has_alpha_channel) { - model_transparent_used = g_media->GetModel(m); + model_transparent_used = g_assets->GetModel(m); } else { - model_opaque_used = g_media->GetModel(m); + model_opaque_used = g_assets->GetModel(m); } } } diff --git a/src/ballistica/scene/node/image_node.h b/src/ballistica/scene/node/image_node.h index 813f9b5b..689d8280 100644 --- a/src/ballistica/scene/node/image_node.h +++ b/src/ballistica/scene/node/image_node.h @@ -6,8 +6,8 @@ #include #include -#include "ballistica/media/component/model.h" -#include "ballistica/media/component/texture.h" +#include "ballistica/assets/component/model.h" +#include "ballistica/assets/component/texture.h" #include "ballistica/scene/node/node.h" namespace ballistica { diff --git a/src/ballistica/scene/node/locator_node.cc b/src/ballistica/scene/node/locator_node.cc index 319ed2b3..d54c3d54 100644 --- a/src/ballistica/scene/node/locator_node.cc +++ b/src/ballistica/scene/node/locator_node.cc @@ -140,11 +140,11 @@ void LocatorNode::Draw(FrameDef* frame_def) { c.SetTransparent(true); } c.SetColor(color_[0], color_[1], color_[2], opacity_); - c.SetTexture(g_media->GetTexture(texture)); + c.SetTexture(g_assets->GetTexture(texture)); c.PushTransform(); c.Translate(position_[0], position_[1], position_[2]); c.Scale(size_[0], size_[1], size_[2]); - c.DrawModel(g_media->GetModel(model)); + c.DrawModel(g_assets->GetModel(model)); c.PopTransform(); c.Submit(); } @@ -164,11 +164,11 @@ void LocatorNode::Draw(FrameDef* frame_def) { } else { c.SetColor(color_[0], color_[1], color_[2], opacity_); } - c.SetTexture(g_media->GetTexture(texture)); + c.SetTexture(g_assets->GetTexture(texture)); c.PushTransform(); c.Translate(position_[0], position_[1], position_[2]); c.Scale(size_[0], size_[1], size_[2]); - c.DrawModel(g_media->GetModel(model)); + c.DrawModel(g_assets->GetModel(model)); c.PopTransform(); c.Submit(); } else { @@ -179,7 +179,7 @@ void LocatorNode::Draw(FrameDef* frame_def) { c.PushTransform(); c.Translate(position_[0], position_[1], position_[2]); c.Scale(size_[0], size_[1], size_[2]); - c.DrawModel(g_media->GetModel(model)); + c.DrawModel(g_assets->GetModel(model)); c.PopTransform(); c.Submit(); } diff --git a/src/ballistica/scene/node/math_node.cc b/src/ballistica/scene/node/math_node.cc index 3fa0662f..2108b0b6 100644 --- a/src/ballistica/scene/node/math_node.cc +++ b/src/ballistica/scene/node/math_node.cc @@ -2,6 +2,8 @@ #include "ballistica/scene/node/math_node.h" +#include + #include "ballistica/scene/node/node_attribute.h" #include "ballistica/scene/node/node_type.h" diff --git a/src/ballistica/scene/node/node.cc b/src/ballistica/scene/node/node.cc index 1b056791..9f369442 100644 --- a/src/ballistica/scene/node/node.cc +++ b/src/ballistica/scene/node/node.cc @@ -3,18 +3,19 @@ #include "ballistica/scene/node/node.h" #include "ballistica/dynamics/part.h" -#include "ballistica/game/game_stream.h" #include "ballistica/python/class/python_class_node.h" #include "ballistica/python/python.h" #include "ballistica/python/python_context_call.h" #include "ballistica/scene/node/node_attribute.h" #include "ballistica/scene/node/node_attribute_connection.h" #include "ballistica/scene/scene.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { NodeType::~NodeType() { - Log("ERROR: SHOULD NOT BE DESTRUCTING A TYPE type=(" + name_ + ")"); + Log(LogLevel::kError, + "SHOULD NOT BE DESTRUCTING A TYPE type=(" + name_ + ")"); } Node::Node(Scene* scene_in, NodeType* node_type) @@ -30,7 +31,7 @@ void Node::AddToScene(Scene* scene) { // id_ = scene->next_node_id_++; // our_iterator_ = // scene->nodes_.insert(scene->nodes_.end(), Object::Ref(this)); - if (GameStream* os = scene->GetGameStream()) { + if (SceneStream* os = scene->GetSceneStream()) { os->AddNode(this); } } @@ -71,7 +72,7 @@ Node::~Node() { // If we were going to an output stream, inform them of our demise. assert(scene()); - if (GameStream* output_stream = scene()->GetGameStream()) { + if (SceneStream* output_stream = scene()->GetSceneStream()) { output_stream->RemoveNode(this); } } @@ -260,7 +261,7 @@ void Node::DispatchOutOfBoundsMessage() { if (instance.exists()) { DispatchUserMessage(instance.get(), "Node OutOfBoundsMessage dispatch"); } else { - Log("Error creating OutOfBoundsMessage"); + Log(LogLevel::kError, "Error creating OutOfBoundsMessage"); } } @@ -275,7 +276,7 @@ void Node::DispatchPickUpMessage(Node* node) { if (instance.exists()) { DispatchUserMessage(instance.get(), "Node PickUpMessage dispatch"); } else { - Log("Error creating PickUpMessage"); + Log(LogLevel::kError, "Error creating PickUpMessage"); } } @@ -288,7 +289,7 @@ void Node::DispatchDropMessage() { if (instance.exists()) { DispatchUserMessage(instance.get(), "Node DropMessage dispatch"); } else { - Log("Error creating DropMessage"); + Log(LogLevel::kError, "Error creating DropMessage"); } } @@ -304,7 +305,7 @@ void Node::DispatchPickedUpMessage(Node* by_node) { if (instance.exists()) { DispatchUserMessage(instance.get(), "Node PickedUpMessage dispatch"); } else { - Log("Error creating PickedUpMessage"); + Log(LogLevel::kError, "Error creating PickedUpMessage"); } } @@ -320,7 +321,7 @@ void Node::DispatchDroppedMessage(Node* by_node) { if (instance.exists()) { DispatchUserMessage(instance.get(), "Node DroppedMessage dispatch"); } else { - Log("Error creating DroppedMessage"); + Log(LogLevel::kError, "Error creating DroppedMessage"); } } @@ -333,7 +334,7 @@ void Node::DispatchShouldShatterMessage() { if (instance.exists()) { DispatchUserMessage(instance.get(), "Node ShouldShatterMessage dispatch"); } else { - Log("Error creating ShouldShatterMessage"); + Log(LogLevel::kError, "Error creating ShouldShatterMessage"); } } @@ -348,7 +349,7 @@ void Node::DispatchImpactDamageMessage(float intensity) { if (instance.exists()) { DispatchUserMessage(instance.get(), "Node ImpactDamageMessage dispatch"); } else { - Log("Error creating ImpactDamageMessage"); + Log(LogLevel::kError, "Error creating ImpactDamageMessage"); } } @@ -376,8 +377,10 @@ void Node::DispatchUserMessage(PyObject* obj, const char* label) { c.Call(PythonRef(Py_BuildValue("(O)", obj), PythonRef::kSteal)); } } catch (const std::exception& e) { - Log(std::string("Error in handlemessage() with message ") - + PythonRef(obj, PythonRef::kAcquire).Str() + ": '" + e.what() + "'"); + Log(LogLevel::kError, + std::string("Error in handlemessage() with message ") + + PythonRef(obj, PythonRef::kAcquire).Str() + ": '" + e.what() + + "'"); } } } diff --git a/src/ballistica/scene/node/node_attribute.cc b/src/ballistica/scene/node/node_attribute.cc index d940857f..42c382b2 100644 --- a/src/ballistica/scene/node/node_attribute.cc +++ b/src/ballistica/scene/node/node_attribute.cc @@ -48,8 +48,8 @@ auto NodeAttributeUnbound::GetNodeAttributeTypeName(NodeAttributeType t) case NodeAttributeType::kCollideModelArray: return "collide-model-array"; default: - Log("Error: Unknown attr type name: " - + std::to_string(static_cast(t))); + Log(LogLevel::kError, + "Unknown attr type name: " + std::to_string(static_cast(t))); return "unknown"; } } @@ -98,7 +98,7 @@ void NodeAttributeUnbound::DisconnectIncoming(Node* node) { #if BA_DEBUG_BUILD if (test_ref.exists()) { - Log("Error: Attr connection still exists after ref releases!"); + Log(LogLevel::kError, "Attr connection still exists after ref releases!"); } #endif } diff --git a/src/ballistica/scene/node/node_attribute_connection.cc b/src/ballistica/scene/node/node_attribute_connection.cc index 4b27fb32..d3b308f9 100644 --- a/src/ballistica/scene/node/node_attribute_connection.cc +++ b/src/ballistica/scene/node/node_attribute_connection.cc @@ -94,12 +94,13 @@ void NodeAttributeConnection::Update() { src_node->type()->GetAttribute(src_attr_index); NodeAttributeUnbound* dst_attr = dst_node->type()->GetAttribute(dst_attr_index); - Log("ERROR: attribute connection update: " + std::string(e.what()) - + "; srcAttr='" + src_attr->name() + "', src_node='" - + src_node->type()->name() + "', srcNodeName='" + src_node->label() - + "', dstAttr='" + dst_attr->name() + "', dstNode='" - + dst_node->type()->name() + "', dstNodeName='" + dst_node->label() - + "'"); + Log(LogLevel::kError, + "Attribute connection update: " + std::string(e.what()) + + "; srcAttr='" + src_attr->name() + "', src_node='" + + src_node->type()->name() + "', srcNodeName='" + + src_node->label() + "', dstAttr='" + dst_attr->name() + + "', dstNode='" + dst_node->type()->name() + "', dstNodeName='" + + dst_node->label() + "'"); } } } diff --git a/src/ballistica/scene/node/prop_node.cc b/src/ballistica/scene/node/prop_node.cc index be16c83d..3e5b534c 100644 --- a/src/ballistica/scene/node/prop_node.cc +++ b/src/ballistica/scene/node/prop_node.cc @@ -265,7 +265,8 @@ void PropNode::SetBody(const std::string& val) { // we're ok with redundant sets, but complain/ignore if they try to switch.. if (body_.exists()) { if (body_type_ != body_type || shape_ != shape) { - Log("ERROR: body attr can not be changed from its initial value"); + Log(LogLevel::kError, + "body attr can not be changed from its initial value"); return; } } @@ -426,8 +427,8 @@ void PropNode::Step() { if (body_type_ == BodyType::UNSET) { if (!reported_unset_body_type_) { reported_unset_body_type_ = true; - Log("ERROR: prop-node " + GetObjectDescription() - + " did not have its 'body' attr set."); + Log(LogLevel::kError, "prop-node " + GetObjectDescription() + + " did not have its 'body' attr set."); return; } } diff --git a/src/ballistica/scene/node/prop_node.h b/src/ballistica/scene/node/prop_node.h index b8ea5684..d50c5122 100644 --- a/src/ballistica/scene/node/prop_node.h +++ b/src/ballistica/scene/node/prop_node.h @@ -6,10 +6,10 @@ #include #include +#include "ballistica/assets/component/model.h" +#include "ballistica/assets/component/texture.h" #include "ballistica/dynamics/bg/bg_dynamics_shadow.h" #include "ballistica/dynamics/part.h" -#include "ballistica/media/component/model.h" -#include "ballistica/media/component/texture.h" #include "ballistica/scene/node/node.h" #include "ballistica/scene/node/node_attribute.h" #include "ballistica/scene/node/node_type.h" diff --git a/src/ballistica/scene/node/region_node.cc b/src/ballistica/scene/node/region_node.cc index 1fc4f016..e509bdc8 100644 --- a/src/ballistica/scene/node/region_node.cc +++ b/src/ballistica/scene/node/region_node.cc @@ -90,7 +90,8 @@ void RegionNode::Step() { RigidBody::kCollideRegion, RigidBody::kCollideActive); } else { if (region_type_ != "box") { - BA_LOG_ONCE("got unexpected region type: " + region_type_); + BA_LOG_ONCE(LogLevel::kError, + "Got unexpected region type: " + region_type_); } body_ = Object::New( 0, &part_, RigidBody::Type::kGeomOnly, RigidBody::Shape::kBox, diff --git a/src/ballistica/scene/node/scorch_node.cc b/src/ballistica/scene/node/scorch_node.cc index 2c468287..a3d025f7 100644 --- a/src/ballistica/scene/node/scorch_node.cc +++ b/src/ballistica/scene/node/scorch_node.cc @@ -66,14 +66,14 @@ void ScorchNode::Draw(FrameDef* frame_def) { SimpleComponent c(frame_def->light_shadow_pass()); c.SetTransparent(true); c.SetColor(color_[0], color_[1], color_[2], o * 0.35f); - c.SetTexture(g_media->GetTexture(big_ ? SystemTextureID::kScorchBig - : SystemTextureID::kScorch)); + c.SetTexture(g_assets->GetTexture(big_ ? SystemTextureID::kScorchBig + : SystemTextureID::kScorch)); c.PushTransform(); c.Translate(position_[0], position_[1], position_[2]); c.Scale(o * size_ * rand_size_[0], o * size_ * rand_size_[1], o * size_ * rand_size_[2]); c.Rotate(Utils::precalc_rand_1(id() % kPrecalcRandsCount) * 360.0f, 0, 1, 0); - c.DrawModel(g_media->GetModel(SystemModelID::kScorch)); + c.DrawModel(g_assets->GetModel(SystemModelID::kScorch)); c.PopTransform(); c.Submit(); } diff --git a/src/ballistica/scene/node/shield_node.cc b/src/ballistica/scene/node/shield_node.cc index c52f859d..44db758c 100644 --- a/src/ballistica/scene/node/shield_node.cc +++ b/src/ballistica/scene/node/shield_node.cc @@ -174,25 +174,25 @@ void ShieldNode::Draw(FrameDef* frame_def) { c.PushTransform(); c.Translate(0.5f, half_height); c.Scale(1.1f, height + 0.1f); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); c.SetColor(0.4f * o, 0.4f * o, 0.8f * o, 0.0f * o); c.PushTransform(); c.Translate(p_left * 0.5f, half_height); c.Scale(p_left, height); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); c.SetColor(1.0f * o, 1.0f * o, 1.0f * o, 0.0f); c.PushTransform(); c.Translate((p_left + p_right) * 0.5f, half_height); c.Scale(p_right - p_left, height); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); c.SetColor(0.1f * o, 0.1f * o, 0.2f * o, 0.4f * o); c.PushTransform(); c.Translate((p_right + 1.0f) * 0.5f, half_height); c.Scale(1.0f - p_right, height); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); c.PopTransform(); c.Submit(); @@ -224,7 +224,7 @@ void ShieldNode::Draw(FrameDef* frame_def) { c.SetLightShadow(LightShadowType::kNone); c.SetReflection(ReflectionType::kSharp); c.SetReflectionScale(0.34f * o, 0.34f * o, 0.34f * o); - c.SetTexture(g_media->GetTexture(SystemTextureID::kShield)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kShield)); c.SetColor(col[0], col[1], col[2], 0.13f * o); c.PushTransform(); Vector3f to_cam = @@ -245,7 +245,7 @@ void ShieldNode::Draw(FrameDef* frame_def) { * (0.97f + 0.05f * Utils::precalc_rand_2(rot_count_ % kPrecalcRandsCount)); c.Scale(r2, r2, r2); - c.DrawModel(g_media->GetModel(SystemModelID::kShield), + c.DrawModel(g_assets->GetModel(SystemModelID::kShield), kModelDrawFlagNoReflection); c.PopTransform(); c.Submit(); @@ -259,7 +259,7 @@ void ShieldNode::Draw(FrameDef* frame_def) { c2.Rotate(Utils::precalc_rand_1(rot_count_ % kPrecalcRandsCount) * 360, 0, 1, 0); c2.Scale(r2, r2, r2); - c2.DrawModel(g_media->GetModel(SystemModelID::kShield)); + c2.DrawModel(g_assets->GetModel(SystemModelID::kShield)); c2.PopTransform(); c2.Submit(); } @@ -273,7 +273,7 @@ void ShieldNode::Draw(FrameDef* frame_def) { 1, 0); float sc = r2 * 1.1f; c2.Scale(sc, sc, sc); - c2.DrawModel(g_media->GetModel(SystemModelID::kShield)); + c2.DrawModel(g_assets->GetModel(SystemModelID::kShield)); c2.PopTransform(); c2.Submit(); } diff --git a/src/ballistica/scene/node/sound_node.cc b/src/ballistica/scene/node/sound_node.cc index 75cc6e8e..da6b7834 100644 --- a/src/ballistica/scene/node/sound_node.cc +++ b/src/ballistica/scene/node/sound_node.cc @@ -2,9 +2,9 @@ #include "ballistica/scene/node/sound_node.h" +#include "ballistica/assets/component/sound.h" #include "ballistica/audio/audio.h" #include "ballistica/audio/audio_source.h" -#include "ballistica/media/component/sound.h" #include "ballistica/scene/node/node_attribute.h" #include "ballistica/scene/node/node_type.h" #include "ballistica/scene/scene.h" @@ -81,7 +81,8 @@ void SoundNode::SetLoop(bool val) { // We don't actually update looping on a playing sound. if (playing_) - BA_LOG_ONCE("Error: can't set 'loop' attr on already-playing sound."); + BA_LOG_ONCE(LogLevel::kError, + "Can't set 'loop' attr on already-playing sound."); } void SoundNode::SetSound(Sound* s) { @@ -97,7 +98,8 @@ void SoundNode::SetPositional(bool val) { if (val == positional_) return; positional_ = val; if (playing_) - BA_LOG_ONCE("Error: can't set 'positional' attr on already-playing sound"); + BA_LOG_ONCE(LogLevel::kError, + "Can't set 'positional' attr on already-playing sound"); } void SoundNode::SetMusic(bool val) { diff --git a/src/ballistica/scene/node/spaz_node.cc b/src/ballistica/scene/node/spaz_node.cc index 1e2386a5..c887d049 100644 --- a/src/ballistica/scene/node/spaz_node.cc +++ b/src/ballistica/scene/node/spaz_node.cc @@ -2,6 +2,7 @@ #include "ballistica/scene/node/spaz_node.h" +#include "ballistica/assets/component/sound.h" #include "ballistica/audio/audio.h" #include "ballistica/audio/audio_source.h" #include "ballistica/dynamics/bg/bg_dynamics_shadow.h" @@ -17,7 +18,6 @@ #include "ballistica/graphics/graphics_server.h" #include "ballistica/graphics/text/text_graphics.h" #include "ballistica/input/device/input_device.h" -#include "ballistica/media/component/sound.h" #include "ballistica/python/python.h" #include "ballistica/scene/node/node_attribute.h" #include "ballistica/scene/node/node_type.h" @@ -1839,7 +1839,7 @@ void SpazNode::DoFlyPress() { } else { s_id = SystemSoundID::kSparkle3; } - s->Play(g_media->GetSound(s_id)); + s->Play(g_assets->GetSound(s_id)); s->End(); } } @@ -4000,9 +4000,9 @@ void SpazNode::DrawEyeBalls(RenderComponent* c, ObjectComponent* oc, if (blink_smooth_ < 0.9f) { if (shading) { oc->SetLightShadow(LightShadowType::kObject); - oc->SetTexture(g_media->GetTexture(SystemTextureID::kEye)); + oc->SetTexture(g_assets->GetTexture(SystemTextureID::kEye)); oc->SetColorizeColor(eye_color_red_, eye_color_green_, eye_color_blue_); - oc->SetColorizeTexture(g_media->GetTexture(SystemTextureID::kEyeTint)); + oc->SetColorizeTexture(g_assets->GetTexture(SystemTextureID::kEyeTint)); oc->SetReflection(ReflectionType::kSharpest); oc->SetReflectionScale(3, 3, 3); oc->SetAddColor(add_color[0], add_color[1], add_color[2]); @@ -4020,12 +4020,12 @@ void SpazNode::DrawEyeBalls(RenderComponent* c, ObjectComponent* oc, if (death_scale != 1.0f) c->Scale(death_scale, death_scale, death_scale); if (!frosty_ && !eyeless_) { - c->DrawModel(g_media->GetModel(SystemModelID::kEyeBall)); + c->DrawModel(g_assets->GetModel(SystemModelID::kEyeBall)); if (shading) { oc->SetReflectionScale(2, 2, 2); } if (death_scale != 1.0f) c->Scale(death_scale, death_scale, death_scale); - c->DrawModel(g_media->GetModel(SystemModelID::kEyeBallIris)); + c->DrawModel(g_assets->GetModel(SystemModelID::kEyeBallIris)); } c->PopTransform(); @@ -4040,12 +4040,12 @@ void SpazNode::DrawEyeBalls(RenderComponent* c, ObjectComponent* oc, c->Rotate(eyes_lr_smooth_, 0, 1, 0); c->Scale(0.09f, 0.09f, 0.09f); if (death_scale != 1.0f) c->Scale(death_scale, death_scale, death_scale); - c->DrawModel(g_media->GetModel(SystemModelID::kEyeBall)); + c->DrawModel(g_assets->GetModel(SystemModelID::kEyeBall)); if (death_scale != 1.0f) c->Scale(death_scale, death_scale, death_scale); if (shading) { oc->SetReflectionScale(2, 2, 2); } - c->DrawModel(g_media->GetModel(SystemModelID::kEyeBallIris)); + c->DrawModel(g_assets->GetModel(SystemModelID::kEyeBallIris)); c->PopTransform(); } c->PopTransform(); @@ -4054,7 +4054,7 @@ void SpazNode::DrawEyeBalls(RenderComponent* c, ObjectComponent* oc, void SpazNode::SetupEyeLidShading(ObjectComponent* c, float death_fade, float* add_color) { - c->SetTexture(g_media->GetTexture(SystemTextureID::kEye)); + c->SetTexture(g_assets->GetTexture(SystemTextureID::kEye)); c->SetColorizeTexture(nullptr); float r, g, b; r = eye_lid_color_red_; @@ -4097,7 +4097,7 @@ void SpazNode::DrawEyeLids(RenderComponent* c, float death_fade, } if (!frosty_ && !eyeless_) { - c->DrawModel(g_media->GetModel(SystemModelID::kEyeLid)); + c->DrawModel(g_assets->GetModel(SystemModelID::kEyeLid)); } c->PopTransform(); @@ -4117,7 +4117,7 @@ void SpazNode::DrawEyeLids(RenderComponent* c, float death_fade, c->Scale(-0.09f, 0.09f, 0.09f); if (death_scale != 1.0f) c->Scale(death_scale, death_scale, death_scale); if (!pirate_ && !frosty_ && !eyeless_) - c->DrawModel(g_media->GetModel(SystemModelID::kEyeLid)); + c->DrawModel(g_assets->GetModel(SystemModelID::kEyeLid)); c->PopTransform(); c->FlipCullFace(); // back to normal } @@ -4179,7 +4179,7 @@ void SpazNode::DrawBodyParts(ObjectComponent* c, bool shading, float death_fade, if (death_scale != 1.0f) { c->Scale(death_scale, death_scale, death_scale); } - c->DrawModel(g_media->GetModel(SystemModelID::kHairTuft1)); + c->DrawModel(g_assets->GetModel(SystemModelID::kHairTuft1)); c->PopTransform(); // Hair tuft 1b; just reuse tuft 1 with some extra translating. @@ -4193,7 +4193,7 @@ void SpazNode::DrawBodyParts(ObjectComponent* c, bool shading, float death_fade, if (death_scale != 1.0f) { c->Scale(death_scale, death_scale, death_scale); } - c->DrawModel(g_media->GetModel(SystemModelID::kHairTuft1b)); + c->DrawModel(g_assets->GetModel(SystemModelID::kHairTuft1b)); c->PopTransform(); } @@ -4202,7 +4202,7 @@ void SpazNode::DrawBodyParts(ObjectComponent* c, bool shading, float death_fade, c->PushTransform(); c->TransformToBody(*hair_front_left_body_); if (death_scale != 1.0f) c->Scale(death_scale, death_scale, death_scale); - c->DrawModel(g_media->GetModel(SystemModelID::kHairTuft2)); + c->DrawModel(g_assets->GetModel(SystemModelID::kHairTuft2)); c->PopTransform(); } @@ -4213,7 +4213,7 @@ void SpazNode::DrawBodyParts(ObjectComponent* c, bool shading, float death_fade, if (death_scale != 1.0f) { c->Scale(death_scale, death_scale, death_scale); } - c->DrawModel(g_media->GetModel(SystemModelID::kHairTuft3)); + c->DrawModel(g_assets->GetModel(SystemModelID::kHairTuft3)); c->PopTransform(); } @@ -4224,7 +4224,7 @@ void SpazNode::DrawBodyParts(ObjectComponent* c, bool shading, float death_fade, if (death_scale != 1.0f) { c->Scale(death_scale, death_scale, death_scale); } - c->DrawModel(g_media->GetModel(SystemModelID::kHairTuft4)); + c->DrawModel(g_assets->GetModel(SystemModelID::kHairTuft4)); c->PopTransform(); } @@ -4864,7 +4864,7 @@ void SpazNode::Draw(FrameDef* frame_def) { c.Translate(torso_pos[0] - 0.3f, torso_pos[1] + 1.47f, torso_pos[2] - 0.2f); c.Scale(1.5f * 0.2f, 1.5f * 0.2f, 1.5f * 0.2f); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); c.Submit(); } @@ -4966,7 +4966,7 @@ void SpazNode::Draw(FrameDef* frame_def) { c.PushTransform(); c.Translate(pos[0], pos[1] + 1.6f, pos[2] - 0.2f); c.Scale(2.3f * 0.2f * s, 2.3f * 0.2f * s, 2.3f * 0.2f * s); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); c.Submit(); @@ -4980,7 +4980,7 @@ void SpazNode::Draw(FrameDef* frame_def) { c2.PushTransform(); c2.Translate(pos[0], pos[1] + 1.6f, pos[2] - 0.2f); c2.Scale(2.3f * 0.2f * s, 2.3f * 0.2f * s, 2.3f * 0.2f * s); - c2.DrawModel(g_media->GetModel(SystemModelID::kCrossOut)); + c2.DrawModel(g_assets->GetModel(SystemModelID::kCrossOut)); c2.PopTransform(); c2.Submit(); } @@ -5028,7 +5028,7 @@ void SpazNode::Draw(FrameDef* frame_def) { c.PushTransform(); c.Translate(0.5f, half_height); c.Scale(1.1f, height + 0.1f); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); c.SetColor(0, 0.35f * o, 0, 0.3f * o); @@ -5036,7 +5036,7 @@ void SpazNode::Draw(FrameDef* frame_def) { c.PushTransform(); c.Translate(p_left * 0.5f, half_height); c.Scale(p_left, height); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); if (dead_ && scene()->stepnum() % 10 < 5) { @@ -5048,7 +5048,7 @@ void SpazNode::Draw(FrameDef* frame_def) { c.PushTransform(); c.Translate((p_left + p_right) * 0.5f, half_height); c.Scale(p_right - p_left, height); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); c.SetColor((dead_ && scene()->stepnum() % 10 < 5) ? 0.55f * o : 0.01f * o, @@ -5057,7 +5057,7 @@ void SpazNode::Draw(FrameDef* frame_def) { c.PushTransform(); c.Translate((p_right + 1.0f) * 0.5f, half_height); c.Scale(1.0f - p_right, height); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); c.PopTransform(); @@ -5098,7 +5098,7 @@ void SpazNode::Draw(FrameDef* frame_def) { c.SetColor(1, 1, 1, 1.0f); c.SetReflection(ReflectionType::kSoft); c.SetReflectionScale(0.4f, 0.4f, 0.4f); - c.SetTexture(g_media->GetTexture(SystemTextureID::kWings)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kWings)); // Fade to reddish on death. if (dead_ && !frozen_) { @@ -5118,14 +5118,14 @@ void SpazNode::Draw(FrameDef* frame_def) { c.PushTransform(); c.Translate(p_wing_l[0], p_wing_l[1], p_wing_l[2]); c.Scale(0.05f, 0.05f, 0.05f); - c.DrawModel(g_media->GetModel(SystemModelID::kBox)); + c.DrawModel(g_assets->GetModel(SystemModelID::kBox)); c.PopTransform(); // Draw wing point. c.PushTransform(); c.Translate(wing_pos_left_.x, wing_pos_left_.y, wing_pos_left_.z); c.Scale(0.1f, 0.1f, 0.1f); - c.DrawModel(g_media->GetModel(SystemModelID::kBox)); + c.DrawModel(g_assets->GetModel(SystemModelID::kBox)); c.PopTransform(); // Draw target. @@ -5134,14 +5134,14 @@ void SpazNode::Draw(FrameDef* frame_def) { c.PushTransform(); c.Translate(p_wing_r[0], p_wing_r[1], p_wing_r[2]); c.Scale(0.05f, 0.05f, 0.05f); - c.DrawModel(g_media->GetModel(SystemModelID::kBox)); + c.DrawModel(g_assets->GetModel(SystemModelID::kBox)); c.PopTransform(); // Draw wing point. c.PushTransform(); c.Translate(wing_pos_right_.x, wing_pos_right_.y, wing_pos_right_.z); c.Scale(0.1f, 0.1f, 0.1f); - c.DrawModel(g_media->GetModel(SystemModelID::kBox)); + c.DrawModel(g_assets->GetModel(SystemModelID::kBox)); c.PopTransform(); } @@ -5167,7 +5167,7 @@ void SpazNode::Draw(FrameDef* frame_def) { if (death_scale != 1.0f) { c.Scale(death_scale, death_scale, death_scale); } - c.DrawModel(g_media->GetModel(SystemModelID::kWing)); + c.DrawModel(g_assets->GetModel(SystemModelID::kWing)); c.PopTransform(); Vector3f to_right_wing = wing_pos_right_ - torso_pos2; @@ -5185,7 +5185,7 @@ void SpazNode::Draw(FrameDef* frame_def) { if (death_scale != 1.0f) { c.Scale(death_scale, death_scale, death_scale); } - c.DrawModel(g_media->GetModel(SystemModelID::kWing)); + c.DrawModel(g_assets->GetModel(SystemModelID::kWing)); c.PopTransform(); c.Submit(); } @@ -5222,14 +5222,14 @@ void SpazNode::Draw(FrameDef* frame_def) { } } c.SetLightShadow(LightShadowType::kObject); - c.SetTexture(g_media->GetTexture(SystemTextureID::kBoxingGlove)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kBoxingGlove)); c.PushTransform(); c.TransformToBody(*lower_right_arm_body_); if (death_scale != 1.0f) { c.Scale(death_scale, death_scale, death_scale); } - c.DrawModel(g_media->GetModel(SystemModelID::kBoxingGlove)); + c.DrawModel(g_assets->GetModel(SystemModelID::kBoxingGlove)); c.PopTransform(); c.FlipCullFace(); @@ -5239,7 +5239,7 @@ void SpazNode::Draw(FrameDef* frame_def) { if (death_scale != 1.0f) { c.Scale(death_scale, death_scale, death_scale); } - c.DrawModel(g_media->GetModel(SystemModelID::kBoxingGlove)); + c.DrawModel(g_assets->GetModel(SystemModelID::kBoxingGlove)); c.FlipCullFace(); c.PopTransform(); c.Submit(); @@ -5885,7 +5885,8 @@ auto SpazNode::GetRigidBody(int id) -> RigidBody* { return hair_ponytail_bottom_body_.get(); break; default: - Log("Error: Request for unknown spaz body: " + std::to_string(id)); + Log(LogLevel::kError, + "Request for unknown spaz body: " + std::to_string(id)); break; } @@ -6149,7 +6150,7 @@ void SpazNode::SetCurseDeathTime(millisecs_t val) { const dReal* p_head = dGeomGetPosition(body_head_->geom()); s->SetPosition(p_head[0], p_head[1], p_head[2]); tick_play_id_ = - s->Play(g_media->GetSound(SystemSoundID::kTickingCrazy)); + s->Play(g_assets->GetSound(SystemSoundID::kTickingCrazy)); s->End(); } } @@ -6430,9 +6431,6 @@ void SpazNode::SetStyle(const std::string& val) { eye_lid_color_green_ = 0.35f; eye_lid_color_blue_ = 0.31f; default_eye_lid_angle_ = 15.0f; // sorta angry eyes - eye_color_red_ = 1.1f; - eye_color_green_ = 0.6f; - eye_color_blue_ = 1.4f; eye_ball_color_red_ = 0.54f; eye_ball_color_green_ = 0.51f; eye_ball_color_blue_ = 0.55f; @@ -6491,7 +6489,7 @@ void SpazNode::SetStyle(const std::string& val) { shoulder_offset_y_ = -0.05f; reflection_scale_ = 0.02f; } else { - BA_LOG_ONCE("Error: Unrecognized spaz style: '" + style_ + "'"); + BA_LOG_ONCE(LogLevel::kError, "Unrecognized spaz style: '" + style_ + "'"); } UpdateBodiesForStyle(); } @@ -6620,20 +6618,25 @@ void SpazNode::SetHoldNode(Node* val) { assert(dynamics); Collision* c = dynamics->active_collision(); if (c) { - Log("SRC NODE: " + ObjToString(dynamics->GetActiveCollideSrcNode())); - Log("OPP NODE: " + ObjToString(dynamics->GetActiveCollideDstNode())); - Log("SRC BODY " - + std::to_string(dynamics->GetCollideMessageReverseOrder() - ? c->body_id_1 - : c->body_id_2)); - Log("OPP BODY " - + std::to_string(dynamics->GetCollideMessageReverseOrder() - ? c->body_id_2 - : c->body_id_1)); - Log("REVERSE " - + std::to_string(dynamics->GetCollideMessageReverseOrder())); + Log(LogLevel::kError, + "SRC NODE: " + ObjToString(dynamics->GetActiveCollideSrcNode())); + Log(LogLevel::kError, + "OPP NODE: " + ObjToString(dynamics->GetActiveCollideDstNode())); + Log(LogLevel::kError, + "SRC BODY " + + std::to_string(dynamics->GetCollideMessageReverseOrder() + ? c->body_id_1 + : c->body_id_2)); + Log(LogLevel::kError, + "OPP BODY " + + std::to_string(dynamics->GetCollideMessageReverseOrder() + ? c->body_id_2 + : c->body_id_1)); + Log(LogLevel::kError, + "REVERSE " + + std::to_string(dynamics->GetCollideMessageReverseOrder())); } else { - Log(""); + Log(LogLevel::kError, ""); } } throw Exception("specified hold_body (" + std::to_string(hold_body_) diff --git a/src/ballistica/scene/node/spaz_node.h b/src/ballistica/scene/node/spaz_node.h index f1e76af5..4133c127 100644 --- a/src/ballistica/scene/node/spaz_node.h +++ b/src/ballistica/scene/node/spaz_node.h @@ -7,8 +7,8 @@ #include #include "ballistica/dynamics/part.h" -#include "ballistica/game/player.h" #include "ballistica/graphics/renderer.h" +#include "ballistica/logic/player.h" #include "ballistica/scene/node/node.h" namespace ballistica { diff --git a/src/ballistica/scene/node/terrain_node.cc b/src/ballistica/scene/node/terrain_node.cc index 338975f6..b8b831ad 100644 --- a/src/ballistica/scene/node/terrain_node.cc +++ b/src/ballistica/scene/node/terrain_node.cc @@ -2,10 +2,10 @@ #include "ballistica/scene/node/terrain_node.h" +#include "ballistica/assets/component/collide_model.h" #include "ballistica/dynamics/bg/bg_dynamics.h" #include "ballistica/dynamics/material/material.h" #include "ballistica/graphics/component/object_component.h" -#include "ballistica/media/component/collide_model.h" #include "ballistica/scene/node/node_attribute.h" #include "ballistica/scene/node/node_type.h" #include "ballistica/scene/scene.h" diff --git a/src/ballistica/scene/node/text_node.cc b/src/ballistica/scene/node/text_node.cc index 10a0c0f8..7be19570 100644 --- a/src/ballistica/scene/node/text_node.cc +++ b/src/ballistica/scene/node/text_node.cc @@ -113,13 +113,14 @@ void TextNode::SetText(const std::string& val) { if (do_format_check) { bool valid; - g_game->CompileResourceString(val, "setText format check", &valid); + g_logic->CompileResourceString(val, "setText format check", &valid); if (!valid) { - BA_LOG_ONCE("Invalid resource string: '" + val + "' on node '" + label() - + "'"); + BA_LOG_ONCE(LogLevel::kError, "Invalid resource string: '" + val + + "' on node '" + label() + "'"); Python::PrintStackTrace(); } else if (print_false_positives) { - BA_LOG_ONCE("Got false positive for json check on '" + val + "'"); + BA_LOG_ONCE(LogLevel::kError, + "Got false positive for json check on '" + val + "'"); Python::PrintStackTrace(); } } @@ -348,7 +349,7 @@ void TextNode::Draw(FrameDef* frame_def) { // Apply subs/resources to get our actual text if need be. if (text_translation_dirty_) { text_translated_ = - g_game->CompileResourceString(text_raw_, "TextNode::OnDraw"); + g_logic->CompileResourceString(text_raw_, "TextNode::OnDraw"); text_translation_dirty_ = false; text_group_dirty_ = true; text_width_dirty_ = true; diff --git a/src/ballistica/scene/node/texture_sequence_node.cc b/src/ballistica/scene/node/texture_sequence_node.cc index 6644f9dd..e5bc8821 100644 --- a/src/ballistica/scene/node/texture_sequence_node.cc +++ b/src/ballistica/scene/node/texture_sequence_node.cc @@ -2,7 +2,7 @@ #include "ballistica/scene/node/texture_sequence_node.h" -#include "ballistica/media/component/texture.h" +#include "ballistica/assets/component/texture.h" #include "ballistica/scene/node/node_attribute.h" #include "ballistica/scene/node/node_type.h" #include "ballistica/scene/scene.h" diff --git a/src/ballistica/scene/node/time_display_node.cc b/src/ballistica/scene/node/time_display_node.cc index 456f60f5..17bc7ebf 100644 --- a/src/ballistica/scene/node/time_display_node.cc +++ b/src/ballistica/scene/node/time_display_node.cc @@ -2,8 +2,10 @@ #include "ballistica/scene/node/time_display_node.h" -#include "ballistica/game/game.h" +#include + #include "ballistica/generic/utils.h" +#include "ballistica/logic/logic.h" #include "ballistica/scene/node/node_attribute.h" #include "ballistica/scene/node/node_type.h" @@ -46,10 +48,10 @@ auto TimeDisplayNode::GetOutput() -> std::string { assert(InLogicThread()); if (translations_dirty_) { time_suffix_hours_ = - g_game->CompileResourceString(R"({"r":"timeSuffixHoursText"})", "tda"); - time_suffix_minutes_ = g_game->CompileResourceString( + g_logic->CompileResourceString(R"({"r":"timeSuffixHoursText"})", "tda"); + time_suffix_minutes_ = g_logic->CompileResourceString( R"({"r":"timeSuffixMinutesText"})", "tdb"); - time_suffix_seconds_ = g_game->CompileResourceString( + time_suffix_seconds_ = g_logic->CompileResourceString( R"({"r":"timeSuffixSecondsText"})", "tdc"); translations_dirty_ = false; output_dirty_ = true; diff --git a/src/ballistica/scene/scene.cc b/src/ballistica/scene/scene.cc index 9bc1d71a..acf77a96 100644 --- a/src/ballistica/scene/scene.cc +++ b/src/ballistica/scene/scene.cc @@ -3,110 +3,21 @@ #include "ballistica/scene/scene.h" #include "ballistica/app/app.h" +#include "ballistica/assets/component/sound.h" #include "ballistica/audio/audio.h" -#include "ballistica/dynamics/bg/bg_dynamics.h" #include "ballistica/dynamics/dynamics.h" -#include "ballistica/dynamics/part.h" -#include "ballistica/game/game_stream.h" -#include "ballistica/game/player.h" #include "ballistica/graphics/camera.h" -#include "ballistica/graphics/graphics.h" -#include "ballistica/input/device/input_device.h" -#include "ballistica/media/component/sound.h" #include "ballistica/networking/networking.h" #include "ballistica/python/python_context_call.h" -#include "ballistica/scene/node/anim_curve_node.h" #include "ballistica/scene/node/bomb_node.h" -#include "ballistica/scene/node/combine_node.h" -#include "ballistica/scene/node/explosion_node.h" -#include "ballistica/scene/node/flag_node.h" -#include "ballistica/scene/node/flash_node.h" -#include "ballistica/scene/node/globals_node.h" -#include "ballistica/scene/node/image_node.h" -#include "ballistica/scene/node/light_node.h" -#include "ballistica/scene/node/locator_node.h" -#include "ballistica/scene/node/math_node.h" #include "ballistica/scene/node/node_attribute_connection.h" -#include "ballistica/scene/node/null_node.h" #include "ballistica/scene/node/player_node.h" -#include "ballistica/scene/node/region_node.h" -#include "ballistica/scene/node/scorch_node.h" -#include "ballistica/scene/node/session_globals_node.h" -#include "ballistica/scene/node/shield_node.h" -#include "ballistica/scene/node/sound_node.h" -#include "ballistica/scene/node/spaz_node.h" -#include "ballistica/scene/node/terrain_node.h" #include "ballistica/scene/node/text_node.h" -#include "ballistica/scene/node/texture_sequence_node.h" -#include "ballistica/scene/node/time_display_node.h" +#include "ballistica/scene/scene_stream.h" namespace ballistica { -void Scene::Init() { - NodeType* node_types[] = {NullNode::InitType(), - GlobalsNode::InitType(), - SessionGlobalsNode::InitType(), - PropNode::InitType(), - FlagNode::InitType(), - BombNode::InitType(), - ExplosionNode::InitType(), - ShieldNode::InitType(), - LightNode::InitType(), - TextNode::InitType(), - AnimCurveNode::InitType(), - ImageNode::InitType(), - TerrainNode::InitType(), - MathNode::InitType(), - LocatorNode::InitType(), - PlayerNode::InitType(), - CombineNode::InitType(), - SoundNode::InitType(), - SpazNode::InitType(), - RegionNode::InitType(), - ScorchNode::InitType(), - FlashNode::InitType(), - TextureSequenceNode::InitType(), - TimeDisplayNode::InitType()}; - - int next_type_id = 0; - assert(g_app != nullptr); - for (auto* t : node_types) { - g_app->node_types[t->name()] = t; - g_app->node_types_by_id[next_type_id] = t; - t->set_id(next_type_id++); - } - - // Types: I is 32 bit int, i is 16 bit int, c is 8 bit int, - // F is 32 bit float, f is 16 bit float, - // s is string, b is bool. - SetupNodeMessageType("flash", NodeMessageType::kFlash, ""); - SetupNodeMessageType("footing", NodeMessageType::kFooting, "c"); - SetupNodeMessageType("impulse", NodeMessageType::kImpulse, "fffffffffifff"); - SetupNodeMessageType("kick_back", NodeMessageType::kKickback, "fffffff"); - SetupNodeMessageType("celebrate", NodeMessageType::kCelebrate, "i"); - SetupNodeMessageType("celebrate_l", NodeMessageType::kCelebrateL, "i"); - SetupNodeMessageType("celebrate_r", NodeMessageType::kCelebrateR, "i"); - SetupNodeMessageType("knockout", NodeMessageType::kKnockout, "f"); - SetupNodeMessageType("hurt_sound", NodeMessageType::kHurtSound, ""); - SetupNodeMessageType("picked_up", NodeMessageType::kPickedUp, ""); - SetupNodeMessageType("jump_sound", NodeMessageType::kJumpSound, ""); - SetupNodeMessageType("attack_sound", NodeMessageType::kAttackSound, ""); - SetupNodeMessageType("scream_sound", NodeMessageType::kScreamSound, ""); - SetupNodeMessageType("stand", NodeMessageType::kStand, "ffff"); -} - -void Scene::SetupNodeMessageType(const std::string& name, NodeMessageType val, - const std::string& format) { - assert(g_app != nullptr); - g_app->node_message_types[name] = val; - assert(static_cast(val) >= 0); - if (g_app->node_message_formats.size() <= static_cast(val)) { - g_app->node_message_formats.resize(static_cast(val) + 1); - } - g_app->node_message_formats[static_cast(val)] = format; -} - -auto Scene::GetGameStream() const -> GameStream* { +auto Scene::GetSceneStream() const -> SceneStream* { return output_stream_.get(); } @@ -170,7 +81,7 @@ void Scene::PlaySound(Sound* sound, float volume, bool host_only) { auto Scene::IsOutOfBounds(float x, float y, float z) -> bool { if (std::isnan(x) || std::isnan(y) || std::isnan(z) || std::isinf(x) || std::isinf(y) || std::isinf(z)) - BA_LOG_ONCE("ERROR: got INF/NAN value on IsOutOfBounds() check"); + BA_LOG_ONCE(LogLevel::kError, "Got INF/NAN value on IsOutOfBounds() check"); return ((x < bounds_min_[0]) || (x > bounds_max_[0]) || (y < bounds_min_[1]) || (y > bounds_max_[1]) || (z < bounds_min_[2]) @@ -235,7 +146,7 @@ void Scene::Step() { } in_step_ = false; } - bool is_foreground = (g_game->GetForegroundScene() == this); + bool is_foreground = (g_logic->GetForegroundScene() == this); // Add a step command to the output stream. if (output_stream_.exists()) { @@ -295,7 +206,7 @@ void Scene::DeleteNode(Node* node) { // Sanity test: at this point the node should be dead. #if BA_DEBUG_BUILD if (temp_weak_ref.exists()) { - Log("Error: node still exists after ref release!!"); + Log(LogLevel::kError, "Node still exists after ref release!!"); } #endif // BA_DEBUG_BUILD @@ -372,17 +283,17 @@ auto Scene::NewNode(const std::string& type_string, const std::string& name, return node.get(); // NOLINT } -void Scene::Dump(GameStream* stream) { +void Scene::Dump(SceneStream* stream) { assert(InLogicThread()); stream->AddScene(this); // If we're the foreground one, communicate that fact as well. - if (g_game->GetForegroundScene() == this) { + if (g_logic->GetForegroundScene() == this) { stream->SetForegroundScene(this); } } -void Scene::DumpNodes(GameStream* out) { +void Scene::DumpNodes(SceneStream* out) { // Dumps commands to the output stream to recreate scene's nodes // in their current state. @@ -485,8 +396,9 @@ void Scene::DumpNodes(GameStream* out) { break; } default: - Log("Invalid attr type for Scene::DumpNodes() attr set: " - + std::to_string(static_cast(attr.type()))); + Log(LogLevel::kError, + "Invalid attr type for Scene::DumpNodes() attr set: " + + std::to_string(static_cast(attr.type()))); break; } } @@ -623,7 +535,7 @@ auto Scene::GetCorrectionMessage(bool blended) -> std::vector { return message; } -void Scene::SetOutputStream(GameStream* val) { output_stream_ = val; } +void Scene::SetOutputStream(SceneStream* val) { output_stream_ = val; } auto Scene::AddNode(Node* node, int64_t* node_id, NodeList::iterator* i) -> void { diff --git a/src/ballistica/scene/scene.h b/src/ballistica/scene/scene.h index b55f4c97..e9557eee 100644 --- a/src/ballistica/scene/scene.h +++ b/src/ballistica/scene/scene.h @@ -8,14 +8,13 @@ #include #include "ballistica/core/object.h" -#include "ballistica/game/game.h" +#include "ballistica/logic/logic.h" #include "ballistica/scene/node/node.h" namespace ballistica { class Scene : public Object { public: - static auto Init() -> void; explicit Scene(millisecs_t starttime); ~Scene() override; auto Step() -> void; @@ -53,7 +52,7 @@ class Scene : public Object { auto DeleteNode(Node* node) -> void; auto shutting_down() const -> bool { return shutting_down_; } auto set_shutting_down(bool val) -> void { shutting_down_ = val; } - auto GetGameStream() const -> GameStream*; + auto GetSceneStream() const -> SceneStream*; auto SetPlayerNode(int id, PlayerNode* n) -> void; auto GetPlayerNode(int id) -> PlayerNode*; auto use_fixed_vr_overlay() const -> bool { return use_fixed_vr_overlay_; } @@ -63,11 +62,11 @@ class Scene : public Object { auto increment_bg_cover_count() -> void { bg_cover_count_++; } auto decrement_bg_cover_count() -> void { bg_cover_count_--; } auto has_bg_cover() const -> bool { return (bg_cover_count_ > 0); } - auto Dump(GameStream* out) -> void; - auto DumpNodes(GameStream* out) -> void; + auto Dump(SceneStream* out) -> void; + auto DumpNodes(SceneStream* out) -> void; auto GetCorrectionMessage(bool blended) -> std::vector; - auto SetOutputStream(GameStream* val) -> void; + auto SetOutputStream(SceneStream* val) -> void; auto stream_id() const -> int64_t { return stream_id_; } auto set_stream_id(int64_t val) -> void { assert(stream_id_ == -1); @@ -85,13 +84,10 @@ class Scene : public Object { auto set_globals_node(GlobalsNode* node) -> void { globals_node_ = node; } private: - static auto SetupNodeMessageType(const std::string& name, NodeMessageType val, - const std::string& format) -> void; - GlobalsNode* globals_node_{}; // Current globals node (if any). std::unordered_map > player_nodes_; int64_t stream_id_{-1}; - Object::WeakRef output_stream_; + Object::WeakRef output_stream_; bool use_fixed_vr_overlay_{}; Context context_; // Context we were made in. millisecs_t time_{}; diff --git a/src/ballistica/game/game_stream.cc b/src/ballistica/scene/scene_stream.cc similarity index 77% rename from src/ballistica/game/game_stream.cc rename to src/ballistica/scene/scene_stream.cc index 71beb4e3..ff1aff8b 100644 --- a/src/ballistica/game/game_stream.cc +++ b/src/ballistica/scene/scene_stream.cc @@ -1,23 +1,23 @@ // Released under the MIT License. See LICENSE for details. -#include "ballistica/game/game_stream.h" +#include "ballistica/scene/scene_stream.h" #include "ballistica/app/app.h" +#include "ballistica/assets/assets_server.h" +#include "ballistica/assets/component/collide_model.h" +#include "ballistica/assets/component/data.h" +#include "ballistica/assets/component/model.h" +#include "ballistica/assets/component/sound.h" +#include "ballistica/assets/component/texture.h" #include "ballistica/dynamics/bg/bg_dynamics.h" #include "ballistica/dynamics/material/material.h" #include "ballistica/dynamics/material/material_action.h" #include "ballistica/dynamics/material/material_component.h" #include "ballistica/dynamics/material/material_condition_node.h" #include "ballistica/dynamics/part.h" -#include "ballistica/game/connection/connection_set.h" -#include "ballistica/game/connection/connection_to_client.h" -#include "ballistica/game/session/host_session.h" -#include "ballistica/media/component/collide_model.h" -#include "ballistica/media/component/data.h" -#include "ballistica/media/component/model.h" -#include "ballistica/media/component/sound.h" -#include "ballistica/media/component/texture.h" -#include "ballistica/media/media_server.h" +#include "ballistica/logic/connection/connection_set.h" +#include "ballistica/logic/connection/connection_to_client.h" +#include "ballistica/logic/session/host_session.h" #include "ballistica/networking/networking.h" #include "ballistica/scene/node/node_attribute.h" #include "ballistica/scene/node/node_type.h" @@ -25,7 +25,7 @@ namespace ballistica { -GameStream::GameStream(HostSession* host_session, bool save_replay) +SceneStream::SceneStream(HostSession* host_session, bool save_replay) : time_(0), host_session_(host_session), next_flush_time_(0), @@ -35,10 +35,11 @@ GameStream::GameStream(HostSession* host_session, bool save_replay) if (save_replay) { // Sanity check - we should only ever be writing one replay at once. if (g_app->replay_open) { - Log("ERROR: g_replay_open true at replay start; shouldn't happen."); + Log(LogLevel::kError, + "g_replay_open true at replay start; shouldn't happen."); } - assert(g_media_server); - g_media_server->PushBeginWriteReplayCall(); + assert(g_assets_server); + g_assets_server->PushBeginWriteReplayCall(); writing_replay_ = true; g_app->replay_open = true; } @@ -46,28 +47,29 @@ GameStream::GameStream(HostSession* host_session, bool save_replay) // If we're the live output-stream from a host-session, // take responsibility for feeding all clients to this device. if (host_session_) { - g_game->connections()->RegisterClientController(this); + g_logic->connections()->RegisterClientController(this); } } -GameStream::~GameStream() { +SceneStream::~SceneStream() { // Ship our last commands (if it matters..) Flush(); if (writing_replay_) { // Sanity check: We should only ever be writing one replay at once. if (!g_app->replay_open) { - Log("ERROR: g_replay_open false at replay close; shouldn't happen."); + Log(LogLevel::kError, + "g_replay_open false at replay close; shouldn't happen."); } g_app->replay_open = false; - assert(g_media_server); - g_media_server->PushEndWriteReplayCall(); + assert(g_assets_server); + g_assets_server->PushEndWriteReplayCall(); writing_replay_ = false; } // If we're wired to the host-session, go ahead and release clients. if (host_session_) { - g_game->connections()->UnregisterClientController(this); + g_logic->connections()->UnregisterClientController(this); #pragma clang diagnostic push #pragma ide diagnostic ignored "UnreachableCode" @@ -77,38 +79,40 @@ GameStream::~GameStream() { size_t count; count = GetPointerCount(scenes_); if (count != 0) { - Log("ERROR: " + std::to_string(count) - + " scene graphs in output stream at shutdown"); + Log(LogLevel::kError, + std::to_string(count) + + " scene graphs in output stream at shutdown"); } count = GetPointerCount(nodes_); if (count != 0) { - Log("ERROR: " + std::to_string(count) - + " nodes in output stream at shutdown"); + Log(LogLevel::kError, + std::to_string(count) + " nodes in output stream at shutdown"); } count = GetPointerCount(materials_); if (count != 0) { - Log("ERROR: " + std::to_string(count) - + " materials in output stream at shutdown"); + Log(LogLevel::kError, + std::to_string(count) + " materials in output stream at shutdown"); } count = GetPointerCount(textures_); if (count != 0) { - Log("ERROR: " + std::to_string(count) - + " textures in output stream at shutdown"); + Log(LogLevel::kError, + std::to_string(count) + " textures in output stream at shutdown"); } count = GetPointerCount(models_); if (count != 0) { - Log("ERROR: " + std::to_string(count) - + " models in output stream at shutdown"); + Log(LogLevel::kError, + std::to_string(count) + " models in output stream at shutdown"); } count = GetPointerCount(sounds_); if (count != 0) { - Log("ERROR: " + std::to_string(count) - + " sounds in output stream at shutdown"); + Log(LogLevel::kError, + std::to_string(count) + " sounds in output stream at shutdown"); } count = GetPointerCount(collide_models_); if (count != 0) { - Log("ERROR: " + std::to_string(count) - + " collide_models in output stream at shutdown"); + Log(LogLevel::kError, + std::to_string(count) + + " collide_models in output stream at shutdown"); } } } @@ -117,17 +121,18 @@ GameStream::~GameStream() { } // Pull the current built-up message. -auto GameStream::GetOutMessage() const -> std::vector { +auto SceneStream::GetOutMessage() const -> std::vector { assert(!host_session_); // this should only be getting used for // standalone temp ones.. if (!out_command_.empty()) { - Log("Error: GameStream shutting down with non-empty outCommand"); + Log(LogLevel::kError, + "SceneStream shutting down with non-empty outCommand"); } return out_message_; } template -auto GameStream::GetPointerCount(const std::vector& vec) -> size_t { +auto SceneStream::GetPointerCount(const std::vector& vec) -> size_t { size_t count = 0; auto size = vec.size(); @@ -143,8 +148,8 @@ auto GameStream::GetPointerCount(const std::vector& vec) -> size_t { // Given a vector of pointers, return an index to an available (nullptr) entry, // expanding the vector if need be. template -auto GameStream::GetFreeIndex(std::vector* vec, - std::vector* free_indices) -> size_t { +auto SceneStream::GetFreeIndex(std::vector* vec, + std::vector* free_indices) -> size_t { // If we have any free indices, use one of them. if (!free_indices->empty()) { size_t val = free_indices->back(); @@ -159,8 +164,8 @@ auto GameStream::GetFreeIndex(std::vector* vec, // Add an entry. template -void GameStream::Add(T* val, std::vector* vec, - std::vector* free_indices) { +void SceneStream::Add(T* val, std::vector* vec, + std::vector* free_indices) { // This should only get used when we're being driven by the host-session. assert(host_session_); assert(val); @@ -172,8 +177,8 @@ void GameStream::Add(T* val, std::vector* vec, // Remove an entry. template -void GameStream::Remove(T* val, std::vector* vec, - std::vector* free_indices) { +void SceneStream::Remove(T* val, std::vector* vec, + std::vector* free_indices) { assert(val); assert(val->stream_id() >= 0); assert(static_cast(vec->size()) > val->stream_id()); @@ -185,23 +190,25 @@ void GameStream::Remove(T* val, std::vector* vec, val->clear_stream_id(); } -void GameStream::Fail() { - Log("Error writing replay file"); +void SceneStream::Fail() { + Log(LogLevel::kError, "Error writing replay file"); if (writing_replay_) { // Sanity check: We should only ever be writing one replay at once. if (!g_app->replay_open) { - Log("ERROR: g_replay_open false at replay close; shouldn't happen."); + Log(LogLevel::kError, + "g_replay_open false at replay close; shouldn't happen."); } - assert(g_media_server); - g_media_server->PushEndWriteReplayCall(); + assert(g_assets_server); + g_assets_server->PushEndWriteReplayCall(); writing_replay_ = false; g_app->replay_open = false; } } -void GameStream::Flush() { +void SceneStream::Flush() { if (!out_command_.empty()) - Log("Error: GameStream flushing down with non-empty outCommand"); + Log(LogLevel::kError, + "SceneStream flushing down with non-empty outCommand"); if (!out_message_.empty()) { ShipSessionCommandsMessage(); } @@ -211,7 +218,7 @@ void GameStream::Flush() { #pragma ide diagnostic ignored "ConstantParameter" // Writes just a command. -void GameStream::WriteCommand(SessionCommand cmd) { +void SceneStream::WriteCommand(SessionCommand cmd) { assert(out_command_.empty()); // For now just use full size values. @@ -224,7 +231,7 @@ void GameStream::WriteCommand(SessionCommand cmd) { #pragma clang diagnostic pop // Writes a command plus an int to the stream, using whatever size is optimal. -void GameStream::WriteCommandInt32(SessionCommand cmd, int32_t value) { +void SceneStream::WriteCommandInt32(SessionCommand cmd, int32_t value) { assert(out_command_.empty()); // For now just use full size values. @@ -236,8 +243,8 @@ void GameStream::WriteCommandInt32(SessionCommand cmd, int32_t value) { memcpy(ptr, vals, 4); } -void GameStream::WriteCommandInt32_2(SessionCommand cmd, int32_t value1, - int32_t value2) { +void SceneStream::WriteCommandInt32_2(SessionCommand cmd, int32_t value1, + int32_t value2) { assert(out_command_.empty()); // For now just use full size vals. @@ -249,8 +256,8 @@ void GameStream::WriteCommandInt32_2(SessionCommand cmd, int32_t value1, memcpy(ptr, vals, 8); } -void GameStream::WriteCommandInt32_3(SessionCommand cmd, int32_t value1, - int32_t value2, int32_t value3) { +void SceneStream::WriteCommandInt32_3(SessionCommand cmd, int32_t value1, + int32_t value2, int32_t value3) { assert(out_command_.empty()); // For now just use full size vals. @@ -262,9 +269,9 @@ void GameStream::WriteCommandInt32_3(SessionCommand cmd, int32_t value1, memcpy(ptr, vals, 12); } -void GameStream::WriteCommandInt32_4(SessionCommand cmd, int32_t value1, - int32_t value2, int32_t value3, - int32_t value4) { +void SceneStream::WriteCommandInt32_4(SessionCommand cmd, int32_t value1, + int32_t value2, int32_t value3, + int32_t value4) { assert(out_command_.empty()); // For now just use full size vals. @@ -280,33 +287,33 @@ void GameStream::WriteCommandInt32_4(SessionCommand cmd, int32_t value1, // adding these placeholders for if/when we do. // They will also catch values greater than 32 bits in debug mode. // We'll need a protocol update to add support for 64 bit over the wire. -void GameStream::WriteCommandInt64(SessionCommand cmd, int64_t value) { +void SceneStream::WriteCommandInt64(SessionCommand cmd, int64_t value) { WriteCommandInt32(cmd, static_cast_check_fit(value)); } -void GameStream::WriteCommandInt64_2(SessionCommand cmd, int64_t value1, - int64_t value2) { +void SceneStream::WriteCommandInt64_2(SessionCommand cmd, int64_t value1, + int64_t value2) { WriteCommandInt32_2(cmd, static_cast_check_fit(value1), static_cast_check_fit(value2)); } -void GameStream::WriteCommandInt64_3(SessionCommand cmd, int64_t value1, - int64_t value2, int64_t value3) { +void SceneStream::WriteCommandInt64_3(SessionCommand cmd, int64_t value1, + int64_t value2, int64_t value3) { WriteCommandInt32_3(cmd, static_cast_check_fit(value1), static_cast_check_fit(value2), static_cast_check_fit(value3)); } -void GameStream::WriteCommandInt64_4(SessionCommand cmd, int64_t value1, - int64_t value2, int64_t value3, - int64_t value4) { +void SceneStream::WriteCommandInt64_4(SessionCommand cmd, int64_t value1, + int64_t value2, int64_t value3, + int64_t value4) { WriteCommandInt32_4(cmd, static_cast_check_fit(value1), static_cast_check_fit(value2), static_cast_check_fit(value3), static_cast_check_fit(value4)); } -void GameStream::WriteString(const std::string& s) { +void SceneStream::WriteString(const std::string& s) { // Write length int. auto string_size = s.size(); auto size = out_command_.size(); @@ -317,13 +324,13 @@ void GameStream::WriteString(const std::string& s) { } } -void GameStream::WriteFloat(float val) { +void SceneStream::WriteFloat(float val) { auto size = static_cast(out_command_.size()); out_command_.resize(size + sizeof(val)); memcpy(&out_command_[size], &val, 4); } -void GameStream::WriteFloats(size_t count, const float* vals) { +void SceneStream::WriteFloats(size_t count, const float* vals) { assert(count > 0); auto size = out_command_.size(); size_t vals_size = sizeof(float) * count; @@ -331,7 +338,7 @@ void GameStream::WriteFloats(size_t count, const float* vals) { memcpy(&(out_command_[size]), vals, vals_size); } -void GameStream::WriteInts32(size_t count, const int32_t* vals) { +void SceneStream::WriteInts32(size_t count, const int32_t* vals) { assert(count > 0); auto size = out_command_.size(); size_t vals_size = sizeof(int32_t) * count; @@ -339,7 +346,7 @@ void GameStream::WriteInts32(size_t count, const int32_t* vals) { memcpy(&(out_command_[size]), vals, vals_size); } -void GameStream::WriteInts64(size_t count, const int64_t* vals) { +void SceneStream::WriteInts64(size_t count, const int64_t* vals) { // FIXME: we don't actually support writing 64 bit values to the wire // at the moment; will need a protocol update for that. // This is just implemented as a placeholder. @@ -350,7 +357,7 @@ void GameStream::WriteInts64(size_t count, const int64_t* vals) { WriteInts32(count, vals32.data()); } -void GameStream::WriteChars(size_t count, const char* vals) { +void SceneStream::WriteChars(size_t count, const char* vals) { assert(count > 0); auto size = out_command_.size(); auto vals_size = static_cast(count); @@ -358,7 +365,7 @@ void GameStream::WriteChars(size_t count, const char* vals) { memcpy(&(out_command_[size]), vals, vals_size); } -void GameStream::ShipSessionCommandsMessage() { +void SceneStream::ShipSessionCommandsMessage() { BA_PRECONDITION(!out_message_.empty()); // Send this message to all client-connections we're attached to. @@ -372,9 +379,9 @@ void GameStream::ShipSessionCommandsMessage() { last_send_time_ = GetRealTime(); } -void GameStream::AddMessageToReplay(const std::vector& message) { +void SceneStream::AddMessageToReplay(const std::vector& message) { assert(writing_replay_); - assert(g_media_server); + assert(g_assets_server); assert(!message.empty()); if (g_buildconfig.debug_build()) { @@ -389,10 +396,10 @@ void GameStream::AddMessageToReplay(const std::vector& message) { } } - g_media_server->PushAddMessageToReplayCall(message); + g_assets_server->PushAddMessageToReplayCall(message); } -void GameStream::SendPhysicsCorrection(bool blend) { +void SceneStream::SendPhysicsCorrection(bool blend) { assert(host_session_); std::vector > messages; @@ -410,7 +417,7 @@ void GameStream::SendPhysicsCorrection(bool blend) { } } -void GameStream::EndCommand(bool is_time_set) { +void SceneStream::EndCommand(bool is_time_set) { assert(!out_command_.empty()); int out_message_size; @@ -456,7 +463,7 @@ void GameStream::EndCommand(bool is_time_set) { out_command_.clear(); } -auto GameStream::IsValidScene(Scene* s) -> bool { +auto SceneStream::IsValidScene(Scene* s) -> bool { if (!host_session_) { return true; // We don't build lists in this mode so can't verify this. } @@ -465,7 +472,7 @@ auto GameStream::IsValidScene(Scene* s) -> bool { && scenes_[s->stream_id()] == s); } -auto GameStream::IsValidNode(Node* n) -> bool { +auto SceneStream::IsValidNode(Node* n) -> bool { if (!host_session_) { return true; // We don't build lists in this mode so can't verify this. } @@ -474,7 +481,7 @@ auto GameStream::IsValidNode(Node* n) -> bool { && nodes_[n->stream_id()] == n); } -auto GameStream::IsValidTexture(Texture* n) -> bool { +auto SceneStream::IsValidTexture(Texture* n) -> bool { if (!host_session_) { return true; // We don't build lists in this mode so can't verify this. } @@ -483,7 +490,7 @@ auto GameStream::IsValidTexture(Texture* n) -> bool { && textures_[n->stream_id()] == n); } -auto GameStream::IsValidModel(Model* n) -> bool { +auto SceneStream::IsValidModel(Model* n) -> bool { if (!host_session_) { return true; // We don't build lists in this mode so can't verify this. } @@ -492,7 +499,7 @@ auto GameStream::IsValidModel(Model* n) -> bool { && models_[n->stream_id()] == n); } -auto GameStream::IsValidSound(Sound* n) -> bool { +auto SceneStream::IsValidSound(Sound* n) -> bool { if (!host_session_) { return true; // We don't build lists in this mode so can't verify this. } @@ -501,7 +508,7 @@ auto GameStream::IsValidSound(Sound* n) -> bool { && sounds_[n->stream_id()] == n); } -auto GameStream::IsValidData(Data* n) -> bool { +auto SceneStream::IsValidData(Data* n) -> bool { if (!host_session_) { return true; // We don't build lists in this mode so can't verify this. } @@ -510,7 +517,7 @@ auto GameStream::IsValidData(Data* n) -> bool { && datas_[n->stream_id()] == n); } -auto GameStream::IsValidCollideModel(CollideModel* n) -> bool { +auto SceneStream::IsValidCollideModel(CollideModel* n) -> bool { if (!host_session_) { return true; // We don't build lists in this mode so can't verify this. } @@ -519,7 +526,7 @@ auto GameStream::IsValidCollideModel(CollideModel* n) -> bool { && collide_models_[n->stream_id()] == n); } -auto GameStream::IsValidMaterial(Material* n) -> bool { +auto SceneStream::IsValidMaterial(Material* n) -> bool { if (!host_session_) { return true; // We don't build lists in this mode so can't verify this. } @@ -528,13 +535,13 @@ auto GameStream::IsValidMaterial(Material* n) -> bool { && materials_[n->stream_id()] == n); } -void GameStream::SetTime(millisecs_t t) { +void SceneStream::SetTime(millisecs_t t) { if (time_ == t) { return; // Ignore redundants. } millisecs_t diff = t - time_; if (diff > 255) { - Log("Error: GameStream got time diff > 255; not expected."); + Log(LogLevel::kError, "SceneStream got time diff > 255; not expected."); diff = 255; } WriteCommandInt64(SessionCommand::kBaseTimeStep, diff); @@ -542,7 +549,7 @@ void GameStream::SetTime(millisecs_t t) { EndCommand(true); } -void GameStream::AddScene(Scene* s) { +void SceneStream::AddScene(Scene* s) { // Host mode. if (host_session_) { Add(s, &scenes_, &free_indices_scene_graphs_); @@ -556,19 +563,19 @@ void GameStream::AddScene(Scene* s) { EndCommand(); } -void GameStream::RemoveScene(Scene* s) { +void SceneStream::RemoveScene(Scene* s) { WriteCommandInt64(SessionCommand::kRemoveSceneGraph, s->stream_id()); Remove(s, &scenes_, &free_indices_scene_graphs_); EndCommand(); } -void GameStream::StepScene(Scene* s) { +void SceneStream::StepScene(Scene* s) { assert(IsValidScene(s)); WriteCommandInt64(SessionCommand::kStepSceneGraph, s->stream_id()); EndCommand(); } -void GameStream::AddNode(Node* n) { +void SceneStream::AddNode(Node* n) { assert(n); if (host_session_) { Add(n, &nodes_, &free_indices_nodes_); @@ -583,26 +590,26 @@ void GameStream::AddNode(Node* n) { EndCommand(); } -void GameStream::NodeOnCreate(Node* n) { +void SceneStream::NodeOnCreate(Node* n) { assert(IsValidNode(n)); WriteCommandInt64(SessionCommand::kNodeOnCreate, n->stream_id()); EndCommand(); } -void GameStream::SetForegroundScene(Scene* sg) { +void SceneStream::SetForegroundScene(Scene* sg) { assert(IsValidScene(sg)); WriteCommandInt64(SessionCommand::kSetForegroundSceneGraph, sg->stream_id()); EndCommand(); } -void GameStream::RemoveNode(Node* n) { +void SceneStream::RemoveNode(Node* n) { assert(IsValidNode(n)); WriteCommandInt64(SessionCommand::kRemoveNode, n->stream_id()); Remove(n, &nodes_, &free_indices_nodes_); EndCommand(); } -void GameStream::AddTexture(Texture* t) { +void SceneStream::AddTexture(Texture* t) { // Register an ID in host mode. if (host_session_) { Add(t, &textures_, &free_indices_textures_); @@ -617,14 +624,14 @@ void GameStream::AddTexture(Texture* t) { EndCommand(); } -void GameStream::RemoveTexture(Texture* t) { +void SceneStream::RemoveTexture(Texture* t) { assert(IsValidTexture(t)); WriteCommandInt64(SessionCommand::kRemoveTexture, t->stream_id()); Remove(t, &textures_, &free_indices_textures_); EndCommand(); } -void GameStream::AddModel(Model* t) { +void SceneStream::AddModel(Model* t) { // Register an ID in host mode. if (host_session_) { Add(t, &models_, &free_indices_models_); @@ -639,14 +646,14 @@ void GameStream::AddModel(Model* t) { EndCommand(); } -void GameStream::RemoveModel(Model* t) { +void SceneStream::RemoveModel(Model* t) { assert(IsValidModel(t)); WriteCommandInt64(SessionCommand::kRemoveModel, t->stream_id()); Remove(t, &models_, &free_indices_models_); EndCommand(); } -void GameStream::AddSound(Sound* t) { +void SceneStream::AddSound(Sound* t) { // Register an ID in host mode. if (host_session_) { Add(t, &sounds_, &free_indices_sounds_); @@ -661,14 +668,14 @@ void GameStream::AddSound(Sound* t) { EndCommand(); } -void GameStream::RemoveSound(Sound* t) { +void SceneStream::RemoveSound(Sound* t) { assert(IsValidSound(t)); WriteCommandInt64(SessionCommand::kRemoveSound, t->stream_id()); Remove(t, &sounds_, &free_indices_sounds_); EndCommand(); } -void GameStream::AddData(Data* t) { +void SceneStream::AddData(Data* t) { // Register an ID in host mode. if (host_session_) { Add(t, &datas_, &free_indices_datas_); @@ -683,14 +690,14 @@ void GameStream::AddData(Data* t) { EndCommand(); } -void GameStream::RemoveData(Data* t) { +void SceneStream::RemoveData(Data* t) { assert(IsValidData(t)); WriteCommandInt64(SessionCommand::kRemoveData, t->stream_id()); Remove(t, &datas_, &free_indices_datas_); EndCommand(); } -void GameStream::AddCollideModel(CollideModel* t) { +void SceneStream::AddCollideModel(CollideModel* t) { if (host_session_) { Add(t, &collide_models_, &free_indices_collide_models_); } else { @@ -704,14 +711,14 @@ void GameStream::AddCollideModel(CollideModel* t) { EndCommand(); } -void GameStream::RemoveCollideModel(CollideModel* t) { +void SceneStream::RemoveCollideModel(CollideModel* t) { assert(IsValidCollideModel(t)); WriteCommandInt64(SessionCommand::kRemoveCollideModel, t->stream_id()); Remove(t, &collide_models_, &free_indices_collide_models_); EndCommand(); } -void GameStream::AddMaterial(Material* m) { +void SceneStream::AddMaterial(Material* m) { if (host_session_) { Add(m, &materials_, &free_indices_materials_); } else { @@ -724,14 +731,14 @@ void GameStream::AddMaterial(Material* m) { EndCommand(); } -void GameStream::RemoveMaterial(Material* m) { +void SceneStream::RemoveMaterial(Material* m) { assert(IsValidMaterial(m)); WriteCommandInt64(SessionCommand::kRemoveMaterial, m->stream_id()); Remove(m, &materials_, &free_indices_materials_); EndCommand(); } -void GameStream::AddMaterialComponent(Material* m, MaterialComponent* c) { +void SceneStream::AddMaterialComponent(Material* m, MaterialComponent* c) { assert(IsValidMaterial(m)); auto flattened_size = c->GetFlattenedSize(); assert(flattened_size > 0 && flattened_size < 10000); @@ -750,10 +757,10 @@ void GameStream::AddMaterialComponent(Material* m, MaterialComponent* c) { EndCommand(); } -void GameStream::ConnectNodeAttribute(Node* src_node, - NodeAttributeUnbound* src_attr, - Node* dst_node, - NodeAttributeUnbound* dst_attr) { +void SceneStream::ConnectNodeAttribute(Node* src_node, + NodeAttributeUnbound* src_attr, + Node* dst_node, + NodeAttributeUnbound* dst_attr) { assert(IsValidNode(src_node)); assert(IsValidNode(dst_node)); assert(src_attr->node_type() == src_node->type()); @@ -768,7 +775,7 @@ void GameStream::ConnectNodeAttribute(Node* src_node, EndCommand(); } -void GameStream::NodeMessage(Node* node, const char* buffer, size_t size) { +void SceneStream::NodeMessage(Node* node, const char* buffer, size_t size) { assert(IsValidNode(node)); BA_PRECONDITION(size > 0 && size < 10000); WriteCommandInt64_2(SessionCommand::kNodeMessage, node->stream_id(), @@ -777,7 +784,7 @@ void GameStream::NodeMessage(Node* node, const char* buffer, size_t size) { EndCommand(); } -void GameStream::SetNodeAttr(const NodeAttribute& attr, float val) { +void SceneStream::SetNodeAttr(const NodeAttribute& attr, float val) { assert(IsValidNode(attr.node)); WriteCommandInt64_2(SessionCommand::kSetNodeAttrFloat, attr.node->stream_id(), attr.index()); @@ -785,22 +792,22 @@ void GameStream::SetNodeAttr(const NodeAttribute& attr, float val) { EndCommand(); } -void GameStream::SetNodeAttr(const NodeAttribute& attr, int64_t val) { +void SceneStream::SetNodeAttr(const NodeAttribute& attr, int64_t val) { assert(IsValidNode(attr.node)); WriteCommandInt64_3(SessionCommand::kSetNodeAttrInt32, attr.node->stream_id(), attr.index(), val); EndCommand(); } -void GameStream::SetNodeAttr(const NodeAttribute& attr, bool val) { +void SceneStream::SetNodeAttr(const NodeAttribute& attr, bool val) { assert(IsValidNode(attr.node)); WriteCommandInt64_3(SessionCommand::kSetNodeAttrBool, attr.node->stream_id(), attr.index(), val); EndCommand(); } -void GameStream::SetNodeAttr(const NodeAttribute& attr, - const std::vector& vals) { +void SceneStream::SetNodeAttr(const NodeAttribute& attr, + const std::vector& vals) { assert(IsValidNode(attr.node)); size_t count{vals.size()}; WriteCommandInt64_3(SessionCommand::kSetNodeAttrFloats, @@ -812,8 +819,8 @@ void GameStream::SetNodeAttr(const NodeAttribute& attr, EndCommand(); } -void GameStream::SetNodeAttr(const NodeAttribute& attr, - const std::vector& vals) { +void SceneStream::SetNodeAttr(const NodeAttribute& attr, + const std::vector& vals) { assert(IsValidNode(attr.node)); size_t count{vals.size()}; WriteCommandInt64_3(SessionCommand::kSetNodeAttrInt32s, @@ -825,8 +832,8 @@ void GameStream::SetNodeAttr(const NodeAttribute& attr, EndCommand(); } -void GameStream::SetNodeAttr(const NodeAttribute& attr, - const std::string& val) { +void SceneStream::SetNodeAttr(const NodeAttribute& attr, + const std::string& val) { assert(IsValidNode(attr.node)); WriteCommandInt64_2(SessionCommand::kSetNodeAttrString, attr.node->stream_id(), attr.index()); @@ -834,7 +841,7 @@ void GameStream::SetNodeAttr(const NodeAttribute& attr, EndCommand(); } -void GameStream::SetNodeAttr(const NodeAttribute& attr, Node* val) { +void SceneStream::SetNodeAttr(const NodeAttribute& attr, Node* val) { assert(IsValidNode(attr.node)); if (val) { assert(IsValidNode(val)); @@ -850,8 +857,8 @@ void GameStream::SetNodeAttr(const NodeAttribute& attr, Node* val) { EndCommand(); } -void GameStream::SetNodeAttr(const NodeAttribute& attr, - const std::vector& vals) { +void SceneStream::SetNodeAttr(const NodeAttribute& attr, + const std::vector& vals) { assert(IsValidNode(attr.node)); if (g_buildconfig.debug_build()) { for (auto val : vals) { @@ -878,12 +885,12 @@ void GameStream::SetNodeAttr(const NodeAttribute& attr, EndCommand(); } -void GameStream::SetNodeAttr(const NodeAttribute& attr, Player* val) { +void SceneStream::SetNodeAttr(const NodeAttribute& attr, Player* val) { // cout << "SET PLAYER ATTR " << attr.getIndex() << endl; } -void GameStream::SetNodeAttr(const NodeAttribute& attr, - const std::vector& vals) { +void SceneStream::SetNodeAttr(const NodeAttribute& attr, + const std::vector& vals) { assert(IsValidNode(attr.node)); if (g_buildconfig.debug_build()) { for (auto val : vals) { @@ -911,7 +918,7 @@ void GameStream::SetNodeAttr(const NodeAttribute& attr, EndCommand(); } -void GameStream::SetNodeAttr(const NodeAttribute& attr, Texture* val) { +void SceneStream::SetNodeAttr(const NodeAttribute& attr, Texture* val) { if (val) { assert(IsValidNode(attr.node)); assert(IsValidTexture(val)); @@ -927,8 +934,8 @@ void GameStream::SetNodeAttr(const NodeAttribute& attr, Texture* val) { EndCommand(); } -void GameStream::SetNodeAttr(const NodeAttribute& attr, - const std::vector& vals) { +void SceneStream::SetNodeAttr(const NodeAttribute& attr, + const std::vector& vals) { assert(IsValidNode(attr.node)); if (g_buildconfig.debug_build()) { for (auto val : vals) { @@ -956,7 +963,7 @@ void GameStream::SetNodeAttr(const NodeAttribute& attr, EndCommand(); } -void GameStream::SetNodeAttr(const NodeAttribute& attr, Sound* val) { +void SceneStream::SetNodeAttr(const NodeAttribute& attr, Sound* val) { if (val) { assert(IsValidNode(attr.node)); assert(IsValidSound(val)); @@ -972,8 +979,8 @@ void GameStream::SetNodeAttr(const NodeAttribute& attr, Sound* val) { EndCommand(); } -void GameStream::SetNodeAttr(const NodeAttribute& attr, - const std::vector& vals) { +void SceneStream::SetNodeAttr(const NodeAttribute& attr, + const std::vector& vals) { assert(IsValidNode(attr.node)); if (g_buildconfig.debug_build()) { for (auto val : vals) { @@ -1001,7 +1008,7 @@ void GameStream::SetNodeAttr(const NodeAttribute& attr, EndCommand(); } -void GameStream::SetNodeAttr(const NodeAttribute& attr, Model* val) { +void SceneStream::SetNodeAttr(const NodeAttribute& attr, Model* val) { if (val) { assert(IsValidNode(attr.node)); assert(IsValidModel(val)); @@ -1017,8 +1024,8 @@ void GameStream::SetNodeAttr(const NodeAttribute& attr, Model* val) { EndCommand(); } -void GameStream::SetNodeAttr(const NodeAttribute& attr, - const std::vector& vals) { +void SceneStream::SetNodeAttr(const NodeAttribute& attr, + const std::vector& vals) { assert(IsValidNode(attr.node)); if (g_buildconfig.debug_build()) { for (auto val : vals) { @@ -1045,7 +1052,7 @@ void GameStream::SetNodeAttr(const NodeAttribute& attr, } EndCommand(); } -void GameStream::SetNodeAttr(const NodeAttribute& attr, CollideModel* val) { +void SceneStream::SetNodeAttr(const NodeAttribute& attr, CollideModel* val) { if (val) { assert(IsValidNode(attr.node)); assert(IsValidCollideModel(val)); @@ -1060,8 +1067,8 @@ void GameStream::SetNodeAttr(const NodeAttribute& attr, CollideModel* val) { } EndCommand(); } -void GameStream::SetNodeAttr(const NodeAttribute& attr, - const std::vector& vals) { +void SceneStream::SetNodeAttr(const NodeAttribute& attr, + const std::vector& vals) { assert(IsValidNode(attr.node)); if (g_buildconfig.debug_build()) { for (auto val : vals) { @@ -1089,8 +1096,8 @@ void GameStream::SetNodeAttr(const NodeAttribute& attr, EndCommand(); } -void GameStream::PlaySoundAtPosition(Sound* sound, float volume, float x, - float y, float z) { +void SceneStream::PlaySoundAtPosition(Sound* sound, float volume, float x, + float y, float z) { assert(IsValidSound(sound)); assert(IsValidScene(sound->scene())); @@ -1103,7 +1110,7 @@ void GameStream::PlaySoundAtPosition(Sound* sound, float volume, float x, EndCommand(); } -void GameStream::EmitBGDynamics(const BGDynamicsEmission& e) { +void SceneStream::EmitBGDynamics(const BGDynamicsEmission& e) { WriteCommandInt64_4(SessionCommand::kEmitBGDynamics, static_cast(e.emit_type), e.count, static_cast(e.chunk_type), @@ -1121,7 +1128,7 @@ void GameStream::EmitBGDynamics(const BGDynamicsEmission& e) { EndCommand(); } -void GameStream::PlaySound(Sound* sound, float volume) { +void SceneStream::PlaySound(Sound* sound, float volume) { assert(IsValidSound(sound)); assert(IsValidScene(sound->scene())); @@ -1131,11 +1138,11 @@ void GameStream::PlaySound(Sound* sound, float volume) { EndCommand(); } -void GameStream::ScreenMessageTop(const std::string& val, float r, float g, - float b, Texture* texture, - Texture* tint_texture, float tint_r, - float tint_g, float tint_b, float tint2_r, - float tint2_g, float tint2_b) { +void SceneStream::ScreenMessageTop(const std::string& val, float r, float g, + float b, Texture* texture, + Texture* tint_texture, float tint_r, + float tint_g, float tint_b, float tint2_r, + float tint2_g, float tint2_b) { assert(IsValidTexture(texture)); assert(IsValidTexture(tint_texture)); assert(IsValidScene(texture->scene())); @@ -1157,8 +1164,8 @@ void GameStream::ScreenMessageTop(const std::string& val, float r, float g, EndCommand(); } -void GameStream::ScreenMessageBottom(const std::string& val, float r, float g, - float b) { +void SceneStream::ScreenMessageBottom(const std::string& val, float r, float g, + float b) { WriteCommand(SessionCommand::kScreenMessageBottom); WriteString(val); float color[3]; @@ -1169,27 +1176,29 @@ void GameStream::ScreenMessageBottom(const std::string& val, float r, float g, EndCommand(); } -auto GameStream::GetSoundID(Sound* s) -> int64_t { +auto SceneStream::GetSoundID(Sound* s) -> int64_t { assert(IsValidSound(s)); return s->stream_id(); } -auto GameStream::GetMaterialID(Material* m) -> int64_t { +auto SceneStream::GetMaterialID(Material* m) -> int64_t { assert(IsValidMaterial(m)); return m->stream_id(); } -void GameStream::OnClientConnected(ConnectionToClient* c) { +void SceneStream::OnClientConnected(ConnectionToClient* c) { // Sanity check - abort if its on either of our lists already. for (auto& connections_to_client : connections_to_clients_) { if (connections_to_client == c) { - Log("Error: GameStream::OnClientConnected() got duplicate connection."); + Log(LogLevel::kError, + "SceneStream::OnClientConnected() got duplicate connection."); return; } } for (auto& i : connections_to_clients_ignored_) { if (i == c) { - Log("Error: GameStream::OnClientConnected() got duplicate connection."); + Log(LogLevel::kError, + "SceneStream::OnClientConnected() got duplicate connection."); return; } } @@ -1208,7 +1217,7 @@ void GameStream::OnClientConnected(ConnectionToClient* c) { // We create a temporary output stream just for the purpose of building // a giant session-commands message to reconstruct everything in our // host-session in its current form. - GameStream out(nullptr, false); + SceneStream out(nullptr, false); // Ask the host-session that we came from to dump it's complete state. host_session_->DumpFullState(&out); @@ -1226,7 +1235,7 @@ void GameStream::OnClientConnected(ConnectionToClient* c) { } } -void GameStream::OnClientDisconnected(ConnectionToClient* c) { +void SceneStream::OnClientDisconnected(ConnectionToClient* c) { // Search for it on either our ignored or regular lists. for (auto i = connections_to_clients_.begin(); i != connections_to_clients_.end(); i++) { @@ -1242,7 +1251,8 @@ void GameStream::OnClientDisconnected(ConnectionToClient* c) { return; } } - Log("Error: GameStream::OnClientDisconnected() called for connection not on " + Log(LogLevel::kError, + "SceneStream::OnClientDisconnected() called for connection not on " "lists"); } diff --git a/src/ballistica/game/game_stream.h b/src/ballistica/scene/scene_stream.h similarity index 95% rename from src/ballistica/game/game_stream.h rename to src/ballistica/scene/scene_stream.h index 0cb56b24..a43906a4 100644 --- a/src/ballistica/game/game_stream.h +++ b/src/ballistica/scene/scene_stream.h @@ -1,22 +1,22 @@ // Released under the MIT License. See LICENSE for details. -#ifndef BALLISTICA_GAME_GAME_STREAM_H_ -#define BALLISTICA_GAME_GAME_STREAM_H_ +#ifndef BALLISTICA_SCENE_SCENE_STREAM_H_ +#define BALLISTICA_SCENE_SCENE_STREAM_H_ #include #include #include "ballistica/core/object.h" -#include "ballistica/game/client_controller_interface.h" +#include "ballistica/logic/client_controller_interface.h" namespace ballistica { // A mechanism for dumping a live session or session-creation-commands to a // stream of messages that can be saved to file or sent over the network. -class GameStream : public Object, public ClientControllerInterface { +class SceneStream : public Object, public ClientControllerInterface { public: - GameStream(HostSession* host_session, bool save_replay); - ~GameStream() override; + SceneStream(HostSession* host_session, bool save_replay); + ~SceneStream() override; auto SetTime(millisecs_t t) -> void; auto AddScene(Scene* s) -> void; auto RemoveScene(Scene* s) -> void; @@ -169,4 +169,4 @@ class GameStream : public Object, public ClientControllerInterface { } // namespace ballistica -#endif // BALLISTICA_GAME_GAME_STREAM_H_ +#endif // BALLISTICA_SCENE_SCENE_STREAM_H_ diff --git a/src/ballistica/scene/v1/scene_v1.cc b/src/ballistica/scene/v1/scene_v1.cc new file mode 100644 index 00000000..ad532926 --- /dev/null +++ b/src/ballistica/scene/v1/scene_v1.cc @@ -0,0 +1,96 @@ +// Released under the MIT License. See LICENSE for details. + +#include "ballistica/scene/v1/scene_v1.h" + +#include "ballistica/app/app.h" +#include "ballistica/scene/node/anim_curve_node.h" +#include "ballistica/scene/node/bomb_node.h" +#include "ballistica/scene/node/combine_node.h" +#include "ballistica/scene/node/explosion_node.h" +#include "ballistica/scene/node/flag_node.h" +#include "ballistica/scene/node/flash_node.h" +#include "ballistica/scene/node/globals_node.h" +#include "ballistica/scene/node/image_node.h" +#include "ballistica/scene/node/light_node.h" +#include "ballistica/scene/node/locator_node.h" +#include "ballistica/scene/node/math_node.h" +#include "ballistica/scene/node/null_node.h" +#include "ballistica/scene/node/player_node.h" +#include "ballistica/scene/node/region_node.h" +#include "ballistica/scene/node/scorch_node.h" +#include "ballistica/scene/node/session_globals_node.h" +#include "ballistica/scene/node/shield_node.h" +#include "ballistica/scene/node/sound_node.h" +#include "ballistica/scene/node/spaz_node.h" +#include "ballistica/scene/node/terrain_node.h" +#include "ballistica/scene/node/text_node.h" +#include "ballistica/scene/node/texture_sequence_node.h" +#include "ballistica/scene/node/time_display_node.h" + +namespace ballistica { + +static void SetupNodeMessageType(const std::string& name, NodeMessageType val, + const std::string& format) { + assert(g_app != nullptr); + g_app->node_message_types[name] = val; + assert(static_cast(val) >= 0); + if (g_app->node_message_formats.size() <= static_cast(val)) { + g_app->node_message_formats.resize(static_cast(val) + 1); + } + g_app->node_message_formats[static_cast(val)] = format; +} + +SceneV1::SceneV1() { + NodeType* node_types[] = {NullNode::InitType(), + GlobalsNode::InitType(), + SessionGlobalsNode::InitType(), + PropNode::InitType(), + FlagNode::InitType(), + BombNode::InitType(), + ExplosionNode::InitType(), + ShieldNode::InitType(), + LightNode::InitType(), + TextNode::InitType(), + AnimCurveNode::InitType(), + ImageNode::InitType(), + TerrainNode::InitType(), + MathNode::InitType(), + LocatorNode::InitType(), + PlayerNode::InitType(), + CombineNode::InitType(), + SoundNode::InitType(), + SpazNode::InitType(), + RegionNode::InitType(), + ScorchNode::InitType(), + FlashNode::InitType(), + TextureSequenceNode::InitType(), + TimeDisplayNode::InitType()}; + + int next_type_id = 0; + assert(g_app != nullptr); + for (auto* t : node_types) { + g_app->node_types[t->name()] = t; + g_app->node_types_by_id[next_type_id] = t; + t->set_id(next_type_id++); + } + + // Types: I is 32 bit int, i is 16 bit int, c is 8 bit int, + // F is 32 bit float, f is 16 bit float, + // s is string, b is bool. + SetupNodeMessageType("flash", NodeMessageType::kFlash, ""); + SetupNodeMessageType("footing", NodeMessageType::kFooting, "c"); + SetupNodeMessageType("impulse", NodeMessageType::kImpulse, "fffffffffifff"); + SetupNodeMessageType("kick_back", NodeMessageType::kKickback, "fffffff"); + SetupNodeMessageType("celebrate", NodeMessageType::kCelebrate, "i"); + SetupNodeMessageType("celebrate_l", NodeMessageType::kCelebrateL, "i"); + SetupNodeMessageType("celebrate_r", NodeMessageType::kCelebrateR, "i"); + SetupNodeMessageType("knockout", NodeMessageType::kKnockout, "f"); + SetupNodeMessageType("hurt_sound", NodeMessageType::kHurtSound, ""); + SetupNodeMessageType("picked_up", NodeMessageType::kPickedUp, ""); + SetupNodeMessageType("jump_sound", NodeMessageType::kJumpSound, ""); + SetupNodeMessageType("attack_sound", NodeMessageType::kAttackSound, ""); + SetupNodeMessageType("scream_sound", NodeMessageType::kScreamSound, ""); + SetupNodeMessageType("stand", NodeMessageType::kStand, "ffff"); +} + +} // namespace ballistica diff --git a/src/ballistica/scene/v1/scene_v1.h b/src/ballistica/scene/v1/scene_v1.h new file mode 100644 index 00000000..ffed4963 --- /dev/null +++ b/src/ballistica/scene/v1/scene_v1.h @@ -0,0 +1,16 @@ +// Released under the MIT License. See LICENSE for details. + +#ifndef BALLISTICA_SCENE_V1_SCENE_V1_H_ +#define BALLISTICA_SCENE_V1_SCENE_V1_H_ + +namespace ballistica { + +// Overall scene subsystem. +class SceneV1 { + public: + SceneV1(); +}; + +} // namespace ballistica + +#endif // BALLISTICA_SCENE_V1_SCENE_V1_H_ diff --git a/src/ballistica/ui/console.cc b/src/ballistica/ui/console.cc index cc9ffc5a..89cff2e0 100644 --- a/src/ballistica/ui/console.cc +++ b/src/ballistica/ui/console.cc @@ -4,10 +4,10 @@ #include "ballistica/app/app.h" #include "ballistica/audio/audio.h" -#include "ballistica/game/game.h" #include "ballistica/generic/utils.h" #include "ballistica/graphics/component/simple_component.h" #include "ballistica/graphics/text/text_graphics.h" +#include "ballistica/logic/logic.h" #include "ballistica/platform/min_sdl.h" namespace ballistica { @@ -53,8 +53,8 @@ auto Console::HandleKeyPress(const SDL_Keysym* keysym) -> bool { case kActivateKey2: { if (!g_buildconfig.demo_build() && !g_buildconfig.arcade_build()) { // (reset input so characters don't continue walking and stuff) - g_game->ResetInput(); - g_game->ToggleConsole(); + g_logic->ResetInput(); + g_logic->ToggleConsole(); } return true; } @@ -113,7 +113,7 @@ auto Console::HandleKeyPress(const SDL_Keysym* keysym) -> bool { last_line_.clear(); lines_.clear(); } else { - g_game->PushInGameConsoleScriptCommand(input_string_); + g_logic->PushInGameConsoleScriptCommand(input_string_); } input_history_.push_front(input_string_); if (input_history_.size() > 100) input_history_.pop_back(); @@ -153,7 +153,7 @@ void Console::ToggleState() { state_ = State::kInactive; break; } - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kBlip)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kBlip)); transition_start_ = GetRealTime(); } @@ -312,7 +312,7 @@ void Console::Draw(RenderPass* pass) { c.Translate(19.0f + g_text_graphics->GetStringWidth(input_string_) * 0.5f, bottom + 23.0f, kConsoleZDepth); c.Scale(5, 11, 1.0f); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); c.Submit(); } diff --git a/src/ballistica/ui/root_ui.cc b/src/ballistica/ui/root_ui.cc index 7b1e4705..1b0cbe72 100644 --- a/src/ballistica/ui/root_ui.cc +++ b/src/ballistica/ui/root_ui.cc @@ -2,13 +2,13 @@ #include "ballistica/ui/root_ui.h" -#include "ballistica/game/connection/connection_set.h" -#include "ballistica/game/game.h" -#include "ballistica/game/session/host_session.h" #include "ballistica/graphics/component/simple_component.h" #include "ballistica/input/device/keyboard_input.h" #include "ballistica/input/device/touch_input.h" #include "ballistica/input/input.h" +#include "ballistica/logic/connection/connection_set.h" +#include "ballistica/logic/logic.h" +#include "ballistica/logic/session/host_session.h" #include "ballistica/python/python.h" #include "ballistica/ui/ui.h" #include "ballistica/ui/widget/container_widget.h" @@ -43,7 +43,8 @@ RootUI::~RootUI() = default; void RootUI::TogglePartyWindowKeyPress() { assert(InLogicThread()); - if (g_game->GetPartySize() > 1 || g_game->connections()->connection_to_host() + if (g_logic->GetPartySize() > 1 + || g_logic->connections()->connection_to_host() || always_draw_party_icon()) { ActivatePartyIcon(); } @@ -51,7 +52,7 @@ void RootUI::TogglePartyWindowKeyPress() { void RootUI::ActivatePartyIcon() const { assert(InLogicThread()); - ScopedSetContext cp(g_game->GetUIContext()); + ScopedSetContext cp(g_logic->GetUIContext()); // Originate from center of party icon. If menu button is shown, it is to the // left of that. @@ -78,8 +79,8 @@ auto RootUI::HandleMouseButtonDown(float x, float y) -> bool { if (explicit_bool(DO_OLD_MENU_PARTY_BUTTONS)) { bool party_button_active = (!party_window_open_ - && (g_game->connections()->GetConnectedClientCount() > 0 - || g_game->connections()->connection_to_host() + && (g_logic->connections()->GetConnectedClientCount() > 0 + || g_logic->connections()->connection_to_host() || always_draw_party_icon())); float party_button_left = menu_active ? 2 * menu_button_size_ : menu_button_size_; @@ -123,7 +124,7 @@ void RootUI::HandleMouseButtonUp(float x, float y) { } if ((g_graphics->screen_virtual_width() - x < menu_button_size_) && (g_graphics->screen_virtual_height() - y < menu_button_size_)) { - g_game->PushMainMenuPressCall(input_device); + g_logic->PushMainMenuPressCall(input_device); last_menu_button_press_time_ = GetRealTime(); } } @@ -176,7 +177,7 @@ void RootUI::Draw(FrameDef* frame_def) { if (draw_menu_button) { SimpleComponent c(frame_def->overlay_pass()); c.SetTransparent(true); - c.SetTexture(g_media->GetTexture(SystemTextureID::kMenuButton)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kMenuButton)); // Draw menu button. float width = g_graphics->screen_virtual_width(); @@ -191,7 +192,7 @@ void RootUI::Draw(FrameDef* frame_def) { c.Translate(width - menu_button_size_ * 0.5f, height - menu_button_size_ * 0.38f, kMenuButtonDrawDepth); c.Scale(menu_button_size_ * 0.8f, menu_button_size_ * 0.8f); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); c.Submit(); } @@ -199,17 +200,17 @@ void RootUI::Draw(FrameDef* frame_def) { // To the left of the menu button, draw our connected-players indicator // (this probably shouldn't live here). bool draw_connected_players_icon = false; - int party_size = g_game->GetPartySize(); - bool is_host = (g_game->connections()->connection_to_host() == nullptr); + int party_size = g_logic->GetPartySize(); + bool is_host = (g_logic->connections()->connection_to_host() == nullptr); millisecs_t last_connection_to_client_join_time = - g_game->last_connection_to_client_join_time(); + g_logic->last_connection_to_client_join_time(); bool show_client_joined = (is_host && last_connection_to_client_join_time != 0 && real_time - last_connection_to_client_join_time < 5000); if (!party_window_open_ - && (party_size != 0 || g_game->connections()->connection_to_host() + && (party_size != 0 || g_logic->connections()->connection_to_host() || always_draw_party_icon_)) { draw_connected_players_icon = true; } @@ -218,13 +219,13 @@ void RootUI::Draw(FrameDef* frame_def) { // Flash and show a message if we're in the main menu instructing the // player to start a game. bool flash = false; - HostSession* s = g_game->GetForegroundContext().GetHostSession(); + HostSession* s = g_logic->GetForegroundContext().GetHostSession(); if (s && s->is_main_menu() && party_size > 0 && show_client_joined) flash = true; SimpleComponent c(frame_def->overlay_pass()); c.SetTransparent(true); - c.SetTexture(g_media->GetTexture(SystemTextureID::kUsersButton)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kUsersButton)); // Draw button. float width = g_graphics->screen_virtual_width(); @@ -245,7 +246,7 @@ void RootUI::Draw(FrameDef* frame_def) { if (flash && frame_def->base_time() % 250 < 125) { c.SetColor(1.0f, 1.4f, 1.0f); } - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); c.Submit(); @@ -306,7 +307,7 @@ void RootUI::Draw(FrameDef* frame_def) { } if (party_size == 2) { // (includes us as host) start_a_game_text_group_->SetText( - g_game->GetResourceString("joinedPartyInstructionsText"), + g_logic->GetResourceString("joinedPartyInstructionsText"), TextMesh::HAlign::kRight, TextMesh::VAlign::kTop); } else if (party_size > 2) { start_a_game_text_group_->SetText( diff --git a/src/ballistica/ui/ui.cc b/src/ballistica/ui/ui.cc index 967a96a9..945d51fb 100644 --- a/src/ballistica/ui/ui.cc +++ b/src/ballistica/ui/ui.cc @@ -3,13 +3,13 @@ #include "ballistica/ui/ui.h" #include "ballistica/app/app.h" +#include "ballistica/assets/component/data.h" +#include "ballistica/assets/component/sound.h" #include "ballistica/audio/audio.h" #include "ballistica/generic/lambda_runnable.h" #include "ballistica/graphics/component/empty_component.h" #include "ballistica/input/device/input_device.h" #include "ballistica/input/input.h" -#include "ballistica/media/component/data.h" -#include "ballistica/media/component/sound.h" #include "ballistica/python/python.h" #include "ballistica/scene/scene.h" #include "ballistica/ui/root_ui.h" @@ -27,25 +27,19 @@ UI::UI() { // Allow overriding via an environment variable. auto* ui_override = getenv("BA_UI_SCALE"); - bool force_test_small{}; - bool force_test_medium{}; - bool force_test_large{}; if (ui_override) { if (ui_override == std::string("small")) { - force_test_small = true; + scale_ = UIScale::kSmall; + force_scale_ = true; } else if (ui_override == std::string("medium")) { - force_test_medium = true; + scale_ = UIScale::kMedium; + force_scale_ = true; } else if (ui_override == std::string("large")) { - force_test_large = true; + scale_ = UIScale::kLarge; + force_scale_ = true; } } - if (force_test_small) { - scale_ = UIScale::kSmall; - } else if (force_test_medium) { - scale_ = UIScale::kMedium; - } else if (force_test_large) { - scale_ = UIScale::kLarge; - } else { + if (!force_scale_) { // Use automatic val. if (g_buildconfig.iircade_build()) { // NOLINT(bugprone-branch-clone) scale_ = UIScale::kMedium; @@ -56,17 +50,26 @@ UI::UI() { scale_ = g_platform->GetUIScale(); } } +} +auto UI::OnAppStart() -> void { + assert(InLogicThread()); + + root_ui_ = new RootUI(); // Make sure we know when forced-ui-scale is enabled. - if (force_test_small) { - ScreenMessage("FORCING SMALL UI FOR TESTING", Vector3f(1, 0, 0)); - Log("FORCING SMALL UI FOR TESTING"); - } else if (force_test_medium) { - ScreenMessage("FORCING MEDIUM UI FOR TESTING", Vector3f(1, 0, 0)); - Log("FORCING MEDIUM UI FOR TESTING"); - } else if (force_test_large) { - ScreenMessage("FORCING LARGE UI FOR TESTING", Vector3f(1, 0, 0)); - Log("FORCING LARGE UI FOR TESTING"); + if (force_scale_) { + if (scale_ == UIScale::kSmall) { + ScreenMessage("FORCING SMALL UI FOR TESTING", Vector3f(1, 0, 0)); + Log(LogLevel::kInfo, "FORCING SMALL UI FOR TESTING"); + } else if (scale_ == UIScale::kMedium) { + ScreenMessage("FORCING MEDIUM UI FOR TESTING", Vector3f(1, 0, 0)); + Log(LogLevel::kInfo, "FORCING MEDIUM UI FOR TESTING"); + } else if (scale_ == UIScale::kLarge) { + ScreenMessage("FORCING LARGE UI FOR TESTING", Vector3f(1, 0, 0)); + Log(LogLevel::kInfo, "FORCING LARGE UI FOR TESTING"); + } else { + FatalError("Unhandled scale."); + } } step_scene_timer_ = @@ -75,8 +78,6 @@ UI::UI() { scene_ = Object::New(0); } -auto UI::PostInit() -> void { root_ui_ = new RootUI(); } - // Currently the UI never dies so we don't bother doing a clean tear-down.. // (verifying scene cleanup, etc) UI::~UI() { @@ -150,7 +151,7 @@ void UI::Update(millisecs_t time_advance) { if (node_warning_count_ > 3) { static bool complained = false; if (!complained) { - Log(">10 nodes in UI context!"); + Log(LogLevel::kError, ">10 nodes in UI context!"); complained = true; } } @@ -220,7 +221,7 @@ void UI::AddWidget(Widget* w, ContainerWidget* parent) { // will not get stuck running or whatnot. if (screen_root_widget_.exists() && !screen_root_widget_->HasChildren() && parent == &(*screen_root_widget_)) { - g_game->ResetInput(); + g_logic->ResetInput(); } parent->AddWidget(w); @@ -292,7 +293,7 @@ auto UI::GetWidgetForInput(InputDevice* input_device) -> Widget* { // they're not the chosen one. if (time - last_widget_input_reject_err_sound_time_ > 5000) { last_widget_input_reject_err_sound_time_ = time; - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kErrorBeep)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kErrorBeep)); print_menu_owner = true; } ret_val = nullptr; // Rejected! @@ -306,18 +307,18 @@ auto UI::GetWidgetForInput(InputDevice* input_device) -> Widget* { kUIOwnerTimeoutSeconds - (time - last_input_device_use_time_) / 1000; std::string time_out_str; if (timeout > 0 && timeout < (kUIOwnerTimeoutSeconds - 10)) { - time_out_str = " " + g_game->GetResourceString("timeOutText"); + time_out_str = " " + g_logic->GetResourceString("timeOutText"); Utils::StringReplaceOne(&time_out_str, "${TIME}", std::to_string(timeout)); } else { - time_out_str = " " + g_game->GetResourceString("willTimeOutText"); + time_out_str = " " + g_logic->GetResourceString("willTimeOutText"); } std::string name; if (input->GetDeviceName() == "Keyboard") { - name = g_game->GetResourceString("keyboardText"); + name = g_logic->GetResourceString("keyboardText"); } else if (input->GetDeviceName() == "TouchScreen") { - name = g_game->GetResourceString("touchScreenText"); + name = g_logic->GetResourceString("touchScreenText"); } else { // We used to use player names here, but that's kinda sloppy and random; // lets just go with device names/numbers. @@ -332,7 +333,7 @@ auto UI::GetWidgetForInput(InputDevice* input_device) -> Widget* { } } - std::string b = g_game->GetResourceString("hasMenuControlText"); + std::string b = g_logic->GetResourceString("hasMenuControlText"); Utils::StringReplaceOne(&b, "${NAME}", name); ScreenMessage(b + time_out_str, {0.45f, 0.4f, 0.5f}); } @@ -341,19 +342,19 @@ auto UI::GetWidgetForInput(InputDevice* input_device) -> Widget* { } auto UI::GetModel(const std::string& name) -> Object::Ref { - return Media::GetMedia(&models_, name, scene()); + return Assets::GetAsset(&models_, name, scene()); } auto UI::GetTexture(const std::string& name) -> Object::Ref { - return Media::GetMedia(&textures_, name, scene()); + return Assets::GetAsset(&textures_, name, scene()); } auto UI::GetSound(const std::string& name) -> Object::Ref { - return Media::GetMedia(&sounds_, name, scene()); + return Assets::GetAsset(&sounds_, name, scene()); } auto UI::GetData(const std::string& name) -> Object::Ref { - return Media::GetMedia(&datas_, name, scene()); + return Assets::GetAsset(&datas_, name, scene()); } auto UI::GetAsUIContext() -> UI* { return this; } @@ -372,7 +373,7 @@ auto UI::NewTimer(TimeType timetype, TimerMedium length, bool repeat, case TimeType::kSim: case TimeType::kBase: case TimeType::kReal: - return g_game->NewRealTimer(length, repeat, runnable); + return g_logic->NewRealTimer(length, repeat, runnable); default: // Fall back to default for descriptive error otherwise. return ContextTarget::NewTimer(timetype, length, repeat, runnable); @@ -384,7 +385,7 @@ void UI::DeleteTimer(TimeType timetype, int timer_id) { case TimeType::kSim: case TimeType::kBase: case TimeType::kReal: - g_game->DeleteRealTimer(timer_id); + g_logic->DeleteRealTimer(timer_id); break; default: // Fall back to default for descriptive error otherwise. diff --git a/src/ballistica/ui/ui.h b/src/ballistica/ui/ui.h index f5f5e10a..95d3e537 100644 --- a/src/ballistica/ui/ui.h +++ b/src/ballistica/ui/ui.h @@ -31,7 +31,7 @@ namespace ballistica { class UI : public ContextTarget { public: UI(); - auto PostInit() -> void; + auto OnAppStart() -> void; ~UI() override; auto Reset() -> void; @@ -142,6 +142,7 @@ class UI : public ContextTarget { Object::Ref root_widget_; int ui_lock_count_{}; UIScale scale_{UIScale::kLarge}; + bool force_scale_{}; // Media loaded in the UI context. std::unordered_map > textures_; diff --git a/src/ballistica/ui/widget/button_widget.cc b/src/ballistica/ui/widget/button_widget.cc index 434c47ef..cfefdad9 100644 --- a/src/ballistica/ui/widget/button_widget.cc +++ b/src/ballistica/ui/widget/button_widget.cc @@ -14,7 +14,7 @@ namespace ballistica { -ButtonWidget::ButtonWidget() : birth_time_{g_game->master_time()} { +ButtonWidget::ButtonWidget() : birth_time_{g_logic->master_time()} { text_ = Object::New(); SetText("Button"); text_->set_valign(TextWidget::VAlign::kCenter); @@ -262,7 +262,7 @@ void ButtonWidget::Draw(RenderPass* pass, bool draw_transparent) { // Custom button texture. if (texture_.exists()) { if (!custom_model.exists()) { - model = g_media->GetModel(SystemModelID::kImage1x1); + model = g_assets->GetModel(SystemModelID::kImage1x1); } else { model = custom_model->model_data(); } @@ -374,8 +374,8 @@ void ButtonWidget::Draw(RenderPass* pass, bool draw_transparent) { break; } } - c.SetTexture(g_media->GetTexture(tex_id)); - model = g_media->GetModel(model_id); + c.SetTexture(g_assets->GetTexture(tex_id)); + model = g_assets->GetModel(model_id); } if (doDraw) { c.PushTransform(); @@ -392,20 +392,20 @@ void ButtonWidget::Draw(RenderPass* pass, bool draw_transparent) { if (icon_type_ == IconType::kStart) { c.SetColor(1.4f * mult * (color_red_), 1.4f * mult * (color_green_), 1.4f * mult * (color_blue_), 1.0f); - c.SetTexture(g_media->GetTexture(SystemTextureID::kStartButton)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kStartButton)); } else if (icon_type_ == IconType::kCancel) { if (remote_icons) { c.SetColor(1.0f * mult * (1.0f), 1.0f * mult * (1.0f), 1.0f * mult * (1.0f), 1.0f); - c.SetTexture(g_media->GetTexture(SystemTextureID::kBackIcon)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kBackIcon)); } else if (ouya_icons) { c.SetColor(1.0f * mult * (1.0f), 1.0f * mult * (1.0f), 1.0f * mult * (1.0f), 1.0f); - c.SetTexture(g_media->GetTexture(SystemTextureID::kOuyaAButton)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kOuyaAButton)); } else { c.SetColor(1.5f * mult * (color_red_), 1.5f * mult * (color_green_), 1.5f * mult * (color_blue_), 1.0f); - c.SetTexture(g_media->GetTexture(SystemTextureID::kBombButton)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kBombButton)); } } else if (icon_.exists()) { c.SetColor(icon_color_red_ @@ -425,7 +425,7 @@ void ButtonWidget::Draw(RenderPass* pass, bool draw_transparent) { } } else { c.SetColor(1, 1, 1); - c.SetTexture(g_media->GetTexture(SystemTextureID::kCircle)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kCircle)); } if (doDrawIcon) { c.PushTransform(); @@ -433,7 +433,7 @@ void ButtonWidget::Draw(RenderPass* pass, bool draw_transparent) { - (string_width * string_scale) * 0.5f - 5.0f, (b + t) * 0.5f + extra_offs_y, 0.001f); c.Scale(34.0f * icon_scale_, 34.f * icon_scale_, 1.0f); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); } } @@ -554,28 +554,29 @@ void ButtonWidget::Activate() { DoActivate(); } void ButtonWidget::DoActivate(bool isRepeat) { if (!enabled_) { - Log("WARNING: ButtonWidget::DoActivate() called on disabled button"); + Log(LogLevel::kWarning, + "ButtonWidget::DoActivate() called on disabled button"); return; } // We don't want holding down a repeat-button to keep flashing it. if (!isRepeat) { - last_activate_time_ = g_game->master_time(); + last_activate_time_ = g_logic->master_time(); } if (sound_enabled_) { int r = rand() % 3; // NOLINT if (r == 0) { - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kSwish)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kSwish)); } else if (r == 1) { - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kSwish2)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kSwish2)); } else { - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kSwish3)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kSwish3)); } } if (on_activate_call_.exists()) { // Call this in the next cycle (don't want to risk mucking with UI from // within a UI loop.) - g_game->PushPythonWeakCall( + g_logic->PushPythonWeakCall( Object::WeakRef(on_activate_call_)); return; } diff --git a/src/ballistica/ui/widget/check_box_widget.cc b/src/ballistica/ui/widget/check_box_widget.cc index 13c8aaa4..8341fffd 100644 --- a/src/ballistica/ui/widget/check_box_widget.cc +++ b/src/ballistica/ui/widget/check_box_widget.cc @@ -3,9 +3,9 @@ #include "ballistica/ui/widget/check_box_widget.h" #include "ballistica/audio/audio.h" -#include "ballistica/game/game.h" #include "ballistica/graphics/component/empty_component.h" #include "ballistica/graphics/component/simple_component.h" +#include "ballistica/logic/logic.h" #include "ballistica/python/python_context_call.h" #include "ballistica/python/python_sys.h" #include "ballistica/ui/ui.h" @@ -88,11 +88,11 @@ void CheckBoxWidget::Draw(RenderPass* pass, bool draw_transparent) { c.SetTransparent(true); c.SetPremultiplied(true); c.SetColor(0.25f * m, 0.3f * m, 0, 0.3f * m); - c.SetTexture(g_media->GetTexture(SystemTextureID::kGlow)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kGlow)); c.PushTransform(); c.Translate(highlight_center_x_, highlight_center_y_); c.Scale(highlight_width_, highlight_height_); - c.DrawModel(g_media->GetModel(SystemModelID::kImage4x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage4x1)); c.PopTransform(); c.Submit(); } @@ -132,14 +132,14 @@ void CheckBoxWidget::Draw(RenderPass* pass, bool draw_transparent) { c.SetTransparent(draw_transparent); c.SetColor(glow_amt * color_r_, glow_amt * color_g_, glow_amt * color_b_, 1); - c.SetTexture(g_media->GetTexture(SystemTextureID::kUIAtlas)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kUIAtlas)); c.PushTransform(); c.Translate(box_center_x_ + extra_offs_x, box_center_y_ + extra_offs_y, 0.1f); c.Scale(box_width_, box_height_, 0.4f); - c.DrawModel(g_media->GetModel(draw_transparent - ? SystemModelID::kButtonSmallTransparent - : SystemModelID::kButtonSmallOpaque)); + c.DrawModel(g_assets->GetModel( + draw_transparent ? SystemModelID::kButtonSmallTransparent + : SystemModelID::kButtonSmallOpaque)); c.PopTransform(); c.Submit(); } @@ -171,9 +171,9 @@ void CheckBoxWidget::Draw(RenderPass* pass, bool draw_transparent) { SimpleComponent c(pass); c.SetTransparent(draw_transparent); if (is_radio_button_) { - c.SetTexture(g_media->GetTexture(SystemTextureID::kNub)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kNub)); } else { - c.SetTexture(g_media->GetTexture(SystemTextureID::kUIAtlas)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kUIAtlas)); } if (mouse_over_ && g_platform->IsRunningOnDesktop()) { @@ -188,12 +188,12 @@ void CheckBoxWidget::Draw(RenderPass* pass, bool draw_transparent) { check_center_y_ + 2 + 3.0f * extra_offs_y, 0.5f); c.Scale(check_width_ * 0.45f, check_height_ * 0.45f, 0.5f); c.Translate(-0.17f, -0.17f, 0.5f); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); } else { c.Translate(check_center_x_ + 3.0f * extra_offs_x, check_center_y_ + 3.0f * extra_offs_y, 0.5f); c.Scale(check_width_, check_height_, 0.5f); - c.DrawModel(g_media->GetModel(SystemModelID::kCheckTransparent)); + c.DrawModel(g_assets->GetModel(SystemModelID::kCheckTransparent)); } c.PopTransform(); c.Submit(); @@ -235,7 +235,7 @@ void CheckBoxWidget::SetValue(bool value) { } void CheckBoxWidget::Activate() { - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kSwish3)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kSwish3)); checked_ = !checked_; check_dirty_ = true; last_change_time_ = GetRealTime(); @@ -245,7 +245,7 @@ void CheckBoxWidget::Activate() { // Call this in the next cycle (don't want to risk mucking with UI from // within a UI loop) - g_game->PushPythonWeakCallArgs( + g_logic->PushPythonWeakCallArgs( Object::WeakRef(on_value_change_call_), args); } } diff --git a/src/ballistica/ui/widget/container_widget.cc b/src/ballistica/ui/widget/container_widget.cc index 2cc0edb6..5d82083b 100644 --- a/src/ballistica/ui/widget/container_widget.cc +++ b/src/ballistica/ui/widget/container_widget.cc @@ -4,10 +4,10 @@ #include "ballistica/audio/audio.h" #include "ballistica/core/thread.h" -#include "ballistica/game/game.h" #include "ballistica/graphics/component/empty_component.h" #include "ballistica/graphics/component/simple_component.h" #include "ballistica/input/input.h" +#include "ballistica/logic/logic.h" #include "ballistica/python/python.h" #include "ballistica/python/python_context_call.h" #include "ballistica/ui/ui.h" @@ -32,7 +32,7 @@ namespace ballistica { ContainerWidget::ContainerWidget(float width_in, float height_in) : width_(width_in), height_(height_in), - dynamics_update_time_(g_game->master_time()) {} + dynamics_update_time_(g_logic->master_time()) {} ContainerWidget::~ContainerWidget() { BA_DEBUG_UI_READ_LOCK; @@ -343,7 +343,7 @@ auto ContainerWidget::HandleMessage(const WidgetMessage& m) -> bool { // Call this in the next cycle (don't wanna risk mucking with UI from // within a UI loop). - g_game->PushPythonWeakCall( + g_logic->PushPythonWeakCall( Object::WeakRef(on_cancel_call_)); } else { OnCancelCustom(); @@ -596,7 +596,7 @@ auto ContainerWidget::HandleMessage(const WidgetMessage& m) -> bool { // First click just selects. if (click_count == 1) { - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kTap)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kTap)); } } else { // Special case: If we've got a child text widget that's @@ -619,7 +619,7 @@ auto ContainerWidget::HandleMessage(const WidgetMessage& m) -> bool { if (!claimed && on_outside_click_call_.exists()) { // Call this in the next cycle (don't wanna risk mucking with UI from // within a UI loop). - g_game->PushPythonWeakCall( + g_logic->PushPythonWeakCall( Object::WeakRef(on_outside_click_call_)); } @@ -809,7 +809,7 @@ void ContainerWidget::Draw(RenderPass* pass, bool draw_transparent) { // Probably not safe to delete ourself here since we're in // the draw loop, but we can push a call to do it. Object::WeakRef weakref(this); - g_game->thread()->PushCall([weakref] { + g_logic->thread()->PushCall([weakref] { Widget* w = weakref.get(); if (w) g_ui->DeleteWidget(w); }); @@ -864,7 +864,7 @@ void ContainerWidget::Draw(RenderPass* pass, bool draw_transparent) { // Probably not safe to delete ourself here since we're in the // draw loop, but we can set up an event to do it. Object::WeakRef weakref(this); - g_game->thread()->PushCall([weakref] { + g_logic->thread()->PushCall([weakref] { Widget* w = weakref.get(); if (w) g_ui->DeleteWidget(w); }); @@ -948,7 +948,7 @@ void ContainerWidget::Draw(RenderPass* pass, bool draw_transparent) { bg_center_x_ = l - l_border + bg_width_ * 0.5f; bg_center_y_ = b - b_border + bg_height_ * 0.5f; if (background_) { - tex_ = g_media->GetTexture(tex_id); + tex_ = g_assets->GetTexture(tex_id); } bg_dirty_ = false; } @@ -974,8 +974,8 @@ void ContainerWidget::Draw(RenderPass* pass, bool draw_transparent) { c.PushTransform(); c.Translate(bg_center_x_, bg_center_y_); c.Scale(bg_width_ * transition_scale_, bg_height_ * transition_scale_); - c.DrawModel(g_media->GetModel(draw_transparent ? bg_model_transparent_i_d_ - : bg_model_opaque_i_d_)); + c.DrawModel(g_assets->GetModel( + draw_transparent ? bg_model_transparent_i_d_ : bg_model_opaque_i_d_)); c.PopTransform(); c.Submit(); } @@ -1005,12 +1005,12 @@ void ContainerWidget::Draw(RenderPass* pass, bool draw_transparent) { SimpleComponent c(pass); c.SetTransparent(true); c.SetPremultiplied(true); - c.SetTexture(g_media->GetTexture(SystemTextureID::kGlow)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kGlow)); c.SetColor(0.25f * m, 0.25f * m, 0, 0.3f * m); c.PushTransform(); c.Translate(glow_center_x_, glow_center_y_); c.Scale(glow_width_, glow_height_); - c.DrawModel(g_media->GetModel(SystemModelID::kImage4x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage4x1)); c.PopTransform(); c.Submit(); } @@ -1046,11 +1046,11 @@ void ContainerWidget::TransformPointFromChild(float* x, float* y, } void ContainerWidget::Activate() { - last_activate_time_ = g_game->master_time(); + last_activate_time_ = g_logic->master_time(); if (on_activate_call_.exists()) { // Call this in the next cycle (don't wanna risk mucking with UI from within // a UI loop). - g_game->PushPythonWeakCall( + g_logic->PushPythonWeakCall( Object::WeakRef(on_activate_call_)); } } @@ -1143,7 +1143,7 @@ void ContainerWidget::SetTransition(TransitionType t) { return; } parent->CheckLayout(); - millisecs_t net_time = g_game->master_time(); + millisecs_t net_time = g_logic->master_time(); transition_type_ = t; // Scale transitions are simpler. @@ -1346,7 +1346,8 @@ void ContainerWidget::SelectWidget(Widget* w, SelectionCause c) { } } else { if (root_selectable_) { - Log("Error: SelectWidget() called on a ContainerWidget which is itself " + Log(LogLevel::kError, + "SelectWidget() called on a ContainerWidget which is itself " "selectable. Ignoring."); return; } @@ -1372,8 +1373,9 @@ void ContainerWidget::SelectWidget(Widget* w, SelectionCause c) { } else { static bool printed = false; if (!printed) { - Log("Warning: SelectWidget called on unselectable widget: " - + w->GetWidgetTypeName()); + Log(LogLevel::kWarning, + "SelectWidget called on unselectable widget: " + + w->GetWidgetTypeName()); Python::PrintStackTrace(); printed = true; } @@ -1543,7 +1545,7 @@ void ContainerWidget::SelectDownWidget() { BA_DEBUG_UI_READ_LOCK; if (!g_ui || !g_ui->root_widget() || !g_ui->screen_root_widget()) { - BA_LOG_ONCE("SelectDownWidget called before UI init."); + BA_LOG_ONCE(LogLevel::kError, "SelectDownWidget called before UI init."); return; } @@ -1576,13 +1578,13 @@ void ContainerWidget::SelectDownWidget() { } if (w) { if (!w->IsSelectable()) { - Log("Error: Down_widget is not selectable."); + Log(LogLevel::kError, "Down_widget is not selectable."); } else { w->Show(); // Avoid tap sounds and whatnot if we're just re-selecting ourself. if (w != selected_widget_) { w->GlobalSelect(); - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kTap)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kTap)); } } } else { @@ -1607,7 +1609,7 @@ void ContainerWidget::SelectUpWidget() { BA_DEBUG_UI_READ_LOCK; if (!g_ui || !g_ui->root_widget() || !g_ui->screen_root_widget()) { - BA_LOG_ONCE("SelectUpWidget called before UI init."); + BA_LOG_ONCE(LogLevel::kError, "SelectUpWidget called before UI init."); return; } @@ -1640,13 +1642,13 @@ void ContainerWidget::SelectUpWidget() { } if (w) { if (!w->IsSelectable()) { - Log("Error: up_widget is not selectable."); + Log(LogLevel::kError, "up_widget is not selectable."); } else { w->Show(); // Avoid tap sounds and whatnot if we're just re-selecting ourself. if (w != selected_widget_) { w->GlobalSelect(); - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kTap)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kTap)); } } } else { @@ -1671,7 +1673,7 @@ void ContainerWidget::SelectLeftWidget() { BA_DEBUG_UI_READ_LOCK; if (!g_ui || !g_ui->root_widget() || !g_ui->screen_root_widget()) { - BA_LOG_ONCE("SelectLeftWidget called before UI init."); + BA_LOG_ONCE(LogLevel::kError, "SelectLeftWidget called before UI init."); return; } @@ -1691,13 +1693,13 @@ void ContainerWidget::SelectLeftWidget() { } if (w) { if (!w->IsSelectable()) { - Log("Error: left_widget is not selectable."); + Log(LogLevel::kError, "left_widget is not selectable."); } else { w->Show(); // Avoid tap sounds and whatnot if we're just re-selecting ourself. if (w != selected_widget_) { w->GlobalSelect(); - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kTap)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kTap)); } } } else { @@ -1721,7 +1723,7 @@ void ContainerWidget::SelectRightWidget() { BA_DEBUG_UI_READ_LOCK; if (!g_ui || !g_ui->root_widget() || !g_ui->screen_root_widget()) { - BA_LOG_ONCE("SelectRightWidget called before UI init."); + BA_LOG_ONCE(LogLevel::kError, "SelectRightWidget called before UI init."); return; } @@ -1742,13 +1744,13 @@ void ContainerWidget::SelectRightWidget() { } if (w) { if (!w->IsSelectable()) { - Log("Error: right_widget is not selectable."); + Log(LogLevel::kError, "right_widget is not selectable."); } else { w->Show(); // Avoid tap sounds and whatnot if we're just re-selecting ourself. if (w != selected_widget_) { w->GlobalSelect(); - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kTap)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kTap)); } } } else { @@ -1773,13 +1775,13 @@ void ContainerWidget::SelectNextWidget() { BA_DEBUG_UI_READ_LOCK; if (!g_ui || !g_ui->root_widget() || !g_ui->screen_root_widget()) { - BA_LOG_ONCE("SelectNextWidget called before UI init."); + BA_LOG_ONCE(LogLevel::kError, "SelectNextWidget called before UI init."); return; } millisecs_t old_last_prev_next_time = last_prev_next_time_; if (should_print_list_exit_instructions_) { - last_prev_next_time_ = g_game->master_time(); + last_prev_next_time_ = g_logic->master_time(); } // Grab the iterator for our selected widget if possible. @@ -1828,7 +1830,7 @@ void ContainerWidget::SelectNextWidget() { } if ((**i).IsSelectable() && (**i).IsSelectableViaKeys()) { SelectWidget(&(**i), SelectionCause::NEXT_SELECTED); - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kTap)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kTap)); return; } i++; @@ -1839,21 +1841,21 @@ void ContainerWidget::SelectNextWidget() { void ContainerWidget::PrintExitListInstructions( millisecs_t old_last_prev_next_time) { if (should_print_list_exit_instructions_) { - millisecs_t t = g_game->master_time(); + millisecs_t t = g_logic->master_time(); if ((t - old_last_prev_next_time > 250) && (t - last_list_exit_instructions_print_time_ > 5000)) { last_list_exit_instructions_print_time_ = t; - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kErrorBeep)); - std::string s = g_game->GetResourceString("arrowsToExitListText"); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kErrorBeep)); + std::string s = g_logic->GetResourceString("arrowsToExitListText"); { // Left arrow. Utils::StringReplaceOne(&s, "${LEFT}", - g_game->CharStr(SpecialChar::kLeftArrow)); + g_logic->CharStr(SpecialChar::kLeftArrow)); } { // Right arrow. Utils::StringReplaceOne(&s, "${RIGHT}", - g_game->CharStr(SpecialChar::kRightArrow)); + g_logic->CharStr(SpecialChar::kRightArrow)); } ScreenMessage(s); } @@ -1865,7 +1867,7 @@ void ContainerWidget::SelectPrevWidget() { millisecs_t old_last_prev_next_time = last_prev_next_time_; if (should_print_list_exit_instructions_) { - last_prev_next_time_ = g_game->master_time(); + last_prev_next_time_ = g_logic->master_time(); } // Grab the iterator for our selected widget if possible. @@ -1915,7 +1917,7 @@ void ContainerWidget::SelectPrevWidget() { if ((**i).IsSelectable() && (**i).IsSelectableViaKeys()) { SelectWidget(&(**i), SelectionCause::PREV_SELECTED); - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kTap)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kTap)); return; } i++; diff --git a/src/ballistica/ui/widget/h_scroll_widget.cc b/src/ballistica/ui/widget/h_scroll_widget.cc index d23164db..49e0e2b7 100644 --- a/src/ballistica/ui/widget/h_scroll_widget.cc +++ b/src/ballistica/ui/widget/h_scroll_widget.cc @@ -348,7 +348,7 @@ auto HScrollWidget::HandleMessage(const WidgetMessage& m) -> bool { inertia_scroll_rate_ = smoothing * inertia_scroll_rate_ + (1.0f - smoothing) * new_val; } - last_velocity_event_time_ = g_game->master_time(); + last_velocity_event_time_ = g_logic->master_time(); MarkForUpdate(); } else { // Not within our widget; dont allow children to claim. @@ -625,12 +625,12 @@ void HScrollWidget::Draw(RenderPass* pass, bool draw_transparent) { SimpleComponent c(pass); c.SetTransparent(true); c.SetColor(1, 1, 1, border_opacity_); - c.SetTexture(g_media->GetTexture(SystemTextureID::kUIAtlas)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kUIAtlas)); c.PushTransform(); c.Translate(trough_center_x_, trough_center_y_, 0.7f); c.Scale(trough_width_, trough_height_, 0.1f); c.Rotate(-90, 0, 0, 1); - c.DrawModel(g_media->GetModel(SystemModelID::kScrollBarTroughTransparent)); + c.DrawModel(g_assets->GetModel(SystemModelID::kScrollBarTroughTransparent)); c.PopTransform(); c.Submit(); } @@ -710,20 +710,21 @@ void HScrollWidget::Draw(RenderPass* pass, bool draw_transparent) { // on touch, just draw these transiently #if 1 if (draw_transparent) { - c.DrawModel(g_media->GetModel( + c.DrawModel(g_assets->GetModel( sb_thumb_width > 100 ? SystemModelID::kScrollBarThumbSimple : SystemModelID::kScrollBarThumbShortSimple)); } #else if (draw_transparent) { - c.DrawModel(g_media->GetModel( + c.DrawModel(g_assets->GetModel( sb_thumb_width > 100 - ? Media::SCROLL_BAR_THUMB_TRANSPARENT_MODEL - : Media::SCROLL_BAR_THUMB_SHORT_TRANSPARENT_MODEL)); + ? Assets::SCROLL_BAR_THUMB_TRANSPARENT_MODEL + : Assets::SCROLL_BAR_THUMB_SHORT_TRANSPARENT_MODEL)); } else { - c.DrawModel(g_media->GetModel( - sb_thumb_width > 100 ? Media::SCROLL_BAR_THUMB_OPAQUE_MODEL - : Media::SCROLL_BAR_THUMB_SHORT_OPAQUE_MODEL)); + c.DrawModel(g_assets->GetModel( + sb_thumb_width > 100 + ? Assets::SCROLL_BAR_THUMB_OPAQUE_MODEL + : Assets::SCROLL_BAR_THUMB_SHORT_OPAQUE_MODEL)); } #endif @@ -755,11 +756,11 @@ void HScrollWidget::Draw(RenderPass* pass, bool draw_transparent) { SimpleComponent c(pass); c.SetTransparent(true); c.SetColor(1, 1, 1, border_opacity_); - c.SetTexture(g_media->GetTexture(SystemTextureID::kScrollWidget)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kScrollWidget)); c.PushTransform(); c.Translate(outline_center_x_, outline_center_y_, 0.9f); c.Scale(outline_width_, outline_height_, 0.1f); - c.DrawModel(g_media->GetModel(SystemModelID::kSoftEdgeOutside)); + c.DrawModel(g_assets->GetModel(SystemModelID::kSoftEdgeOutside)); c.PopTransform(); c.Submit(); } @@ -792,11 +793,11 @@ void HScrollWidget::Draw(RenderPass* pass, bool draw_transparent) { c.SetTransparent(true); c.SetPremultiplied(true); c.SetColor(0.4f * m, 0.5f * m, 0.05f * m, 0.0f); - c.SetTexture(g_media->GetTexture(SystemTextureID::kScrollWidgetGlow)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kScrollWidgetGlow)); c.PushTransform(); c.Translate(glow_center_x_, glow_center_y_, 0.9f); c.Scale(glow_width_, glow_height_, 0.1f); - c.DrawModel(g_media->GetModel(SystemModelID::kSoftEdgeOutside)); + c.DrawModel(g_assets->GetModel(SystemModelID::kSoftEdgeOutside)); c.PopTransform(); c.Submit(); } diff --git a/src/ballistica/ui/widget/image_widget.cc b/src/ballistica/ui/widget/image_widget.cc index 3dd117a7..28b2a369 100644 --- a/src/ballistica/ui/widget/image_widget.cc +++ b/src/ballistica/ui/widget/image_widget.cc @@ -2,12 +2,12 @@ #include "ballistica/ui/widget/image_widget.h" -#include "ballistica/game/game.h" #include "ballistica/graphics/component/simple_component.h" +#include "ballistica/logic/logic.h" namespace ballistica { -ImageWidget::ImageWidget() : birth_time_{g_game->master_time()} {} +ImageWidget::ImageWidget() : birth_time_{g_logic->master_time()} {} ImageWidget::~ImageWidget() = default; @@ -70,13 +70,13 @@ void ImageWidget::Draw(RenderPass* pass, bool draw_transparent) { draw_radial_transparent = true; } else { model_transparent_used = - g_media->GetModel(SystemModelID::kImage1x1); + g_assets->GetModel(SystemModelID::kImage1x1); } } else { if (radial_amount_ < 1.0f) { draw_radial_opaque = true; } else { - model_opaque_used = g_media->GetModel(SystemModelID::kImage1x1); + model_opaque_used = g_assets->GetModel(SystemModelID::kImage1x1); } } } diff --git a/src/ballistica/ui/widget/image_widget.h b/src/ballistica/ui/widget/image_widget.h index 00979437..18dc66a2 100644 --- a/src/ballistica/ui/widget/image_widget.h +++ b/src/ballistica/ui/widget/image_widget.h @@ -5,8 +5,8 @@ #include -#include "ballistica/media/component/model.h" -#include "ballistica/media/component/texture.h" +#include "ballistica/assets/component/model.h" +#include "ballistica/assets/component/texture.h" #include "ballistica/ui/widget/widget.h" namespace ballistica { diff --git a/src/ballistica/ui/widget/root_widget.cc b/src/ballistica/ui/widget/root_widget.cc index 846a92b0..f3d24b03 100644 --- a/src/ballistica/ui/widget/root_widget.cc +++ b/src/ballistica/ui/widget/root_widget.cc @@ -2,10 +2,10 @@ #include "ballistica/ui/widget/root_widget.h" -#include "ballistica/game/game.h" -#include "ballistica/game/session/host_session.h" #include "ballistica/graphics/renderer.h" #include "ballistica/input/input.h" +#include "ballistica/logic/logic.h" +#include "ballistica/logic/session/host_session.h" #include "ballistica/python/python.h" #include "ballistica/ui/ui.h" #include "ballistica/ui/widget/button_widget.h" @@ -346,7 +346,7 @@ void RootWidget::Setup() { td.x = 5.0f; td.y = 3.0f; td.width = bd.width * 0.9f; - td.text = g_game->CharStr(SpecialChar::kBack); + td.text = g_logic->CharStr(SpecialChar::kBack); td.color_a = 1.0f; td.scale = 2.0f; td.flatness = 0.0f; @@ -812,7 +812,7 @@ void RootWidget::Draw(RenderPass* pass, bool transparent) { } auto RootWidget::AddButton(const ButtonDef& def) -> RootWidget::Button* { - ScopedSetContext cp(g_game->GetUIContextTarget()); + ScopedSetContext cp(g_logic->GetUIContextTarget()); buttons_.emplace_back(); Button& b(buttons_.back()); b.x = b.x_smoothed = b.x_target = def.x; @@ -862,7 +862,7 @@ auto RootWidget::AddButton(const ButtonDef& def) -> RootWidget::Button* { } auto RootWidget::AddText(const TextDef& def) -> RootWidget::Text* { - ScopedSetContext cp(g_game->GetUIContextTarget()); + ScopedSetContext cp(g_logic->GetUIContextTarget()); texts_.emplace_back(); Text& t(texts_.back()); t.button = def.button; @@ -896,7 +896,7 @@ void RootWidget::UpdateForFocusedWindow() { void RootWidget::UpdateForFocusedWindow(Widget* widget) { // Take note if the current session is the main menu; we do a few things // differently there. - HostSession* s = g_game->GetForegroundContext().GetHostSession(); + HostSession* s = g_logic->GetForegroundContext().GetHostSession(); in_main_menu_ = (s ? s->is_main_menu() : false); if (widget == nullptr) { diff --git a/src/ballistica/ui/widget/scroll_widget.cc b/src/ballistica/ui/widget/scroll_widget.cc index d479ac94..9c3b29f3 100644 --- a/src/ballistica/ui/widget/scroll_widget.cc +++ b/src/ballistica/ui/widget/scroll_widget.cc @@ -265,7 +265,7 @@ auto ScrollWidget::HandleMessage(const WidgetMessage& m) -> bool { inertia_scroll_rate_ = smoothing * inertia_scroll_rate_ + (1.0f - smoothing) * new_val; } - last_velocity_event_time_ = g_game->master_time(); + last_velocity_event_time_ = g_logic->master_time(); MarkForUpdate(); } else { // Not within our widget; don't allow children to claim. @@ -696,11 +696,11 @@ void ScrollWidget::Draw(RenderPass* pass, bool draw_transparent) { SimpleComponent c(pass); c.SetTransparent(true); c.SetColor(1, 1, 1, border_opacity_); - c.SetTexture(g_media->GetTexture(SystemTextureID::kUIAtlas)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kUIAtlas)); c.PushTransform(); c.Translate(trough_center_x_, trough_center_y_, 0.7f); c.Scale(trough_width_, trough_height_, 0.1f); - c.DrawModel(g_media->GetModel(SystemModelID::kScrollBarTroughTransparent)); + c.DrawModel(g_assets->GetModel(SystemModelID::kScrollBarTroughTransparent)); c.PopTransform(); c.Submit(); } @@ -748,19 +748,19 @@ void ScrollWidget::Draw(RenderPass* pass, bool draw_transparent) { c.SetColor(color_red_ * c_scale, color_green_ * c_scale, color_blue_ * c_scale, 1.0f); - c.SetTexture(g_media->GetTexture(SystemTextureID::kUIAtlas)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kUIAtlas)); c.ScissorPush(Rect(l + border_width_, b + border_height_ + 1, l + (width()), b + (height() * 0.995f))); c.PushTransform(); c.Translate(thumb_center_x_, thumb_center_y_, 0.8f); c.Scale(thumb_width_, thumb_height_, 0.1f); if (draw_transparent) { - c.DrawModel(g_media->GetModel( + c.DrawModel(g_assets->GetModel( sb_thumb_height > 100 ? SystemModelID::kScrollBarThumbTransparent : SystemModelID::kScrollBarThumbShortTransparent)); } else { - c.DrawModel(g_media->GetModel( + c.DrawModel(g_assets->GetModel( sb_thumb_height > 100 ? SystemModelID::kScrollBarThumbOpaque : SystemModelID::kScrollBarThumbShortOpaque)); } @@ -791,11 +791,11 @@ void ScrollWidget::Draw(RenderPass* pass, bool draw_transparent) { SimpleComponent c(pass); c.SetTransparent(true); c.SetColor(1, 1, 1, border_opacity_); - c.SetTexture(g_media->GetTexture(SystemTextureID::kScrollWidget)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kScrollWidget)); c.PushTransform(); c.Translate(outline_center_x_, outline_center_y_, 0.9f); c.Scale(outline_width_, outline_height_, 0.1f); - c.DrawModel(g_media->GetModel(SystemModelID::kSoftEdgeOutside)); + c.DrawModel(g_assets->GetModel(SystemModelID::kSoftEdgeOutside)); c.PopTransform(); c.Submit(); } @@ -827,11 +827,11 @@ void ScrollWidget::Draw(RenderPass* pass, bool draw_transparent) { c.SetTransparent(true); c.SetPremultiplied(true); c.SetColor(0.4f * m, 0.5f * m, 0.05f * m, 0.0f); - c.SetTexture(g_media->GetTexture(SystemTextureID::kScrollWidgetGlow)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kScrollWidgetGlow)); c.PushTransform(); c.Translate(glow_center_x_, glow_center_y_, 0.9f); c.Scale(glow_width_, glow_height_, 0.1f); - c.DrawModel(g_media->GetModel(SystemModelID::kSoftEdgeOutside)); + c.DrawModel(g_assets->GetModel(SystemModelID::kSoftEdgeOutside)); c.PopTransform(); c.Submit(); } diff --git a/src/ballistica/ui/widget/text_widget.cc b/src/ballistica/ui/widget/text_widget.cc index 064d3323..c45241e9 100644 --- a/src/ballistica/ui/widget/text_widget.cc +++ b/src/ballistica/ui/widget/text_widget.cc @@ -4,13 +4,13 @@ #include "ballistica/app/app_flavor.h" #include "ballistica/audio/audio.h" -#include "ballistica/game/game.h" #include "ballistica/generic/utils.h" #include "ballistica/graphics/component/empty_component.h" #include "ballistica/graphics/component/simple_component.h" #include "ballistica/graphics/text/text_graphics.h" #include "ballistica/input/device/keyboard_input.h" #include "ballistica/input/input.h" +#include "ballistica/logic/logic.h" #include "ballistica/python/python.h" #include "ballistica/python/python_context_call.h" #include "ballistica/ui/ui.h" @@ -39,7 +39,7 @@ TextWidget::TextWidget() { } } - birth_time_ = g_game->master_time(); + birth_time_ = g_logic->master_time(); } TextWidget::~TextWidget() = default; @@ -177,11 +177,11 @@ void TextWidget::Draw(RenderPass* pass, bool draw_transparent) { c.SetTransparent(true); c.SetPremultiplied(true); c.SetColor(0.25f * m, 0.3f * m, 0, 0.3f * m); - c.SetTexture(g_media->GetTexture(SystemTextureID::kGlow)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kGlow)); c.PushTransform(); c.Translate(highlight_center_x_, highlight_center_y_, 0.1f); c.Scale(highlight_width_, highlight_height_); - c.DrawModel(g_media->GetModel(SystemModelID::kImage4x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage4x1)); c.PopTransform(); c.Submit(); } @@ -202,11 +202,11 @@ void TextWidget::Draw(RenderPass* pass, bool draw_transparent) { SimpleComponent c(pass); c.SetTransparent(true); c.SetColor(1, 1, 1, 1); - c.SetTexture(g_media->GetTexture(SystemTextureID::kUIAtlas)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kUIAtlas)); c.PushTransform(); c.Translate(outline_center_x_, outline_center_y_, 0.1f); c.Scale(outline_width_, outline_height_); - c.DrawModel(g_media->GetModel(SystemModelID::kTextBoxTransparent)); + c.DrawModel(g_assets->GetModel(SystemModelID::kTextBoxTransparent)); c.PopTransform(); c.Submit(); } @@ -221,7 +221,7 @@ void TextWidget::Draw(RenderPass* pass, bool draw_transparent) { } else { c.SetColor(0.5f, 0.5f, 0.5f, 1); } - c.SetTexture(g_media->GetTexture(SystemTextureID::kTextClearButton)); + c.SetTexture(g_assets->GetTexture(SystemTextureID::kTextClearButton)); c.PushTransform(); c.Translate(r - 20, b * 0.5f + t * 0.5f, 0.1f); if (g_ui->scale() == UIScale::kSmall) { @@ -229,7 +229,7 @@ void TextWidget::Draw(RenderPass* pass, bool draw_transparent) { } else { c.Scale(25, 25); } - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); c.Submit(); } @@ -401,10 +401,10 @@ void TextWidget::Draw(RenderPass* pass, bool draw_transparent) { c.Scale(max_width_height_scale, max_width_height_scale); c.Translate(h + 4, v + 17.0f); c.Scale(6, 27); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.SetColor(1, 1, 1, 0); c.Scale(0.3f, 0.8f); - c.DrawModel(g_media->GetModel(SystemModelID::kImage1x1)); + c.DrawModel(g_assets->GetModel(SystemModelID::kImage1x1)); c.PopTransform(); c.Submit(); } @@ -461,13 +461,15 @@ void TextWidget::SetText(const std::string& text_in_raw) { if (do_format_check) { bool valid; - g_game->CompileResourceString(text_in_raw, - "TextWidget::SetText format check", &valid); + g_logic->CompileResourceString(text_in_raw, + "TextWidget::SetText format check", &valid); if (!valid) { - BA_LOG_ONCE("Invalid resource string: '" + text_in_raw + "'"); + BA_LOG_ONCE(LogLevel::kError, + "Invalid resource string: '" + text_in_raw + "'"); Python::PrintStackTrace(); } else if (explicit_bool(print_false_positives)) { - BA_LOG_ONCE("Got false positive for json check on '" + text_in_raw + "'"); + BA_LOG_ONCE(LogLevel::kError, + "Got false positive for json check on '" + text_in_raw + "'"); Python::PrintStackTrace(); } } @@ -557,12 +559,12 @@ void TextWidget::BringUpEditDialog() { } void TextWidget::Activate() { - last_activate_time_ = g_game->master_time(); + last_activate_time_ = g_logic->master_time(); if (on_activate_call_.exists()) { // Call this in the next cycle (don't wanna risk mucking with UI from within // a UI loop). - g_game->PushPythonWeakCall( + g_logic->PushPythonWeakCall( Object::WeakRef(on_activate_call_)); } @@ -603,7 +605,7 @@ auto TextWidget::HandleMessage(const WidgetMessage& m) -> bool { } // If we're doing inline editing, handle some key events. if (m.has_keysym && !ShouldUseStringEditDialog()) { - last_carat_change_time_ = g_game->master_time(); + last_carat_change_time_ = g_logic->master_time(); text_group_dirty_ = true; bool claimed = false; @@ -617,7 +619,7 @@ auto TextWidget::HandleMessage(const WidgetMessage& m) -> bool { case SDLK_KP_ENTER: if (g_buildconfig.ostype_ios_tvos() || g_buildconfig.ostype_android()) { // On mobile, return currently just deselects us. - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kSwish)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kSwish)); parent_widget()->SelectWidget(nullptr); return true; } else { @@ -626,7 +628,7 @@ auto TextWidget::HandleMessage(const WidgetMessage& m) -> bool { if (on_return_press_call_.exists()) { // Call this in the next cycle (don't wanna risk mucking with UI // from within a UI loop) - g_game->PushPythonWeakCall( + g_logic->PushPythonWeakCall( Object::WeakRef(on_return_press_call_)); } } @@ -838,7 +840,7 @@ auto TextWidget::HandleMessage(const WidgetMessage& m) -> bool { pressed_activate_ = (click_count == 2 || click_activate_) && !editable_; if (click_count == 1) { - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kTap)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kTap)); } } return true; @@ -861,7 +863,7 @@ auto TextWidget::HandleMessage(const WidgetMessage& m) -> bool { carat_position_ = 0; text_group_dirty_ = true; clear_pressed_ = false; - g_audio->PlaySound(g_media->GetSound(SystemSoundID::kTap)); + g_audio->PlaySound(g_assets->GetSound(SystemSoundID::kTap)); return true; } clear_pressed_ = false; @@ -933,7 +935,7 @@ void TextWidget::UpdateTranslation() { if (editable()) { text_translated_ = text_raw_; } else { - text_translated_ = g_game->CompileResourceString( + text_translated_ = g_logic->CompileResourceString( text_raw_, "TextWidget::UpdateTranslation"); } text_translation_dirty_ = false; diff --git a/src/ballistica/ui/widget/widget.cc b/src/ballistica/ui/widget/widget.cc index ced12799..094c35a9 100644 --- a/src/ballistica/ui/widget/widget.cc +++ b/src/ballistica/ui/widget/widget.cc @@ -2,7 +2,7 @@ #include "ballistica/ui/widget/widget.h" -#include "ballistica/game/game.h" +#include "ballistica/logic/logic.h" #include "ballistica/python/class/python_class_widget.h" #include "ballistica/python/python_context_call.h" #include "ballistica/ui/ui.h" @@ -46,7 +46,8 @@ void Widget::SetDepthRange(float min_depth, float max_depth) { auto Widget::IsInMainStack() const -> bool { if (!g_ui) { - BA_LOG_ONCE("Widget::IsInMainStack() called before ui creation."); + BA_LOG_ONCE(LogLevel::kError, + "Widget::IsInMainStack() called before ui creation."); return false; } // Navigate up to the top of the hierarchy and see if the @@ -87,7 +88,7 @@ void Widget::SetSelected(bool s, SelectionCause cause) { if (selected_ && on_select_call_.exists()) { // Call this in the next cycle (don't wanna risk mucking // with UI from within a UI loop). - g_game->PushPythonWeakCall( + g_logic->PushPythonWeakCall( Object::WeakRef(on_select_call_)); } } @@ -200,9 +201,10 @@ void Widget::ScreenPointToWidget(float* x, float* y) const { float y_test = *y; WidgetPointToScreen(&x_test, &y_test); if (std::abs(x_test - x_old) > 0.01f || std::abs(y_test - y_old) > 0.01f) { - Log("ScreenPointToWidget sanity check error: expected (" - + std::to_string(x_old) + "," + std::to_string(y_old) + ") got (" - + std::to_string(x_test) + "," + std::to_string(y_test) + ")"); + Log(LogLevel::kError, + "ScreenPointToWidget sanity check error: expected (" + + std::to_string(x_old) + "," + std::to_string(y_old) + ") got (" + + std::to_string(x_test) + "," + std::to_string(y_test) + ")"); } #endif // BA_DEBUG_BUILD || BA_TEST_BUILD } diff --git a/src/meta/.meta_manifest_public.json b/src/meta/.meta_manifest_public.json index dd1d3882..a5a5e757 100644 --- a/src/meta/.meta_manifest_public.json +++ b/src/meta/.meta_manifest_public.json @@ -2,5 +2,6 @@ "assets/src/ba_data/python/ba/_generated/__init__.py", "assets/src/ba_data/python/ba/_generated/enums.py", "src/ballistica/generated/python_embedded/binding.inc", - "src/ballistica/generated/python_embedded/bootstrap.inc" + "src/ballistica/generated/python_embedded/bootstrap.inc", + "src/ballistica/generated/python_embedded/bootstrap_monolithic.inc" ] \ No newline at end of file diff --git a/src/meta/Makefile b/src/meta/Makefile index 073a4d54..ce68dfee 100644 --- a/src/meta/Makefile +++ b/src/meta/Makefile @@ -17,7 +17,8 @@ sources: \ ../../assets/src/ba_data/python/ba/_generated/__init__.py \ ../../assets/src/ba_data/python/ba/_generated/enums.py \ ../ballistica/generated/python_embedded/binding.inc \ - ../ballistica/generated/python_embedded/bootstrap.inc + ../ballistica/generated/python_embedded/bootstrap.inc \ + ../ballistica/generated/python_embedded/bootstrap_monolithic.inc ../ballistica/generated/python_embedded/binding.inc : bameta/python_embedded/binding.py @$(PCOMMAND) gen_binding_code $< $@ @@ -25,6 +26,9 @@ sources: \ ../ballistica/generated/python_embedded/bootstrap.inc : bameta/python_embedded/bootstrap.py @$(PCOMMAND) gen_flat_data_code $< $@ bootstrap_code +../ballistica/generated/python_embedded/bootstrap_monolithic.inc : bameta/python_embedded/bootstrap_monolithic.py + @$(PCOMMAND) gen_flat_data_code $< $@ bootstrap_monolithic_code + ../../assets/src/ba_data/python/ba/_generated/__init__.py : ../../tools/batools/pcommand.py @$(PCOMMAND) gen_python_init_module $@ @@ -45,7 +49,8 @@ efrocache-list: @echo "../../assets/src/ba_data/python/ba/_generated/__init__.py" \ "../../assets/src/ba_data/python/ba/_generated/enums.py" \ "../ballistica/generated/python_embedded/binding.inc" \ - "../ballistica/generated/python_embedded/bootstrap.inc" + "../ballistica/generated/python_embedded/bootstrap.inc" \ + "../ballistica/generated/python_embedded/bootstrap_monolithic.inc" efrocache-build: sources diff --git a/src/meta/bameta/python_embedded/binding.py b/src/meta/bameta/python_embedded/binding.py index b565fd20..9408b71d 100644 --- a/src/meta/bameta/python_embedded/binding.py +++ b/src/meta/bameta/python_embedded/binding.py @@ -8,6 +8,7 @@ from __future__ import annotations import json import copy +import logging from typing import TYPE_CHECKING import ba @@ -50,7 +51,7 @@ def get_binding_values() -> tuple[Any, ...]: _hooks.orientation_reset_cb_message, # kVROrientationResetCBMessageCall _hooks.orientation_reset_message, # kVROrientationResetMessageCall _hooks.on_app_resume, # kHandleAppResumeCall - _apputils.handle_log, # kHandleLogCall + _apputils.handle_v1_cloud_log, # kHandleV1CloudLogCall _hooks.launch_main_menu_session, # kLaunchMainMenuSessionCall _hooks.language_test_toggle, # kLanguageTestToggleCall _hooks.award_in_control_achievement, # kAwardInControlAchievementCall @@ -136,4 +137,9 @@ def get_binding_values() -> tuple[Any, ...]: _hooks.uuid_str, # kUUIDStrCall _hooks.hash_strings, # kHashStringsCall _hooks.have_account_v2_credentials, # kHaveAccountV2CredentialsCall + logging.debug, # kLoggingDebugCall + logging.info, # kLoggingInfoCall + logging.warning, # kLoggingWarningCall + logging.error, # kLoggingErrorCall + logging.critical, # kLoggingCriticalCall ) # yapf: disable diff --git a/src/meta/bameta/python_embedded/bootstrap.py b/src/meta/bameta/python_embedded/bootstrap.py index d84cd45d..85d06b15 100644 --- a/src/meta/bameta/python_embedded/bootstrap.py +++ b/src/meta/bameta/python_embedded/bootstrap.py @@ -1,6 +1,8 @@ # Released under the MIT License. See LICENSE for details. # -"""Initial ballistica bootstrapping.""" +"""Ballistica bootstrapping.""" + +# This code runs in the logic thread to bootstrap ballistica. from __future__ import annotations diff --git a/src/meta/bameta/python_embedded/bootstrap_monolithic.py b/src/meta/bameta/python_embedded/bootstrap_monolithic.py new file mode 100644 index 00000000..d6534a1a --- /dev/null +++ b/src/meta/bameta/python_embedded/bootstrap_monolithic.py @@ -0,0 +1,22 @@ +# Released under the MIT License. See LICENSE for details. +# +"""Main thread bootstrapping for Python monolithic builds.""" + +# This code runs in the main thread just after the interpreter comes up. +# It should *ONLY* do things that must be done in the main thread and +# should not import any ballistica stuff. + +from __future__ import annotations + +import signal +from typing import TYPE_CHECKING + +if TYPE_CHECKING: + pass + +# Tell Python to not handle SIGINT itself (it normally generates +# KeyboardInterrupts which make a mess; we want to intercept them +# for simple clean exit). We have to do this part here because it must +# run in the main thread. We add our own handler later in the logic thread +# alongside our other ba bootstrapping. +signal.signal(signal.SIGINT, signal.SIG_DFL) # Do default handling. diff --git a/tests/test_efro/test_message.py b/tests/test_efro/test_message.py index 185ca140..a88e90bb 100644 --- a/tests/test_efro/test_message.py +++ b/tests/test_efro/test_message.py @@ -18,11 +18,13 @@ from efro.dataclassio import ioprepped from efro.message import (Message, Response, MessageProtocol, MessageSender, BoundMessageSender, MessageReceiver, BoundMessageReceiver, UnregisteredMessageIDError, - EmptyResponse) + EmptySysResponse) if TYPE_CHECKING: from typing import Any, Callable, Awaitable + from efro.message import SysResponse + @ioprepped @dataclass @@ -31,7 +33,7 @@ class _TMsg1(Message): ival: int @classmethod - def get_response_types(cls) -> list[type[Response]]: + def get_response_types(cls) -> list[type[Response] | None]: return [_TResp1] @@ -42,7 +44,7 @@ class _TMsg2(Message): sval: str @classmethod - def get_response_types(cls) -> list[type[Response]]: + def get_response_types(cls) -> list[type[Response] | None]: return [_TResp1, _TResp2] @@ -424,8 +426,8 @@ TEST_PROTOCOL = MessageProtocol( 0: _TResp1, 1: _TResp2, }, - receiver_returns_stack_traces=True, - receiver_logs_exceptions=False, + forward_clean_errors=True, + remote_errors_include_stack_traces=True, ) # Represents an 'evolved' TEST_PROTOCOL (one extra message type added). @@ -441,8 +443,8 @@ TEST_PROTOCOL_B = MessageProtocol( 0: _TResp1, 1: _TResp2, }, - receiver_returns_stack_traces=True, - receiver_logs_exceptions=False, + forward_clean_errors=True, + remote_errors_include_stack_traces=True, ) TEST_PROTOCOL_SINGLE = MessageProtocol( @@ -452,8 +454,7 @@ TEST_PROTOCOL_SINGLE = MessageProtocol( response_types={ 0: _TResp1, }, - receiver_returns_stack_traces=True, - receiver_logs_exceptions=False, + remote_errors_include_stack_traces=True, ) @@ -463,12 +464,16 @@ def test_protocol_creation() -> None: # This should fail because _TMsg1 can return _TResp1 which # is not given an id here. with pytest.raises(ValueError): - _protocol = MessageProtocol(message_types={0: _TMsg1}, - response_types={0: _TResp2}) + _protocol = MessageProtocol( + message_types={0: _TMsg1}, + response_types={0: _TResp2}, + ) # Now it should work. - _protocol = MessageProtocol(message_types={0: _TMsg1}, - response_types={0: _TResp1}) + _protocol = MessageProtocol( + message_types={0: _TMsg1}, + response_types={0: _TResp1}, + ) def test_sender_module_single_emb() -> None: @@ -777,7 +782,7 @@ def test_full_pipeline() -> None: # Emulate forwarding unregistered messages on to some # other handler... response_dict = self.msg.protocol.response_to_dict( - EmptyResponse()) + EmptySysResponse()) return self.msg.protocol.encode_dict(response_dict) raise @@ -805,7 +810,7 @@ def test_full_pipeline() -> None: @msg.decode_filter_method def _decode_filter(self, message: Message, indata: dict, - response: Response) -> None: + response: Response | SysResponse) -> None: """Filter our incoming responses.""" del message # Unused. if self.test_sidecar: @@ -849,7 +854,8 @@ def test_full_pipeline() -> None: setattr(message, '_sidecar_data', indata['_sidecar_data']) @receiver.encode_filter_method - def _encode_filter(self, message: Message | None, response: Response, + def _encode_filter(self, message: Message | None, + response: Response | SysResponse, outdict: dict) -> None: """Filter our outgoing responses.""" del message # Unused. @@ -909,7 +915,8 @@ def test_full_pipeline() -> None: assert response3 is None assert isinstance(response4, _TResp1) - # Remote CleanErrors should come across locally as the same. + # Remote CleanErrors should come across locally as the same + # (provided our protocol has enabled support for them). try: _response5 = obj.msg.send(_TMsg1(ival=1)) except Exception as exc: diff --git a/tools/bacommon/cloud.py b/tools/bacommon/cloud.py index 34e9ccaa..91512f92 100644 --- a/tools/bacommon/cloud.py +++ b/tools/bacommon/cloud.py @@ -21,7 +21,7 @@ class LoginProxyRequestMessage(Message): """Request send to the cloud to ask for a login-proxy.""" @classmethod - def get_response_types(cls) -> list[type[Response]]: + def get_response_types(cls) -> list[type[Response] | None]: return [LoginProxyRequestResponse] @@ -48,7 +48,7 @@ class LoginProxyStateQueryMessage(Message): proxykey: Annotated[str, IOAttrs('k')] @classmethod - def get_response_types(cls) -> list[type[Response]]: + def get_response_types(cls) -> list[type[Response] | None]: return [LoginProxyStateQueryResponse] @@ -82,7 +82,7 @@ class PingMessage(Message): """Standard ping.""" @classmethod - def get_response_types(cls) -> list[type[Response]]: + def get_response_types(cls) -> list[type[Response] | None]: return [PingResponse] @@ -99,7 +99,7 @@ class TestMessage(Message): testfoo: Annotated[int, IOAttrs('f')] @classmethod - def get_response_types(cls) -> list[type[Response]]: + def get_response_types(cls) -> list[type[Response] | None]: return [TestResponse] @@ -130,7 +130,7 @@ class WorkspaceFetchMessage(Message): state: Annotated[WorkspaceFetchState, IOAttrs('s')] @classmethod - def get_response_types(cls) -> list[type[Response]]: + def get_response_types(cls) -> list[type[Response] | None]: return [WorkspaceFetchResponse] diff --git a/tools/batools/dummymodule.py b/tools/batools/dummymodule.py index 3f4954a7..dc5afbbb 100755 --- a/tools/batools/dummymodule.py +++ b/tools/batools/dummymodule.py @@ -770,7 +770,7 @@ def update(projroot: str, check: bool, force: bool) -> None: for mname in ('_ba', '_bainternal'): # Skip internal module in public since it might # not exist and is read-only anyway. - if mname == '_ba' and public: + if mname == '_bainternal' and public: continue outfilename = os.path.abspath( diff --git a/tools/batools/project.py b/tools/batools/project.py index 507b8ebe..a810b09f 100755 --- a/tools/batools/project.py +++ b/tools/batools/project.py @@ -202,7 +202,9 @@ class Updater: if auto_changes: if not self._fix: for i, change in enumerate(auto_changes): - print(f'{Clr.RED}#{i}: {change[0]}:{Clr.RST}') + print(f'{Clr.RED}#{i}:' + f' {change[0]}:{change[1].line_number+1}:' + f'{Clr.RST}') print( f'{Clr.RED} Expected "{change[1].expected}"{Clr.RST}') with open(change[0], encoding='utf-8') as infile: @@ -262,6 +264,9 @@ class Updater: def _add_line_correction(self, filename: str, line_number: int, expected: str, can_auto_update: bool) -> None: + # No longer allowing negatives here since they don't show up nicely + # in correction list. + assert line_number >= 0 self._line_corrections.setdefault(filename, []).append( LineChange(line_number=line_number, expected=expected, @@ -301,7 +306,7 @@ class Updater: if self._license_line_checks: self._check_c_license(fname, lines) - # Check for header guard at top + # Check for header guard lines at top line = '#ifndef ' + guard lnum = 2 if lines[lnum] != line: @@ -312,10 +317,20 @@ class Updater: line_number=lnum, expected=line, can_auto_update=allow_auto) + line = '#define ' + guard + lnum = 3 + if lines[lnum] != line: + # Allow auto-correcting if it looks close already + # (don't want to blow away an unrelated line) + allow_auto = lines[lnum].startswith('#define BALLISTICA_') + self._add_line_correction(fname, + line_number=lnum, + expected=line, + can_auto_update=allow_auto) # Check for header guard at bottom line = '#endif // ' + guard - lnum = -1 + lnum = len(lines) - 1 if lines[lnum] != line: # Allow auto-correcting if it looks close already # (don't want to blow away an unrelated line) diff --git a/tools/efro/error.py b/tools/efro/error.py index 640d0143..d33c42db 100644 --- a/tools/efro/error.py +++ b/tools/efro/error.py @@ -188,7 +188,14 @@ def is_asyncio_streams_communication_error(exc: BaseException) -> bool: # Let's still complain, however, if we get any SSL errors besides # this one. https://bugs.python.org/issue39951 if isinstance(exc, ssl.SSLError): - if 'APPLICATION_DATA_AFTER_CLOSE_NOTIFY' in str(exc): + excstr = str(exc) + if 'APPLICATION_DATA_AFTER_CLOSE_NOTIFY' in excstr: + return True + + # Also occasionally am getting WRONG_VERSION_NUMBER ssl errors; + # Assuming this just means client is attempting to connect from some + # outdated browser or whatnot. + if 'SSL: WRONG_VERSION_NUMBER' in excstr: return True return False diff --git a/tools/efro/log.py b/tools/efro/log.py index af647f56..b313c37b 100644 --- a/tools/efro/log.py +++ b/tools/efro/log.py @@ -5,20 +5,22 @@ from __future__ import annotations import sys import time +import asyncio import logging import datetime -import threading from enum import Enum from dataclasses import dataclass from typing import TYPE_CHECKING, Annotated +from threading import Thread, current_thread, Lock from efro.util import utc_now +from efro.call import tpartial from efro.terminal import TerminalColor from efro.dataclassio import ioprepped, IOAttrs, dataclass_to_json if TYPE_CHECKING: from pathlib import Path - from typing import Any, Callable + from typing import Any, Callable, TextIO class LogLevel(Enum): @@ -35,6 +37,25 @@ class LogLevel(Enum): CRITICAL = 4 +LEVELNO_LOG_LEVELS = { + logging.DEBUG: LogLevel.DEBUG, + logging.INFO: LogLevel.INFO, + logging.WARNING: LogLevel.WARNING, + logging.ERROR: LogLevel.ERROR, + logging.CRITICAL: LogLevel.CRITICAL +} + +LEVELNO_COLOR_CODES: dict[int, tuple[str, str]] = { + logging.DEBUG: (TerminalColor.CYAN.value, TerminalColor.RESET.value), + logging.INFO: ('', ''), + logging.WARNING: (TerminalColor.YELLOW.value, TerminalColor.RESET.value), + logging.ERROR: (TerminalColor.RED.value, TerminalColor.RESET.value), + logging.CRITICAL: + (TerminalColor.STRONG_MAGENTA.value + TerminalColor.BOLD.value + + TerminalColor.BG_BLACK.value, TerminalColor.RESET.value), +} + + @ioprepped @dataclass class LogEntry: @@ -46,6 +67,21 @@ class LogEntry: time: Annotated[datetime.datetime, IOAttrs('t')] +@ioprepped +@dataclass +class LogArchive: + """Info and data for a log.""" + + # Total number of entries submitted to the log. + log_size: Annotated[int, IOAttrs('t')] + + # Offset for the entries contained here. + # (10 means our first entry is the 10th in the log, etc.) + start_index: Annotated[int, IOAttrs('c')] + + entries: Annotated[list[LogEntry], IOAttrs('e')] + + class LogHandler(logging.Handler): """Fancy-pants handler for logging output. @@ -53,19 +89,110 @@ class LogHandler(logging.Handler): to stdout/stderr with pretty colors. """ + _event_loop: asyncio.AbstractEventLoop + + # IMPORTANT: Any debug prints we do here should ONLY go to echofile. + # Otherwise we can get infinite loops as those prints come back to us + # as new log entries. + def __init__(self, path: str | Path | None, - echofile: Any, - suppress_non_root_debug: bool = False): + echofile: TextIO | None, + suppress_non_root_debug: bool = False, + cache_size_limit: int = 0): super().__init__() # pylint: disable=consider-using-with self._file = (None if path is None else open(path, 'w', encoding='utf-8')) self._echofile = echofile + self._callbacks_lock = Lock() self._callbacks: list[Callable[[LogEntry], None]] = [] self._suppress_non_root_debug = suppress_non_root_debug + self._file_chunks: dict[str, list[str]] = {'stdout': [], 'stderr': []} + self._file_chunk_ship_task: dict[str, asyncio.Task | None] = { + 'stdout': None, + 'stderr': None + } + self._cache_size = 0 + assert cache_size_limit >= 0 + self._cache_size_limit = cache_size_limit + self._cache: list[tuple[int, LogEntry]] = [] + self._cache_index_offset = 0 + self._cache_lock = Lock() + self._printed_callback_error = False + self._thread_bootstrapped = False + self._thread = Thread(target=self._thread_main, daemon=True) + self._thread.start() + + # Spin until our thread is up and running; otherwise we could + # wind up trying to push stuff to our event loop before the + # loop exists. + while not self._thread_bootstrapped: + time.sleep(0.001) + + def add_callback(self, call: Callable[[LogEntry], None]) -> None: + """Add a callback to be run for each LogEntry. + + Note that this callback will always run in a background thread. + """ + with self._callbacks_lock: + self._callbacks.append(call) + + def _thread_main(self) -> None: + self._event_loop = asyncio.new_event_loop() + # NOTE: if we ever use default threadpool at all we should allow + # setting it for our loop. + asyncio.set_event_loop(self._event_loop) + self._thread_bootstrapped = True + try: + self._event_loop.run_forever() + except BaseException: + # If this ever goes down we're in trouble. + # We won't be able to log about it though... + # Try to make some noise however we can. + print('LogHandler died!!!', file=sys.stderr) + import traceback + traceback.print_exc() + raise + + def get_cached(self, + start_index: int = 0, + max_entries: int | None = None) -> LogArchive: + """Build and return an archive of cached log entries. + + This will only include entries that have been processed by the + background thread, so may not include just-submitted logs or + entries for partially written stdout/stderr lines. + Entries from the range [start_index:start_index+max_entries] + which are still present in the cache will be returned. + """ + + assert start_index >= 0 + if max_entries is not None: + assert max_entries >= 0 + with self._cache_lock: + # Transform start_index to our present cache space. + start_index -= self._cache_index_offset + # Calc end-index in our present cache space. + end_index = (len(self._cache) + if max_entries is None else start_index + max_entries) + + # Clamp both indexes to both ends of our present space. + start_index = max(0, min(start_index, len(self._cache))) + end_index = max(0, min(end_index, len(self._cache))) + + return LogArchive( + log_size=self._cache_index_offset + len(self._cache), + start_index=start_index + self._cache_index_offset, + entries=[e[1] for e in self._cache[start_index:end_index]]) def emit(self, record: logging.LogRecord) -> None: + # Called by logging to send us records. + # We simply package them up and ship them to our thread. + # UPDATE: turns out we CAN get log messages from this thread + # (the C++ layer can spit out some performance metrics when + # calls take too long/etc.) + # assert current_thread() is not self._thread # Special case - filter out this common extra-chatty category. # TODO - should use a standard logging.Filter for this. @@ -73,143 +200,165 @@ class LogHandler(logging.Handler): and record.levelname == 'DEBUG'): return - # Bake down all log formatting into a simple string. + # We want to forward as much as we can along without processing it + # (better to do so in a bg thread). + # However its probably best to flatten the message string here since + # it could cause problems stringifying things in threads where they + # didn't expect to be stringified. msg = self.format(record) - # Translate Python log levels to our own. - level = { - 'DEBUG': LogLevel.DEBUG, - 'INFO': LogLevel.INFO, - 'WARNING': LogLevel.WARNING, - 'ERROR': LogLevel.ERROR, - 'CRITICAL': LogLevel.CRITICAL - }[record.levelname] - - entry = LogEntry(message=msg, - name=record.name, - level=level, - time=datetime.datetime.fromtimestamp( - record.created, datetime.timezone.utc)) - - for call in self._callbacks: - call(entry) - - # Also route log entries to the echo file (generally stdout/stderr) - # with pretty colors. + # Also immediately print pretty colored output to our echo file + # (generally stderr). We do this part here instead of in our bg + # thread because the delay can throw off command line prompts or + # make tight debugging harder. if self._echofile is not None: - cbegin: str - cend: str - cbegin, cend = { - LogLevel.DEBUG: - (TerminalColor.CYAN.value, TerminalColor.RESET.value), - LogLevel.INFO: ('', ''), - LogLevel.WARNING: - (TerminalColor.YELLOW.value, TerminalColor.RESET.value), - LogLevel.ERROR: - (TerminalColor.RED.value, TerminalColor.RESET.value), - LogLevel.CRITICAL: - (TerminalColor.STRONG_MAGENTA.value + - TerminalColor.BOLD.value + TerminalColor.BG_BLACK.value, - TerminalColor.RESET.value), - }[level] + ends = LEVELNO_COLOR_CODES.get(record.levelno) + if ends is not None: + self._echofile.write(f'{ends[0]}{msg}{ends[1]}\n') + else: + self._echofile.write(f'{msg}\n') - self._echofile.write(f'{cbegin}{msg}{cend}\n') + self._event_loop.call_soon_threadsafe( + tpartial(self._emit_in_thread, record.name, record.levelno, + record.created, msg)) - # Note to self: it sounds like logging wraps calls to us - # in a lock so we shouldn't have to worry about garbled - # json output due to multiple threads writing at once, - # but may be good to find out for sure? - if self._file is not None: - entry_s = dataclass_to_json(entry) - assert '\n' not in entry_s # make sure its a single line - print(entry_s, file=self._file, flush=True) - - def emit_custom(self, name: str, message: str, level: LogLevel) -> None: - """Custom emit call for our stdout/stderr redirection.""" - entry = LogEntry(name=name, + def _emit_in_thread(self, name: str, levelno: int, created: float, + message: str) -> None: + try: + self._emit_entry( + LogEntry(name=name, message=message, - level=level, - time=utc_now()) + level=LEVELNO_LOG_LEVELS.get(levelno, LogLevel.INFO), + time=datetime.datetime.fromtimestamp( + created, datetime.timezone.utc))) + except Exception: + import traceback + traceback.print_exc(file=self._echofile) - for call in self._callbacks: - call(entry) + def file_write(self, name: str, output: str) -> None: + """Send raw stdout/stderr output to the logger to be collated.""" + self._event_loop.call_soon_threadsafe( + tpartial(self._file_write_in_thread, name, output)) + + def _file_write_in_thread(self, name: str, output: str) -> None: + try: + assert name in ('stdout', 'stderr') + + # Here we try to be somewhat smart about breaking arbitrary + # print output into discrete log entries. + + # Individual parts of a print come across as separate writes, + # and the end of a print will be a standalone '\n' by default. + # So let's ship whatever we've got when one of those comes in. + if output == '\n': + self._ship_file_chunks(name, cancel_ship_task=True) + else: + # By default just keep adding chunks. + # However we keep a timer running anytime we've got + # unshipped chunks so that we can ship what we've got + # after a short bit if we never get a newline. + self._file_chunks[name].append(output) + + ship_task = self._file_chunk_ship_task[name] + if ship_task is None: + self._file_chunk_ship_task[name] = ( + self._event_loop.create_task( + self._ship_chunks_task(name))) + + except Exception: + import traceback + traceback.print_exc(file=self._echofile) + + async def _ship_chunks_task(self, name: str) -> None: + await asyncio.sleep(0.1) + self._ship_file_chunks(name, cancel_ship_task=False) + + def _ship_file_chunks(self, name: str, cancel_ship_task: bool) -> None: + self._emit_entry( + LogEntry(name=name, + message=''.join(self._file_chunks[name]), + level=LogLevel.INFO, + time=utc_now())) + self._file_chunks[name] = [] + ship_task = self._file_chunk_ship_task[name] + if cancel_ship_task and ship_task is not None: + ship_task.cancel() + self._file_chunk_ship_task[name] = None + + def _emit_entry(self, entry: LogEntry) -> None: + assert current_thread() is self._thread + + # Store to our cache. + if self._cache_size_limit > 0: + with self._cache_lock: + # Do a rough calc of how many bytes this entry consumes. + entry_size = sum( + sys.getsizeof(x) + for x in (entry, entry.name, entry.message, entry.level, + entry.time)) + self._cache.append((entry_size, entry)) + self._cache_size += entry_size + + # Prune old until we are back at or under our limit. + while self._cache_size > self._cache_size_limit: + popped = self._cache.pop(0) + self._cache_size -= popped[0] + self._cache_index_offset += 1 + + # Pass to callbacks. + with self._callbacks_lock: + for call in self._callbacks: + try: + call(entry) + except Exception: + # Only print one callback error to avoid insanity. + if not self._printed_callback_error: + import traceback + traceback.print_exc(file=self._echofile) + self._printed_callback_error = True + + # Dump to our structured log file. + # TODO: set a timer for flushing; don't flush every line. if self._file is not None: entry_s = dataclass_to_json(entry) assert '\n' not in entry_s # Make sure its a single line. print(entry_s, file=self._file, flush=True) - def add_callback(self, call: Callable[[LogEntry], None]) -> None: - """Add a callback to be run for each added entry.""" - self._callbacks.append(call) +class FileLogEcho: + """A file-like object for forwarding stdout/stderr to a LogHandler.""" -class LogRedirect: - """A file-like object for redirecting stdout/stderr to our log.""" - - def __init__(self, name: str, orig_out: Any, log_handler: LogHandler, - log_level: LogLevel): + def __init__(self, original: TextIO, name: str, + handler: LogHandler) -> None: + assert name in ('stdout', 'stderr') + self._original = original self._name = name - self._orig_out = orig_out - self._log_handler = log_handler - self._log_level = log_level - self._chunk = '' - self._chunk_start_time = 0.0 - self._lock = threading.Lock() + self._handler = handler - def write(self, s: str) -> None: - """Write something to output.""" + def write(self, output: Any) -> None: + """Override standard write call.""" + self._original.write(output) + self._handler.file_write(self._name, output) - assert isinstance(s, str) + def flush(self) -> None: + """Flush the file.""" + self._original.flush() - # First, ship it off to the original destination. - self._orig_out.write(s) - - # Now add this to our chunk and ship completed chunks - # off to the logger. - # Let's consider a chunk completed when we're passed - # a single '\n' by itself. (print() statement will do - # this at the end by default). - # We may get some false positives/negatives this way - # but it should result in *most* big multi-line print - # statements being wrapped into a single log entry. - # Also, flush with only_old=True can be called periodically - # to dump any pending chunks that don't happen to fit - # this pattern. - with self._lock: - if s == '\n': - self._log_handler.emit_custom(name=self._name, - message=self._chunk, - level=self._log_level) - self._chunk = '' - else: - if self._chunk == '': - self._chunk_start_time = time.time() - self._chunk += s - - def flush(self, only_old: bool = False) -> None: - """Flushhhhh!""" - self._orig_out.flush() - if only_old and time.time() - self._chunk_start_time < 0.5: - return - with self._lock: - if self._chunk != '': - chunk = self._chunk - if chunk.endswith('\n'): - chunk = chunk[:-1] - self._log_handler.emit_custom(name=self._name, - message=chunk, - level=self._log_level) - self._chunk = '' + def isatty(self) -> bool: + """Are we a terminal?""" + return self._original.isatty() def setup_logging(log_path: str | Path | None, level: LogLevel, - suppress_non_root_debug: bool = False) -> LogHandler: + suppress_non_root_debug: bool = False, + log_stdout_stderr: bool = False, + cache_size_limit: int = 0) -> LogHandler: """Set up our logging environment. Returns the custom handler which can be used to fetch information - about logs that have passed through it. (worst log-levels, etc.). + about logs that have passed through it. (worst log-levels, caches, etc.). """ lmap = { @@ -222,26 +371,39 @@ def setup_logging(log_path: str | Path | None, # Wire logger output to go to a structured log file. # Also echo it to stderr IF we're running in a terminal. + # UPDATE: Actually gonna always go to stderr. Is there a + # reason we shouldn't? This makes debugging possible if all + # we have is access to a non-interactive terminal or file dump. + # We could add a '--quiet' arg or whatnot to change this behavior. + + # Note: by passing in the *original* stderr here before we + # (potentially) replace it, we ensure that our log echos + # won't themselves be intercepted and sent to the logger + # which would create an infinite loop. loghandler = LogHandler( log_path, - echofile=sys.stderr if sys.stderr.isatty() else None, - suppress_non_root_debug=suppress_non_root_debug) + # echofile=sys.stderr if sys.stderr.isatty() else None, + echofile=sys.stderr, + suppress_non_root_debug=suppress_non_root_debug, + cache_size_limit=cache_size_limit) + # Note: going ahead with force=True here so that we replace any + # existing logger. Though we warn if it looks like we are doing + # that so we can try to avoid creating the first one. + had_previous_handlers = bool(logging.root.handlers) logging.basicConfig(level=lmap[level], format='%(message)s', - handlers=[loghandler]) + handlers=[loghandler], + force=True) + if had_previous_handlers: + logging.warning('setup_logging: force-replacing previous handlers.') - # DISABLING THIS BIT FOR NOW - want to keep things as pure as possible. - if bool(False): - # Now wire Python stdout/stderr output to generate log entries - # in addition to its regular routing. Make sure to do this *after* we - # tell the log-handler to write to stderr, otherwise we get an infinite - # loop. - # NOTE: remember that this won't capture subcommands or other - # non-python stdout/stderr output. - sys.stdout = LogRedirect( # type: ignore - 'stdout', sys.stdout, loghandler, LogLevel.INFO) - sys.stderr = LogRedirect( # type: ignore - 'stderr', sys.stderr, loghandler, LogLevel.INFO) + # Optionally intercept Python's stdout/stderr output and generate + # log entries from it. + if log_stdout_stderr: + sys.stdout = FileLogEcho( # type: ignore + sys.stdout, 'stdout', loghandler) + sys.stderr = FileLogEcho( # type: ignore + sys.stderr, 'stderr', loghandler) return loghandler diff --git a/tools/efro/message/__init__.py b/tools/efro/message/__init__.py index 11a82fd3..326ba025 100644 --- a/tools/efro/message/__init__.py +++ b/tools/efro/message/__init__.py @@ -11,15 +11,17 @@ from efro.message._protocol import MessageProtocol from efro.message._sender import (MessageSender, BoundMessageSender) from efro.message._receiver import (MessageReceiver, BoundMessageReceiver) from efro.message._module import (create_sender_module, create_receiver_module) -from efro.message._message import (Message, Response, EmptyResponse, - ErrorResponse, StringResponse, BoolResponse, +from efro.message._message import (Message, Response, SysResponse, + EmptySysResponse, ErrorSysResponse, + StringResponse, BoolResponse, UnregisteredMessageIDError) __all__ = [ - 'Message', 'Response', 'EmptyResponse', 'ErrorResponse', 'StringResponse', - 'BoolResponse', 'MessageProtocol', 'MessageSender', 'BoundMessageSender', - 'MessageReceiver', 'BoundMessageReceiver', 'create_sender_module', - 'create_receiver_module', 'UnregisteredMessageIDError' + 'Message', 'Response', 'SysResponse', 'EmptySysResponse', + 'ErrorSysResponse', 'StringResponse', 'BoolResponse', 'MessageProtocol', + 'MessageSender', 'BoundMessageSender', 'MessageReceiver', + 'BoundMessageReceiver', 'create_sender_module', 'create_receiver_module', + 'UnregisteredMessageIDError' ] # Have these things present themselves cleanly as 'thismodule.SomeClass' diff --git a/tools/efro/message/_message.py b/tools/efro/message/_message.py index adc2d190..7a00070c 100644 --- a/tools/efro/message/_message.py +++ b/tools/efro/message/_message.py @@ -24,31 +24,35 @@ class Message: """Base class for messages.""" @classmethod - def get_response_types(cls) -> list[type[Response]]: - """Return all message types this Message can result in when sent. + def get_response_types(cls) -> list[type[Response] | None]: + """Return all Response types this Message can return when sent. - The default implementation specifies EmptyResponse, so messages with - no particular response needs can leave this untouched. - Note that ErrorMessage is handled as a special case and does not - need to be specified here. + The default implementation specifies a None return type. """ - return [EmptyResponse] + return [None] class Response: """Base class for responses to messages.""" +class SysResponse: + """Base class for system-responses to messages. + + These are only sent/handled by the messaging system itself; + users of the api never see them. + """ + + # Some standard response types: @ioprepped @dataclass -class ErrorResponse(Response): - """Response saying some error has occurred for the send. +class ErrorSysResponse(SysResponse): + """SysResponse saying some error has occurred for the send. - This type is unique in that it is not returned to the user; it - instead results in a local exception being raised. + This generally results in an Exception being raised for the caller. """ class ErrorType(Enum): @@ -64,12 +68,12 @@ class ErrorResponse(Response): @ioprepped @dataclass -class EmptyResponse(Response): +class EmptySysResponse(SysResponse): """The response equivalent of None.""" # TODO: could allow handlers to deal in raw values for these -# types similar to how we allow None in place of EmptyResponse. +# types similar to how we allow None in place of EmptySysResponse. # Though not sure if they are widely used enough to warrant the # extra code complexity. @ioprepped diff --git a/tools/efro/message/_protocol.py b/tools/efro/message/_protocol.py index ae6c2df0..86387deb 100644 --- a/tools/efro/message/_protocol.py +++ b/tools/efro/message/_protocol.py @@ -14,8 +14,9 @@ import json from efro.error import CleanError from efro.dataclassio import (is_ioprepped_dataclass, dataclass_to_dict, dataclass_from_dict) -from efro.message._message import (Message, Response, ErrorResponse, - EmptyResponse, UnregisteredMessageIDError) +from efro.message._message import (Message, Response, SysResponse, + ErrorSysResponse, EmptySysResponse, + UnregisteredMessageIDError) if TYPE_CHECKING: from typing import Any, Literal @@ -33,37 +34,30 @@ class MessageProtocol: def __init__(self, message_types: dict[int, type[Message]], response_types: dict[int, type[Response]], - preserve_clean_errors: bool = True, - receiver_logs_exceptions: bool = True, - receiver_returns_stack_traces: bool = False) -> None: + forward_clean_errors: bool = False, + remote_errors_include_stack_traces: bool = False) -> None: """Create a protocol with a given configuration. Note that common response types are automatically registered with (unchanging negative ids) so they don't need to be passed explicitly (but can be if a different id is desired). - If 'preserve_clean_errors' is True, efro.error.CleanError + If 'forward_clean_errors' is True, efro.error.CleanError exceptions raised on the receiver end will result in a matching CleanError raised back on the sender. All other Exception types come across as efro.error.RemoteError. - When 'receiver_logs_exceptions' is True, any uncaught Exceptions - on the receiver end will be logged there via logging.exception() - (in addition to the usual behavior of returning an ErrorResponse - to the sender). This is good to leave enabled if your - intention is to never return ErrorResponses. Looser setups - making routine use of CleanErrors or whatnot may want to - disable this, however. - - If 'receiver_returns_stack_traces' is True, stringified stack + If 'remote_errors_include_stack_traces' is True, stringified stack traces will be returned to the sender for exceptions occurring on the receiver end. This can make debugging easier but should only be used when the client is trusted to see such info. """ self.message_types_by_id: dict[int, type[Message]] = {} self.message_ids_by_type: dict[type[Message], int] = {} - self.response_types_by_id: dict[int, type[Response]] = {} - self.response_ids_by_type: dict[type[Response], int] = {} + self.response_types_by_id: dict[int, type[Response] + | type[SysResponse]] = {} + self.response_ids_by_type: dict[type[Response] | type[SysResponse], + int] = {} for m_id, m_type in message_types.items(): # Make sure only valid message types were passed and each @@ -85,32 +79,33 @@ class MessageProtocol: self.response_types_by_id[r_id] = r_type self.response_ids_by_type[r_type] = r_id - # Go ahead and auto-register a few common response types - # if the user has not done so explicitly. Use unique negative - # IDs which will never change or overlap with user ids. - def _reg_if_not(reg_tp: type[Response], reg_id: int) -> None: - if reg_tp in self.response_ids_by_type: - return + # Register our SysResponse types. These use negative + # IDs so as to never overlap with user Response types. + def _reg_sys(reg_tp: type[SysResponse], reg_id: int) -> None: assert self.response_types_by_id.get(reg_id) is None self.response_types_by_id[reg_id] = reg_tp self.response_ids_by_type[reg_tp] = reg_id - _reg_if_not(ErrorResponse, -1) - _reg_if_not(EmptyResponse, -2) + _reg_sys(ErrorSysResponse, -1) + _reg_sys(EmptySysResponse, -2) # Some extra-thorough validation in debug mode. if __debug__: # Make sure all Message types' return types are valid # and have been assigned an ID as well. - all_response_types: set[type[Response]] = set() + all_response_types: set[type[Response] | None] = set() for m_id, m_type in message_types.items(): m_rtypes = m_type.get_response_types() + assert isinstance(m_rtypes, list) assert m_rtypes, ( f'Message type {m_type} specifies no return types.') assert len(set(m_rtypes)) == len(m_rtypes) # check dups - all_response_types.update(m_rtypes) + for m_rtype in m_rtypes: + all_response_types.add(m_rtype) for cls in all_response_types: + if cls is None: + continue assert is_ioprepped_dataclass(cls) assert issubclass(cls, Response) if cls not in self.response_ids_by_type: @@ -127,9 +122,9 @@ class MessageProtocol: 'message_types contains duplicate __name__s;' ' all types are required to have unique names.') - self.preserve_clean_errors = preserve_clean_errors - self.receiver_logs_exceptions = receiver_logs_exceptions - self.receiver_returns_stack_traces = receiver_returns_stack_traces + self.forward_clean_errors = forward_clean_errors + self.remote_errors_include_stack_traces = ( + remote_errors_include_stack_traces) @staticmethod def encode_dict(obj: dict) -> str: @@ -140,28 +135,27 @@ class MessageProtocol: """Encode a message to a json ready dict.""" return self._to_dict(message, self.message_ids_by_type, 'message') - def response_to_dict(self, response: Response) -> dict: + def response_to_dict(self, response: Response | SysResponse) -> dict: """Encode a response to a json ready dict.""" return self._to_dict(response, self.response_ids_by_type, 'response') - def error_to_response(self, exc: Exception) -> Response: + def error_to_response(self, exc: Exception) -> SysResponse: """Translate an error to a response.""" - # Log any errors we got during handling if so desired. - if self.receiver_logs_exceptions: - logging.exception('Error handling message.') + # Log any errors we got during handling. + logging.exception('Error in efro.message handling.') - # If anything goes wrong, return a ErrorResponse instead. + # If anything goes wrong, return a ErrorSysResponse instead. # (either CLEAN or generic REMOTE) - if isinstance(exc, CleanError) and self.preserve_clean_errors: - return ErrorResponse( + if isinstance(exc, CleanError) and self.forward_clean_errors: + return ErrorSysResponse( error_message=str(exc), - error_type=ErrorResponse.ErrorType.REMOTE_CLEAN) - return ErrorResponse( + error_type=ErrorSysResponse.ErrorType.REMOTE_CLEAN) + return ErrorSysResponse( error_message=(traceback.format_exc() - if self.receiver_returns_stack_traces else + if self.remote_errors_include_stack_traces else 'An internal error has occurred.'), - error_type=ErrorResponse.ErrorType.REMOTE) + error_type=ErrorSysResponse.ErrorType.REMOTE) def _to_dict(self, message: Any, ids_by_type: dict[type, int], opname: str) -> dict: @@ -187,10 +181,10 @@ class MessageProtocol: assert isinstance(out, Message) return out - def response_from_dict(self, data: dict) -> Response: + def response_from_dict(self, data: dict) -> Response | SysResponse: """Decode a response from a json string.""" out = self._from_dict(data, self.response_types_by_id, 'response') - assert isinstance(out, Response) + assert isinstance(out, Response | SysResponse) return out # Weeeird; we get mypy errors returning dict[int, type] but @@ -236,7 +230,7 @@ class MessageProtocol: rsptypes.append(Response) for rsp_tp in rsptypes: # Skip these as they don't actually show up in code. - if rsp_tp is EmptyResponse or rsp_tp is ErrorResponse: + if rsp_tp is EmptySysResponse or rsp_tp is ErrorSysResponse: continue if (single_message_type and part == 'sender' and rsp_tp is not Response): @@ -343,10 +337,8 @@ class MessageProtocol: f'class {ppre}Bound{basename}(BoundMessageSender):\n' f' """Protocol-specific bound sender."""\n') - def _filt_tp_name(rtype: type[Response]) -> str: - # We accept None to equal EmptyResponse so reflect that - # in the type annotation. - return 'None' if rtype is EmptyResponse else rtype.__name__ + def _filt_tp_name(rtype: type[Response] | None) -> str: + return 'None' if rtype is None else rtype.__name__ # Define handler() overloads for all registered message types. if msgtypes: @@ -383,6 +375,7 @@ class MessageProtocol: for msgtype in msgtypes: msgtypevar = msgtype.__name__ + # rtypes = msgtype.get_response_types() rtypes = msgtype.get_response_types() if len(rtypes) > 1: rtypevar = ' | '.join( @@ -440,10 +433,8 @@ class MessageProtocol: # Define handler() overloads for all registered message types. - def _filt_tp_name(rtype: type[Response]) -> str: - # We accept None to equal EmptyResponse so reflect that - # in the type annotation. - return 'None' if rtype is EmptyResponse else rtype.__name__ + def _filt_tp_name(rtype: type[Response] | None) -> str: + return 'None' if rtype is None else rtype.__name__ if msgtypes: cbgn = 'Awaitable[' if is_async else '' diff --git a/tools/efro/message/_receiver.py b/tools/efro/message/_receiver.py index f45b2556..5b23a80c 100644 --- a/tools/efro/message/_receiver.py +++ b/tools/efro/message/_receiver.py @@ -11,13 +11,14 @@ import inspect import logging from typing import TYPE_CHECKING -from efro.message._message import (Message, Response, EmptyResponse, - ErrorResponse, UnregisteredMessageIDError) +from efro.message._message import (Message, Response, EmptySysResponse, + UnregisteredMessageIDError) if TYPE_CHECKING: from typing import Any, Callable, Awaitable from efro.message._protocol import MessageProtocol + from efro.message._message import SysResponse class MessageReceiver: @@ -53,7 +54,8 @@ class MessageReceiver: self._decode_filter_call: Callable[[Any, dict, Message], None] | None = None self._encode_filter_call: Callable[ - [Any, Message | None, Response, dict], None] | None = None + [Any, Message | None, Response | SysResponse, dict], + None] | None = None # TODO: don't currently have async encode equivalent # or either for sender; can add as needed. @@ -106,26 +108,27 @@ class MessageReceiver: assert issubclass(msgtype, Message) ret = anns.get('return') - responsetypes: tuple[type[Any] | type[None], ...] + responsetypes: tuple[type[Any] | None, ...] # Return types can be a single type or a union of types. if isinstance(ret, (_GenericAlias, types.UnionType)): targs = get_args(ret) - if not all(isinstance(a, type) for a in targs): + if not all(isinstance(a, (type, type(None))) for a in targs): raise TypeError(f'expected only types for "return" annotation;' f' got {targs}.') responsetypes = targs else: - if not isinstance(ret, type): + if not isinstance(ret, (type, type(None))): raise TypeError(f'expected one or more types for' f' "return" annotation; got a {type(ret)}.') # This seems like maybe a mypy bug. Appeared after adding # types.UnionType above. - responsetypes = (ret, ) # type: ignore + responsetypes = (ret, ) - # Return type of None translates to EmptyResponse. - responsetypes = tuple(EmptyResponse if r is type(None) else r - for r in responsetypes) # noqa + # This will contain NoneType for empty return cases, but + # we expect it to be None. + responsetypes = tuple(None if r is type(None) else r + for r in responsetypes) # Make sure our protocol has this message type registered and our # return types exactly match. (Technically we could return a subset @@ -178,7 +181,9 @@ class MessageReceiver: return call def encode_filter_method( - self, call: Callable[[Any, Message | None, Response, dict], None] + self, + call: Callable[[Any, Message | None, Response | SysResponse, dict], + None] ) -> Callable[[Any, Message | None, Response, dict], None]: """Function decorator for defining an encode filter. @@ -236,17 +241,21 @@ class MessageReceiver: response: Response | None) -> str: """Encode a response provided by the user for sending.""" - # A return value of None equals EmptyResponse. - if response is None: - response = EmptyResponse() - - assert isinstance(response, Response) + assert isinstance(response, Response | None) # (user should never explicitly return error-responses) - assert not isinstance(response, ErrorResponse) - assert type(response) in message.get_response_types() - response_dict = self.protocol.response_to_dict(response) + assert (response is None + or type(response) in message.get_response_types()) + + # A return value of None equals EmptySysResponse. + out_response: Response | SysResponse + if response is None: + out_response = EmptySysResponse() + else: + out_response = response + + response_dict = self.protocol.response_to_dict(out_response) if self._encode_filter_call is not None: - self._encode_filter_call(bound_obj, message, response, + self._encode_filter_call(bound_obj, message, out_response, response_dict) return self.protocol.encode_dict(response_dict) @@ -280,7 +289,7 @@ class MessageReceiver: if handler is None: raise RuntimeError(f'Got unhandled message type: {msgtype}.') response = handler(bound_obj, msg_decoded) - assert isinstance(response, (Response, type(None))) + assert isinstance(response, Response | None) return self.encode_user_response(bound_obj, msg_decoded, response) except Exception as exc: @@ -308,7 +317,7 @@ class MessageReceiver: if handler is None: raise RuntimeError(f'Got unhandled message type: {msgtype}.') response = await handler(bound_obj, msg_decoded) - assert isinstance(response, (Response, type(None))) + assert isinstance(response, Response | None) return self.encode_user_response(bound_obj, msg_decoded, response) except Exception as exc: diff --git a/tools/efro/message/_sender.py b/tools/efro/message/_sender.py index c9d343d2..48a32e9f 100644 --- a/tools/efro/message/_sender.py +++ b/tools/efro/message/_sender.py @@ -10,12 +10,12 @@ import logging from typing import TYPE_CHECKING from efro.error import CleanError, RemoteError, CommunicationError -from efro.message._message import EmptyResponse, ErrorResponse +from efro.message._message import EmptySysResponse, ErrorSysResponse, Response if TYPE_CHECKING: from typing import Any, Callable, Awaitable - from efro.message._message import Message, Response + from efro.message._message import Message, SysResponse from efro.message._protocol import MessageProtocol @@ -46,8 +46,8 @@ class MessageSender: [Any, str], Awaitable[str]] | None = None self._encode_filter_call: Callable[[Any, Message, dict], None] | None = None - self._decode_filter_call: Callable[[Any, Message, dict, Response], - None] | None = None + self._decode_filter_call: Callable[ + [Any, Message, dict, Response | SysResponse], None] | None = None def send_method( self, call: Callable[[Any, str], @@ -90,7 +90,8 @@ class MessageSender: return call def decode_filter_method( - self, call: Callable[[Any, Message, dict, Response], None] + self, call: Callable[[Any, Message, dict, Response | SysResponse], + None] ) -> Callable[[Any, Message, dict, Response], None]: """Function decorator for defining a decode filter. @@ -122,7 +123,8 @@ class MessageSender: ), ) - def send_split_part_1(self, bound_obj: Any, message: Message) -> Response: + def send_split_part_1(self, bound_obj: Any, + message: Message) -> Response | SysResponse: """Send a message synchronously. Generally you can just call send(); these split versions are @@ -139,16 +141,16 @@ class MessageSender: except Exception as exc: # Any error in the raw send call gets recorded as either # a local or communication error. - return ErrorResponse( + return ErrorSysResponse( error_message= f'Error in MessageSender @send_method ({type(exc)}): {exc}', - error_type=(ErrorResponse.ErrorType.COMMUNICATION + error_type=(ErrorSysResponse.ErrorType.COMMUNICATION if isinstance(exc, CommunicationError) else - ErrorResponse.ErrorType.LOCAL)) + ErrorSysResponse.ErrorType.LOCAL)) return self._decode_raw_response(bound_obj, message, response_encoded) - async def send_split_part_1_async(self, bound_obj: Any, - message: Message) -> Response: + async def send_split_part_1_async( + self, bound_obj: Any, message: Message) -> Response | SysResponse: """Send a message asynchronously. Generally you can just call send(); these split versions are @@ -166,17 +168,18 @@ class MessageSender: except Exception as exc: # Any error in the raw send call gets recorded as either # a local or communication error. - return ErrorResponse( + return ErrorSysResponse( error_message= f'Error in MessageSender @send_async_method ({type(exc)}):' f' {exc}', - error_type=(ErrorResponse.ErrorType.COMMUNICATION + error_type=(ErrorSysResponse.ErrorType.COMMUNICATION if isinstance(exc, CommunicationError) else - ErrorResponse.ErrorType.LOCAL)) + ErrorSysResponse.ErrorType.LOCAL)) return self._decode_raw_response(bound_obj, message, response_encoded) - def send_split_part_2(self, message: Message, - raw_response: Response) -> Response | None: + def send_split_part_2( + self, message: Message, + raw_response: Response | SysResponse) -> Response | None: """Complete message sending (both sync and async). Generally you can just call send(); these split versions are @@ -196,7 +199,7 @@ class MessageSender: return self.protocol.encode_dict(msg_dict) def _decode_raw_response(self, bound_obj: Any, message: Message, - response_encoded: str) -> Response: + response_encoded: str) -> Response | SysResponse: """Create a Response from returned data. These Responses may encapsulate things like remote errors and @@ -204,6 +207,7 @@ class MessageSender: should be used to translate to special values like None or raise Exceptions. This function itself should never raise Exceptions. """ + response: Response | SysResponse try: response_dict = self.protocol.decode_dict(response_encoded) response = self.protocol.response_from_dict(response_dict) @@ -214,16 +218,17 @@ class MessageSender: # If we got to this point, we successfully communicated # with the other end so errors represent protocol mismatches # or other invalid data. For now let's just log it but perhaps - # we'd want to somehow embed it in the ErrorResponse to be + # we'd want to somehow embed it in the ErrorSysResponse to be # available directly to the user later. logging.exception('Error decoding raw response') - response = ErrorResponse( + response = ErrorSysResponse( error_message= 'Error decoding raw response; see log for details.', - error_type=ErrorResponse.ErrorType.LOCAL) + error_type=ErrorSysResponse.ErrorType.LOCAL) return response - def _unpack_raw_response(self, raw_response: Response) -> Response | None: + def _unpack_raw_response( + self, raw_response: Response | SysResponse) -> Response | None: """Given a raw Response, unpacks to special values or Exceptions. The result of this call is what should be passed to users. @@ -232,30 +237,31 @@ class MessageSender: run such that any raised Exception is active when the callback fires; not on the thread where the message was sent. """ - # EmptyResponse translates to None - if isinstance(raw_response, EmptyResponse): + # EmptySysResponse translates to None + if isinstance(raw_response, EmptySysResponse): return None # Some error occurred. Raise a local Exception for it. - if isinstance(raw_response, ErrorResponse): + if isinstance(raw_response, ErrorSysResponse): if (raw_response.error_type is - ErrorResponse.ErrorType.COMMUNICATION): + ErrorSysResponse.ErrorType.COMMUNICATION): raise CommunicationError(raw_response.error_message) # If something went wrong on *our* end of the connection, # don't say it was a remote error. - if raw_response.error_type is ErrorResponse.ErrorType.LOCAL: + if raw_response.error_type is ErrorSysResponse.ErrorType.LOCAL: raise RuntimeError(raw_response.error_message) # If they want to support clean errors, do those. - if (self.protocol.preserve_clean_errors and raw_response.error_type - is ErrorResponse.ErrorType.REMOTE_CLEAN): + if (self.protocol.forward_clean_errors and raw_response.error_type + is ErrorSysResponse.ErrorType.REMOTE_CLEAN): raise CleanError(raw_response.error_message) # Everything else gets lumped in as a remote error. raise RemoteError(raw_response.error_message) + assert isinstance(raw_response, Response) return raw_response @@ -292,15 +298,16 @@ class BoundMessageSender: return await self._sender.send_async(bound_obj=self._obj, message=message) - async def send_split_part_1_async_untyped(self, - message: Message) -> Response: + async def send_split_part_1_async_untyped( + self, message: Message) -> Response | SysResponse: """Split send (part 1 of 2).""" assert self._obj is not None return await self._sender.send_split_part_1_async(bound_obj=self._obj, message=message) - def send_split_part_2_untyped(self, message: Message, - raw_response: Response) -> Response | None: + def send_split_part_2_untyped( + self, message: Message, + raw_response: Response | SysResponse) -> Response | None: """Split send (part 2 of 2).""" return self._sender.send_split_part_2(message=message, raw_response=raw_response) diff --git a/tools/efro/rpc.py b/tools/efro/rpc.py index a360ca62..93c78ab5 100644 --- a/tools/efro/rpc.py +++ b/tools/efro/rpc.py @@ -559,7 +559,7 @@ class RPCEndpoint: # If that doesn't happen, make a fuss so we know to fix it. # The other end will simply never get a response to this # message. - logging.exception('Error handling message') + logging.exception('Error handling raw rpc message') return assert self._peer_info is not None