opening more internal sources

This commit is contained in:
Eric Froemling 2022-08-10 17:20:44 -07:00
parent dcf8671f4a
commit 20745818b2
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
15 changed files with 3940 additions and 42 deletions

View File

@ -3995,49 +3995,49 @@
"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/02/e0/0336db3c3989a1768271ee7a12ba",
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/3d/8c/9689aa6ff3fa826fc3f524b3115f",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/00/04/1ea1f6833f131458cbc8e7039591",
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/d9/f7/c87406c9229cbe5aa9439e3562c5",
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/66/1e/53122c4f96bca326aaa42d5cb98b",
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/6b/28/ee8a01e3155e05c5c970ace23107",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/89/5e/fbf702ecbd5376efe63720632883",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/c2/c4/958ba681823bcad105bc51d6b3ac",
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/95/55/3188c8463c50ce1cb7182788d0b8",
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/70/10/b99df359d806c3f4fc3c91685749",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/3e/40/9778e11391ca3832caf982a03832",
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/7a/0c/756391725d7bbbab568b8d946753",
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/b9/fe/e0f9747f9f03647d1becb31ac08d",
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/ee/d1/b0e387dfdbe8ac032456d4124bdf",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/10/db/85cb3b50f58df978a42f5b199c0d",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/3f/77/d66d99ba56643c20493a56cb3dba",
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/2b/23/eb989bada6166e03bf5756717194",
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/73/26/4670e939bfef0ad417f56d532fc0",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/04/73/6951027e00d62310e59eca1737c3",
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/1d/31/7698b0ee7abe6090d62295df170e",
"build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/02/ca/38de2750e629dd5f193a7c00a7d9",
"build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7a/b3/e30adb7116efb17abe63ce70def8",
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e9/c3/191d440460fbb95eb8c131081c4a",
"build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ed/06/e6d9a361c5df654e5db27bec0999",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a8/a5/70d83d086f2658e42c5d1e11aa53",
"build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b2/09/4105d848598a9cb1695f8be31696",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/3f/57/1ec9697d93ade3ef0a29d8ad2008",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e6/54/c828ae0046a1023f28a02edffc4f",
"build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/92/ef/bbe7906558281bb1a57a9ebe4ad1",
"build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/62/ea/d5de2257b62b625308df5760ce32",
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e5/a5/6304f685116de2c62c9d4b445dab",
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a0/86/1a7de5eb337f446ef952a0572214",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/cb/f1/f26f8c525637c9a29b511a6ffc2f",
"build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/49/ca/11a87c0fecc6220b9756caa2a86e",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/59/65/0b8fb1aeb2ae528f31d1455bb180",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/9f/88/c4ec00dff29865d4899395067b03",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/6c/05/f66f58d20fab5d0e658961ac65b5",
"build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/e1/8c/2ee1f6a353878bf6cef93870f424",
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/eb/a9/fa25af1dbae41645d1c670e36404",
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a9/42/4f64a2f1ea9421f883e34f29793f",
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/41/ad/bebe0575f1e1c4f2e09ef522b326",
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/05/f8/4dcd0854a6ac62afe9f22736c1c7",
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/e3/7a/f510a5e6c5f69feb1c9ed139a29b",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ea/cf/ceabc09c70adab68291bb9fa816f",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/87/f7/3b5ec7037e46b6b58f650048e39c",
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/6a/46/fa941d72c4f0b8c998f9403d5608",
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/f2/f3/ae030d3f548d6ab7586a867f1c7a",
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/da/c0/9af017be0becf14ab1fc1ee96254",
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b6/63/f870c817beebd8ee046d2dfc0364",
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/1c/2c/3aa53aeab23a9816877787e3ab88",
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/64/b5/a615b79eee5bea7bb8138195fb00",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/0b/6a/d385633d0f26444362fbd79b262d",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/42/14/7505a806e090eca242dfb3ebb136",
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/54/be/f241c6b573f203851221eaa3d58b",
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/a4/d3/44dd3f15ba30b945c0064fcbd0f5",
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/62/ed/67a279057579f659bed20b32f44e",
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/b1/cf/a68c3ced4167252df2cba97f3233",
"build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/d3/e8/063c0165376117c575262aa34e8e",
"build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/4a/ac/490695a5b3def64b27e5293c3abb",
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/76/6e/318f1a492c6d861f0f2eba2cf174",
"build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/9f/f8/2ab60dff814ac7652328150fa70d",
"build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/49/28/12f42bffe9da914a7bed5fca11e5",
"build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/24/c2/124f383e647b185c644e2d8a6c01",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/94/99/b39898c43f81f5f85e13aebed284",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/90/04/1035d3fcc228298846a6293f3d8b",
"build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e1/0a/92574d0a13fe1cff98ea380b80f5",
"build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/50/47/f0285e4ef62926f50f7bc737ff7d",
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/49/e7/4a8abfd1d32071165cfd3693d085",
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ff/97/87a7d0fd328e28c42e069ad6f9cf",
"build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/71/4e/59cb0a44813ce3a291b7665f3981",
"build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2d/77/38da1bc0cf1f1a63fd0b7c408d15",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/72/fc/96d3489a0237c5727fbe2288b7c9",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/64/7a/c1889e8bc60641cdb465f142caac",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/b0/6e/7f5f4a266768a9800ae9e95f1200",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/e9/54/b71bde6d97129447853b43879d5b",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/66/90/610ebef166a7729d764f8ef34d5d",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/d1/68/701d8bc9740ecbf54b21c5cc2255",
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/09/a7/875d4894eac1ec9bdbc2634b4815",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/96/bc/6d79640b1553959e85df1c12fb8d",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/89/46/802c29697180eaf95d4afb9a258e",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/a0/64/5b30f7f3139e59e54ad5244e9101",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/35/f8/8e2e0eb328b241af4e73b81d59a9",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/4d/1f/6bda20a013ce0be6fbd603fcf42c",
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/b2/5c/3008c02fffd966044e7ba803f52d",
"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/d3/db/e73d4dcf1280d5f677c3cf8b47c3"

View File

@ -1,4 +1,4 @@
### 1.7.6 (build 20681, api 7, 2022-08-10)
### 1.7.6 (build 20683, api 7, 2022-08-10)
- 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.
- Newly discovered plugins are now activated immediately instead of requiring a restart.

View File

@ -300,6 +300,7 @@ 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
@ -317,6 +318,7 @@ add_executable(ballisticacore
${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
@ -325,10 +327,15 @@ add_executable(ballisticacore
${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

View File

@ -291,6 +291,7 @@
<ClInclude Include="..\..\src\ballistica\dynamics\part.h" />
<ClCompile Include="..\..\src\ballistica\dynamics\rigid_body.cc" />
<ClInclude Include="..\..\src\ballistica\dynamics\rigid_body.h" />
<ClCompile Include="..\..\src\ballistica\game\account.cc" />
<ClInclude Include="..\..\src\ballistica\game\account.h" />
<ClInclude Include="..\..\src\ballistica\game\client_controller_interface.h" />
<ClCompile Include="..\..\src\ballistica\game\connection\connection.cc" />
@ -308,6 +309,7 @@
<ClInclude Include="..\..\src\ballistica\game\friend_score_set.h" />
<ClCompile Include="..\..\src\ballistica\game\game.cc" />
<ClInclude Include="..\..\src\ballistica\game\game.h" />
<ClCompile Include="..\..\src\ballistica\game\game_stream.cc" />
<ClInclude Include="..\..\src\ballistica\game\game_stream.h" />
<ClCompile Include="..\..\src\ballistica\game\host_activity.cc" />
<ClInclude Include="..\..\src\ballistica\game\host_activity.h" />
@ -316,10 +318,15 @@
<ClCompile Include="..\..\src\ballistica\game\player_spec.cc" />
<ClInclude Include="..\..\src\ballistica\game\player_spec.h" />
<ClInclude Include="..\..\src\ballistica\game\score_to_beat.h" />
<ClCompile Include="..\..\src\ballistica\game\session\client_session.cc" />
<ClInclude Include="..\..\src\ballistica\game\session\client_session.h" />
<ClCompile Include="..\..\src\ballistica\game\session\host_session.cc" />
<ClInclude Include="..\..\src\ballistica\game\session\host_session.h" />
<ClCompile Include="..\..\src\ballistica\game\session\net_client_session.cc" />
<ClInclude Include="..\..\src\ballistica\game\session\net_client_session.h" />
<ClCompile Include="..\..\src\ballistica\game\session\replay_client_session.cc" />
<ClInclude Include="..\..\src\ballistica\game\session\replay_client_session.h" />
<ClCompile Include="..\..\src\ballistica\game\session\session.cc" />
<ClInclude Include="..\..\src\ballistica\game\session\session.h" />
<ClCompile Include="..\..\src\ballistica\generic\base64.cc" />
<ClInclude Include="..\..\src\ballistica\generic\base64.h" />

View File

@ -307,6 +307,9 @@
<ClInclude Include="..\..\src\ballistica\dynamics\rigid_body.h">
<Filter>ballistica\dynamics</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\game\account.cc">
<Filter>ballistica\game</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\game\account.h">
<Filter>ballistica\game</Filter>
</ClInclude>
@ -358,6 +361,9 @@
<ClInclude Include="..\..\src\ballistica\game\game.h">
<Filter>ballistica\game</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\game\game_stream.cc">
<Filter>ballistica\game</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\game\game_stream.h">
<Filter>ballistica\game</Filter>
</ClInclude>
@ -382,18 +388,33 @@
<ClInclude Include="..\..\src\ballistica\game\score_to_beat.h">
<Filter>ballistica\game</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\game\session\client_session.cc">
<Filter>ballistica\game\session</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\game\session\client_session.h">
<Filter>ballistica\game\session</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\game\session\host_session.cc">
<Filter>ballistica\game\session</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\game\session\host_session.h">
<Filter>ballistica\game\session</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\game\session\net_client_session.cc">
<Filter>ballistica\game\session</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\game\session\net_client_session.h">
<Filter>ballistica\game\session</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\game\session\replay_client_session.cc">
<Filter>ballistica\game\session</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\game\session\replay_client_session.h">
<Filter>ballistica\game\session</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\game\session\session.cc">
<Filter>ballistica\game\session</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\game\session\session.h">
<Filter>ballistica\game\session</Filter>
</ClInclude>

View File

@ -286,6 +286,7 @@
<ClInclude Include="..\..\src\ballistica\dynamics\part.h" />
<ClCompile Include="..\..\src\ballistica\dynamics\rigid_body.cc" />
<ClInclude Include="..\..\src\ballistica\dynamics\rigid_body.h" />
<ClCompile Include="..\..\src\ballistica\game\account.cc" />
<ClInclude Include="..\..\src\ballistica\game\account.h" />
<ClInclude Include="..\..\src\ballistica\game\client_controller_interface.h" />
<ClCompile Include="..\..\src\ballistica\game\connection\connection.cc" />
@ -303,6 +304,7 @@
<ClInclude Include="..\..\src\ballistica\game\friend_score_set.h" />
<ClCompile Include="..\..\src\ballistica\game\game.cc" />
<ClInclude Include="..\..\src\ballistica\game\game.h" />
<ClCompile Include="..\..\src\ballistica\game\game_stream.cc" />
<ClInclude Include="..\..\src\ballistica\game\game_stream.h" />
<ClCompile Include="..\..\src\ballistica\game\host_activity.cc" />
<ClInclude Include="..\..\src\ballistica\game\host_activity.h" />
@ -311,10 +313,15 @@
<ClCompile Include="..\..\src\ballistica\game\player_spec.cc" />
<ClInclude Include="..\..\src\ballistica\game\player_spec.h" />
<ClInclude Include="..\..\src\ballistica\game\score_to_beat.h" />
<ClCompile Include="..\..\src\ballistica\game\session\client_session.cc" />
<ClInclude Include="..\..\src\ballistica\game\session\client_session.h" />
<ClCompile Include="..\..\src\ballistica\game\session\host_session.cc" />
<ClInclude Include="..\..\src\ballistica\game\session\host_session.h" />
<ClCompile Include="..\..\src\ballistica\game\session\net_client_session.cc" />
<ClInclude Include="..\..\src\ballistica\game\session\net_client_session.h" />
<ClCompile Include="..\..\src\ballistica\game\session\replay_client_session.cc" />
<ClInclude Include="..\..\src\ballistica\game\session\replay_client_session.h" />
<ClCompile Include="..\..\src\ballistica\game\session\session.cc" />
<ClInclude Include="..\..\src\ballistica\game\session\session.h" />
<ClCompile Include="..\..\src\ballistica\generic\base64.cc" />
<ClInclude Include="..\..\src\ballistica\generic\base64.h" />

View File

@ -307,6 +307,9 @@
<ClInclude Include="..\..\src\ballistica\dynamics\rigid_body.h">
<Filter>ballistica\dynamics</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\game\account.cc">
<Filter>ballistica\game</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\game\account.h">
<Filter>ballistica\game</Filter>
</ClInclude>
@ -358,6 +361,9 @@
<ClInclude Include="..\..\src\ballistica\game\game.h">
<Filter>ballistica\game</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\game\game_stream.cc">
<Filter>ballistica\game</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\game\game_stream.h">
<Filter>ballistica\game</Filter>
</ClInclude>
@ -382,18 +388,33 @@
<ClInclude Include="..\..\src\ballistica\game\score_to_beat.h">
<Filter>ballistica\game</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\game\session\client_session.cc">
<Filter>ballistica\game\session</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\game\session\client_session.h">
<Filter>ballistica\game\session</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\game\session\host_session.cc">
<Filter>ballistica\game\session</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\game\session\host_session.h">
<Filter>ballistica\game\session</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\game\session\net_client_session.cc">
<Filter>ballistica\game\session</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\game\session\net_client_session.h">
<Filter>ballistica\game\session</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\game\session\replay_client_session.cc">
<Filter>ballistica\game\session</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\game\session\replay_client_session.h">
<Filter>ballistica\game\session</Filter>
</ClInclude>
<ClCompile Include="..\..\src\ballistica\game\session\session.cc">
<Filter>ballistica\game\session</Filter>
</ClCompile>
<ClInclude Include="..\..\src\ballistica\game\session\session.h">
<Filter>ballistica\game\session</Filter>
</ClInclude>

View File

@ -21,7 +21,7 @@
namespace ballistica {
// These are set automatically via script; don't modify them here.
const int kAppBuildNumber = 20681;
const int kAppBuildNumber = 20683;
const char* kAppVersion = "1.7.6";
// Our standalone globals.

View File

@ -0,0 +1,207 @@
// Released under the MIT License. See LICENSE for details.
#include "ballistica/game/account.h"
#include "ballistica/app/app_globals.h"
#include "ballistica/app/app_internal.h"
#include "ballistica/game/game.h"
#include "ballistica/generic/utils.h"
#include "ballistica/platform/platform.h"
namespace ballistica {
auto Account::AccountTypeFromString(const std::string& val) -> V1AccountType {
if (val == "Game Center") {
return V1AccountType::kGameCenter;
} else if (val == "Game Circle") {
return V1AccountType::kGameCircle;
} else if (val == "Google Play") {
return V1AccountType::kGooglePlay;
} else if (val == "Steam") {
return V1AccountType::kSteam;
} else if (val == "Oculus") {
return V1AccountType::kOculus;
} else if (val == "NVIDIA China") {
return V1AccountType::kNvidiaChina;
} else if (val == "Test") {
return V1AccountType::kTest;
} else if (val == "Local") {
return V1AccountType::kDevice;
} else if (val == "Server") {
return V1AccountType::kServer;
} else if (val == "V2") {
return V1AccountType::kV2;
} else {
return V1AccountType::kInvalid;
}
}
auto Account::AccountTypeToString(V1AccountType type) -> std::string {
switch (type) {
case V1AccountType::kGameCenter:
return "Game Center";
case V1AccountType::kGameCircle:
return "Game Circle";
case V1AccountType::kGooglePlay:
return "Google Play";
case V1AccountType::kSteam:
return "Steam";
case V1AccountType::kOculus:
return "Oculus";
case V1AccountType::kTest:
return "Test";
case V1AccountType::kDevice:
return "Local";
case V1AccountType::kServer:
return "Server";
case V1AccountType::kNvidiaChina:
return "NVIDIA China";
case V1AccountType::kV2:
return "V2";
default:
return "";
}
}
auto Account::AccountTypeToIconString(V1AccountType type) -> std::string {
switch (type) {
case V1AccountType::kTest:
return g_game->CharStr(SpecialChar::kTestAccount);
case V1AccountType::kNvidiaChina:
return g_game->CharStr(SpecialChar::kNvidiaLogo);
case V1AccountType::kGooglePlay:
return g_game->CharStr(SpecialChar::kGooglePlayGamesLogo);
case V1AccountType::kSteam:
return g_game->CharStr(SpecialChar::kSteamLogo);
case V1AccountType::kOculus:
return g_game->CharStr(SpecialChar::kOculusLogo);
case V1AccountType::kGameCenter:
return g_game->CharStr(SpecialChar::kGameCenterLogo);
case V1AccountType::kGameCircle:
return g_game->CharStr(SpecialChar::kGameCircleLogo);
case V1AccountType::kDevice:
case V1AccountType::kServer:
return g_game->CharStr(SpecialChar::kLocalAccount);
case V1AccountType::kV2:
return g_game->CharStr(SpecialChar::kV2Logo);
default:
return "";
}
}
Account::Account() = default;
auto Account::GetLoginName() -> std::string {
std::lock_guard<std::mutex> lock(mutex_);
return login_name_;
}
auto Account::GetLoginID() -> std::string {
std::lock_guard<std::mutex> lock(mutex_);
return login_id_;
}
auto Account::GetToken() -> std::string {
std::lock_guard<std::mutex> lock(mutex_);
return token_;
}
auto Account::GetExtra() -> std::string {
std::lock_guard<std::mutex> lock(mutex_);
return extra_;
}
auto Account::GetExtra2() -> std::string {
std::lock_guard<std::mutex> lock(mutex_);
return extra_2_;
}
auto Account::GetLoginState(int* state_num) -> V1LoginState {
std::lock_guard<std::mutex> lock(mutex_);
if (state_num) {
*state_num = login_state_num_;
}
return login_state_;
}
void Account::SetExtra(const std::string& extra) {
std::lock_guard<std::mutex> lock(mutex_);
extra_ = extra;
}
void Account::SetExtra2(const std::string& extra) {
std::lock_guard<std::mutex> lock(mutex_);
extra_2_ = extra;
}
void Account::SetToken(const std::string& account_id,
const std::string& token) {
std::lock_guard<std::mutex> lock(mutex_);
// Hmm, does this compare logic belong in here?
if (login_id_ == account_id) {
token_ = token;
}
}
void Account::SetLogin(V1AccountType account_type, V1LoginState login_state,
const std::string& login_name,
const std::string& login_id) {
bool call_login_did_change = false;
{
std::lock_guard<std::mutex> lock(mutex_);
// We call out to Python so need to be in game thread.
assert(InGameThread());
if (login_state_ != login_state
|| g_app_globals->account_type != account_type || login_id_ != login_id
|| login_name_ != login_name) {
// Special case: if they sent a sign-out for an account type that is
// currently not signed in, ignore it.
if (login_state == V1LoginState::kSignedOut
&& (account_type != g_app_globals->account_type)) {
// No-op.
} else {
login_state_ = login_state;
g_app_globals->account_type = account_type;
login_id_ = login_id;
login_name_ = Utils::GetValidUTF8(login_name.c_str(), "gthm");
// If they signed out of an account, account type switches to invalid.
if (login_state == V1LoginState::kSignedOut) {
g_app_globals->account_type = V1AccountType::kInvalid;
}
login_state_num_ += 1;
call_login_did_change = true;
}
}
}
if (call_login_did_change) {
// Inform a few subsystems of the change.
g_app_internal->V1LoginDidChange();
g_platform->V1LoginDidChange();
}
}
void Account::SetProductsPurchased(const std::vector<std::string>& products) {
std::lock_guard<std::mutex> lock(mutex_);
std::unordered_map<std::string, bool> purchases_old = product_purchases_;
product_purchases_.clear();
for (auto&& i : products) {
product_purchases_[i] = true;
}
if (product_purchases_ != purchases_old) {
product_purchases_state_++;
}
}
auto Account::GetProductPurchased(const std::string& product) -> bool {
std::lock_guard<std::mutex> lock(mutex_);
auto i = product_purchases_.find(product);
if (i == product_purchases_.end()) {
return false;
} else {
return i->second;
}
}
} // namespace ballistica

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,762 @@
// Released under the MIT License. See LICENSE for details.
#include "ballistica/game/session/host_session.h"
#include "ballistica/game/game_stream.h"
#include "ballistica/game/host_activity.h"
#include "ballistica/game/player.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/python/python.h"
#include "ballistica/python/python_command.h"
#include "ballistica/python/python_context_call.h"
#include "ballistica/python/python_sys.h"
namespace ballistica {
HostSession::HostSession(PyObject* session_type_obj)
: last_kick_idle_players_decrement_time_(GetRealTime()) {
assert(g_game);
assert(InGameThread());
assert(session_type_obj != nullptr);
ScopedSetContext cp(this);
// FIXME: Should be an attr of the session class, not hard-coded.
is_main_menu_ =
static_cast<bool>(strstr(Python::ObjToString(session_type_obj).c_str(),
"bastd.mainmenu.MainMenuSession"));
// Log("MAIN MENU? " + std::to_string(is_main_menu()));
kick_idle_players_ = g_game->kick_idle_players();
// Create a timer to step our session scene.
step_scene_timer_ =
base_timers_.NewTimer(base_time_, kGameStepMilliseconds, 0, -1,
NewLambdaRunnable([this] { StepScene(); }));
// Set up our output-stream, which will go to a replay and/or the network.
// We don't dump to a replay if we're doing the main menu; that replay
// would be boring.
bool do_replay = !is_main_menu_;
// At the moment headless-server don't write replays.
if (HeadlessMode()) {
do_replay = false;
}
output_stream_ = Object::New<GameStream>(this, do_replay);
// Make a scene for our session-level nodes, etc.
scene_ = Object::New<Scene>(0);
if (output_stream_.exists()) {
output_stream_->AddScene(scene_.get());
}
// Fade in from our current blackness.
g_graphics->FadeScreen(true, 250, nullptr);
// Start by showing the progress bar instead of hitching.
g_graphics->EnableProgressBar(true);
// Now's a good time to run garbage collection; there should be pretty much
// no game stuff to speak of in existence (provided the last session went
// down peacefully).
g_python->obj(Python::ObjID::kGarbageCollectSessionEndCall).Call();
// Instantiate our Python Session instance.
PythonRef obj;
PythonRef session_type(session_type_obj, PythonRef::kAcquire);
{
Python::ScopedCallLabel label("Session instantiation");
obj = session_type.Call();
}
if (!obj.exists()) {
throw Exception("Error creating game session: '" + session_type.Str()
+ "'");
}
// The session python object should have called
// _ba.register_session() in its constructor to set session_py_obj_.
if (session_py_obj_ != obj) {
throw Exception("session not set up correctly");
}
// 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<int>(players_.size()));
}
auto HostSession::GetHostSession() -> HostSession* { return this; }
void HostSession::DestroyHostActivity(HostActivity* a) {
BA_PRECONDITION(a);
BA_PRECONDITION(a->GetHostSession() == this);
if (a == foreground_host_activity_.get()) {
foreground_host_activity_.Clear();
}
// Clear it from our activities list if its still on there.
for (auto i = host_activities_.begin(); i < host_activities_.end(); i++) {
if (i->get() == a) {
host_activities_.erase(i);
return;
}
}
// The only reason it wouldn't be there should be because the activity is
// dying due our clearing of the list in our destructor; make sure that's
// the case.
assert(shutting_down_);
}
auto HostSession::GetMutableScene() -> Scene* {
assert(scene_.exists());
return scene_.get();
}
void HostSession::DebugSpeedMultChanged() {
// FIXME - should we progress our own scene faster/slower depending on
// this too? Is there really a need to?
// Let all our activities know.
for (auto&& i : host_activities_) {
i->DebugSpeedMultChanged();
}
}
void HostSession::ScreenSizeChanged() {
// Let our internal scene know.
scene()->ScreenSizeChanged();
// Also let all our activities know.
for (auto&& i : host_activities_) {
i->ScreenSizeChanged();
}
}
void HostSession::LanguageChanged() {
// Let our internal scene know.
scene()->LanguageChanged();
// Also let all our activities know.
for (auto&& i : host_activities_) {
i->LanguageChanged();
}
}
void HostSession::GraphicsQualityChanged(GraphicsQuality q) {
// Let our internal scene know.
scene()->GraphicsQualityChanged(q);
// Let all our activities know.
for (auto&& i : host_activities_) {
i->GraphicsQualityChanged(q);
}
}
auto HostSession::DoesFillScreen() const -> bool {
// FIXME not necessarily the case.
return true;
}
void HostSession::Draw(FrameDef* f) {
// First draw our session scene.
scene()->Draw(f);
// Let all our activities draw their own scenes/etc.
for (auto&& i : host_activities_) {
i->Draw(f);
}
}
auto HostSession::NewTimer(TimerMedium length, bool repeat,
const Object::Ref<Runnable>& runnable) -> int {
if (shutting_down_) {
BA_LOG_PYTHON_TRACE_ONCE(
"WARNING: Creating game timer during host-session shutdown");
return 123; // dummy...
}
if (length == 0 && repeat) {
throw Exception("Can't add game-timer with length 0 and repeat on");
}
if (length < 0) {
throw Exception("Timer length cannot be < 0 (got " + std::to_string(length)
+ ")");
}
int offset = 0;
Timer* t = sim_timers_.NewTimer(scene()->time(), length, offset,
repeat ? -1 : 0, runnable);
return t->id();
}
void HostSession::DeleteTimer(int timer_id) {
assert(InGameThread());
if (shutting_down_) return;
sim_timers_.DeleteTimer(timer_id);
}
auto HostSession::GetSound(const std::string& name) -> Object::Ref<Sound> {
if (shutting_down_) {
throw Exception("can't load assets during session shutdown");
}
return Media::GetMedia(&sounds_, name, scene());
}
auto HostSession::GetData(const std::string& name) -> Object::Ref<Data> {
if (shutting_down_) {
throw Exception("can't load assets during session shutdown");
}
return Media::GetMedia(&datas_, name, scene());
}
auto HostSession::GetTexture(const std::string& name) -> Object::Ref<Texture> {
if (shutting_down_) {
throw Exception("can't load assets during session shutdown");
}
return Media::GetMedia(&textures_, name, scene());
}
auto HostSession::GetModel(const std::string& name) -> Object::Ref<Model> {
if (shutting_down_) {
throw Exception("can't load media during session shutdown");
}
return Media::GetMedia(&models_, name, scene());
}
auto HostSession::GetForegroundContext() -> Context {
HostActivity* a = foreground_host_activity_.get();
if (a) {
return Context(a);
}
return Context(this);
}
void HostSession::RequestPlayer(InputDevice* device) {
assert(InGameThread());
// Ignore if we have no Python session obj.
if (!GetSessionPyObj()) {
Log("Error: HostSession::RequestPlayer() called w/no session_py_obj_.");
return;
}
// Need to at least temporarily create and attach to a player for passing to
// the callback.
int player_id = next_player_id_++;
auto player(Object::New<Player>(player_id, this));
players_.push_back(player);
device->AttachToLocalPlayer(player.get());
// Ask the python layer to accept/deny this guy.
bool accept;
{
// Set the session as context.
ScopedSetContext cp(this);
accept = static_cast<bool>(
session_py_obj_.GetAttr("_request_player")
.Call(PythonRef(Py_BuildValue("(O)", player->BorrowPyRef()),
PythonRef::kSteal))
.ValueAsInt());
if (accept) {
player->set_accepted(true);
} else {
RemovePlayer(player.get());
}
}
// If he was accepted, update our game roster with the new info.
if (accept) {
g_game->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<int>(players_.size()));
}
void HostSession::RemovePlayer(Player* player) {
assert(player);
for (auto i = players_.begin(); i != players_.end(); ++i) {
if (i->get() == player) {
// Grab a ref to keep the player alive, pull him off the list, then call
// his leaving callback.
Object::Ref<Player> player2 = *i;
players_.erase(i);
// Only make the callback for this player if they were accepted.
if (player2->accepted()) {
IssuePlayerLeft(player2.get());
}
// Update our game roster with the departure.
g_game->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<int>(players_.size()));
return;
}
}
BA_LOG_ERROR_TRACE("Player not found in HostSession::RemovePlayer()");
}
void HostSession::IssuePlayerLeft(Player* player) {
assert(player);
assert(InGameThread());
try {
if (GetSessionPyObj()) {
if (player) {
// Make sure we're the context for session callbacks.
ScopedSetContext cp(this);
Python::ScopedCallLabel label("Session on_player_leave");
session_py_obj_.GetAttr("on_player_leave")
.Call(PythonRef(Py_BuildValue("(O)", player->BorrowPyRef()),
PythonRef::kSteal));
} else {
BA_LOG_PYTHON_TRACE_ONCE("missing player on IssuePlayerLeft");
}
} else {
Log("WARNING: HostSession: IssuePlayerLeft caled with no "
"session_py_obj_");
}
} catch (const std::exception& e) {
Log(std::string("Error calling on_player_leave(): ") + e.what());
}
}
void HostSession::SetKickIdlePlayers(bool enable) {
// If this has changed, reset our disconnect-time reporting.
assert(InGameThread());
if (enable != kick_idle_players_) {
last_kick_idle_players_decrement_time_ = GetRealTime();
}
kick_idle_players_ = enable;
}
void HostSession::SetForegroundHostActivity(HostActivity* a) {
assert(a);
assert(InGameThread());
if (shutting_down_) {
Log("WARNING: SetForegroundHostActivity called during session shutdown; "
"ignoring.");
return;
}
// Sanity check: make sure the one provided is part of this session.
bool found = false;
for (auto&& i : host_activities_) {
if (i == a) {
found = true;
break;
}
}
if ((a->GetHostSession() != this) || !found) {
throw Exception("HostActivity is not part of this HostSession");
}
foreground_host_activity_ = 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);
for (auto&& i : host_activities_) {
i->SetIsForeground(session_is_foreground && (i == a));
}
}
void HostSession::AddHostActivity(HostActivity* a) {
host_activities_.emplace_back(a);
}
// Called by the constructor of the session python object.
void HostSession::RegisterPySession(PyObject* obj) {
session_py_obj_.Acquire(obj);
}
// Given an activity python type, instantiates and returns a new activity.
auto HostSession::NewHostActivity(PyObject* activity_type_obj,
PyObject* settings_obj) -> PyObject* {
PythonRef activity_type(activity_type_obj, PythonRef::kAcquire);
if (!activity_type.CallableCheck()) {
throw Exception("Invalid HostActivity type passed; not callable");
}
// First generate our C++ activity instance and point the context at it.
auto activity(Object::New<HostActivity>(this));
AddHostActivity(activity.get());
ScopedSetContext cp(activity.get());
// Now instantiate the Python instance.. pass args if some were provided, or
// an empty dict otherwise.
PythonRef args;
if (settings_obj == Py_None) {
args.Steal(Py_BuildValue("({})"));
} else {
args.Steal(Py_BuildValue("(O)", settings_obj));
}
PythonRef result = activity_type.Call(args);
if (!result.exists()) {
throw Exception("HostActivity creation failed");
}
// If all went well, the python activity constructor should have called
// _ba.register_activity(), so we should be able to get at the same python
// activity we just instantiated through the c++ class.
if (activity->GetPyActivity() != result.get()) {
throw Exception("Error on HostActivity construction");
}
PyObject* obj = result.get();
Py_INCREF(obj);
return obj;
}
auto HostSession::RegisterPyActivity(PyObject* activity_obj) -> HostActivity* {
// The context should be pointing to an unregistered HostActivity;
// register and return it.
HostActivity* activity = Context::current().GetHostActivity();
if (!activity)
throw Exception(
"No current activity in RegisterPyActivity; did you remember to call "
"ba.newHostActivity() to instantiate your activity?");
activity->RegisterPyActivity(activity_obj);
return activity;
}
void HostSession::DecrementPlayerTimeOuts(millisecs_t millisecs) {
for (auto&& i : players_) {
Player* player = i.get();
assert(player);
if (player->time_out() < millisecs) {
std::string kick_str =
g_game->GetResourceString("kickIdlePlayersKickedText");
Utils::StringReplaceOne(&kick_str, "${NAME}", player->GetName());
ScreenMessage(kick_str);
RemovePlayer(player);
return; // Bail for this round since we prolly mucked with the list.
} 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");
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"));
}
player->set_time_out(player->time_out() - millisecs);
}
}
void HostSession::ProcessPlayerTimeOuts() {
millisecs_t real_time = GetRealTime();
if (foreground_host_activity_.exists()
&& foreground_host_activity_->game_speed() > 0.0
&& !foreground_host_activity_->paused()
&& foreground_host_activity_->getAllowKickIdlePlayers()
&& kick_idle_players_) {
// Let's only do this every now and then.
if (real_time - last_kick_idle_players_decrement_time_ > 1000) {
DecrementPlayerTimeOuts(real_time
- last_kick_idle_players_decrement_time_);
last_kick_idle_players_decrement_time_ = real_time;
}
} else {
// If we're not kicking, we still store the latest time (so it doesnt
// accumulate for when we start again).
last_kick_idle_players_decrement_time_ = real_time;
}
}
void HostSession::StepScene() {
// Run up our game-time timers.
sim_timers_.Run(scene()->time());
// And step.
scene()->Step();
}
void HostSession::Update(int time_advance) {
assert(InGameThread());
// We can be killed at any time, so let's keep an eye out for that.
WeakRef<HostSession> test_ref(this);
assert(test_ref.exists());
ProcessPlayerTimeOuts();
GameStream* output_stream = GetGameStream();
// Advance base time by the specified amount,
// firing all timers along the way.
millisecs_t target_base_time = base_time_ + time_advance;
while (!base_timers_.empty()
&& (base_time_ + base_timers_.GetTimeToNextExpire(base_time_)
<= target_base_time)) {
base_time_ += base_timers_.GetTimeToNextExpire(base_time_);
if (output_stream) {
output_stream->SetTime(base_time_);
}
base_timers_.Run(base_time_);
}
base_time_ = target_base_time;
if (output_stream) {
output_stream->SetTime(base_time_);
}
assert(test_ref.exists());
// Update our activities (iterate via weak-refs as this list may change under
// us at any time).
std::vector<Object::WeakRef<HostActivity> > activities =
PointersToWeakRefs(RefsToPointers(host_activities_));
for (auto&& i : activities) {
if (i.exists()) {
i->Update(time_advance);
assert(test_ref.exists());
}
}
assert(test_ref.exists());
// Periodically prune various dead refs.
if (base_time_ > next_prune_time_) {
PruneDeadMapRefs(&textures_);
PruneDeadMapRefs(&sounds_);
PruneDeadMapRefs(&models_);
PruneDeadRefs(&python_calls_);
next_prune_time_ = base_time_ + 5000;
}
assert(test_ref.exists());
}
HostSession::~HostSession() {
try {
shutting_down_ = true;
// Put the scene in shut-down mode before we start killing stuff
// (this generates warnings, suppresses messages, etc).
scene_->set_shutting_down(true);
// Clear out all python calls registered in our context
// (should wipe out refs to our session and prevent them from running
// without a valid session context).
for (auto&& i : python_calls_) {
if (i.exists()) {
i->MarkDead();
}
}
// Mark all our media dead to clear it out of our output-stream cleanly.
for (auto&& i : textures_) {
if (i.second.exists()) {
i.second->MarkDead();
}
}
for (auto&& i : models_) {
if (i.second.exists()) {
i.second->MarkDead();
}
}
for (auto&& i : sounds_) {
if (i.second.exists()) {
i.second->MarkDead();
}
}
// Clear our timers and scene; this should wipe out any remaining refs
// to our session scene.
base_timers_.Clear();
sim_timers_.Clear();
scene_.Clear();
// Kill our python session object.
{
ScopedSetContext cp(this);
session_py_obj_.Release();
}
// Kill any remaining activity data. Generally all activities should die
// when the session python object goes down, but lets clean up in case any
// didn't.
for (auto&& i : host_activities_) {
ScopedSetContext cp{Object::Ref<ContextTarget>(i)};
i.Clear();
}
// Report outstanding calls. There shouldn't be any at this point. Actually
// it turns out there's generally 1; whichever call was responsible for
// killing this activity will still be in progress.. so let's report on 2 or
// more I guess.
if (g_buildconfig.debug_build()) {
PruneDeadRefs(&python_calls_);
if (python_calls_.size() > 1) {
std::string s = "WARNING: " + std::to_string(python_calls_.size())
+ " live PythonContextCalls at shutdown for "
+ "HostSession" + " (1 call is expected):";
int count = 1;
for (auto&& i : python_calls_) {
s += ("\n " + std::to_string(count++) + ": "
+ i->GetObjectDescription());
}
Log(s);
}
}
} catch (const std::exception& e) {
Log("Exception in HostSession destructor: " + std::string(e.what()));
}
}
void HostSession::RegisterCall(PythonContextCall* call) {
assert(call);
python_calls_.emplace_back(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());
call->MarkDead();
}
}
auto HostSession::GetUnusedPlayerName(Player* p, const std::string& base_name)
-> std::string {
// Now find the first non-taken variation.
int index = 1;
std::string name_test;
while (true) {
if (index > 1) {
name_test = base_name + " " + std::to_string(index);
} else {
name_test = base_name;
}
bool name_found = false;
for (auto&& j : players_) {
if ((j->GetName() == name_test) && (j.get() != p)) {
name_found = true;
break;
}
}
if (!name_found) break;
index += 1;
}
return name_test;
}
void HostSession::DumpFullState(GameStream* out) {
// Add session-scene.
if (scene_.exists()) {
scene_->Dump(out);
}
// Dump media associated with session-scene.
for (auto&& i : textures_) {
if (Texture* t = i.second.get()) {
out->AddTexture(t);
}
}
for (auto&& i : sounds_) {
if (Sound* s = i.second.get()) {
out->AddSound(s);
}
}
for (auto&& i : models_) {
if (Model* s = i.second.get()) {
out->AddModel(s);
}
}
// Dump session-scene's nodes.
if (scene_.exists()) {
scene_->DumpNodes(out);
}
// Now let our activities dump themselves.
for (auto&& i : host_activities_) {
i->DumpFullState(out);
}
}
void HostSession::GetCorrectionMessages(
bool blend, std::vector<std::vector<uint8_t> >* messages) {
std::vector<uint8_t> message;
// Grab correction for session scene (though there shouldn't be one).
if (scene_.exists()) {
message = scene_->GetCorrectionMessage(blend);
if (message.size() > 4) {
// A correction packet of size 4 is empty; ignore it.
messages->push_back(message);
}
}
// Now do same for activity scenes.
for (auto&& i : host_activities_) {
if (HostActivity* ha = i.get()) {
if (Scene* sg = ha->scene()) {
message = sg->GetCorrectionMessage(blend);
if (message.size() > 4) {
// A correction packet of size 4 is empty; ignore it.
messages->push_back(message);
}
}
}
}
}
auto HostSession::NewTimer(TimeType timetype, TimerMedium length, bool repeat,
const Object::Ref<Runnable>& runnable) -> int {
// Make sure the runnable passed in is reference-managed already
// (we may not add an initial reference ourself).
assert(runnable->is_valid_refcounted_object());
// We currently support game and base timers.
switch (timetype) {
case TimeType::kSim:
case TimeType::kBase:
// Game and base timers are the same thing for us.
return NewTimer(length, repeat, runnable);
default:
// Gall back to default for descriptive error otherwise.
return ContextTarget::NewTimer(timetype, length, repeat, runnable);
}
}
void HostSession::DeleteTimer(TimeType timetype, int timer_id) {
switch (timetype) {
case TimeType::kSim:
case TimeType::kBase:
// Game and base timers are the same thing for us.
DeleteTimer(timer_id);
break;
default:
// Fall back to default for descriptive error otherwise.
ContextTarget::DeleteTimer(timetype, timer_id);
break;
}
}
auto HostSession::GetTime(TimeType timetype) -> millisecs_t {
switch (timetype) {
case TimeType::kSim:
case TimeType::kBase:
return scene_->time();
default:
// Fall back to default for descriptive error otherwise.
return ContextTarget::GetTime(timetype);
}
}
} // namespace ballistica

View File

@ -0,0 +1,201 @@
// Released under the MIT License. See LICENSE for details.
#include "ballistica/game/session/net_client_session.h"
#include "ballistica/app/app_globals.h"
#include "ballistica/game/connection/connection_to_host.h"
#include "ballistica/graphics/graphics.h"
#include "ballistica/graphics/net_graph.h"
#include "ballistica/media/media_server.h"
namespace ballistica {
NetClientSession::NetClientSession() {
// Sanity check: we should only ever be writing one replay at once.
if (g_app_globals->replay_open) {
Log("ERROR: g_replay_open true at netclient start; shouldn't happen.");
}
assert(g_media_server);
g_media_server->PushBeginWriteReplayCall();
writing_replay_ = true;
g_app_globals->replay_open = true;
}
NetClientSession::~NetClientSession() {
if (writing_replay_) {
// Sanity check: we should only ever be writing one replay at once.
if (!g_app_globals->replay_open) {
Log("ERROR: g_replay_open false at net-client close; shouldn't happen.");
}
g_app_globals->replay_open = false;
assert(g_media_server);
g_media_server->PushEndWriteReplayCall();
writing_replay_ = false;
}
}
void NetClientSession::SetConnectionToHost(ConnectionToHost* c) {
connection_to_host_ = c;
}
void NetClientSession::OnCommandBufferUnderrun() {
// We currently don't do anything here; we want to just power
// through hitches and keep aiming for our target time.
// (though perhaps we could take note here for analytics purposes).
// printf("Underrun at %d\n", GetRealTime());
// fflush(stdout);
}
void NetClientSession::Update(int time_advance) {
if (shutting_down()) {
return;
}
// Now do standard step.
ClientSession::Update(time_advance);
// And update our timing to try and ensure we don't run out of buffer.
UpdateBuffering();
}
auto NetClientSession::GetBucketNum() -> int {
return (delay_sample_counter_ / g_app_globals->delay_bucket_samples)
% static_cast<int>(buckets_.size());
}
auto NetClientSession::UpdateBuffering() -> void {
// Keep record of the most and least amount of time we've had buffered
// recently, and slow down/speed up a bit based on that.
{
// Change bucket every `g_delay_samples` samples.
int bucketnum{GetBucketNum()};
int bucket_iteration =
delay_sample_counter_ % g_app_globals->delay_bucket_samples;
delay_sample_counter_++;
SampleBucket& bucket{buckets_[bucketnum]};
if (bucket_iteration == 0) {
bucket.max_delay_from_projection = 0;
}
// After the last sample in each bucket, update our smoothed values with
// the full sample set in the bucket.
if (bucket_iteration == g_app_globals->delay_bucket_samples - 1) {
float smoothing = 0.7f;
last_bucket_max_delay_ =
static_cast<float>(bucket.max_delay_from_projection);
max_delay_smoothed_ =
smoothing * max_delay_smoothed_
+ (1.0f - smoothing)
* static_cast<float>(bucket.max_delay_from_projection);
}
auto now = GetRealTime();
// We want target-base-time to wind up at our projected time minus some
// safety offset to account for buffering fluctuations.
// We might want to consider exposing this value or calculate it in a smart
// way based on conditions. 0.0 gives us lowest latency possible but makes
// lag spikes very noticeable. 1.0 should avoid most lag spikes. Higher
// values even moreso at the price of latency;
float safety_amt{1.0};
float to_ideal_offset =
static_cast<float>(ProjectedBaseTime(now) - target_base_time())
- safety_amt * max_delay_smoothed_;
// How aggressively we throttle the game speed up or down to accommodate lag
// spikes.
float speed_change_aggression{0.004f};
float new_consume_rate = std::min(
10.0f,
std::max(0.5f, 1.0f + speed_change_aggression * to_ideal_offset));
set_consume_rate(new_consume_rate);
if (g_graphics->network_debug_info_display_enabled()) {
if (NetGraph* graph =
g_graphics->GetDebugGraph("1: packet delay", false)) {
graph->AddSample(now, current_delay_);
}
if (NetGraph* graph =
g_graphics->GetDebugGraph("2: max delay bucketed", false)) {
graph->AddSample(now, last_bucket_max_delay_);
}
if (NetGraph* graph =
g_graphics->GetDebugGraph("3: filtered delay", false)) {
graph->AddSample(now, max_delay_smoothed_);
}
if (NetGraph* graph = g_graphics->GetDebugGraph("4: run rate", false)) {
graph->AddSample(now, new_consume_rate);
}
if (NetGraph* graph =
g_graphics->GetDebugGraph("5: time buffered", true)) {
graph->AddSample(now, base_time_buffered());
}
}
}
}
auto NetClientSession::OnReset(bool rewind) -> void {
// Resets should never happen for us after we start, right?...
base_time_received_ = 0;
last_base_time_receive_time_ = 0;
leading_base_time_received_ = 0;
leading_base_time_receive_time_ = 0;
ClientSession::OnReset(rewind);
}
auto NetClientSession::OnBaseTimeStepAdded(int step) -> void {
auto now = GetRealTime();
millisecs_t new_base_time_received = base_time_received_ + step;
// We want to be able to project as close as possible to what the
// current base time is based on when we receive steps (regardless of lag
// spikes). To do this, we only factor in steps we receive if their times are
// newer than what we get projecting forward from the last one.
bool use;
if (leading_base_time_receive_time_ == 0) {
use = true;
} else {
millisecs_t projected = ProjectedBaseTime(now);
// Hopefully we'll keep refreshing our leading value consistently
// but force the issue if it becomes too old.
use = (new_base_time_received >= projected
|| (now - leading_base_time_receive_time_ > 250));
// Keep track of the biggest recent delays we get compared to the projected
// time. (we can use this when calcing how much to buffer to avoid stutter).
if (new_base_time_received < projected) {
auto& bucket{buckets_[GetBucketNum()]};
current_delay_ = bucket.max_delay_from_projection =
std::max(bucket.max_delay_from_projection,
static_cast<int>(projected - new_base_time_received));
} else {
current_delay_ = 0.0f;
}
}
base_time_received_ = new_base_time_received;
last_base_time_receive_time_ = now;
if (use) {
leading_base_time_received_ = new_base_time_received;
leading_base_time_receive_time_ = now;
}
}
void NetClientSession::HandleSessionMessage(
const std::vector<uint8_t>& message) {
// Do the standard thing, but also write this message straight to our replay
// stream if we have one.
ClientSession::HandleSessionMessage(message);
if (writing_replay_) {
assert(g_media_server);
g_media_server->PushAddMessageToReplayCall(message);
}
}
} // namespace ballistica

View File

@ -0,0 +1,257 @@
// Released under the MIT License. See LICENSE for details.
#include "ballistica/game/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/math/vector3f.h"
#include "ballistica/networking/networking.h"
#include "ballistica/platform/platform.h"
#include "ballistica/scene/scene.h"
namespace ballistica {
auto ReplayClientSession::GetActualTimeAdvance(int advance_in) -> int {
return static_cast<int>(
round(advance_in * pow(2.0f, g_game->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);
// go ahead and just do a reset here, which will get things going..
Reset(true);
}
ReplayClientSession::~ReplayClientSession() {
// we no longer are responsible for feeding clients to this device..
g_game->connections()->UnregisterClientController(this);
if (file_) {
fclose(file_);
file_ = nullptr;
}
}
void ReplayClientSession::OnCommandBufferUnderrun() { ResetTargetBaseTime(); }
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()"
" got duplicate connection");
return;
}
}
for (ConnectionToClient* i : connections_to_clients_ignored_) {
if (i == c) {
Log("Error: ReplayClientSession::OnClientConnected()"
" got duplicate connection");
return;
}
}
// if we've sent *any* commands out to clients so far, we currently have to
// ignore new connections (need to rebuild state to match current session
// state)
{
connections_to_clients_.push_back(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);
// go ahead and dump our full state..
DumpFullState(&out);
// grab the message that's been built up..
// if its not empty, send it to the client.
std::vector<uint8_t> out_message = out.GetOutMessage();
if (!out_message.empty()) {
c->SendReliableMessage(out_message);
}
// also send a correction packet to sync up all our dynamics
// (technically could do this *just* for the new client)
{
std::vector<std::vector<uint8_t> > messages;
bool blend = false;
GetCorrectionMessages(blend, &messages);
// FIXME - have to send reliably at the moment since these will most
// likely be bigger than our unreliable packet limit.. :-(
for (auto&& i : messages) {
for (auto&& j : connections_to_clients_) {
j->SendReliableMessage(i);
}
}
}
}
}
void ReplayClientSession::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++) {
if (*i == c) {
connections_to_clients_.erase(i);
return;
}
}
for (auto i = connections_to_clients_ignored_.begin();
i != connections_to_clients_ignored_.end(); i++) {
if (*i == c) {
connections_to_clients_ignored_.erase(i);
return;
}
}
Log("Error: ReplayClientSession::OnClientDisconnected()"
" called for connection not on lists");
}
void ReplayClientSession::FetchMessages() {
if (!file_ || shutting_down()) {
return;
}
// If we have no messages left, read from the file until we get some.
while (commands().empty()) {
std::vector<uint8_t> buffer;
uint8_t len8;
uint32_t len32;
// Read the size of the message.
// the first byte represents the actual size if the value is < 254
// if it is 254, the 2 bytes after it represent size
// if it is 255, the 4 bytes after it represent size
if (fread(&len8, 1, 1, file_) != 1) {
// So they know to be done when they reach the end of the command list
// (instead of just waiting for more commands)
add_end_of_file_command();
fclose(file_);
file_ = nullptr;
return;
}
if (len8 < 254) {
len32 = len8;
} else {
// Pull 16 bit len.
if (len8 == 254) {
uint16_t len16;
if (fread(&len16, 2, 1, file_) != 1) {
// so they know to be done when they reach the end of the command list
// (instead of just waiting for more commands)
add_end_of_file_command();
fclose(file_);
file_ = nullptr;
return;
}
assert(len16 >= 254);
len32 = len16;
} else {
// Pull 32 bit len.
if (fread(&len32, 4, 1, file_) != 1) {
// so they know to be done when they reach the end of the command list
// (instead of just waiting for more commands)
add_end_of_file_command();
fclose(file_);
file_ = nullptr;
return;
}
assert(len32 > 65535);
}
}
// Read and decompress the actual message.
BA_PRECONDITION(len32 > 0);
buffer.resize(len32);
if (fread(&(buffer[0]), len32, 1, file_) != 1) {
add_end_of_file_command();
fclose(file_);
file_ = nullptr;
return;
}
std::vector<uint8_t> data_decompressed =
g_utils->huffman()->decompress(buffer);
HandleSessionMessage(data_decompressed);
// Also send it to all client-connections we're attached to.
// NOTE: We currently are sending everything as reliable; we can maybe do
// unreliable for certain type of messages. Though perhaps when passing
// around replays maybe its best to keep everything intact.
have_sent_client_message_ = true;
for (auto&& i : connections_to_clients_) {
i->SendReliableMessage(data_decompressed);
}
message_fetch_num_++;
}
}
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});
if (file_) {
fclose(file_);
file_ = nullptr;
}
ClientSession::Error(description);
}
void ReplayClientSession::OnReset(bool rewind) {
// Handles base resetting.
ClientSession::OnReset(rewind);
// If we've got any clients attached to us, tell them to reset as well.
for (auto&& i : connections_to_clients_) {
i->SendReliableMessage(std::vector<uint8_t>(1, BA_MESSAGE_SESSION_RESET));
}
// If rewinding, pop back to the start of our file.
if (rewind) {
if (file_) {
fclose(file_);
file_ = nullptr;
}
file_ = g_platform->FOpen(file_name_.c_str(), "rb");
if (!file_) {
Error("can't open file for reading");
return;
}
// Read file ID and version to make sure we support this file.
uint32_t file_id;
if ((fread(&file_id, sizeof(file_id), 1, file_) != 1)) {
Error("error reading file_id");
return;
}
if (file_id != kBrpFileID) {
Error("incorrect file_id");
return;
}
// Make sure its a compatible protocol version.
uint16_t version;
if (fread(&version, sizeof(version), 1, file_) != 1) {
Error("error reading version");
return;
}
if (version > kProtocolVersion || version < kProtocolVersionMin) {
ScreenMessage(g_game->GetResourceString("replayVersionErrorText"),
{1, 0, 0});
End();
return;
}
}
}
} // namespace ballistica

View File

@ -0,0 +1,37 @@
// Released under the MIT License. See LICENSE for details.
#include "ballistica/game/session/session.h"
#include "ballistica/app/app_globals.h"
#include "ballistica/game/game.h"
namespace ballistica {
Session::Session() {
g_app_globals->session_count++;
// New sessions immediately become foreground.
g_game->SetForegroundSession(this);
}
Session::~Session() { g_app_globals->session_count--; }
void Session::Update(int time_advance) {}
auto Session::GetForegroundContext() -> Context { return Context(); }
void Session::Draw(FrameDef*) {}
void Session::ScreenSizeChanged() {}
void Session::LanguageChanged() {}
void Session::GraphicsQualityChanged(GraphicsQuality q) {}
void Session::DebugSpeedMultChanged() {}
void Session::DumpFullState(GameStream* out) {
Log("Session::DumpFullState() being called; shouldn't happen.");
}
} // namespace ballistica