mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-02-07 16:13:23 +08:00
opening more internal sources
This commit is contained in:
parent
dcf8671f4a
commit
20745818b2
@ -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/__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",
|
"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",
|
"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/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/3d/8c/9689aa6ff3fa826fc3f524b3115f",
|
"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/00/04/1ea1f6833f131458cbc8e7039591",
|
"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/d9/f7/c87406c9229cbe5aa9439e3562c5",
|
"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/66/1e/53122c4f96bca326aaa42d5cb98b",
|
"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/6b/28/ee8a01e3155e05c5c970ace23107",
|
"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/89/5e/fbf702ecbd5376efe63720632883",
|
"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/c2/c4/958ba681823bcad105bc51d6b3ac",
|
"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/95/55/3188c8463c50ce1cb7182788d0b8",
|
"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/70/10/b99df359d806c3f4fc3c91685749",
|
"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/3e/40/9778e11391ca3832caf982a03832",
|
"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/7a/0c/756391725d7bbbab568b8d946753",
|
"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/b9/fe/e0f9747f9f03647d1becb31ac08d",
|
"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/ee/d1/b0e387dfdbe8ac032456d4124bdf",
|
"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/10/db/85cb3b50f58df978a42f5b199c0d",
|
"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/3f/77/d66d99ba56643c20493a56cb3dba",
|
"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/2b/23/eb989bada6166e03bf5756717194",
|
"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/73/26/4670e939bfef0ad417f56d532fc0",
|
"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/04/73/6951027e00d62310e59eca1737c3",
|
"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/1d/31/7698b0ee7abe6090d62295df170e",
|
"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/02/ca/38de2750e629dd5f193a7c00a7d9",
|
"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/7a/b3/e30adb7116efb17abe63ce70def8",
|
"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/e9/c3/191d440460fbb95eb8c131081c4a",
|
"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/ed/06/e6d9a361c5df654e5db27bec0999",
|
"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/a8/a5/70d83d086f2658e42c5d1e11aa53",
|
"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/b2/09/4105d848598a9cb1695f8be31696",
|
"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/3f/57/1ec9697d93ade3ef0a29d8ad2008",
|
"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/e6/54/c828ae0046a1023f28a02edffc4f",
|
"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/92/ef/bbe7906558281bb1a57a9ebe4ad1",
|
"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/62/ea/d5de2257b62b625308df5760ce32",
|
"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/e5/a5/6304f685116de2c62c9d4b445dab",
|
"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/a0/86/1a7de5eb337f446ef952a0572214",
|
"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/cb/f1/f26f8c525637c9a29b511a6ffc2f",
|
"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/49/ca/11a87c0fecc6220b9756caa2a86e",
|
"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/59/65/0b8fb1aeb2ae528f31d1455bb180",
|
"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/9f/88/c4ec00dff29865d4899395067b03",
|
"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/6c/05/f66f58d20fab5d0e658961ac65b5",
|
"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/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/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/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",
|
"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/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"
|
"src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/d3/db/e73d4dcf1280d5f677c3cf8b47c3"
|
||||||
|
|||||||
@ -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.
|
- 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.
|
- 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.
|
- Newly discovered plugins are now activated immediately instead of requiring a restart.
|
||||||
|
|||||||
@ -300,6 +300,7 @@ add_executable(ballisticacore
|
|||||||
${BA_SRC_ROOT}/ballistica/dynamics/part.h
|
${BA_SRC_ROOT}/ballistica/dynamics/part.h
|
||||||
${BA_SRC_ROOT}/ballistica/dynamics/rigid_body.cc
|
${BA_SRC_ROOT}/ballistica/dynamics/rigid_body.cc
|
||||||
${BA_SRC_ROOT}/ballistica/dynamics/rigid_body.h
|
${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/account.h
|
||||||
${BA_SRC_ROOT}/ballistica/game/client_controller_interface.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.cc
|
||||||
@ -317,6 +318,7 @@ add_executable(ballisticacore
|
|||||||
${BA_SRC_ROOT}/ballistica/game/friend_score_set.h
|
${BA_SRC_ROOT}/ballistica/game/friend_score_set.h
|
||||||
${BA_SRC_ROOT}/ballistica/game/game.cc
|
${BA_SRC_ROOT}/ballistica/game/game.cc
|
||||||
${BA_SRC_ROOT}/ballistica/game/game.h
|
${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/game_stream.h
|
||||||
${BA_SRC_ROOT}/ballistica/game/host_activity.cc
|
${BA_SRC_ROOT}/ballistica/game/host_activity.cc
|
||||||
${BA_SRC_ROOT}/ballistica/game/host_activity.h
|
${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.cc
|
||||||
${BA_SRC_ROOT}/ballistica/game/player_spec.h
|
${BA_SRC_ROOT}/ballistica/game/player_spec.h
|
||||||
${BA_SRC_ROOT}/ballistica/game/score_to_beat.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/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/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/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/replay_client_session.h
|
||||||
|
${BA_SRC_ROOT}/ballistica/game/session/session.cc
|
||||||
${BA_SRC_ROOT}/ballistica/game/session/session.h
|
${BA_SRC_ROOT}/ballistica/game/session/session.h
|
||||||
${BA_SRC_ROOT}/ballistica/generic/base64.cc
|
${BA_SRC_ROOT}/ballistica/generic/base64.cc
|
||||||
${BA_SRC_ROOT}/ballistica/generic/base64.h
|
${BA_SRC_ROOT}/ballistica/generic/base64.h
|
||||||
|
|||||||
@ -291,6 +291,7 @@
|
|||||||
<ClInclude Include="..\..\src\ballistica\dynamics\part.h" />
|
<ClInclude Include="..\..\src\ballistica\dynamics\part.h" />
|
||||||
<ClCompile Include="..\..\src\ballistica\dynamics\rigid_body.cc" />
|
<ClCompile Include="..\..\src\ballistica\dynamics\rigid_body.cc" />
|
||||||
<ClInclude Include="..\..\src\ballistica\dynamics\rigid_body.h" />
|
<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\account.h" />
|
||||||
<ClInclude Include="..\..\src\ballistica\game\client_controller_interface.h" />
|
<ClInclude Include="..\..\src\ballistica\game\client_controller_interface.h" />
|
||||||
<ClCompile Include="..\..\src\ballistica\game\connection\connection.cc" />
|
<ClCompile Include="..\..\src\ballistica\game\connection\connection.cc" />
|
||||||
@ -308,6 +309,7 @@
|
|||||||
<ClInclude Include="..\..\src\ballistica\game\friend_score_set.h" />
|
<ClInclude Include="..\..\src\ballistica\game\friend_score_set.h" />
|
||||||
<ClCompile Include="..\..\src\ballistica\game\game.cc" />
|
<ClCompile Include="..\..\src\ballistica\game\game.cc" />
|
||||||
<ClInclude Include="..\..\src\ballistica\game\game.h" />
|
<ClInclude Include="..\..\src\ballistica\game\game.h" />
|
||||||
|
<ClCompile Include="..\..\src\ballistica\game\game_stream.cc" />
|
||||||
<ClInclude Include="..\..\src\ballistica\game\game_stream.h" />
|
<ClInclude Include="..\..\src\ballistica\game\game_stream.h" />
|
||||||
<ClCompile Include="..\..\src\ballistica\game\host_activity.cc" />
|
<ClCompile Include="..\..\src\ballistica\game\host_activity.cc" />
|
||||||
<ClInclude Include="..\..\src\ballistica\game\host_activity.h" />
|
<ClInclude Include="..\..\src\ballistica\game\host_activity.h" />
|
||||||
@ -316,10 +318,15 @@
|
|||||||
<ClCompile Include="..\..\src\ballistica\game\player_spec.cc" />
|
<ClCompile Include="..\..\src\ballistica\game\player_spec.cc" />
|
||||||
<ClInclude Include="..\..\src\ballistica\game\player_spec.h" />
|
<ClInclude Include="..\..\src\ballistica\game\player_spec.h" />
|
||||||
<ClInclude Include="..\..\src\ballistica\game\score_to_beat.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" />
|
<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" />
|
<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" />
|
<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" />
|
<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" />
|
<ClInclude Include="..\..\src\ballistica\game\session\session.h" />
|
||||||
<ClCompile Include="..\..\src\ballistica\generic\base64.cc" />
|
<ClCompile Include="..\..\src\ballistica\generic\base64.cc" />
|
||||||
<ClInclude Include="..\..\src\ballistica\generic\base64.h" />
|
<ClInclude Include="..\..\src\ballistica\generic\base64.h" />
|
||||||
|
|||||||
@ -307,6 +307,9 @@
|
|||||||
<ClInclude Include="..\..\src\ballistica\dynamics\rigid_body.h">
|
<ClInclude Include="..\..\src\ballistica\dynamics\rigid_body.h">
|
||||||
<Filter>ballistica\dynamics</Filter>
|
<Filter>ballistica\dynamics</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClCompile Include="..\..\src\ballistica\game\account.cc">
|
||||||
|
<Filter>ballistica\game</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClInclude Include="..\..\src\ballistica\game\account.h">
|
<ClInclude Include="..\..\src\ballistica\game\account.h">
|
||||||
<Filter>ballistica\game</Filter>
|
<Filter>ballistica\game</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@ -358,6 +361,9 @@
|
|||||||
<ClInclude Include="..\..\src\ballistica\game\game.h">
|
<ClInclude Include="..\..\src\ballistica\game\game.h">
|
||||||
<Filter>ballistica\game</Filter>
|
<Filter>ballistica\game</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClCompile Include="..\..\src\ballistica\game\game_stream.cc">
|
||||||
|
<Filter>ballistica\game</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClInclude Include="..\..\src\ballistica\game\game_stream.h">
|
<ClInclude Include="..\..\src\ballistica\game\game_stream.h">
|
||||||
<Filter>ballistica\game</Filter>
|
<Filter>ballistica\game</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@ -382,18 +388,33 @@
|
|||||||
<ClInclude Include="..\..\src\ballistica\game\score_to_beat.h">
|
<ClInclude Include="..\..\src\ballistica\game\score_to_beat.h">
|
||||||
<Filter>ballistica\game</Filter>
|
<Filter>ballistica\game</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClInclude Include="..\..\src\ballistica\game\session\client_session.h">
|
||||||
<Filter>ballistica\game\session</Filter>
|
<Filter>ballistica\game\session</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClInclude Include="..\..\src\ballistica\game\session\host_session.h">
|
||||||
<Filter>ballistica\game\session</Filter>
|
<Filter>ballistica\game\session</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClInclude Include="..\..\src\ballistica\game\session\net_client_session.h">
|
||||||
<Filter>ballistica\game\session</Filter>
|
<Filter>ballistica\game\session</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClInclude Include="..\..\src\ballistica\game\session\replay_client_session.h">
|
||||||
<Filter>ballistica\game\session</Filter>
|
<Filter>ballistica\game\session</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClCompile Include="..\..\src\ballistica\game\session\session.cc">
|
||||||
|
<Filter>ballistica\game\session</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClInclude Include="..\..\src\ballistica\game\session\session.h">
|
<ClInclude Include="..\..\src\ballistica\game\session\session.h">
|
||||||
<Filter>ballistica\game\session</Filter>
|
<Filter>ballistica\game\session</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|||||||
@ -286,6 +286,7 @@
|
|||||||
<ClInclude Include="..\..\src\ballistica\dynamics\part.h" />
|
<ClInclude Include="..\..\src\ballistica\dynamics\part.h" />
|
||||||
<ClCompile Include="..\..\src\ballistica\dynamics\rigid_body.cc" />
|
<ClCompile Include="..\..\src\ballistica\dynamics\rigid_body.cc" />
|
||||||
<ClInclude Include="..\..\src\ballistica\dynamics\rigid_body.h" />
|
<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\account.h" />
|
||||||
<ClInclude Include="..\..\src\ballistica\game\client_controller_interface.h" />
|
<ClInclude Include="..\..\src\ballistica\game\client_controller_interface.h" />
|
||||||
<ClCompile Include="..\..\src\ballistica\game\connection\connection.cc" />
|
<ClCompile Include="..\..\src\ballistica\game\connection\connection.cc" />
|
||||||
@ -303,6 +304,7 @@
|
|||||||
<ClInclude Include="..\..\src\ballistica\game\friend_score_set.h" />
|
<ClInclude Include="..\..\src\ballistica\game\friend_score_set.h" />
|
||||||
<ClCompile Include="..\..\src\ballistica\game\game.cc" />
|
<ClCompile Include="..\..\src\ballistica\game\game.cc" />
|
||||||
<ClInclude Include="..\..\src\ballistica\game\game.h" />
|
<ClInclude Include="..\..\src\ballistica\game\game.h" />
|
||||||
|
<ClCompile Include="..\..\src\ballistica\game\game_stream.cc" />
|
||||||
<ClInclude Include="..\..\src\ballistica\game\game_stream.h" />
|
<ClInclude Include="..\..\src\ballistica\game\game_stream.h" />
|
||||||
<ClCompile Include="..\..\src\ballistica\game\host_activity.cc" />
|
<ClCompile Include="..\..\src\ballistica\game\host_activity.cc" />
|
||||||
<ClInclude Include="..\..\src\ballistica\game\host_activity.h" />
|
<ClInclude Include="..\..\src\ballistica\game\host_activity.h" />
|
||||||
@ -311,10 +313,15 @@
|
|||||||
<ClCompile Include="..\..\src\ballistica\game\player_spec.cc" />
|
<ClCompile Include="..\..\src\ballistica\game\player_spec.cc" />
|
||||||
<ClInclude Include="..\..\src\ballistica\game\player_spec.h" />
|
<ClInclude Include="..\..\src\ballistica\game\player_spec.h" />
|
||||||
<ClInclude Include="..\..\src\ballistica\game\score_to_beat.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" />
|
<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" />
|
<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" />
|
<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" />
|
<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" />
|
<ClInclude Include="..\..\src\ballistica\game\session\session.h" />
|
||||||
<ClCompile Include="..\..\src\ballistica\generic\base64.cc" />
|
<ClCompile Include="..\..\src\ballistica\generic\base64.cc" />
|
||||||
<ClInclude Include="..\..\src\ballistica\generic\base64.h" />
|
<ClInclude Include="..\..\src\ballistica\generic\base64.h" />
|
||||||
|
|||||||
@ -307,6 +307,9 @@
|
|||||||
<ClInclude Include="..\..\src\ballistica\dynamics\rigid_body.h">
|
<ClInclude Include="..\..\src\ballistica\dynamics\rigid_body.h">
|
||||||
<Filter>ballistica\dynamics</Filter>
|
<Filter>ballistica\dynamics</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClCompile Include="..\..\src\ballistica\game\account.cc">
|
||||||
|
<Filter>ballistica\game</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClInclude Include="..\..\src\ballistica\game\account.h">
|
<ClInclude Include="..\..\src\ballistica\game\account.h">
|
||||||
<Filter>ballistica\game</Filter>
|
<Filter>ballistica\game</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@ -358,6 +361,9 @@
|
|||||||
<ClInclude Include="..\..\src\ballistica\game\game.h">
|
<ClInclude Include="..\..\src\ballistica\game\game.h">
|
||||||
<Filter>ballistica\game</Filter>
|
<Filter>ballistica\game</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClCompile Include="..\..\src\ballistica\game\game_stream.cc">
|
||||||
|
<Filter>ballistica\game</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClInclude Include="..\..\src\ballistica\game\game_stream.h">
|
<ClInclude Include="..\..\src\ballistica\game\game_stream.h">
|
||||||
<Filter>ballistica\game</Filter>
|
<Filter>ballistica\game</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
@ -382,18 +388,33 @@
|
|||||||
<ClInclude Include="..\..\src\ballistica\game\score_to_beat.h">
|
<ClInclude Include="..\..\src\ballistica\game\score_to_beat.h">
|
||||||
<Filter>ballistica\game</Filter>
|
<Filter>ballistica\game</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClInclude Include="..\..\src\ballistica\game\session\client_session.h">
|
||||||
<Filter>ballistica\game\session</Filter>
|
<Filter>ballistica\game\session</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClInclude Include="..\..\src\ballistica\game\session\host_session.h">
|
||||||
<Filter>ballistica\game\session</Filter>
|
<Filter>ballistica\game\session</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClInclude Include="..\..\src\ballistica\game\session\net_client_session.h">
|
||||||
<Filter>ballistica\game\session</Filter>
|
<Filter>ballistica\game\session</Filter>
|
||||||
</ClInclude>
|
</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">
|
<ClInclude Include="..\..\src\ballistica\game\session\replay_client_session.h">
|
||||||
<Filter>ballistica\game\session</Filter>
|
<Filter>ballistica\game\session</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClCompile Include="..\..\src\ballistica\game\session\session.cc">
|
||||||
|
<Filter>ballistica\game\session</Filter>
|
||||||
|
</ClCompile>
|
||||||
<ClInclude Include="..\..\src\ballistica\game\session\session.h">
|
<ClInclude Include="..\..\src\ballistica\game\session\session.h">
|
||||||
<Filter>ballistica\game\session</Filter>
|
<Filter>ballistica\game\session</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|||||||
@ -21,7 +21,7 @@
|
|||||||
namespace ballistica {
|
namespace ballistica {
|
||||||
|
|
||||||
// These are set automatically via script; don't modify them here.
|
// 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";
|
const char* kAppVersion = "1.7.6";
|
||||||
|
|
||||||
// Our standalone globals.
|
// Our standalone globals.
|
||||||
|
|||||||
207
src/ballistica/game/account.cc
Normal file
207
src/ballistica/game/account.cc
Normal 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
|
||||||
1249
src/ballistica/game/game_stream.cc
Normal file
1249
src/ballistica/game/game_stream.cc
Normal file
File diff suppressed because it is too large
Load Diff
1122
src/ballistica/game/session/client_session.cc
Normal file
1122
src/ballistica/game/session/client_session.cc
Normal file
File diff suppressed because it is too large
Load Diff
762
src/ballistica/game/session/host_session.cc
Normal file
762
src/ballistica/game/session/host_session.cc
Normal 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
|
||||||
201
src/ballistica/game/session/net_client_session.cc
Normal file
201
src/ballistica/game/session/net_client_session.cc
Normal 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
|
||||||
257
src/ballistica/game/session/replay_client_session.cc
Normal file
257
src/ballistica/game/session/replay_client_session.cc
Normal 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
|
||||||
37
src/ballistica/game/session/session.cc
Normal file
37
src/ballistica/game/session/session.cc
Normal 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
|
||||||
Loading…
x
Reference in New Issue
Block a user