mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-02-07 16:13:23 +08:00
various fixes and polishing
This commit is contained in:
parent
d775eefdde
commit
baa09a775d
74
.efrocachemap
generated
74
.efrocachemap
generated
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"ballisticakit-windows/Generic/BallisticaKit.ico": "be1b956dcd7f7a261b1afe5bce2a0336",
|
"ballisticakit-windows/Generic/BallisticaKit.ico": "6f33e74cb282f070871413f092983fcd",
|
||||||
"build/assets/ba_data/audio/achievement.ogg": "079a366ce183b25a63550ef7072af605",
|
"build/assets/ba_data/audio/achievement.ogg": "079a366ce183b25a63550ef7072af605",
|
||||||
"build/assets/ba_data/audio/actionHero1.ogg": "f0f986f268f036a5ac2f940e07f2f27e",
|
"build/assets/ba_data/audio/actionHero1.ogg": "f0f986f268f036a5ac2f940e07f2f27e",
|
||||||
"build/assets/ba_data/audio/actionHero2.ogg": "204a6735dc655f0975cf8308b585f2fd",
|
"build/assets/ba_data/audio/actionHero2.ogg": "204a6735dc655f0975cf8308b585f2fd",
|
||||||
@ -4056,50 +4056,50 @@
|
|||||||
"build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1",
|
"build/assets/windows/Win32/ucrtbased.dll": "2def5335207d41b21b9823f6805997f1",
|
||||||
"build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
|
"build/assets/windows/Win32/vc_redist.x86.exe": "b08a55e2e77623fe657bea24f223a3ae",
|
||||||
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
|
"build/assets/windows/Win32/vcruntime140d.dll": "865b2af4d1e26a1a8073c89acb06e599",
|
||||||
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "4129fd84c3c64b770c4343d347bff97a",
|
"build/prefab/full/linux_arm64_gui/debug/ballisticakit": "af600306cf8085f909e40a7d2129a73b",
|
||||||
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "76b293b2d942716c2606c56e13483e66",
|
"build/prefab/full/linux_arm64_gui/release/ballisticakit": "54f7cdbdbc16601ff1f841e48cecf05c",
|
||||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "9cc2a5d464da1e0822ecf5493ac28b64",
|
"build/prefab/full/linux_arm64_server/debug/dist/ballisticakit_headless": "cc6387a5d3a8f36b7bf667f9b7e719df",
|
||||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "40e605e595f00286805cf15ffc096813",
|
"build/prefab/full/linux_arm64_server/release/dist/ballisticakit_headless": "0de15cf59051ba17ee91e3d3ac25be93",
|
||||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "bdc47e8dd94bcfc2a7b8268ea366f9b5",
|
"build/prefab/full/linux_x86_64_gui/debug/ballisticakit": "345c4646377145cf88b30fcba122f42b",
|
||||||
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "6b57b2de2d4aefcb3a5f7df6cef53a9d",
|
"build/prefab/full/linux_x86_64_gui/release/ballisticakit": "4dae703893e19bed1e8a16029a646104",
|
||||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "34c65f2a9f280420387af135d5bc6a2d",
|
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticakit_headless": "573adb2913309a6c375a6d3b92a20208",
|
||||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "79e4d857fbd0871940c7fd6985d11af1",
|
"build/prefab/full/linux_x86_64_server/release/dist/ballisticakit_headless": "3db3c7e41182fd05f5f9731fee3002d0",
|
||||||
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "ce179db11df98c7dc53bd2fd2a710909",
|
"build/prefab/full/mac_arm64_gui/debug/ballisticakit": "1e3e5ee34645928ec3bdc3c52de98839",
|
||||||
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "d47c86f2654185ab0690949c3b8f2913",
|
"build/prefab/full/mac_arm64_gui/release/ballisticakit": "41354efa8cdbb83a3b2e4cd7fce6b308",
|
||||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "a33fb01bdbeb7be34046d7b64673991c",
|
"build/prefab/full/mac_arm64_server/debug/dist/ballisticakit_headless": "a304c1a0d2d2f5bdbc31a27ec899a95b",
|
||||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "5f4cb90415aed9415231e95394384d2a",
|
"build/prefab/full/mac_arm64_server/release/dist/ballisticakit_headless": "6b236e3246ded0460fcc7ea50fc51b36",
|
||||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "26540ab81b0ad717818e077efcb9029d",
|
"build/prefab/full/mac_x86_64_gui/debug/ballisticakit": "e26b4e3b5e2bca5606d2ac674fcc7496",
|
||||||
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "11fc4a0cdf83c362b2f5c0c62a53014e",
|
"build/prefab/full/mac_x86_64_gui/release/ballisticakit": "ae954a12775213c64d45b535b289ebff",
|
||||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "ba10cfebc435f211be18dbdc7416097d",
|
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticakit_headless": "960c3e2fd9c3e19de75fd92546447890",
|
||||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "551b8dba96bfc65c5166cde6bec37539",
|
"build/prefab/full/mac_x86_64_server/release/dist/ballisticakit_headless": "e0c05531b48ec5e36da10783280957fe",
|
||||||
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "bc18f0765c9d96731a1b1a7acf5151db",
|
"build/prefab/full/windows_x86_gui/debug/BallisticaKit.exe": "e1c137fdefcf34a642c0962999973eff",
|
||||||
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "1f1a8227190894db122fb43691371a92",
|
"build/prefab/full/windows_x86_gui/release/BallisticaKit.exe": "bdf75b68e6b0e1c8dcfa76ac04306fbb",
|
||||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "9d546413ee365044d77e0e9c39ed77bb",
|
"build/prefab/full/windows_x86_server/debug/dist/BallisticaKitHeadless.exe": "5f01ab596c3d389c95761db531b1766b",
|
||||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "470b427c4e51254538291ac56298c212",
|
"build/prefab/full/windows_x86_server/release/dist/BallisticaKitHeadless.exe": "4826ca9c757dbf38749710bc94844aca",
|
||||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "a61cf6ac9afd43081df7f63ff81f4036",
|
"build/prefab/lib/linux_arm64_gui/debug/libballisticaplus.a": "20cc128ff9d44f9d74e4301c6d49f48f",
|
||||||
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "f93bc8f98ee31f39b54ab46264eccb22",
|
"build/prefab/lib/linux_arm64_gui/release/libballisticaplus.a": "f93bc8f98ee31f39b54ab46264eccb22",
|
||||||
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "a61cf6ac9afd43081df7f63ff81f4036",
|
"build/prefab/lib/linux_arm64_server/debug/libballisticaplus.a": "20cc128ff9d44f9d74e4301c6d49f48f",
|
||||||
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "f93bc8f98ee31f39b54ab46264eccb22",
|
"build/prefab/lib/linux_arm64_server/release/libballisticaplus.a": "f93bc8f98ee31f39b54ab46264eccb22",
|
||||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "117f4fc8b60372763ecae00d332947a8",
|
"build/prefab/lib/linux_x86_64_gui/debug/libballisticaplus.a": "1565f3b227843827d692cb3ef65847b6",
|
||||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "d8d74c6c40db43054ccc7d27920cfbfe",
|
"build/prefab/lib/linux_x86_64_gui/release/libballisticaplus.a": "d8d74c6c40db43054ccc7d27920cfbfe",
|
||||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "117f4fc8b60372763ecae00d332947a8",
|
"build/prefab/lib/linux_x86_64_server/debug/libballisticaplus.a": "1565f3b227843827d692cb3ef65847b6",
|
||||||
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "d8d74c6c40db43054ccc7d27920cfbfe",
|
"build/prefab/lib/linux_x86_64_server/release/libballisticaplus.a": "d8d74c6c40db43054ccc7d27920cfbfe",
|
||||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "ba3a67e11c268a5b81b3416bc90cc297",
|
"build/prefab/lib/mac_arm64_gui/debug/libballisticaplus.a": "43bdfa8acd84e9cf2e443ce8e923c229",
|
||||||
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "d935dc21becdfd65bec51c5f5b2fd770",
|
"build/prefab/lib/mac_arm64_gui/release/libballisticaplus.a": "d935dc21becdfd65bec51c5f5b2fd770",
|
||||||
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "ba3a67e11c268a5b81b3416bc90cc297",
|
"build/prefab/lib/mac_arm64_server/debug/libballisticaplus.a": "43bdfa8acd84e9cf2e443ce8e923c229",
|
||||||
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "d935dc21becdfd65bec51c5f5b2fd770",
|
"build/prefab/lib/mac_arm64_server/release/libballisticaplus.a": "d935dc21becdfd65bec51c5f5b2fd770",
|
||||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "c5fb4b7f765cfd68ff09bcef3c5f84e6",
|
"build/prefab/lib/mac_x86_64_gui/debug/libballisticaplus.a": "6d49ad39f194480da458b431405f5a2b",
|
||||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "c8715c85010ea431d7346f40f5421819",
|
"build/prefab/lib/mac_x86_64_gui/release/libballisticaplus.a": "c8715c85010ea431d7346f40f5421819",
|
||||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "737778fc5e6a6ed5ca154c7953fcb377",
|
"build/prefab/lib/mac_x86_64_server/debug/libballisticaplus.a": "49775819d4ba9af15061080d17377a18",
|
||||||
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "c8715c85010ea431d7346f40f5421819",
|
"build/prefab/lib/mac_x86_64_server/release/libballisticaplus.a": "c8715c85010ea431d7346f40f5421819",
|
||||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "dfd5dca061d6eacaccc38c12d391cc82",
|
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.lib": "99e44969ef0f9421f21cb32554464bc7",
|
||||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "96ab39f16820a39fa7c42af6779c3556",
|
"build/prefab/lib/windows/Debug_Win32/BallisticaKitGenericPlus.pdb": "a14441d4ff9adae6e015a9f0d107a61a",
|
||||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "d87f20b0a0192b90687b936b6c2ad103",
|
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.lib": "90b5c353d5296be7e3b06e8c823564bb",
|
||||||
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "4a336c3e976924b70b39d18af6040e41",
|
"build/prefab/lib/windows/Debug_Win32/BallisticaKitHeadlessPlus.pdb": "84409ce44260a641295c1459e4d3af8f",
|
||||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "77d10f03566f29816de9b2ff806cc905",
|
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.lib": "35c58dd0a7482bb5f1ef5d6edb647c46",
|
||||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "d90ef473768f7ba232ae3ca58c5c8c04",
|
"build/prefab/lib/windows/Release_Win32/BallisticaKitGenericPlus.pdb": "a7c2895f59f75b767258277c766e9ed4",
|
||||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "743d872f2c848a84e4e5d49ca6b426e9",
|
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.lib": "e0430eeeb324ccfca8ace41d743e069c",
|
||||||
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "36272a00b45729022167baa81749a37e",
|
"build/prefab/lib/windows/Release_Win32/BallisticaKitHeadlessPlus.pdb": "d594057005fc56c8576f555f892b3dc4",
|
||||||
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
|
"src/assets/ba_data/python/babase/_mgen/__init__.py": "f885fed7f2ed98ff2ba271f9dbe3391c",
|
||||||
"src/assets/ba_data/python/babase/_mgen/enums.py": "28323912b56ec07701eda3d41a6a4101",
|
"src/assets/ba_data/python/babase/_mgen/enums.py": "28323912b56ec07701eda3d41a6a4101",
|
||||||
"src/ballistica/base/mgen/pyembed/binding_base.inc": "ba8ce3ca3858b4c2d20db68f99b788b2",
|
"src/ballistica/base/mgen/pyembed/binding_base.inc": "ba8ce3ca3858b4c2d20db68f99b788b2",
|
||||||
|
|||||||
2
.gitignore
vendored
2
.gitignore
vendored
@ -120,10 +120,10 @@ xcuserdata/
|
|||||||
/ballisticakit-android/BallisticaKit/src/main/res/mipmap-*/ic_launcher*.png
|
/ballisticakit-android/BallisticaKit/src/main/res/mipmap-*/ic_launcher*.png
|
||||||
/ballisticakit-android/BallisticaKit/src/cardboard/res/mipmap-*/ic_launcher*.png
|
/ballisticakit-android/BallisticaKit/src/cardboard/res/mipmap-*/ic_launcher*.png
|
||||||
BallisticaKit.ico
|
BallisticaKit.ico
|
||||||
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/Cursor macOS.appiconset/cursor_*.png
|
|
||||||
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/AppIcon iOS.appiconset/icon_*.png
|
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/AppIcon iOS.appiconset/icon_*.png
|
||||||
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/AppIcon macOS.appiconset/icon_*.png
|
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/AppIcon macOS.appiconset/icon_*.png
|
||||||
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/tvOS App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Layer*.imagestacklayer/Content.imageset/*.png
|
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/tvOS App Icon & Top Shelf Image.brandassets/App Icon - App Store.imagestack/Layer*.imagestacklayer/Content.imageset/*.png
|
||||||
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/tvOS App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Layer*.imagestacklayer/Content.imageset/*.png
|
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/tvOS App Icon & Top Shelf Image.brandassets/App Icon.imagestack/Layer*.imagestacklayer/Content.imageset/*.png
|
||||||
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/tvOS App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/*.png
|
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/tvOS App Icon & Top Shelf Image.brandassets/Top Shelf Image Wide.imageset/*.png
|
||||||
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/tvOS App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/*.png
|
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/tvOS App Icon & Top Shelf Image.brandassets/Top Shelf Image.imageset/*.png
|
||||||
|
/ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/Cursor macOS.imageset/cursor_*.png
|
||||||
|
|||||||
2
.idea/misc.xml
generated
2
.idea/misc.xml
generated
@ -4,7 +4,7 @@
|
|||||||
<option name="cmdArguments" value="--line-length 80 --skip-string-normalization" />
|
<option name="cmdArguments" value="--line-length 80 --skip-string-normalization" />
|
||||||
<option name="enabledOnReformat" value="true" />
|
<option name="enabledOnReformat" value="true" />
|
||||||
<option name="pathToExecutable" value="/opt/homebrew/bin/black" />
|
<option name="pathToExecutable" value="/opt/homebrew/bin/black" />
|
||||||
<option name="sdkUUID" value="1b270adb-5261-4492-85e8-d79b3894255d" />
|
<option name="sdkName" value="Python 3.11" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11" project-jdk-type="Python SDK" />
|
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.11" project-jdk-type="Python SDK" />
|
||||||
<component name="PythonCompatibilityInspectionAdvertiser">
|
<component name="PythonCompatibilityInspectionAdvertiser">
|
||||||
|
|||||||
23
CHANGELOG.md
23
CHANGELOG.md
@ -1,4 +1,4 @@
|
|||||||
### 1.7.28 (build 21447, api 8, 2023-10-12)
|
### 1.7.28 (build 21453, api 8, 2023-10-13)
|
||||||
|
|
||||||
- Massively cleaned up code related to rendering and window systems (OpenGL,
|
- Massively cleaned up code related to rendering and window systems (OpenGL,
|
||||||
SDL, etc). This code had been growing into a nasty tangle for 15 years
|
SDL, etc). This code had been growing into a nasty tangle for 15 years
|
||||||
@ -12,8 +12,10 @@
|
|||||||
newer on mobile. This means we're cutting off a few percent of old devices on
|
newer on mobile. This means we're cutting off a few percent of old devices on
|
||||||
Android that only support ES 2, but ES 3 has been out for 10 years now so I
|
Android that only support ES 2, but ES 3 has been out for 10 years now so I
|
||||||
feel it is time. As mentioned above, this allows massively cleaning up the
|
feel it is time. As mentioned above, this allows massively cleaning up the
|
||||||
graphics code which means we can start to improve it.
|
graphics code which means we can start to improve it. Ideally now the GL
|
||||||
- Removed gamma controls. These were only active on the old Mac version anyway
|
renderer can be abstracted a bit more which will make the process of writing
|
||||||
|
other renderers easier.
|
||||||
|
- Removed gamma controls. These were only active on the old Mac builds anyway
|
||||||
and are being removed from the upcoming SDL3, so if we want this sort of thing
|
and are being removed from the upcoming SDL3, so if we want this sort of thing
|
||||||
we should do it through shading in the renderer now.
|
we should do it through shading in the renderer now.
|
||||||
- Implemented both vsync and max-fps for the SDL build of the game. This means
|
- Implemented both vsync and max-fps for the SDL build of the game. This means
|
||||||
@ -129,7 +131,20 @@
|
|||||||
before. It also takes a `confirm` bool arg which allows it to be used to bring
|
before. It also takes a `confirm` bool arg which allows it to be used to bring
|
||||||
up a confirm dialog.
|
up a confirm dialog.
|
||||||
- Clicking on a window close button to quit no longer brings up a confirm dialog
|
- Clicking on a window close button to quit no longer brings up a confirm dialog
|
||||||
and instead quits immediately (though with a proper graceful shutdown).
|
and instead quits immediately (though with a proper graceful shutdown and a
|
||||||
|
lovely little fade).
|
||||||
|
- Camera shake is now supported in network games and replays. Somehow I didn't
|
||||||
|
notice that was missing for years. The downside is this requires a server to
|
||||||
|
be hosting protocol 35, which cuts off support for 1.4 clients. So for now I
|
||||||
|
am keeping the default at 33. Once there a fewer 1.4 clients around we can
|
||||||
|
consider changing this (if everything hasn't moved to SceneV2 by then).
|
||||||
|
- Added a server option to set the hosting protocol for servers who might want
|
||||||
|
to allow camera shake (or other minor features/fixes) that don't work in the
|
||||||
|
default protocol 33. See `protocol_version` in `config.yaml`. Just remember
|
||||||
|
that you will be cutting off support for older clients if you use 35.
|
||||||
|
- Fixed a bug with screen-messages animating off screen too fast when frame
|
||||||
|
rates are high.
|
||||||
|
|
||||||
|
|
||||||
### 1.7.27 (build 21282, api 8, 2023-08-30)
|
### 1.7.27 (build 21282, api 8, 2023-08-30)
|
||||||
|
|
||||||
|
|||||||
9
Makefile
9
Makefile
@ -779,13 +779,12 @@ check-full: py_check_prereqs
|
|||||||
|
|
||||||
# Same as 'check' plus optional/slow extra checks.
|
# Same as 'check' plus optional/slow extra checks.
|
||||||
check2: py_check_prereqs
|
check2: py_check_prereqs
|
||||||
@$(DMAKE) -j$(CPUS) update-check cpplint pylint mypy pycharm
|
@$(DMAKE) -j$(CPUS) update-check cpplint pylint mypy
|
||||||
@$(PCOMMANDBATCH) echo SGRN BLD ALL CHECKS PASSED!
|
@$(PCOMMANDBATCH) echo SGRN BLD ALL CHECKS PASSED!
|
||||||
|
|
||||||
# Same as check2 but no caching (all files are checked).
|
# Same as check2 but no caching (all files are checked).
|
||||||
check2-full: py_check_prereqs
|
check2-full: py_check_prereqs
|
||||||
@$(DMAKE) -j$(CPUS) update-check cpplint-full pylint-full mypy-full \
|
@$(DMAKE) -j$(CPUS) update-check cpplint-full pylint-full mypy-full
|
||||||
pycharm-full
|
|
||||||
@$(PCOMMANDBATCH) echo SGRN BLD ALL CHECKS PASSED!
|
@$(PCOMMANDBATCH) echo SGRN BLD ALL CHECKS PASSED!
|
||||||
|
|
||||||
# Run Cpplint checks on all C/C++ code.
|
# Run Cpplint checks on all C/C++ code.
|
||||||
@ -924,14 +923,14 @@ preflight-full:
|
|||||||
preflight2:
|
preflight2:
|
||||||
@$(MAKE) format
|
@$(MAKE) format
|
||||||
@$(MAKE) update
|
@$(MAKE) update
|
||||||
@$(MAKE) -j$(CPUS) cpplint pylint mypy pycharm test
|
@$(MAKE) -j$(CPUS) cpplint pylint mypy test
|
||||||
@$(PCOMMANDBATCH) echo SGRN BLD PREFLIGHT SUCCESSFUL!
|
@$(PCOMMANDBATCH) echo SGRN BLD PREFLIGHT SUCCESSFUL!
|
||||||
|
|
||||||
# Same as 'preflight2' but without caching (all files visited).
|
# Same as 'preflight2' but without caching (all files visited).
|
||||||
preflight2-full:
|
preflight2-full:
|
||||||
@$(MAKE) format-full
|
@$(MAKE) format-full
|
||||||
@$(MAKE) update
|
@$(MAKE) update
|
||||||
@$(MAKE) -j$(CPUS) cpplint-full pylint-full mypy-full pycharm-full test-full
|
@$(MAKE) -j$(CPUS) cpplint-full pylint-full mypy-full test-full
|
||||||
@$(PCOMMANDBATCH) echo SGRN BLD PREFLIGHT SUCCESSFUL!
|
@$(PCOMMANDBATCH) echo SGRN BLD PREFLIGHT SUCCESSFUL!
|
||||||
|
|
||||||
# Tell make which of these targets don't represent files.
|
# Tell make which of these targets don't represent files.
|
||||||
|
|||||||
@ -53,47 +53,58 @@ ctx.src_omit_paths = {
|
|||||||
'src/assets/workspace',
|
'src/assets/workspace',
|
||||||
}
|
}
|
||||||
|
|
||||||
# Use this to 'carve out' directories or exact file paths which will be
|
# Use this to 'carve out' files or directories which will be git-managed
|
||||||
# git-managed on dst. By default, spinoff will consider dirs containing
|
# on dst.
|
||||||
# the files it syncs from src as 'spinoff-managed'; it will set them as
|
#
|
||||||
# git-ignored and will complain if any files appear in them that it does
|
# By default, spinoff will consider dirs containing the files it syncs
|
||||||
# not manage itself (to prevent accidentally doing work in such places).
|
# from src as 'spinoff-managed'; it will set them as git-ignored and
|
||||||
# Note that adding a dir to src_write_paths does not prevent files
|
# will complain if any files appear in them that it does not manage
|
||||||
# within it from being synced by spinoff; it just means that each of
|
# itself (to prevent accidentally doing work in such places). Note that
|
||||||
# those individual spinoff-managed files will have their own gitignore
|
# adding a dir to src_write_paths does not prevent files within it from
|
||||||
# entry since there is no longer one covering the whole dir. So to keep
|
# being synced by spinoff; it just means that each of those individual
|
||||||
# things tidy, carve out the minimal set of exact file/dir paths that
|
# spinoff-managed files will have their own gitignore entry since there
|
||||||
# you need.
|
# can't be a single one covering the whole dir. So to keep things tidy,
|
||||||
|
# carve out the minimal set of exact file/dir paths that you need.
|
||||||
ctx.src_write_paths = {
|
ctx.src_write_paths = {
|
||||||
'tools/spinoff',
|
'tools/spinoff',
|
||||||
'config/spinoffconfig.py',
|
'config/spinoffconfig.py',
|
||||||
}
|
}
|
||||||
|
|
||||||
# Normally spinoff errors if it finds any files in its managed dirs that
|
# Use this to 'carve out' files or directories under spinoff managed
|
||||||
# it did not put there. This is to prevent accidentally working in these
|
# dirs which will be completely ignored by spinoff (but *not* placed
|
||||||
# parts of a dst project; since these sections are git-ignored, git
|
# under git control).
|
||||||
# itself won't raise any warnings in such cases and it would be easy to
|
|
||||||
# accidentally lose work otherwise.
|
|
||||||
#
|
#
|
||||||
# This list can be used to suppress spinoff's errors for specific
|
# Normally spinoff will error if it finds any files under its managed
|
||||||
# locations. This is generally used to allow build output or other
|
# dirs that it did not put there. This is to prevent accidentally
|
||||||
# dynamically generated files to exist within spinoff-managed
|
# working in these parts of a dst project; since spinoff-controlled
|
||||||
# directories. It is possible to use src_write_paths for such purposes,
|
# stuff is git-ignored, git itself won't raise any warnings in such
|
||||||
# but this has the side-effect of greatly complicating the dst project's
|
# cases and it would be easy to accidentally blow away changes if
|
||||||
# gitignore list; selectively marking a few dirs as unchecked makes for
|
# spinoff didn't raise a stink.
|
||||||
# a cleaner setup. Just be careful to not set excessively broad regions
|
#
|
||||||
# as unchecked; you don't want to mask actual useful error messages.
|
# This list is used to suppress raising of said stink for specific
|
||||||
|
# locations. This allows build output or other dynamically generated
|
||||||
|
# files to exist under spinoff-managed directories. It is also possible
|
||||||
|
# to use src_write_paths for such carve-outs, but that can have the
|
||||||
|
# negative side-effect of greatly complicating the dst project's
|
||||||
|
# .gitignore file. Selectively marking a few specific files or dirs as
|
||||||
|
# unchecked instead can keep things tidier and more understandable.
|
||||||
|
#
|
||||||
|
# Note that files and paths marked as unchecked cannot be the
|
||||||
|
# destination for synced files, as that would be ambiguous (We can
|
||||||
|
# either sync the file ourself or expect someone else to write it, but
|
||||||
|
# not both).
|
||||||
ctx.src_unchecked_paths = {
|
ctx.src_unchecked_paths = {
|
||||||
'src/ballistica/mgen',
|
'src/ballistica/mgen',
|
||||||
'src/ballistica/*/mgen',
|
'src/ballistica/*/mgen',
|
||||||
'src/assets/ba_data/python/*/_mgen',
|
'src/assets/ba_data/python/*/_mgen',
|
||||||
'src/meta/*/mgen',
|
'src/meta/*/mgen',
|
||||||
'ballisticakit-cmake/.clang-format',
|
'ballisticakit-cmake/.clang-format',
|
||||||
'ballisticakit-android/BallisticaKit/src/cardboard/res',
|
|
||||||
'ballisticakit-windows/*/BallisticaKit.ico',
|
'ballisticakit-windows/*/BallisticaKit.ico',
|
||||||
'ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets',
|
'ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/*/*.png',
|
||||||
'ballisticakit-android/BallisticaKit/src/*/res',
|
'ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/*/*/*.png',
|
||||||
'ballisticakit-android/BallisticaKit/src/*/assets',
|
'ballisticakit-xcode/BallisticaKit Shared/Assets.xcassets/*/*/*/*/*.png',
|
||||||
|
'ballisticakit-android/BallisticaKit/src/*/res/*/*.png',
|
||||||
|
'ballisticakit-android/BallisticaKit/src/*/assets/ballistica_files',
|
||||||
'ballisticakit-android/local.properties',
|
'ballisticakit-android/local.properties',
|
||||||
'ballisticakit-android/.gradle',
|
'ballisticakit-android/.gradle',
|
||||||
'ballisticakit-android/build',
|
'ballisticakit-android/build',
|
||||||
|
|||||||
@ -56,6 +56,8 @@ class App:
|
|||||||
|
|
||||||
# pylint: disable=too-many-public-methods
|
# pylint: disable=too-many-public-methods
|
||||||
|
|
||||||
|
# A few things defined as non-optional values but not actually
|
||||||
|
# available until the app starts.
|
||||||
plugins: PluginSubsystem
|
plugins: PluginSubsystem
|
||||||
lang: LanguageSubsystem
|
lang: LanguageSubsystem
|
||||||
health_monitor: AppHealthMonitor
|
health_monitor: AppHealthMonitor
|
||||||
@ -92,7 +94,7 @@ class App:
|
|||||||
|
|
||||||
# Used on platforms such as mobile where the app basically needs
|
# Used on platforms such as mobile where the app basically needs
|
||||||
# to shut down while backgrounded. In this state, all event
|
# to shut down while backgrounded. In this state, all event
|
||||||
# loops are suspended and all graphics and audio should cease
|
# loops are suspended and all graphics and audio must cease
|
||||||
# completely. Be aware that the suspended state can be entered
|
# completely. Be aware that the suspended state can be entered
|
||||||
# from any other state including NATIVE_BOOTSTRAPPING and
|
# from any other state including NATIVE_BOOTSTRAPPING and
|
||||||
# SHUTTING_DOWN.
|
# SHUTTING_DOWN.
|
||||||
@ -149,9 +151,9 @@ class App:
|
|||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
"""(internal)
|
"""(internal)
|
||||||
|
|
||||||
Do not instantiate this class; access the single shared instance
|
Do not instantiate this class. You can access the single shared
|
||||||
of it as 'app' which is available in various Ballistica
|
instance of it through various high level packages: 'babase.app',
|
||||||
feature-set modules such as babase.
|
'bascenev1.app', 'bauiv1.app', etc.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
# Hack for docs-generation: we can be imported with dummy modules
|
# Hack for docs-generation: we can be imported with dummy modules
|
||||||
@ -508,7 +510,7 @@ class App:
|
|||||||
except Exception:
|
except Exception:
|
||||||
logging.exception('Error setting app intent to %s.', intent)
|
logging.exception('Error setting app intent to %s.', intent)
|
||||||
_babase.pushcall(
|
_babase.pushcall(
|
||||||
tpartial(self._apply_intent_error, intent),
|
tpartial(self._display_set_intent_error, intent),
|
||||||
from_other_thread=True,
|
from_other_thread=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -553,10 +555,11 @@ class App:
|
|||||||
'Error handling intent %s in app-mode %s.', intent, mode
|
'Error handling intent %s in app-mode %s.', intent, mode
|
||||||
)
|
)
|
||||||
|
|
||||||
def _apply_intent_error(self, intent: AppIntent) -> None:
|
def _display_set_intent_error(self, intent: AppIntent) -> None:
|
||||||
|
"""Show the *user* something went wrong setting an intent."""
|
||||||
from babase._language import Lstr
|
from babase._language import Lstr
|
||||||
|
|
||||||
del intent # Unused.
|
del intent
|
||||||
_babase.screenmessage(Lstr(resource='errorText'), color=(1, 0, 0))
|
_babase.screenmessage(Lstr(resource='errorText'), color=(1, 0, 0))
|
||||||
_babase.getsimplesound('error').play()
|
_babase.getsimplesound('error').play()
|
||||||
|
|
||||||
|
|||||||
@ -302,6 +302,11 @@ class AccountV1Subsystem:
|
|||||||
"""(internal)"""
|
"""(internal)"""
|
||||||
plus = babase.app.plus
|
plus = babase.app.plus
|
||||||
if plus is None:
|
if plus is None:
|
||||||
|
import logging
|
||||||
|
|
||||||
|
logging.warning(
|
||||||
|
'Error adding pending promo code; plus not present.'
|
||||||
|
)
|
||||||
babase.screenmessage(
|
babase.screenmessage(
|
||||||
babase.Lstr(resource='errorText'), color=(1, 0, 0)
|
babase.Lstr(resource='errorText'), color=(1, 0, 0)
|
||||||
)
|
)
|
||||||
|
|||||||
@ -52,7 +52,7 @@ if TYPE_CHECKING:
|
|||||||
|
|
||||||
# Build number and version of the ballistica binary we expect to be
|
# Build number and version of the ballistica binary we expect to be
|
||||||
# using.
|
# using.
|
||||||
TARGET_BALLISTICA_BUILD = 21447
|
TARGET_BALLISTICA_BUILD = 21453
|
||||||
TARGET_BALLISTICA_VERSION = '1.7.28'
|
TARGET_BALLISTICA_VERSION = '1.7.28'
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1184,6 +1184,9 @@ class AccountSettingsWindow(bui.Window):
|
|||||||
self, response: bacommon.cloud.ManageAccountResponse | Exception
|
self, response: bacommon.cloud.ManageAccountResponse | Exception
|
||||||
) -> None:
|
) -> None:
|
||||||
if isinstance(response, Exception) or response.url is None:
|
if isinstance(response, Exception) or response.url is None:
|
||||||
|
logging.warning(
|
||||||
|
'Got error in manage-account-response: %s.', response
|
||||||
|
)
|
||||||
bui.screenmessage(bui.Lstr(resource='errorText'), color=(1, 0, 0))
|
bui.screenmessage(bui.Lstr(resource='errorText'), color=(1, 0, 0))
|
||||||
bui.getsound('error').play()
|
bui.getsound('error').play()
|
||||||
return
|
return
|
||||||
@ -1466,6 +1469,7 @@ class AccountSettingsWindow(bui.Window):
|
|||||||
if isinstance(result, Exception):
|
if isinstance(result, Exception):
|
||||||
# For now just make a bit of noise if anything went wrong;
|
# For now just make a bit of noise if anything went wrong;
|
||||||
# can get more specific as needed later.
|
# can get more specific as needed later.
|
||||||
|
logging.warning('Got error in v2 sign-in result: %s.', result)
|
||||||
bui.screenmessage(bui.Lstr(resource='errorText'), color=(1, 0, 0))
|
bui.screenmessage(bui.Lstr(resource='errorText'), color=(1, 0, 0))
|
||||||
bui.getsound('error').play()
|
bui.getsound('error').play()
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -15,8 +15,8 @@ from threading import Lock, Thread, current_thread
|
|||||||
from typing import TYPE_CHECKING
|
from typing import TYPE_CHECKING
|
||||||
|
|
||||||
# We make use of the bacommon and efro packages as well as site-packages
|
# We make use of the bacommon and efro packages as well as site-packages
|
||||||
# included with our bundled Ballistica dist, so we need to add those paths
|
# included with our bundled Ballistica dist, so we need to add those
|
||||||
# before we import them.
|
# paths before we import them.
|
||||||
sys.path += [
|
sys.path += [
|
||||||
str(Path(Path(__file__).parent, 'dist', 'ba_data', 'python')),
|
str(Path(Path(__file__).parent, 'dist', 'ba_data', 'python')),
|
||||||
str(Path(Path(__file__).parent, 'dist', 'ba_data', 'python-site-packages')),
|
str(Path(Path(__file__).parent, 'dist', 'ba_data', 'python-site-packages')),
|
||||||
@ -34,31 +34,55 @@ if TYPE_CHECKING:
|
|||||||
VERSION_STR = '1.3.1'
|
VERSION_STR = '1.3.1'
|
||||||
|
|
||||||
# Version history:
|
# Version history:
|
||||||
|
#
|
||||||
# 1.3.1
|
# 1.3.1
|
||||||
# Windows binary is now named BallisticaKitHeadless.exe
|
#
|
||||||
|
# - Windows binary is now named 'BallisticaKitHeadless.exe'.
|
||||||
|
#
|
||||||
# 1.3:
|
# 1.3:
|
||||||
# Added show_tutorial config option
|
#
|
||||||
# Added team_names config option
|
# - Added show_tutorial config option.
|
||||||
# Added team_colors config option
|
#
|
||||||
# Added playlist_inline config option
|
# - Added team_names config option.
|
||||||
|
#
|
||||||
|
# - Added team_colors config option.
|
||||||
|
#
|
||||||
|
# - Added playlist_inline config option.
|
||||||
|
#
|
||||||
# 1.2:
|
# 1.2:
|
||||||
# Added optional --help arg
|
#
|
||||||
# Added --config arg for specifying config file and --root for ba_root path
|
# - Added optional --help arg.
|
||||||
# Added noninteractive mode and --interactive/--noninteractive args to
|
#
|
||||||
# explicitly enable/disable it (it is autodetected by default)
|
# - Added --config arg for specifying config file and --root for
|
||||||
# Added explicit control for auto-restart: --no-auto-restart
|
# ba_root path.
|
||||||
# Config file is now reloaded each time server binary is restarted; no more
|
#
|
||||||
# need to bring down server wrapper to pick up changes
|
# - Added noninteractive mode and --interactive/--noninteractive args
|
||||||
# Now automatically restarts server binary when config file is modified
|
# to explicitly enable/disable it (it is autodetected by default).
|
||||||
# (use --no-config-auto-restart to disable that behavior)
|
#
|
||||||
|
# - Added explicit control for auto-restart: --no-auto-restart.
|
||||||
|
#
|
||||||
|
# - Config file is now reloaded each time server binary is restarted;
|
||||||
|
# no more need to bring down server wrapper to pick up changes.
|
||||||
|
#
|
||||||
|
# - Now automatically restarts server binary when config file is
|
||||||
|
# modified (use --no-config-auto-restart to disable that behavior).
|
||||||
|
#
|
||||||
# 1.1.1:
|
# 1.1.1:
|
||||||
# Switched config reading to use efro.dataclasses.dataclass_from_dict()
|
#
|
||||||
|
# - Switched config reading to use
|
||||||
|
# efro.dataclasses.dataclass_from_dict().
|
||||||
|
#
|
||||||
# 1.1.0:
|
# 1.1.0:
|
||||||
# Added shutdown command
|
#
|
||||||
# Changed restart to default to immediate=True
|
# - Added shutdown command.
|
||||||
# Added clean_exit_minutes, unclean_exit_minutes, and idle_exit_minutes
|
#
|
||||||
|
# - Changed restart to default to immediate=True.
|
||||||
|
#
|
||||||
|
# - Added clean_exit_minutes, unclean_exit_minutes, and idle_exit_minutes.
|
||||||
|
#
|
||||||
# 1.0.0:
|
# 1.0.0:
|
||||||
# Initial release
|
#
|
||||||
|
# - Initial release.
|
||||||
|
|
||||||
|
|
||||||
class ServerManagerApp:
|
class ServerManagerApp:
|
||||||
@ -101,8 +125,9 @@ class ServerManagerApp:
|
|||||||
# This may override the above defaults.
|
# This may override the above defaults.
|
||||||
self._parse_command_line_args()
|
self._parse_command_line_args()
|
||||||
|
|
||||||
# Do an initial config-load. If the config is invalid at this point
|
# Do an initial config-load. If the config is invalid at this
|
||||||
# we can cleanly die (we're more lenient later on reloads).
|
# point we can cleanly die (we're more lenient later on
|
||||||
|
# reloads).
|
||||||
self.load_config(strict=True, print_confirmation=False)
|
self.load_config(strict=True, print_confirmation=False)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
@ -131,9 +156,9 @@ class ServerManagerApp:
|
|||||||
)
|
)
|
||||||
|
|
||||||
# Python will handle SIGINT for us (as KeyboardInterrupt) but we
|
# Python will handle SIGINT for us (as KeyboardInterrupt) but we
|
||||||
# need to register a SIGTERM handler so we have a chance to clean
|
# need to register a SIGTERM handler so we have a chance to
|
||||||
# up our subprocess when someone tells us to die. (and avoid
|
# clean up our subprocess when someone tells us to die. (and
|
||||||
# zombie processes)
|
# avoid zombie processes)
|
||||||
signal.signal(signal.SIGTERM, self._handle_term_signal)
|
signal.signal(signal.SIGTERM, self._handle_term_signal)
|
||||||
|
|
||||||
# During a run, we make the assumption that cwd is the dir
|
# During a run, we make the assumption that cwd is the dir
|
||||||
@ -155,7 +180,8 @@ class ServerManagerApp:
|
|||||||
f'{Clr.CYN}Waiting for subprocess exit...{Clr.RST}', flush=True
|
f'{Clr.CYN}Waiting for subprocess exit...{Clr.RST}', flush=True
|
||||||
)
|
)
|
||||||
|
|
||||||
# Mark ourselves as shutting down and wait for the process to wrap up.
|
# Mark ourselves as shutting down and wait for the process to
|
||||||
|
# wrap up.
|
||||||
self._done = True
|
self._done = True
|
||||||
self._subprocess_thread.join()
|
self._subprocess_thread.join()
|
||||||
|
|
||||||
@ -181,9 +207,10 @@ class ServerManagerApp:
|
|||||||
# Gracefully bow out if we kill ourself via keyboard.
|
# Gracefully bow out if we kill ourself via keyboard.
|
||||||
pass
|
pass
|
||||||
except SystemExit:
|
except SystemExit:
|
||||||
# We get this from the builtin quit(), our signal handler, etc.
|
# We get this from the builtin quit(), our signal handler,
|
||||||
# Need to catch this so we can clean up, otherwise we'll be
|
# etc. Need to catch this so we can clean up, otherwise
|
||||||
# left in limbo with our process thread still running.
|
# we'll be left in limbo with our process thread still
|
||||||
|
# running.
|
||||||
pass
|
pass
|
||||||
self._postrun()
|
self._postrun()
|
||||||
|
|
||||||
@ -207,14 +234,17 @@ class ServerManagerApp:
|
|||||||
self._enable_tab_completion(context)
|
self._enable_tab_completion(context)
|
||||||
|
|
||||||
# Now just sit in an interpreter.
|
# Now just sit in an interpreter.
|
||||||
# TODO: make it possible to use IPython if the user has it available.
|
#
|
||||||
|
# TODO: make it possible to use IPython if the user has it
|
||||||
|
# available.
|
||||||
try:
|
try:
|
||||||
self._interpreter_start_time = time.time()
|
self._interpreter_start_time = time.time()
|
||||||
code.interact(local=context, banner='', exitmsg='')
|
code.interact(local=context, banner='', exitmsg='')
|
||||||
except SystemExit:
|
except SystemExit:
|
||||||
# We get this from the builtin quit(), our signal handler, etc.
|
# We get this from the builtin quit(), our signal handler,
|
||||||
# Need to catch this so we can clean up, otherwise we'll be
|
# etc. Need to catch this so we can clean up, otherwise
|
||||||
# left in limbo with our process thread still running.
|
# we'll be left in limbo with our process thread still
|
||||||
|
# running.
|
||||||
pass
|
pass
|
||||||
except BaseException as exc:
|
except BaseException as exc:
|
||||||
print(
|
print(
|
||||||
@ -238,19 +268,21 @@ class ServerManagerApp:
|
|||||||
self._block_for_command_completion()
|
self._block_for_command_completion()
|
||||||
|
|
||||||
def _block_for_command_completion(self) -> None:
|
def _block_for_command_completion(self) -> None:
|
||||||
# Ideally we'd block here until the command was run so our prompt would
|
# Ideally we'd block here until the command was run so our
|
||||||
# print after it's results. We currently don't get any response from
|
# prompt would print after it's results. We currently don't get
|
||||||
# the app so the best we can do is block until our bg thread has sent
|
# any response from the app so the best we can do is block until
|
||||||
# it. In the future we can perhaps add a proper 'command port'
|
# our bg thread has sent it. In the future we can perhaps add a
|
||||||
# interface for proper blocking two way communication.
|
# proper 'command port' interface for proper blocking two way
|
||||||
|
# communication.
|
||||||
while True:
|
while True:
|
||||||
with self._subprocess_commands_lock:
|
with self._subprocess_commands_lock:
|
||||||
if not self._subprocess_commands:
|
if not self._subprocess_commands:
|
||||||
break
|
break
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
# One last short delay so if we come out *just* as the command is sent
|
# One last short delay so if we come out *just* as the command
|
||||||
# we'll hopefully still give it enough time to process/print.
|
# is sent we'll hopefully still give it enough time to
|
||||||
|
# process/print.
|
||||||
time.sleep(0.1)
|
time.sleep(0.1)
|
||||||
|
|
||||||
def screenmessage(
|
def screenmessage(
|
||||||
@ -320,8 +352,8 @@ class ServerManagerApp:
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
# If we're asking for an immediate restart but don't get one within
|
# If we're asking for an immediate restart but don't get one
|
||||||
# the grace period, bring down the hammer.
|
# within the grace period, bring down the hammer.
|
||||||
if immediate:
|
if immediate:
|
||||||
self._subprocess_force_kill_time = (
|
self._subprocess_force_kill_time = (
|
||||||
time.time() + self.IMMEDIATE_SHUTDOWN_TIME_LIMIT
|
time.time() + self.IMMEDIATE_SHUTDOWN_TIME_LIMIT
|
||||||
@ -340,12 +372,12 @@ class ServerManagerApp:
|
|||||||
ShutdownCommand(reason=ShutdownReason.NONE, immediate=immediate)
|
ShutdownCommand(reason=ShutdownReason.NONE, immediate=immediate)
|
||||||
)
|
)
|
||||||
|
|
||||||
# An explicit shutdown means we know to bail completely once this
|
# An explicit shutdown means we know to bail completely once
|
||||||
# subprocess completes.
|
# this subprocess completes.
|
||||||
self._wrapper_shutdown_desired = True
|
self._wrapper_shutdown_desired = True
|
||||||
|
|
||||||
# If we're asking for an immediate shutdown but don't get one within
|
# If we're asking for an immediate shutdown but don't get one
|
||||||
# the grace period, bring down the hammer.
|
# within the grace period, bring down the hammer.
|
||||||
if immediate:
|
if immediate:
|
||||||
self._subprocess_force_kill_time = (
|
self._subprocess_force_kill_time = (
|
||||||
time.time() + self.IMMEDIATE_SHUTDOWN_TIME_LIMIT
|
time.time() + self.IMMEDIATE_SHUTDOWN_TIME_LIMIT
|
||||||
@ -378,9 +410,10 @@ class ServerManagerApp:
|
|||||||
if i + 1 >= argc:
|
if i + 1 >= argc:
|
||||||
raise CleanError('Expected a path as next arg.')
|
raise CleanError('Expected a path as next arg.')
|
||||||
path = sys.argv[i + 1]
|
path = sys.argv[i + 1]
|
||||||
# Unlike config_path, this one doesn't have to exist now.
|
# Unlike config_path, this one doesn't have to exist
|
||||||
# We do however need an abs path because we may be in a
|
# now. We do however need an abs path because we may be
|
||||||
# different cwd currently than we will be during the run.
|
# in a different cwd currently than we will be during
|
||||||
|
# the run.
|
||||||
self._ba_root_path = os.path.abspath(path)
|
self._ba_root_path = os.path.abspath(path)
|
||||||
i += 2
|
i += 2
|
||||||
elif arg == '--interactive':
|
elif arg == '--interactive':
|
||||||
@ -538,6 +571,7 @@ class ServerManagerApp:
|
|||||||
|
|
||||||
if not os.path.exists(self._config_path):
|
if not os.path.exists(self._config_path):
|
||||||
# Special case:
|
# Special case:
|
||||||
|
#
|
||||||
# If the user didn't specify a particular config file, allow
|
# If the user didn't specify a particular config file, allow
|
||||||
# gracefully falling back to defaults if the default one is
|
# gracefully falling back to defaults if the default one is
|
||||||
# missing.
|
# missing.
|
||||||
@ -606,24 +640,26 @@ class ServerManagerApp:
|
|||||||
"""Spin up the server subprocess and run it until exit."""
|
"""Spin up the server subprocess and run it until exit."""
|
||||||
# pylint: disable=consider-using-with
|
# pylint: disable=consider-using-with
|
||||||
|
|
||||||
# Reload our config, and update our overall behavior based on it.
|
# Reload our config, and update our overall behavior based on
|
||||||
# We do non-strict this time to give the user repeated attempts if
|
# it. We do non-strict this time to give the user repeated
|
||||||
# if they mess up while modifying the config on the fly.
|
# attempts if if they mess up while modifying the config on the
|
||||||
|
# fly.
|
||||||
self.load_config(strict=False, print_confirmation=True)
|
self.load_config(strict=False, print_confirmation=True)
|
||||||
|
|
||||||
self._prep_subprocess_environment()
|
self._prep_subprocess_environment()
|
||||||
|
|
||||||
# Launch the binary and grab its stdin;
|
# Launch the binary and grab its stdin; we'll use this to feed
|
||||||
# we'll use this to feed it commands.
|
# it commands.
|
||||||
self._subprocess_launch_time = time.time()
|
self._subprocess_launch_time = time.time()
|
||||||
|
|
||||||
# Set an environment var so the server process knows its being
|
# Set an environment var so the server process knows its being
|
||||||
# run under us. This causes it to ignore ctrl-c presses and other
|
# run under us. This causes it to ignore ctrl-c presses and
|
||||||
# slight behavior tweaks. Hmm; should this be an argument instead?
|
# other slight behavior tweaks. Hmm; should this be an argument
|
||||||
|
# instead?
|
||||||
os.environ['BA_SERVER_WRAPPER_MANAGED'] = '1'
|
os.environ['BA_SERVER_WRAPPER_MANAGED'] = '1'
|
||||||
|
|
||||||
# Set an environment var to change the device name.
|
# Set an environment var to change the device name. Device name
|
||||||
# Device name is used while making connection with master server,
|
# is used while making connection with master server,
|
||||||
# cloud-console recognize us with this name.
|
# cloud-console recognize us with this name.
|
||||||
os.environ['BA_DEVICE_NAME'] = self._config.party_name
|
os.environ['BA_DEVICE_NAME'] = self._config.party_name
|
||||||
|
|
||||||
@ -663,9 +699,10 @@ class ServerManagerApp:
|
|||||||
|
|
||||||
assert self._subprocess_exited_cleanly is not None
|
assert self._subprocess_exited_cleanly is not None
|
||||||
|
|
||||||
# EW: it seems that if we die before the main thread has fully started
|
# EW: it seems that if we die before the main thread has fully
|
||||||
# up the interpreter, its possible that it will not break out of its
|
# started up the interpreter, its possible that it will not
|
||||||
# loop via the usual SystemExit that gets sent when we die.
|
# break out of its loop via the usual SystemExit that gets sent
|
||||||
|
# when we die.
|
||||||
if self._interactive:
|
if self._interactive:
|
||||||
while (
|
while (
|
||||||
self._interpreter_start_time is None
|
self._interpreter_start_time is None
|
||||||
@ -694,8 +731,8 @@ class ServerManagerApp:
|
|||||||
# tell the main thread to die.
|
# tell the main thread to die.
|
||||||
if self._wrapper_shutdown_desired:
|
if self._wrapper_shutdown_desired:
|
||||||
# Only do this if the main thread is not already waiting for
|
# Only do this if the main thread is not already waiting for
|
||||||
# us to die; otherwise it can lead to deadlock.
|
# us to die; otherwise it can lead to deadlock. (we hang in
|
||||||
# (we hang in os.kill while main thread is blocked in Thread.join)
|
# os.kill while main thread is blocked in Thread.join)
|
||||||
if not self._done:
|
if not self._done:
|
||||||
self._done = True
|
self._done = True
|
||||||
|
|
||||||
@ -721,6 +758,8 @@ class ServerManagerApp:
|
|||||||
bincfg['Auto Balance Teams'] = self._config.auto_balance_teams
|
bincfg['Auto Balance Teams'] = self._config.auto_balance_teams
|
||||||
bincfg['Show Tutorial'] = self._config.show_tutorial
|
bincfg['Show Tutorial'] = self._config.show_tutorial
|
||||||
|
|
||||||
|
if self._config.protocol_version is not None:
|
||||||
|
bincfg['SceneV1 Host Protocol'] = self._config.protocol_version
|
||||||
if self._config.team_names is not None:
|
if self._config.team_names is not None:
|
||||||
bincfg['Custom Team Names'] = self._config.team_names
|
bincfg['Custom Team Names'] = self._config.team_names
|
||||||
elif 'Custom Team Names' in bincfg:
|
elif 'Custom Team Names' in bincfg:
|
||||||
@ -769,8 +808,8 @@ class ServerManagerApp:
|
|||||||
assert current_thread() is self._subprocess_thread
|
assert current_thread() is self._subprocess_thread
|
||||||
assert self._subprocess.stdin is not None
|
assert self._subprocess.stdin is not None
|
||||||
|
|
||||||
# Send the initial server config which should kick things off.
|
# Send the initial server config which should kick things off
|
||||||
# (but make sure its values are still valid first)
|
# (but make sure its values are still valid first).
|
||||||
dataclass_validate(self._config)
|
dataclass_validate(self._config)
|
||||||
self._send_server_command(StartServerModeCommand(self._config))
|
self._send_server_command(StartServerModeCommand(self._config))
|
||||||
|
|
||||||
@ -782,8 +821,8 @@ class ServerManagerApp:
|
|||||||
# Pass along any commands to our process.
|
# Pass along any commands to our process.
|
||||||
with self._subprocess_commands_lock:
|
with self._subprocess_commands_lock:
|
||||||
for incmd in self._subprocess_commands:
|
for incmd in self._subprocess_commands:
|
||||||
# If we're passing a raw string to exec, no need to wrap it
|
# If we're passing a raw string to exec, no need to
|
||||||
# in any proper structure.
|
# wrap it in any proper structure.
|
||||||
if isinstance(incmd, str):
|
if isinstance(incmd, str):
|
||||||
self._subprocess.stdin.write((incmd + '\n').encode())
|
self._subprocess.stdin.write((incmd + '\n').encode())
|
||||||
self._subprocess.stdin.flush()
|
self._subprocess.stdin.flush()
|
||||||
@ -794,9 +833,9 @@ class ServerManagerApp:
|
|||||||
# Request restarts/shut-downs for various reasons.
|
# Request restarts/shut-downs for various reasons.
|
||||||
self._request_shutdowns_or_restarts()
|
self._request_shutdowns_or_restarts()
|
||||||
|
|
||||||
# If they want to force-kill our subprocess, simply exit this
|
# If they want to force-kill our subprocess, simply exit
|
||||||
# loop; the cleanup code will kill the process if its still
|
# this loop; the cleanup code will kill the process if its
|
||||||
# alive.
|
# still alive.
|
||||||
if (
|
if (
|
||||||
self._subprocess_force_kill_time is not None
|
self._subprocess_force_kill_time is not None
|
||||||
and time.time() > self._subprocess_force_kill_time
|
and time.time() > self._subprocess_force_kill_time
|
||||||
@ -855,8 +894,8 @@ class ServerManagerApp:
|
|||||||
self.restart(immediate=True)
|
self.restart(immediate=True)
|
||||||
self._subprocess_sent_config_auto_restart = True
|
self._subprocess_sent_config_auto_restart = True
|
||||||
|
|
||||||
# Attempt clean exit if our clean-exit-time passes.
|
# Attempt clean exit if our clean-exit-time passes (and enforce
|
||||||
# (and enforce a 6 hour max if not provided)
|
# a 6 hour max if not provided).
|
||||||
clean_exit_minutes = 360.0
|
clean_exit_minutes = 360.0
|
||||||
if self._config.clean_exit_minutes is not None:
|
if self._config.clean_exit_minutes is not None:
|
||||||
clean_exit_minutes = min(
|
clean_exit_minutes = min(
|
||||||
@ -881,8 +920,8 @@ class ServerManagerApp:
|
|||||||
self.shutdown(immediate=False)
|
self.shutdown(immediate=False)
|
||||||
self._subprocess_sent_clean_exit = True
|
self._subprocess_sent_clean_exit = True
|
||||||
|
|
||||||
# Attempt unclean exit if our unclean-exit-time passes.
|
# Attempt unclean exit if our unclean-exit-time passes (and
|
||||||
# (and enforce a 7 hour max if not provided)
|
# enforce a 7 hour max if not provided).
|
||||||
unclean_exit_minutes = 420.0
|
unclean_exit_minutes = 420.0
|
||||||
if self._config.unclean_exit_minutes is not None:
|
if self._config.unclean_exit_minutes is not None:
|
||||||
unclean_exit_minutes = min(
|
unclean_exit_minutes = min(
|
||||||
@ -924,8 +963,8 @@ class ServerManagerApp:
|
|||||||
|
|
||||||
print(f'{Clr.CYN}Stopping subprocess...{Clr.RST}', flush=True)
|
print(f'{Clr.CYN}Stopping subprocess...{Clr.RST}', flush=True)
|
||||||
|
|
||||||
# First, ask it nicely to die and give it a moment.
|
# First, ask it nicely to die and give it a moment. If that
|
||||||
# If that doesn't work, bring down the hammer.
|
# doesn't work, bring down the hammer.
|
||||||
self._subprocess.terminate()
|
self._subprocess.terminate()
|
||||||
try:
|
try:
|
||||||
self._subprocess.wait(timeout=10)
|
self._subprocess.wait(timeout=10)
|
||||||
@ -941,8 +980,9 @@ def main() -> None:
|
|||||||
try:
|
try:
|
||||||
ServerManagerApp().run()
|
ServerManagerApp().run()
|
||||||
except CleanError as exc:
|
except CleanError as exc:
|
||||||
# For clean errors, do a simple print and fail; no tracebacks/etc.
|
# For clean errors, do a simple print and fail; no
|
||||||
# Any others will bubble up and give us the usual mess.
|
# tracebacks/etc. Any others will bubble up and give us the
|
||||||
|
# usual mess.
|
||||||
exc.pretty_print()
|
exc.pretty_print()
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
|||||||
@ -59,6 +59,7 @@ void AppAdapterSDL::OnMainThreadStartApp() {
|
|||||||
"AppAdapterSDL strict_graphics_context_ is enabled."
|
"AppAdapterSDL strict_graphics_context_ is enabled."
|
||||||
" Remember to turn this off.");
|
" Remember to turn this off.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// We may or may not want xinput on windows.
|
// We may or may not want xinput on windows.
|
||||||
if (g_buildconfig.ostype_windows()) {
|
if (g_buildconfig.ostype_windows()) {
|
||||||
if (!g_core->platform->GetLowLevelConfigValue("enablexinput", 1)) {
|
if (!g_core->platform->GetLowLevelConfigValue("enablexinput", 1)) {
|
||||||
@ -66,6 +67,9 @@ void AppAdapterSDL::OnMainThreadStartApp() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We wrangle our own signal handling; don't bring SDL into it.
|
||||||
|
SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1");
|
||||||
|
|
||||||
int result = SDL_Init(sdl_flags);
|
int result = SDL_Init(sdl_flags);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
FatalError(std::string("SDL_Init failed: ") + SDL_GetError());
|
FatalError(std::string("SDL_Init failed: ") + SDL_GetError());
|
||||||
@ -96,7 +100,7 @@ void AppAdapterSDL::OnMainThreadStartApp() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We currently use a software cursor, so hide the system one.
|
// This adapter draws a software cursor; hide the actual OS one.
|
||||||
SDL_ShowCursor(SDL_DISABLE);
|
SDL_ShowCursor(SDL_DISABLE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
#include "ballistica/base/app_mode/app_mode.h"
|
#include "ballistica/base/app_mode/app_mode.h"
|
||||||
|
|
||||||
#include "ballistica/base/input/device/input_device_delegate.h"
|
#include "ballistica/base/input/device/input_device_delegate.h"
|
||||||
|
#include "ballistica/base/logic/logic.h"
|
||||||
#include "ballistica/base/support/context.h"
|
#include "ballistica/base/support/context.h"
|
||||||
|
|
||||||
namespace ballistica::base {
|
namespace ballistica::base {
|
||||||
@ -43,8 +44,8 @@ void AppMode::ChangeGameSpeed(int offs) {}
|
|||||||
|
|
||||||
void AppMode::StepDisplayTime() {}
|
void AppMode::StepDisplayTime() {}
|
||||||
|
|
||||||
auto AppMode::GetHeadlessDisplayStep() -> microsecs_t {
|
auto AppMode::GetHeadlessNextDisplayTimeStep() -> microsecs_t {
|
||||||
return kAppModeMaxHeadlessDisplayStep;
|
return kHeadlessMaxDisplayTimeStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto AppMode::GetPartySize() const -> int { return 0; }
|
auto AppMode::GetPartySize() const -> int { return 0; }
|
||||||
|
|||||||
@ -9,15 +9,6 @@
|
|||||||
|
|
||||||
namespace ballistica::base {
|
namespace ballistica::base {
|
||||||
|
|
||||||
/// The max amount of time a headless app can sleep if no events are pending.
|
|
||||||
/// This should not be *too* high or it might cause delays when going from
|
|
||||||
/// no events present to events present.
|
|
||||||
const microsecs_t kAppModeMaxHeadlessDisplayStep{500000};
|
|
||||||
|
|
||||||
/// The min amount of time a headless app can sleep. This provides an upper
|
|
||||||
/// limit on stepping overhead in cases where events are densely packed.
|
|
||||||
const microsecs_t kAppModeMinHeadlessDisplayStep{1000};
|
|
||||||
|
|
||||||
/// Represents 'what the app is doing'. The global app-mode can be switched
|
/// Represents 'what the app is doing'. The global app-mode can be switched
|
||||||
/// as the app is running. The Python layer has its own Python AppMode
|
/// as the app is running. The Python layer has its own Python AppMode
|
||||||
/// classes, and generally when one of them becomes active it calls down
|
/// classes, and generally when one of them becomes active it calls down
|
||||||
@ -51,9 +42,9 @@ class AppMode {
|
|||||||
/// Called right after stepping; should return the exact microseconds
|
/// Called right after stepping; should return the exact microseconds
|
||||||
/// between the current display time and the next event the app-mode has
|
/// between the current display time and the next event the app-mode has
|
||||||
/// scheduled. If no events are pending, should return
|
/// scheduled. If no events are pending, should return
|
||||||
/// kAppModeMaxHeadlessDisplayStep. This will only be called on headless
|
/// kHeadlessMaxDisplayTimeStep. This will only be called on headless
|
||||||
/// builds.
|
/// builds.
|
||||||
virtual auto GetHeadlessDisplayStep() -> microsecs_t;
|
virtual auto GetHeadlessNextDisplayTimeStep() -> microsecs_t;
|
||||||
|
|
||||||
/// Create a delegate for an input-device.
|
/// Create a delegate for an input-device.
|
||||||
/// Return a raw pointer allocated using Object::NewDeferred.
|
/// Return a raw pointer allocated using Object::NewDeferred.
|
||||||
|
|||||||
@ -197,9 +197,9 @@ void BaseFeatureSet::StartApp() {
|
|||||||
assets_server->OnMainThreadStartApp();
|
assets_server->OnMainThreadStartApp();
|
||||||
app_adapter->OnMainThreadStartApp();
|
app_adapter->OnMainThreadStartApp();
|
||||||
|
|
||||||
// Take note that we're now 'running'. Various code such as anything that
|
// Ok; we're now official 'started'. Various code such as anything that
|
||||||
// pushes messages to threads can watch for this state to avoid crashing
|
// pushes messages to threads can watch for this state (via IsAppStarted()
|
||||||
// if called early.
|
// to avoid crashing if called early.
|
||||||
app_started_ = true;
|
app_started_ = true;
|
||||||
|
|
||||||
// Inform anyone who wants to know that we're done starting.
|
// Inform anyone who wants to know that we're done starting.
|
||||||
|
|||||||
@ -603,14 +603,13 @@ class BaseFeatureSet : public FeatureSetNativeComponent,
|
|||||||
/// Start app systems in motion.
|
/// Start app systems in motion.
|
||||||
void StartApp() override;
|
void StartApp() override;
|
||||||
|
|
||||||
/// Issue a high level app quit request. Can be called from any thread.
|
/// Issue a high level app quit request. Can be called from any thread and
|
||||||
/// 'soft' means the app can simply reset/hide itself instead of actually
|
/// can be safely called repeatedly. If 'confirm' is true, a confirmation
|
||||||
/// exiting the process (common behavior on mobile platforms). 'back'
|
/// dialog will be presented if the environment and situation allows;
|
||||||
/// means that a soft-quit should behave as if a back-button was pressed,
|
/// otherwise the quit will be immediate. A QuitType arg can optionally be
|
||||||
/// which may trigger different behavior in the OS than a standard soft
|
/// passed to influence quit behavior; on some platforms such as mobile
|
||||||
/// quit. If 'confirm' is true, a confirmation dialog will be presented if
|
/// the default is for the app to recede to the background but physically
|
||||||
/// the current app-mode provides one and the app is in gui mode.
|
/// remain running.
|
||||||
/// Otherwise the quit will be immediate.
|
|
||||||
void QuitApp(bool confirm = false, QuitType quit_type = QuitType::kSoft);
|
void QuitApp(bool confirm = false, QuitType quit_type = QuitType::kSoft);
|
||||||
|
|
||||||
/// Called when app shutdown process completes. Sets app to exit.
|
/// Called when app shutdown process completes. Sets app to exit.
|
||||||
|
|||||||
@ -336,6 +336,7 @@ class Graphics::ScreenMessageEntry {
|
|||||||
float v_smoothed{};
|
float v_smoothed{};
|
||||||
bool translation_dirty{true};
|
bool translation_dirty{true};
|
||||||
bool mesh_dirty{true};
|
bool mesh_dirty{true};
|
||||||
|
millisecs_t smooth_time{};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Object::Ref<TextGroup> s_mesh_;
|
Object::Ref<TextGroup> s_mesh_;
|
||||||
@ -594,13 +595,21 @@ void Graphics::DrawMiscOverlays(FrameDef* frame_def) {
|
|||||||
{
|
{
|
||||||
auto xf = c.ScopedTransform();
|
auto xf = c.ScopedTransform();
|
||||||
|
|
||||||
if (i->v_smoothed == 0.0f) {
|
// This logic needs to run at a fixed hz or it breaks on high frame
|
||||||
i->v_smoothed = v + v_extra;
|
// rates.
|
||||||
} else {
|
auto now_millisecs = pass->frame_def()->display_time_millisecs();
|
||||||
float smoothing = 0.8f;
|
i->smooth_time = std::max(i->smooth_time, now_millisecs - 100);
|
||||||
i->v_smoothed = smoothing * i->v_smoothed
|
while (i->smooth_time < now_millisecs) {
|
||||||
+ (1.0f - smoothing) * (v + v_extra);
|
i->smooth_time += 1000 / 60;
|
||||||
|
if (i->v_smoothed == 0.0f) {
|
||||||
|
i->v_smoothed = v + v_extra;
|
||||||
|
} else {
|
||||||
|
float smoothing = 0.8f;
|
||||||
|
i->v_smoothed = smoothing * i->v_smoothed
|
||||||
|
+ (1.0f - smoothing) * (v + v_extra);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Translate(screen_width * 0.5f, i->v_smoothed,
|
c.Translate(screen_width * 0.5f, i->v_smoothed,
|
||||||
vr ? 60 : kScreenMessageZDepth);
|
vr ? 60 : kScreenMessageZDepth);
|
||||||
if (vr) {
|
if (vr) {
|
||||||
@ -767,10 +776,17 @@ void Graphics::DrawMiscOverlays(FrameDef* frame_def) {
|
|||||||
a = 1;
|
a = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
i->v_smoothed += 0.1f;
|
// This logic needs to run at a fixed hz or it breaks on high frame
|
||||||
if (i->v_smoothed - last_v < min_spacing) {
|
// rates.
|
||||||
i->v_smoothed +=
|
auto now_millisecs = pass->frame_def()->display_time_millisecs();
|
||||||
8.0f * (1.0f - ((i->v_smoothed - last_v) / min_spacing));
|
i->smooth_time = std::max(i->smooth_time, now_millisecs - 100);
|
||||||
|
while (i->smooth_time < now_millisecs) {
|
||||||
|
i->smooth_time += 1000 / 60;
|
||||||
|
i->v_smoothed += 0.1f;
|
||||||
|
if (i->v_smoothed - last_v < min_spacing) {
|
||||||
|
i->v_smoothed +=
|
||||||
|
8.0f * (1.0f - ((i->v_smoothed - last_v) / min_spacing));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
last_v = i->v_smoothed;
|
last_v = i->v_smoothed;
|
||||||
|
|
||||||
@ -864,11 +880,11 @@ void Graphics::GetSafeColor(float* red, float* green, float* blue,
|
|||||||
*blue = std::min(1.0f, (*blue) * s);
|
*blue = std::min(1.0f, (*blue) * s);
|
||||||
}
|
}
|
||||||
|
|
||||||
// We may still be short of our target intensity due to clamping (ie: (10,0,0)
|
// We may still be short of our target intensity due to clamping (ie:
|
||||||
// will not look any brighter than (1,0,0)) if that's the case, just convert
|
// (10,0,0) will not look any brighter than (1,0,0)) if that's the case,
|
||||||
// the difference to a grey value and add that to all channels... this *still*
|
// just convert the difference to a grey value and add that to all
|
||||||
// might not get us there so lets do it a few times if need be. (i'm sure
|
// channels... this *still* might not get us there so lets do it a few times
|
||||||
// there's a less bone-headed way to do this)
|
// if need be. (i'm sure there's a less bone-headed way to do this)
|
||||||
for (int i = 0; i < 4; i++) {
|
for (int i = 0; i < 4; i++) {
|
||||||
float remaining =
|
float remaining =
|
||||||
(0.2989f * (*red) + 0.5870f * (*green) + 0.1140f * (*blue)) - 1.0f;
|
(0.2989f * (*red) + 0.5870f * (*green) + 0.1140f * (*blue)) - 1.0f;
|
||||||
@ -1050,14 +1066,13 @@ void Graphics::UpdateGyro(microsecs_t time_microsecs,
|
|||||||
tilt_vel_ = tilt_smoothed_ * 3.0f;
|
tilt_vel_ = tilt_smoothed_ * 3.0f;
|
||||||
tilt_pos_ += tilt_vel_ * timescale;
|
tilt_pos_ += tilt_vel_ * timescale;
|
||||||
|
|
||||||
// Technically this will behave slightly differently at different time scales,
|
// Technically this will behave slightly differently at different time
|
||||||
// but it should be close to correct..
|
// scales, but it should be close to correct.. tilt_pos_ *= 0.991f;
|
||||||
// tilt_pos_ *= 0.991f;
|
|
||||||
tilt_pos_ *= std::max(0.0f, 1.0f - 0.01f * timescale);
|
tilt_pos_ *= std::max(0.0f, 1.0f - 0.01f * timescale);
|
||||||
|
|
||||||
// Some gyros seem wonky and either give us crazy big values or consistently
|
// Some gyros seem wonky and either give us crazy big values or consistently
|
||||||
// offset ones. Let's keep a running tally of magnitude that slowly drops over
|
// offset ones. Let's keep a running tally of magnitude that slowly drops
|
||||||
// time, and if it reaches a certain value lets just kill gyro input.
|
// over time, and if it reaches a certain value lets just kill gyro input.
|
||||||
if (gyro_broken_) {
|
if (gyro_broken_) {
|
||||||
tilt_pos_ *= 0.0f;
|
tilt_pos_ *= 0.0f;
|
||||||
} else {
|
} else {
|
||||||
@ -1485,9 +1500,9 @@ void Graphics::DrawCursor(FrameDef* frame_def) {
|
|||||||
{
|
{
|
||||||
auto xf = c.ScopedTransform();
|
auto xf = c.ScopedTransform();
|
||||||
|
|
||||||
// Note: we don't plug in known cursor position values here; we tell the
|
// Note: we don't plug in known cursor position values here; we tell
|
||||||
// renderer to insert the latest values on its end; this can lessen
|
// the renderer to insert the latest values on its end; this can
|
||||||
// cursor lag substantially.
|
// lessen cursor lag substantially.
|
||||||
c.CursorTranslate();
|
c.CursorTranslate();
|
||||||
c.Translate(csize * 0.40f, csize * -0.38f, kCursorZDepth);
|
c.Translate(csize * 0.40f, csize * -0.38f, kCursorZDepth);
|
||||||
c.Scale(csize, csize);
|
c.Scale(csize, csize);
|
||||||
|
|||||||
@ -99,7 +99,7 @@ void Logic::OnGraphicsReady() {
|
|||||||
// Anyone dealing in display-time should be able to handle a wide
|
// Anyone dealing in display-time should be able to handle a wide
|
||||||
// variety of rates anyway. NOTE: This length is currently milliseconds.
|
// variety of rates anyway. NOTE: This length is currently milliseconds.
|
||||||
headless_display_time_step_timer_ = event_loop()->NewTimer(
|
headless_display_time_step_timer_ = event_loop()->NewTimer(
|
||||||
kAppModeMinHeadlessDisplayStep / 1000, true,
|
kHeadlessMinDisplayTimeStep / 1000, true,
|
||||||
NewLambdaRunnable([this] { StepDisplayTime_(); }));
|
NewLambdaRunnable([this] { StepDisplayTime_(); }));
|
||||||
} else {
|
} else {
|
||||||
// In gui mode, push an initial frame to the graphics server. From this
|
// In gui mode, push an initial frame to the graphics server. From this
|
||||||
@ -382,7 +382,7 @@ void Logic::OnAppModeChanged() {
|
|||||||
}
|
}
|
||||||
assert(headless_display_time_step_timer_);
|
assert(headless_display_time_step_timer_);
|
||||||
// NOTE: This is currently milliseconds.
|
// NOTE: This is currently milliseconds.
|
||||||
headless_display_time_step_timer_->SetLength(kAppModeMinHeadlessDisplayStep
|
headless_display_time_step_timer_->SetLength(kHeadlessMinDisplayTimeStep
|
||||||
/ 1000);
|
/ 1000);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -424,9 +424,9 @@ void Logic::PostUpdateDisplayTimeForHeadlessMode_() {
|
|||||||
// we've got until the next event. We'll plug this into our display-update
|
// we've got until the next event. We'll plug this into our display-update
|
||||||
// timer so we can try to sleep exactly until that point.
|
// timer so we can try to sleep exactly until that point.
|
||||||
auto headless_display_step_microsecs =
|
auto headless_display_step_microsecs =
|
||||||
std::max(std::min(g_base->app_mode()->GetHeadlessDisplayStep(),
|
std::max(std::min(g_base->app_mode()->GetHeadlessNextDisplayTimeStep(),
|
||||||
kAppModeMaxHeadlessDisplayStep),
|
kHeadlessMaxDisplayTimeStep),
|
||||||
kAppModeMinHeadlessDisplayStep);
|
kHeadlessMinDisplayTimeStep);
|
||||||
|
|
||||||
if (debug_log_display_time_) {
|
if (debug_log_display_time_) {
|
||||||
auto sleepsecs =
|
auto sleepsecs =
|
||||||
|
|||||||
@ -13,6 +13,15 @@ namespace ballistica::base {
|
|||||||
|
|
||||||
const int kDisplayTimeSampleCount{15};
|
const int kDisplayTimeSampleCount{15};
|
||||||
|
|
||||||
|
/// The max amount of time a headless app can sleep if no events are pending.
|
||||||
|
/// This should not be *too* high or it might cause delays when going from
|
||||||
|
/// no events present to events present.
|
||||||
|
const microsecs_t kHeadlessMaxDisplayTimeStep{500000};
|
||||||
|
|
||||||
|
/// The min amount of time a headless app can sleep. This provides an upper
|
||||||
|
/// limit on stepping overhead in cases where events are densely packed.
|
||||||
|
const microsecs_t kHeadlessMinDisplayTimeStep{1000};
|
||||||
|
|
||||||
/// The logic subsystem of the app. This runs on a dedicated thread and is
|
/// The logic subsystem of the app. This runs on a dedicated thread and is
|
||||||
/// where most high level app logic happens. Much app functionality
|
/// where most high level app logic happens. Much app functionality
|
||||||
/// including UI calls must be run on the logic thread.
|
/// including UI calls must be run on the logic thread.
|
||||||
@ -63,8 +72,9 @@ class Logic {
|
|||||||
/// graphical builds we also use this opportunity to step our logic.
|
/// graphical builds we also use this opportunity to step our logic.
|
||||||
void Draw();
|
void Draw();
|
||||||
|
|
||||||
/// Kick off an app shutdown. Shutdown is an asynchronous process which
|
/// Kick off a low level app shutdown. Shutdown is an asynchronous process
|
||||||
/// may take a bit of time to complete. Safe to call repeatedly.
|
/// which may take up to a few seconds to complete. This is safe to call
|
||||||
|
/// repeatedly but must be called from the logic thread.
|
||||||
void Shutdown();
|
void Shutdown();
|
||||||
|
|
||||||
/// Should be called by the Python layer when it has completed all
|
/// Should be called by the Python layer when it has completed all
|
||||||
|
|||||||
@ -208,6 +208,8 @@ void AppConfig::SetupEntries() {
|
|||||||
|
|
||||||
int_entries_[IntID::kPort] = IntEntry("Port", kDefaultPort);
|
int_entries_[IntID::kPort] = IntEntry("Port", kDefaultPort);
|
||||||
int_entries_[IntID::kMaxFPS] = IntEntry("Max FPS", 60);
|
int_entries_[IntID::kMaxFPS] = IntEntry("Max FPS", 60);
|
||||||
|
int_entries_[IntID::kSceneV1HostProtocol] =
|
||||||
|
IntEntry("SceneV1 Host Protocol", 33);
|
||||||
|
|
||||||
bool_entries_[BoolID::kTouchControlsSwipeHidden] =
|
bool_entries_[BoolID::kTouchControlsSwipeHidden] =
|
||||||
BoolEntry("Touch Controls Swipe Hidden", false);
|
BoolEntry("Touch Controls Swipe Hidden", false);
|
||||||
|
|||||||
@ -55,6 +55,7 @@ class AppConfig {
|
|||||||
enum class IntID {
|
enum class IntID {
|
||||||
kPort,
|
kPort,
|
||||||
kMaxFPS,
|
kMaxFPS,
|
||||||
|
kSceneV1HostProtocol,
|
||||||
kLast // Sentinel.
|
kLast // Sentinel.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,10 @@ namespace ballistica::scene_v1 {
|
|||||||
// How long new clients have to wait before starting a kick vote.
|
// How long new clients have to wait before starting a kick vote.
|
||||||
const int kNewClientKickVoteDelay = 60000;
|
const int kNewClientKickVoteDelay = 60000;
|
||||||
|
|
||||||
ConnectionToClient::ConnectionToClient(int id) : id_(id) {
|
ConnectionToClient::ConnectionToClient(int id)
|
||||||
|
: id_(id),
|
||||||
|
protocol_version_{
|
||||||
|
SceneV1AppMode::GetSingleton()->host_protocol_version()} {
|
||||||
// We calc this once just in case it changes on our end
|
// We calc this once just in case it changes on our end
|
||||||
// (the client uses it for their verification hash so we need to
|
// (the client uses it for their verification hash so we need to
|
||||||
// ensure it stays consistent).
|
// ensure it stays consistent).
|
||||||
@ -33,7 +36,7 @@ ConnectionToClient::ConnectionToClient(int id) : id_(id) {
|
|||||||
|
|
||||||
// On newer protocols we include an extra salt value
|
// On newer protocols we include an extra salt value
|
||||||
// to ensure the hash the client generates can't be recycled.
|
// to ensure the hash the client generates can't be recycled.
|
||||||
if (explicit_bool(kProtocolVersion >= 33)) {
|
if (explicit_bool(protocol_version() >= 33)) {
|
||||||
our_handshake_salt_ = std::to_string(rand()); // NOLINT
|
our_handshake_salt_ = std::to_string(rand()); // NOLINT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -95,7 +98,7 @@ void ConnectionToClient::Update() {
|
|||||||
// In newer protocols we embed a json dict as the second part of the
|
// In newer protocols we embed a json dict as the second part of the
|
||||||
// handshake packet; this way we can evolve the protocol more
|
// handshake packet; this way we can evolve the protocol more
|
||||||
// easily in the future.
|
// easily in the future.
|
||||||
if (explicit_bool(kProtocolVersion >= 33)) {
|
if (explicit_bool(protocol_version() >= 33)) {
|
||||||
// Construct a json dict with our player-spec-string as one element.
|
// Construct a json dict with our player-spec-string as one element.
|
||||||
JsonDict dict;
|
JsonDict dict;
|
||||||
dict.AddString("s", our_handshake_player_spec_str_);
|
dict.AddString("s", our_handshake_player_spec_str_);
|
||||||
@ -106,17 +109,17 @@ void ConnectionToClient::Update() {
|
|||||||
std::string out = dict.PrintUnformatted();
|
std::string out = dict.PrintUnformatted();
|
||||||
std::vector<uint8_t> data(3 + out.size());
|
std::vector<uint8_t> data(3 + out.size());
|
||||||
data[0] = BA_SCENEPACKET_HANDSHAKE;
|
data[0] = BA_SCENEPACKET_HANDSHAKE;
|
||||||
uint16_t val = kProtocolVersion;
|
uint16_t val = protocol_version();
|
||||||
memcpy(data.data() + 1, &val, sizeof(val));
|
memcpy(data.data() + 1, &val, sizeof(val));
|
||||||
memcpy(data.data() + 3, out.c_str(), out.size());
|
memcpy(data.data() + 3, out.c_str(), out.size());
|
||||||
SendGamePacket(data);
|
SendGamePacket(data);
|
||||||
} else {
|
} else {
|
||||||
// (KILL THIS WHEN kProtocolVersionMin >= 33)
|
// (KILL THIS WHEN kProtocolVersionClientMin >= 33)
|
||||||
// on older protocols, we simply embedded our spec-string as the second
|
// on older protocols, we simply embedded our spec-string as the second
|
||||||
// part of the handshake packet
|
// part of the handshake packet
|
||||||
std::vector<uint8_t> data(3 + our_handshake_player_spec_str_.size());
|
std::vector<uint8_t> data(3 + our_handshake_player_spec_str_.size());
|
||||||
data[0] = BA_SCENEPACKET_HANDSHAKE;
|
data[0] = BA_SCENEPACKET_HANDSHAKE;
|
||||||
uint16_t val = kProtocolVersion;
|
uint16_t val = protocol_version();
|
||||||
memcpy(data.data() + 1, &val, sizeof(val));
|
memcpy(data.data() + 1, &val, sizeof(val));
|
||||||
memcpy(data.data() + 3, our_handshake_player_spec_str_.c_str(),
|
memcpy(data.data() + 3, our_handshake_player_spec_str_.c_str(),
|
||||||
our_handshake_player_spec_str_.size());
|
our_handshake_player_spec_str_.size());
|
||||||
@ -155,7 +158,7 @@ void ConnectionToClient::HandleGamePacket(const std::vector<uint8_t>& data) {
|
|||||||
|
|
||||||
// In newer builds we expect to be sent a json dict here;
|
// In newer builds we expect to be sent a json dict here;
|
||||||
// pull client's spec from that.
|
// pull client's spec from that.
|
||||||
if (explicit_bool(kProtocolVersion >= 33)) {
|
if (protocol_version() >= 33) {
|
||||||
std::vector<char> string_buffer(data.size() - 3 + 1);
|
std::vector<char> string_buffer(data.size() - 3 + 1);
|
||||||
memcpy(&(string_buffer[0]), &(data[3]), data.size() - 3);
|
memcpy(&(string_buffer[0]), &(data[3]), data.size() - 3);
|
||||||
string_buffer[string_buffer.size() - 1] = 0;
|
string_buffer[string_buffer.size() - 1] = 0;
|
||||||
@ -173,7 +176,7 @@ void ConnectionToClient::HandleGamePacket(const std::vector<uint8_t>& data) {
|
|||||||
cJSON_Delete(handshake);
|
cJSON_Delete(handshake);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// (KILL THIS WHEN kProtocolVersionMin >= 33)
|
// (KILL THIS WHEN kProtocolVersionClientMin >= 33)
|
||||||
// older versions only contained the client spec
|
// older versions only contained the client spec
|
||||||
// pull client's spec from the handshake packet..
|
// pull client's spec from the handshake packet..
|
||||||
std::vector<char> string_buffer(data.size() - 3 + 1);
|
std::vector<char> string_buffer(data.size() - 3 + 1);
|
||||||
@ -195,7 +198,7 @@ void ConnectionToClient::HandleGamePacket(const std::vector<uint8_t>& data) {
|
|||||||
// Bytes 2 and 3 are their protocol version.
|
// Bytes 2 and 3 are their protocol version.
|
||||||
uint16_t val;
|
uint16_t val;
|
||||||
memcpy(&val, data.data() + 1, sizeof(val));
|
memcpy(&val, data.data() + 1, sizeof(val));
|
||||||
if (val != kProtocolVersion) {
|
if (val != protocol_version()) {
|
||||||
// Depending on the connection type we may print the connection
|
// Depending on the connection type we may print the connection
|
||||||
// failure or not. (If we invited them it'd be good to know about the
|
// failure or not. (If we invited them it'd be good to know about the
|
||||||
// failure).
|
// failure).
|
||||||
|
|||||||
@ -55,10 +55,17 @@ class ConnectionToClient : public Connection {
|
|||||||
// or their peer name if they have no players.
|
// or their peer name if they have no players.
|
||||||
auto GetCombinedSpec() -> PlayerSpec;
|
auto GetCombinedSpec() -> PlayerSpec;
|
||||||
|
|
||||||
|
auto protocol_version() const {
|
||||||
|
assert(protocol_version_ != -1);
|
||||||
|
return protocol_version_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
virtual auto ShouldPrintIncompatibleClientErrors() const -> bool;
|
virtual auto ShouldPrintIncompatibleClientErrors() const -> bool;
|
||||||
auto GetClientInputDevice(int remote_id) -> ClientInputDevice*;
|
auto GetClientInputDevice(int remote_id) -> ClientInputDevice*;
|
||||||
void Error(const std::string& error_msg) override;
|
void Error(const std::string& error_msg) override;
|
||||||
|
|
||||||
|
int protocol_version_;
|
||||||
std::string our_handshake_player_spec_str_;
|
std::string our_handshake_player_spec_str_;
|
||||||
std::string our_handshake_salt_;
|
std::string our_handshake_salt_;
|
||||||
std::string peer_public_account_id_;
|
std::string peer_public_account_id_;
|
||||||
|
|||||||
@ -22,7 +22,9 @@ namespace ballistica::scene_v1 {
|
|||||||
// How long to go between sending out null packets for pings.
|
// How long to go between sending out null packets for pings.
|
||||||
const int kPingSendInterval = 2000;
|
const int kPingSendInterval = 2000;
|
||||||
|
|
||||||
ConnectionToHost::ConnectionToHost() = default;
|
ConnectionToHost::ConnectionToHost()
|
||||||
|
: protocol_version_{
|
||||||
|
SceneV1AppMode::GetSingleton()->host_protocol_version()} {}
|
||||||
|
|
||||||
auto ConnectionToHost::GetAsUDP() -> ConnectionToHostUDP* { return nullptr; }
|
auto ConnectionToHost::GetAsUDP() -> ConnectionToHostUDP* { return nullptr; }
|
||||||
|
|
||||||
@ -103,8 +105,8 @@ void ConnectionToHost::HandleGamePacket(const std::vector<uint8_t>& data) {
|
|||||||
uint16_t their_protocol_version;
|
uint16_t their_protocol_version;
|
||||||
memcpy(&their_protocol_version, data.data() + 1,
|
memcpy(&their_protocol_version, data.data() + 1,
|
||||||
sizeof(their_protocol_version));
|
sizeof(their_protocol_version));
|
||||||
if (their_protocol_version >= kProtocolVersionMin
|
if (their_protocol_version >= kProtocolVersionClientMin
|
||||||
&& their_protocol_version <= kProtocolVersion) {
|
&& their_protocol_version <= kProtocolVersionMax) {
|
||||||
compatible = true;
|
compatible = true;
|
||||||
|
|
||||||
// If we are compatible, set our protocol version to match
|
// If we are compatible, set our protocol version to match
|
||||||
@ -136,7 +138,7 @@ void ConnectionToHost::HandleGamePacket(const std::vector<uint8_t>& data) {
|
|||||||
memcpy(data2.data() + 3, out.c_str(), out.size());
|
memcpy(data2.data() + 3, out.c_str(), out.size());
|
||||||
SendGamePacket(data2);
|
SendGamePacket(data2);
|
||||||
} else {
|
} else {
|
||||||
// (KILL THIS WHEN kProtocolVersionMin >= 33)
|
// (KILL THIS WHEN kProtocolVersionClientMin >= 33)
|
||||||
std::string our_spec_str =
|
std::string our_spec_str =
|
||||||
PlayerSpec::GetAccountPlayerSpec().GetSpecString();
|
PlayerSpec::GetAccountPlayerSpec().GetSpecString();
|
||||||
std::vector<uint8_t> response(3 + our_spec_str.size());
|
std::vector<uint8_t> response(3 + our_spec_str.size());
|
||||||
@ -148,7 +150,7 @@ void ConnectionToHost::HandleGamePacket(const std::vector<uint8_t>& data) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!compatible) {
|
if (!compatible) {
|
||||||
if (their_protocol_version > kProtocolVersion) {
|
if (their_protocol_version > protocol_version()) {
|
||||||
Error(g_base->assets->GetResourceString(
|
Error(g_base->assets->GetResourceString(
|
||||||
"incompatibleNewerVersionHostText"));
|
"incompatibleNewerVersionHostText"));
|
||||||
} else {
|
} else {
|
||||||
@ -183,7 +185,7 @@ void ConnectionToHost::HandleGamePacket(const std::vector<uint8_t>& data) {
|
|||||||
cJSON_Delete(handshake);
|
cJSON_Delete(handshake);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// (KILL THIS WHEN kProtocolVersionMin >= 33)
|
// (KILL THIS WHEN kProtocolVersionClientMin >= 33)
|
||||||
// In older protocols, handshake simply contained a
|
// In older protocols, handshake simply contained a
|
||||||
// player-spec for the host.
|
// player-spec for the host.
|
||||||
|
|
||||||
|
|||||||
@ -33,12 +33,12 @@ class ConnectionToHost : public Connection {
|
|||||||
std::string party_name_;
|
std::string party_name_;
|
||||||
std::string peer_hash_input_;
|
std::string peer_hash_input_;
|
||||||
std::string peer_hash_;
|
std::string peer_hash_;
|
||||||
bool printed_connect_message_ = false;
|
// Can remove once back-compat protocol is > 29
|
||||||
int protocol_version_ = kProtocolVersion;
|
bool ignore_old_attach_remote_player_packets_ : 1 {};
|
||||||
int build_number_ = 0;
|
bool printed_connect_message_ : 1 {};
|
||||||
bool got_host_info_ = false;
|
bool got_host_info_ : 1 {};
|
||||||
// can remove once back-compat protocol is > 29
|
int protocol_version_{-1};
|
||||||
bool ignore_old_attach_remote_player_packets_ = false;
|
int build_number_{};
|
||||||
millisecs_t last_ping_send_time_{};
|
millisecs_t last_ping_send_time_{};
|
||||||
// the client-session that we're driving
|
// the client-session that we're driving
|
||||||
Object::WeakRef<ClientSession> client_session_;
|
Object::WeakRef<ClientSession> client_session_;
|
||||||
|
|||||||
@ -13,7 +13,7 @@
|
|||||||
namespace ballistica::scene_v1 {
|
namespace ballistica::scene_v1 {
|
||||||
|
|
||||||
auto ConnectionToHostUDP::SwitchProtocol() -> bool {
|
auto ConnectionToHostUDP::SwitchProtocol() -> bool {
|
||||||
if (protocol_version() > kProtocolVersionMin) {
|
if (protocol_version() > kProtocolVersionClientMin) {
|
||||||
set_protocol_version(protocol_version() - 1);
|
set_protocol_version(protocol_version() - 1);
|
||||||
|
|
||||||
// Need a new request id so we ignore further responses to our previous
|
// Need a new request id so we ignore further responses to our previous
|
||||||
|
|||||||
@ -72,7 +72,7 @@ class ImageNode : public Node {
|
|||||||
void set_front(bool val) { front_ = val; }
|
void set_front(bool val) { front_ = val; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
enum class Attach {
|
enum class Attach : uint8_t {
|
||||||
CENTER,
|
CENTER,
|
||||||
TOP_LEFT,
|
TOP_LEFT,
|
||||||
TOP_CENTER,
|
TOP_CENTER,
|
||||||
@ -83,32 +83,24 @@ class ImageNode : public Node {
|
|||||||
BOTTOM_LEFT,
|
BOTTOM_LEFT,
|
||||||
CENTER_LEFT
|
CENTER_LEFT
|
||||||
};
|
};
|
||||||
bool host_only_{};
|
|
||||||
bool front_{};
|
bool host_only_ : 1 {};
|
||||||
float vr_depth_{};
|
bool front_ : 1 {};
|
||||||
std::vector<float> scale_{1.0f, 1.0f};
|
bool absolute_scale_ : 1 {true};
|
||||||
std::vector<float> position_{0.0f, 0.0f};
|
bool premultiplied_ : 1 {};
|
||||||
std::vector<float> color_{1.0f, 1.0f, 1.0f};
|
bool fill_screen_ : 1 {};
|
||||||
std::vector<float> tint_color_{1.0f, 1.0f, 1.0f};
|
bool has_alpha_channel_ : 1 {true};
|
||||||
std::vector<float> tint2_color_{1.0f, 1.0f, 1.0f};
|
bool dirty_ : 1 {true};
|
||||||
Object::Ref<SceneTexture> texture_;
|
|
||||||
Object::Ref<SceneTexture> tint_texture_;
|
|
||||||
Object::Ref<SceneTexture> mask_texture_;
|
|
||||||
Object::Ref<SceneMesh> mesh_opaque_;
|
|
||||||
Object::Ref<SceneMesh> mesh_transparent_;
|
|
||||||
bool fill_screen_{};
|
|
||||||
bool has_alpha_channel_{true};
|
|
||||||
bool dirty_{true};
|
|
||||||
float opacity_{1.0f};
|
|
||||||
Attach attach_{Attach::CENTER};
|
Attach attach_{Attach::CENTER};
|
||||||
bool absolute_scale_{true};
|
|
||||||
|
float vr_depth_{};
|
||||||
|
float opacity_{1.0f};
|
||||||
float center_x_{};
|
float center_x_{};
|
||||||
float center_y_{};
|
float center_y_{};
|
||||||
float width_{};
|
float width_{};
|
||||||
float height_{};
|
float height_{};
|
||||||
float tilt_translate_{};
|
float tilt_translate_{};
|
||||||
float rotate_{};
|
float rotate_{};
|
||||||
bool premultiplied_{};
|
|
||||||
float red_{1.0f};
|
float red_{1.0f};
|
||||||
float green_{1.0f};
|
float green_{1.0f};
|
||||||
float blue_{1.0f};
|
float blue_{1.0f};
|
||||||
@ -119,6 +111,16 @@ class ImageNode : public Node {
|
|||||||
float tint2_red_{1.0f};
|
float tint2_red_{1.0f};
|
||||||
float tint2_green_{1.0f};
|
float tint2_green_{1.0f};
|
||||||
float tint2_blue_{1.0f};
|
float tint2_blue_{1.0f};
|
||||||
|
std::vector<float> scale_{1.0f, 1.0f};
|
||||||
|
std::vector<float> position_{0.0f, 0.0f};
|
||||||
|
std::vector<float> color_{1.0f, 1.0f, 1.0f};
|
||||||
|
std::vector<float> tint_color_{1.0f, 1.0f, 1.0f};
|
||||||
|
std::vector<float> tint2_color_{1.0f, 1.0f, 1.0f};
|
||||||
|
Object::Ref<SceneTexture> texture_;
|
||||||
|
Object::Ref<SceneTexture> tint_texture_;
|
||||||
|
Object::Ref<SceneTexture> mask_texture_;
|
||||||
|
Object::Ref<SceneMesh> mesh_opaque_;
|
||||||
|
Object::Ref<SceneMesh> mesh_transparent_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace ballistica::scene_v1
|
} // namespace ballistica::scene_v1
|
||||||
|
|||||||
@ -1046,7 +1046,24 @@ static auto PyCameraShake(PyObject* self, PyObject* args, PyObject* keywds)
|
|||||||
const_cast<char**>(kwlist), &intensity)) {
|
const_cast<char**>(kwlist), &intensity)) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
g_base->graphics->LocalCameraShake(intensity);
|
|
||||||
|
if (Scene* scene = ContextRefSceneV1::FromCurrent().GetMutableScene()) {
|
||||||
|
// Send to clients/replays (IF we're servering protocol 35+).
|
||||||
|
if (SceneV1AppMode::GetSingleton()->host_protocol_version() >= 35) {
|
||||||
|
if (SessionStream* output_stream = scene->GetSceneStream()) {
|
||||||
|
output_stream->EmitCameraShake(intensity);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Depict locally.
|
||||||
|
if (!g_core->HeadlessMode()) {
|
||||||
|
g_base->graphics->LocalCameraShake(intensity);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
throw Exception("Can't shake the camera in this context_ref.",
|
||||||
|
PyExcType::kContext);
|
||||||
|
}
|
||||||
|
|
||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
BA_PYTHON_CATCH;
|
BA_PYTHON_CATCH;
|
||||||
}
|
}
|
||||||
@ -1172,9 +1189,13 @@ static auto PyEmitFx(PyObject* self, PyObject* args, PyObject* keywds)
|
|||||||
e.spread = spread;
|
e.spread = spread;
|
||||||
e.chunk_type = chunk_type;
|
e.chunk_type = chunk_type;
|
||||||
e.tendril_type = tendril_type;
|
e.tendril_type = tendril_type;
|
||||||
|
|
||||||
|
// Send to clients/replays.
|
||||||
if (SessionStream* output_stream = scene->GetSceneStream()) {
|
if (SessionStream* output_stream = scene->GetSceneStream()) {
|
||||||
output_stream->EmitBGDynamics(e);
|
output_stream->EmitBGDynamics(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Depict locally.
|
||||||
if (!g_core->HeadlessMode()) {
|
if (!g_core->HeadlessMode()) {
|
||||||
g_base->bg_dynamics->Emit(e);
|
g_base->bg_dynamics->Emit(e);
|
||||||
}
|
}
|
||||||
@ -1722,7 +1743,8 @@ static PyMethodDef PyHandleAppIntentExecDef = {
|
|||||||
|
|
||||||
static auto PyProtocolVersion(PyObject* self) -> PyObject* {
|
static auto PyProtocolVersion(PyObject* self) -> PyObject* {
|
||||||
BA_PYTHON_TRY;
|
BA_PYTHON_TRY;
|
||||||
return PyLong_FromLong(kProtocolVersion);
|
return PyLong_FromLong(
|
||||||
|
SceneV1AppMode::GetSingleton()->host_protocol_version());
|
||||||
BA_PYTHON_CATCH;
|
BA_PYTHON_CATCH;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -103,20 +103,27 @@ SceneV1FeatureSet::SceneV1FeatureSet() : python{new SceneV1Python()} {
|
|||||||
// Types: I is 32 bit int, i is 16 bit int, c is 8 bit int,
|
// Types: I is 32 bit int, i is 16 bit int, c is 8 bit int,
|
||||||
// F is 32 bit float, f is 16 bit float,
|
// F is 32 bit float, f is 16 bit float,
|
||||||
// s is string, b is bool.
|
// s is string, b is bool.
|
||||||
SetupNodeMessageType("flash", NodeMessageType::kFlash, "");
|
SetupNodeMessageType_("flash", NodeMessageType::kFlash, "");
|
||||||
SetupNodeMessageType("footing", NodeMessageType::kFooting, "c");
|
SetupNodeMessageType_("footing", NodeMessageType::kFooting, "c");
|
||||||
SetupNodeMessageType("impulse", NodeMessageType::kImpulse, "fffffffffifff");
|
SetupNodeMessageType_("impulse", NodeMessageType::kImpulse, "fffffffffifff");
|
||||||
SetupNodeMessageType("kick_back", NodeMessageType::kKickback, "fffffff");
|
SetupNodeMessageType_("kick_back", NodeMessageType::kKickback, "fffffff");
|
||||||
SetupNodeMessageType("celebrate", NodeMessageType::kCelebrate, "i");
|
SetupNodeMessageType_("celebrate", NodeMessageType::kCelebrate, "i");
|
||||||
SetupNodeMessageType("celebrate_l", NodeMessageType::kCelebrateL, "i");
|
SetupNodeMessageType_("celebrate_l", NodeMessageType::kCelebrateL, "i");
|
||||||
SetupNodeMessageType("celebrate_r", NodeMessageType::kCelebrateR, "i");
|
SetupNodeMessageType_("celebrate_r", NodeMessageType::kCelebrateR, "i");
|
||||||
SetupNodeMessageType("knockout", NodeMessageType::kKnockout, "f");
|
SetupNodeMessageType_("knockout", NodeMessageType::kKnockout, "f");
|
||||||
SetupNodeMessageType("hurt_sound", NodeMessageType::kHurtSound, "");
|
SetupNodeMessageType_("hurt_sound", NodeMessageType::kHurtSound, "");
|
||||||
SetupNodeMessageType("picked_up", NodeMessageType::kPickedUp, "");
|
SetupNodeMessageType_("picked_up", NodeMessageType::kPickedUp, "");
|
||||||
SetupNodeMessageType("jump_sound", NodeMessageType::kJumpSound, "");
|
SetupNodeMessageType_("jump_sound", NodeMessageType::kJumpSound, "");
|
||||||
SetupNodeMessageType("attack_sound", NodeMessageType::kAttackSound, "");
|
SetupNodeMessageType_("attack_sound", NodeMessageType::kAttackSound, "");
|
||||||
SetupNodeMessageType("scream_sound", NodeMessageType::kScreamSound, "");
|
SetupNodeMessageType_("scream_sound", NodeMessageType::kScreamSound, "");
|
||||||
SetupNodeMessageType("stand", NodeMessageType::kStand, "ffff");
|
SetupNodeMessageType_("stand", NodeMessageType::kStand, "ffff");
|
||||||
|
}
|
||||||
|
|
||||||
|
auto SceneV1FeatureSet::Import() -> SceneV1FeatureSet* {
|
||||||
|
// Since we provide a native Python module, we piggyback our C++ front-end
|
||||||
|
// on top of that. This way our C++ and Python dependencies are resolved
|
||||||
|
// consistently no matter which side we are imported from.
|
||||||
|
return ImportThroughPythonModule<SceneV1FeatureSet>("_bascenev1");
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneV1FeatureSet::Reset() {
|
void SceneV1FeatureSet::Reset() {
|
||||||
@ -132,13 +139,6 @@ void SceneV1FeatureSet::ResetRandomNames() {
|
|||||||
random_name_registry_->clear();
|
random_name_registry_->clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SceneV1FeatureSet::Import() -> SceneV1FeatureSet* {
|
|
||||||
// Since we provide a native Python module, we piggyback our C++ front-end
|
|
||||||
// on top of that. This way our C++ and Python dependencies are resolved
|
|
||||||
// consistently no matter which side we are imported from.
|
|
||||||
return ImportThroughPythonModule<SceneV1FeatureSet>("_bascenev1");
|
|
||||||
}
|
|
||||||
|
|
||||||
auto SceneV1FeatureSet::GetRandomName(const std::string& full_name)
|
auto SceneV1FeatureSet::GetRandomName(const std::string& full_name)
|
||||||
-> std::string {
|
-> std::string {
|
||||||
assert(g_base->InLogicThread());
|
assert(g_base->InLogicThread());
|
||||||
@ -172,9 +172,9 @@ auto SceneV1FeatureSet::GetRandomName(const std::string& full_name)
|
|||||||
return (*random_name_registry_)[full_name];
|
return (*random_name_registry_)[full_name];
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneV1FeatureSet::SetupNodeMessageType(const std::string& name,
|
void SceneV1FeatureSet::SetupNodeMessageType_(const std::string& name,
|
||||||
NodeMessageType val,
|
NodeMessageType val,
|
||||||
const std::string& format) {
|
const std::string& format) {
|
||||||
node_message_types_[name] = val;
|
node_message_types_[name] = val;
|
||||||
assert(static_cast<int>(val) >= 0);
|
assert(static_cast<int>(val) >= 0);
|
||||||
if (node_message_formats_.size() <= static_cast<size_t>(val)) {
|
if (node_message_formats_.size() <= static_cast<size_t>(val)) {
|
||||||
|
|||||||
@ -27,39 +27,54 @@ namespace ballistica::scene_v1 {
|
|||||||
|
|
||||||
// Protocol version we host games with and write replays to. This should be
|
// Protocol version we host games with and write replays to. This should be
|
||||||
// incremented whenever there are changes made to the session-commands layer
|
// incremented whenever there are changes made to the session-commands layer
|
||||||
// (new/removed/changed nodes, attrs, data files, behavior, etc.)
|
// (new/removed/changed nodes, attrs, data files, behavior, etc.).
|
||||||
|
|
||||||
// Note that the packet/gamepacket/message layer can vary more organically
|
// Note that the packet/gamepacket/message layer can vary more organically
|
||||||
// based on build-numbers of connected clients/servers since none of that
|
// based on build-numbers of connected clients/servers since none of that
|
||||||
// data is stored; this just needs to be observed for all the scene stuff
|
// data is stored; these protocol versions just need to be observed by
|
||||||
// that goes into replays since a single stream can get played/replayed on
|
// anything emitting or ingesting scene streams.
|
||||||
// different builds (as long as they support that protocol version).
|
|
||||||
const int kProtocolVersion = 33;
|
// Oldest protocol version we can act as a host for.
|
||||||
|
const int kProtocolVersionHostMin = 33;
|
||||||
|
|
||||||
// Oldest protocol version we can act as a client to. This can generally be
|
// Oldest protocol version we can act as a client to. This can generally be
|
||||||
// left as-is as long as only new nodes/attrs/commands are added and
|
// left as-is as long as only new nodes/attrs/commands are added and old
|
||||||
// existing stuff is unchanged.
|
// behavior remains the same when not using the new stuff.
|
||||||
const int kProtocolVersionMin = 24;
|
const int kProtocolVersionClientMin = 24;
|
||||||
|
|
||||||
// FIXME: We should separate out connection protocol from scene protocol. We
|
// Newest protocol version we can act as a client OR host for.
|
||||||
// want to be able to watch really old replays if possible but being able
|
const int kProtocolVersionMax = 35;
|
||||||
// to connect to old clients is much less important (and slows progress).
|
|
||||||
|
|
||||||
// Protocol additions:
|
// The protocol version we actually host is now read as a setting; see
|
||||||
// 25: added a few new achievement graphics and new node attrs for displaying
|
// kSceneV1HostProtocol in ballistica/base/support/app_config.h.
|
||||||
// stuff in front of the UI
|
|
||||||
// 26: added penguin
|
// Protocol changes:
|
||||||
// 27: added templates for LOTS of characters
|
//
|
||||||
// 28: added cyborg and enabled fallback sounds and textures
|
// 25: Added a few new achievement graphics and new node attrs for displaying
|
||||||
// 29: added bunny and eggs
|
// stuff in front of the UI.
|
||||||
// 30: added support for resource-strings in text-nodes and screen-messages
|
//
|
||||||
// 31: added support for short-form resource-strings, time-display-node, and
|
// 26: Added penguin.
|
||||||
// string-to-string attr connections
|
//
|
||||||
// 32: added json based player profiles message, added shield
|
// 27: Added templates for LOTS of characters.
|
||||||
// alwaysShowHealthBar attr
|
//
|
||||||
// 33: handshake/handshake-response now send json dicts instead of
|
// 28: Added cyborg and enabled fallback sounds and textures.
|
||||||
// just player-specs
|
//
|
||||||
// 34: new image_node enums, data assets.
|
// 29: Added bunny and eggs.
|
||||||
|
//
|
||||||
|
// 30: Added support for resource-strings in text-nodes and screen-messages.
|
||||||
|
//
|
||||||
|
// 31: Added support for short-form resource-strings, time-display-node, and
|
||||||
|
// string-to-string attr connections.
|
||||||
|
//
|
||||||
|
// 32: Added json based player profiles message, added shield
|
||||||
|
// always_show_health_bar attr.
|
||||||
|
//
|
||||||
|
// 33: Handshake/handshake-response now send json dicts instead of
|
||||||
|
// just player-specs.
|
||||||
|
//
|
||||||
|
// 34: New image_node enums, data assets.
|
||||||
|
//
|
||||||
|
// 35: Camera shake in netplay. how did I apparently miss this for 10 years!?!
|
||||||
|
|
||||||
// Sim step size in milliseconds.
|
// Sim step size in milliseconds.
|
||||||
const int kGameStepMilliseconds = 8;
|
const int kGameStepMilliseconds = 8;
|
||||||
@ -215,7 +230,8 @@ enum class SessionCommand {
|
|||||||
kScreenMessageBottom,
|
kScreenMessageBottom,
|
||||||
kScreenMessageTop,
|
kScreenMessageTop,
|
||||||
kAddData,
|
kAddData,
|
||||||
kRemoveData
|
kRemoveData,
|
||||||
|
kCameraShake
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class NodeCollideAttr {
|
enum class NodeCollideAttr {
|
||||||
@ -373,8 +389,8 @@ class SceneV1FeatureSet : public FeatureSetNativeComponent {
|
|||||||
SceneV1Python* const python;
|
SceneV1Python* const python;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void SetupNodeMessageType(const std::string& name, NodeMessageType val,
|
void SetupNodeMessageType_(const std::string& name, NodeMessageType val,
|
||||||
const std::string& format);
|
const std::string& format);
|
||||||
|
|
||||||
SceneV1FeatureSet();
|
SceneV1FeatureSet();
|
||||||
std::unordered_map<std::string, NodeType*> node_types_;
|
std::unordered_map<std::string, NodeType*> node_types_;
|
||||||
|
|||||||
@ -835,6 +835,11 @@ void ClientSession::Update(int time_advance_millisecs, double time_advance) {
|
|||||||
y, z);
|
y, z);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case SessionCommand::kCameraShake: {
|
||||||
|
auto intensity = ReadFloat();
|
||||||
|
g_base->graphics->LocalCameraShake(intensity);
|
||||||
|
break;
|
||||||
|
}
|
||||||
case SessionCommand::kEmitBGDynamics: {
|
case SessionCommand::kEmitBGDynamics: {
|
||||||
int cmdvals[4];
|
int cmdvals[4];
|
||||||
ReadInt32_4(cmdvals);
|
ReadInt32_4(cmdvals);
|
||||||
|
|||||||
@ -17,7 +17,9 @@ ClientSessionNet::ClientSessionNet() {
|
|||||||
"g_replay_open true at netclient start; shouldn't happen.");
|
"g_replay_open true at netclient start; shouldn't happen.");
|
||||||
}
|
}
|
||||||
assert(g_base->assets_server);
|
assert(g_base->assets_server);
|
||||||
g_base->assets_server->PushBeginWriteReplayCall(kProtocolVersion);
|
|
||||||
|
// We always write replays as the highest protocol version we support.
|
||||||
|
g_base->assets_server->PushBeginWriteReplayCall(kProtocolVersionMax);
|
||||||
writing_replay_ = true;
|
writing_replay_ = true;
|
||||||
g_core->replay_open = true;
|
g_core->replay_open = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -26,8 +26,6 @@ class ClientSessionNet : public ClientSession {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
struct SampleBucket {
|
struct SampleBucket {
|
||||||
// int least_buffered_count{};
|
|
||||||
// int most_buffered_count{};
|
|
||||||
int max_delay_from_projection{};
|
int max_delay_from_projection{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -253,7 +253,7 @@ void ClientSessionReplay::OnReset(bool rewind) {
|
|||||||
Error("error reading version");
|
Error("error reading version");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (version > kProtocolVersion || version < kProtocolVersionMin) {
|
if (version > kProtocolVersionMax || version < kProtocolVersionClientMin) {
|
||||||
ScreenMessage(g_base->assets->GetResourceString("replayVersionErrorText"),
|
ScreenMessage(g_base->assets->GetResourceString("replayVersionErrorText"),
|
||||||
{1, 0, 0});
|
{1, 0, 0});
|
||||||
End();
|
End();
|
||||||
|
|||||||
@ -46,7 +46,7 @@ const int kKickVoteFailRetryDelayInitiatorExtra = 120000;
|
|||||||
// to kick).
|
// to kick).
|
||||||
const int kKickVoteMinimumClients = (g_buildconfig.headless_build() ? 3 : 4);
|
const int kKickVoteMinimumClients = (g_buildconfig.headless_build() ? 3 : 4);
|
||||||
|
|
||||||
struct SceneV1AppMode::ScanResultsEntryPriv {
|
struct SceneV1AppMode::ScanResultsEntryPriv_ {
|
||||||
scene_v1::PlayerSpec player_spec;
|
scene_v1::PlayerSpec player_spec;
|
||||||
std::string address;
|
std::string address;
|
||||||
uint32_t last_query_id{};
|
uint32_t last_query_id{};
|
||||||
@ -78,7 +78,16 @@ static SceneV1AppMode* g_scene_v1_app_mode{};
|
|||||||
|
|
||||||
void SceneV1AppMode::OnActivate() {
|
void SceneV1AppMode::OnActivate() {
|
||||||
assert(g_base->InLogicThread());
|
assert(g_base->InLogicThread());
|
||||||
Reset();
|
|
||||||
|
// Make sure we pull this only once when we are first active.
|
||||||
|
if (host_protocol_version_ == -1) {
|
||||||
|
host_protocol_version_ =
|
||||||
|
std::clamp(g_base->app_config->Resolve(
|
||||||
|
base::AppConfig::IntID::kSceneV1HostProtocol),
|
||||||
|
kProtocolVersionHostMin, kProtocolVersionMax);
|
||||||
|
}
|
||||||
|
|
||||||
|
Reset_();
|
||||||
|
|
||||||
// We use UIV1.
|
// We use UIV1.
|
||||||
if (!g_core->HeadlessMode()) {
|
if (!g_core->HeadlessMode()) {
|
||||||
@ -108,9 +117,10 @@ void SceneV1AppMode::OnAppPause() {
|
|||||||
void SceneV1AppMode::OnAppResume() { assert(g_base->InLogicThread()); }
|
void SceneV1AppMode::OnAppResume() { assert(g_base->InLogicThread()); }
|
||||||
|
|
||||||
// Note: for now we're making our host-scan network calls directly from the
|
// Note: for now we're making our host-scan network calls directly from the
|
||||||
// logic thread. This is generally not a good idea since it appears that even in
|
// logic thread. This is generally not a good idea since it appears that even
|
||||||
// non-blocking mode they're still blocking for 3-4ms sometimes. But for now
|
// in non-blocking mode they're still blocking for 3-4ms sometimes. But for
|
||||||
// since this is only used minimally and only while in the UI I guess it's ok.
|
// now since this is only used minimally and only while in the UI I guess it's
|
||||||
|
// ok.
|
||||||
void SceneV1AppMode::HostScanCycle() {
|
void SceneV1AppMode::HostScanCycle() {
|
||||||
assert(g_base->InLogicThread());
|
assert(g_base->InLogicThread());
|
||||||
|
|
||||||
@ -258,7 +268,7 @@ void SceneV1AppMode::HostScanCycle() {
|
|||||||
bool do_update_entry = (i == scan_results_.end()
|
bool do_update_entry = (i == scan_results_.end()
|
||||||
|| i->second.last_query_id != query_id);
|
|| i->second.last_query_id != query_id);
|
||||||
if (do_update_entry) {
|
if (do_update_entry) {
|
||||||
ScanResultsEntryPriv& entry(scan_results_[key]);
|
ScanResultsEntryPriv_& entry(scan_results_[key]);
|
||||||
entry.player_spec = scene_v1::PlayerSpec(player_spec_str);
|
entry.player_spec = scene_v1::PlayerSpec(player_spec_str);
|
||||||
char buffer2[256];
|
char buffer2[256];
|
||||||
entry.address = inet_ntop(
|
entry.address = inet_ntop(
|
||||||
@ -269,7 +279,7 @@ void SceneV1AppMode::HostScanCycle() {
|
|||||||
entry.last_contact_time = g_core->GetAppTimeMillisecs();
|
entry.last_contact_time = g_core->GetAppTimeMillisecs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
PruneScanResults();
|
PruneScanResults_();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Log(LogLevel::kError,
|
Log(LogLevel::kError,
|
||||||
@ -290,7 +300,7 @@ void SceneV1AppMode::EndHostScanning() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneV1AppMode::PruneScanResults() {
|
void SceneV1AppMode::PruneScanResults_() {
|
||||||
millisecs_t t = g_core->GetAppTimeMillisecs();
|
millisecs_t t = g_core->GetAppTimeMillisecs();
|
||||||
auto i = scan_results_.begin();
|
auto i = scan_results_.begin();
|
||||||
while (i != scan_results_.end()) {
|
while (i != scan_results_.end()) {
|
||||||
@ -311,13 +321,13 @@ auto SceneV1AppMode::GetScanResults()
|
|||||||
std::scoped_lock lock(scan_results_mutex_);
|
std::scoped_lock lock(scan_results_mutex_);
|
||||||
int out_num = 0;
|
int out_num = 0;
|
||||||
for (auto&& i : scan_results_) {
|
for (auto&& i : scan_results_) {
|
||||||
ScanResultsEntryPriv& in(i.second);
|
ScanResultsEntryPriv_& in(i.second);
|
||||||
ScanResultsEntry& out(results[out_num]);
|
ScanResultsEntry& out(results[out_num]);
|
||||||
out.display_string = in.player_spec.GetDisplayString();
|
out.display_string = in.player_spec.GetDisplayString();
|
||||||
out.address = in.address;
|
out.address = in.address;
|
||||||
out_num++;
|
out_num++;
|
||||||
}
|
}
|
||||||
PruneScanResults();
|
PruneScanResults_();
|
||||||
}
|
}
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
@ -399,8 +409,8 @@ auto SceneV1AppMode::HandleJSONPing(const std::string& data_str)
|
|||||||
}
|
}
|
||||||
cJSON_Delete(data);
|
cJSON_Delete(data);
|
||||||
|
|
||||||
// Ok lets include some basic info that might be pertinent to someone pinging
|
// Ok lets include some basic info that might be pertinent to someone
|
||||||
// us. Currently that includes our current/max connection count.
|
// pinging us. Currently that includes our current/max connection count.
|
||||||
char buffer[256];
|
char buffer[256];
|
||||||
snprintf(buffer, sizeof(buffer), R"({"b":%d,"ps":%d,"psmx":%d})",
|
snprintf(buffer, sizeof(buffer), R"({"b":%d,"ps":%d,"psmx":%d})",
|
||||||
kEngineBuildNumber, public_party_size(), public_party_max_size());
|
kEngineBuildNumber, public_party_size(), public_party_max_size());
|
||||||
@ -420,7 +430,7 @@ auto SceneV1AppMode::GetPartySize() const -> int {
|
|||||||
return cJSON_GetArraySize(game_roster_);
|
return cJSON_GetArraySize(game_roster_);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SceneV1AppMode::GetHeadlessDisplayStep() -> microsecs_t {
|
auto SceneV1AppMode::GetHeadlessNextDisplayTimeStep() -> microsecs_t {
|
||||||
std::optional<microsecs_t> min_time_to_next;
|
std::optional<microsecs_t> min_time_to_next;
|
||||||
for (auto&& i : sessions_) {
|
for (auto&& i : sessions_) {
|
||||||
if (!i.Exists()) {
|
if (!i.Exists()) {
|
||||||
@ -436,7 +446,7 @@ auto SceneV1AppMode::GetHeadlessDisplayStep() -> microsecs_t {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return min_time_to_next.has_value() ? *min_time_to_next
|
return min_time_to_next.has_value() ? *min_time_to_next
|
||||||
: base::kAppModeMaxHeadlessDisplayStep;
|
: base::kHeadlessMaxDisplayTimeStep;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneV1AppMode::StepDisplayTime() {
|
void SceneV1AppMode::StepDisplayTime() {
|
||||||
@ -468,15 +478,15 @@ void SceneV1AppMode::StepDisplayTime() {
|
|||||||
}
|
}
|
||||||
legacy_display_time_millisecs_prev_ = legacy_display_time_millisecs_;
|
legacy_display_time_millisecs_prev_ = legacy_display_time_millisecs_;
|
||||||
|
|
||||||
UpdateKickVote();
|
UpdateKickVote_();
|
||||||
|
|
||||||
HandleQuitOnIdle();
|
HandleQuitOnIdle_();
|
||||||
|
|
||||||
// Send the game roster to our clients if it's changed recently.
|
// Send the game roster to our clients if it's changed recently.
|
||||||
if (game_roster_dirty_) {
|
if (game_roster_dirty_) {
|
||||||
if (app_time > last_game_roster_send_time_ + 2500) {
|
if (app_time > last_game_roster_send_time_ + 2500) {
|
||||||
// Now send it to all connected clients.
|
// Now send it to all connected clients.
|
||||||
std::vector<uint8_t> msg = GetGameRosterMessage();
|
std::vector<uint8_t> msg = GetGameRosterMessage_();
|
||||||
for (auto&& c : connections()->GetConnectionsToClients()) {
|
for (auto&& c : connections()->GetConnectionsToClients()) {
|
||||||
c->SendReliableMessage(msg);
|
c->SendReliableMessage(msg);
|
||||||
}
|
}
|
||||||
@ -501,7 +511,7 @@ void SceneV1AppMode::StepDisplayTime() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Go ahead and prune dead ones.
|
// Go ahead and prune dead ones.
|
||||||
PruneSessions();
|
PruneSessions_();
|
||||||
|
|
||||||
in_update_ = false;
|
in_update_ = false;
|
||||||
|
|
||||||
@ -521,7 +531,7 @@ void SceneV1AppMode::StepDisplayTime() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SceneV1AppMode::GetGameRosterMessage() -> std::vector<uint8_t> {
|
auto SceneV1AppMode::GetGameRosterMessage_() -> std::vector<uint8_t> {
|
||||||
// This message is simply a flattened json string of our roster (including
|
// This message is simply a flattened json string of our roster (including
|
||||||
// terminating char).
|
// terminating char).
|
||||||
char* s = cJSON_PrintUnformatted(game_roster_);
|
char* s = cJSON_PrintUnformatted(game_roster_);
|
||||||
@ -661,7 +671,7 @@ void SceneV1AppMode::UpdateGameRoster() {
|
|||||||
game_roster_dirty_ = true;
|
game_roster_dirty_ = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneV1AppMode::UpdateKickVote() {
|
void SceneV1AppMode::UpdateKickVote_() {
|
||||||
if (!kick_vote_in_progress_) {
|
if (!kick_vote_in_progress_) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -973,7 +983,7 @@ void SceneV1AppMode::LaunchHostSession(PyObject* session_type_obj,
|
|||||||
base::ScopedSetContext ssc(nullptr);
|
base::ScopedSetContext ssc(nullptr);
|
||||||
|
|
||||||
// This should kill any current session and get us back to a blank slate.
|
// This should kill any current session and get us back to a blank slate.
|
||||||
Reset();
|
Reset_();
|
||||||
|
|
||||||
Object::WeakRef<Session> old_foreground_session(foreground_session_);
|
Object::WeakRef<Session> old_foreground_session(foreground_session_);
|
||||||
try {
|
try {
|
||||||
@ -1004,7 +1014,7 @@ void SceneV1AppMode::LaunchReplaySession(const std::string& file_name) {
|
|||||||
base::ScopedSetContext ssc(nullptr);
|
base::ScopedSetContext ssc(nullptr);
|
||||||
|
|
||||||
// This should kill any current session and get us back to a blank slate.
|
// This should kill any current session and get us back to a blank slate.
|
||||||
Reset();
|
Reset_();
|
||||||
|
|
||||||
// Create the new session.
|
// Create the new session.
|
||||||
Object::WeakRef<Session> old_foreground_session(foreground_session_);
|
Object::WeakRef<Session> old_foreground_session(foreground_session_);
|
||||||
@ -1034,7 +1044,7 @@ void SceneV1AppMode::LaunchClientSession() {
|
|||||||
base::ScopedSetContext ssc(nullptr);
|
base::ScopedSetContext ssc(nullptr);
|
||||||
|
|
||||||
// This should kill any current session and get us back to a blank slate.
|
// This should kill any current session and get us back to a blank slate.
|
||||||
Reset();
|
Reset_();
|
||||||
|
|
||||||
// Create the new session.
|
// Create the new session.
|
||||||
Object::WeakRef<Session> old_foreground_session(foreground_session_);
|
Object::WeakRef<Session> old_foreground_session(foreground_session_);
|
||||||
@ -1052,13 +1062,13 @@ void SceneV1AppMode::LaunchClientSession() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Reset to a blank slate.
|
// Reset to a blank slate.
|
||||||
void SceneV1AppMode::Reset() {
|
void SceneV1AppMode::Reset_() {
|
||||||
assert(g_base);
|
assert(g_base);
|
||||||
assert(g_base->InLogicThread());
|
assert(g_base->InLogicThread());
|
||||||
|
|
||||||
// Tear down our existing session.
|
// Tear down our existing session.
|
||||||
foreground_session_.Clear();
|
foreground_session_.Clear();
|
||||||
PruneSessions();
|
PruneSessions_();
|
||||||
|
|
||||||
// If all is well our sessions should all be dead.
|
// If all is well our sessions should all be dead.
|
||||||
if (g_core->session_count != 0) {
|
if (g_core->session_count != 0) {
|
||||||
@ -1174,7 +1184,7 @@ void SceneV1AppMode::DoApplyAppConfig() {
|
|||||||
base::AppConfig::OptionalFloatID::kIdleExitMinutes);
|
base::AppConfig::OptionalFloatID::kIdleExitMinutes);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneV1AppMode::PruneSessions() {
|
void SceneV1AppMode::PruneSessions_() {
|
||||||
bool have_dead_session = false;
|
bool have_dead_session = false;
|
||||||
for (auto&& i : sessions_) {
|
for (auto&& i : sessions_) {
|
||||||
if (i.Exists()) {
|
if (i.Exists()) {
|
||||||
@ -1368,7 +1378,7 @@ void SceneV1AppMode::BanPlayer(const PlayerSpec& spec, millisecs_t duration) {
|
|||||||
banned_players_.emplace_back(g_core->GetAppTimeMillisecs() + duration, spec);
|
banned_players_.emplace_back(g_core->GetAppTimeMillisecs() + duration, spec);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneV1AppMode::HandleQuitOnIdle() {
|
void SceneV1AppMode::HandleQuitOnIdle_() {
|
||||||
if (idle_exit_minutes_) {
|
if (idle_exit_minutes_) {
|
||||||
auto idle_seconds{static_cast<float>(g_base->input->input_idle_time())
|
auto idle_seconds{static_cast<float>(g_base->input->input_idle_time())
|
||||||
* 0.001f};
|
* 0.001f};
|
||||||
@ -1443,7 +1453,7 @@ void SceneV1AppMode::HandleGameQuery(const char* buffer, size_t size,
|
|||||||
|
|
||||||
msg[0] = BA_PACKET_HOST_QUERY_RESPONSE;
|
msg[0] = BA_PACKET_HOST_QUERY_RESPONSE;
|
||||||
memcpy(msg + 1, &query_id, 4);
|
memcpy(msg + 1, &query_id, 4);
|
||||||
uint32_t protocol_version = kProtocolVersion;
|
uint32_t protocol_version = host_protocol_version();
|
||||||
memcpy(msg + 5, &protocol_version, 4);
|
memcpy(msg + 5, &protocol_version, 4);
|
||||||
msg[9] = static_cast<char>(usid.size());
|
msg[9] = static_cast<char>(usid.size());
|
||||||
msg[10] = static_cast<char>(player_spec_string.size());
|
msg[10] = static_cast<char>(player_spec_string.size());
|
||||||
|
|||||||
@ -182,41 +182,56 @@ class SceneV1AppMode : public base::AppMode {
|
|||||||
auto buffer_time() const { return buffer_time_; }
|
auto buffer_time() const { return buffer_time_; }
|
||||||
void set_buffer_time(int val) { buffer_time_ = val; }
|
void set_buffer_time(int val) { buffer_time_ = val; }
|
||||||
void OnActivate() override;
|
void OnActivate() override;
|
||||||
auto GetHeadlessDisplayStep() -> microsecs_t override;
|
auto GetHeadlessNextDisplayTimeStep() -> microsecs_t override;
|
||||||
|
|
||||||
|
auto host_protocol_version() const {
|
||||||
|
assert(host_protocol_version_ != -1);
|
||||||
|
return host_protocol_version_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void PruneScanResults();
|
|
||||||
void UpdateKickVote();
|
|
||||||
SceneV1AppMode();
|
SceneV1AppMode();
|
||||||
auto GetGameRosterMessage() -> std::vector<uint8_t>;
|
void PruneScanResults_();
|
||||||
void Reset();
|
void UpdateKickVote_();
|
||||||
void PruneSessions();
|
auto GetGameRosterMessage_() -> std::vector<uint8_t>;
|
||||||
void HandleQuitOnIdle();
|
void Reset_();
|
||||||
struct ScanResultsEntryPriv;
|
void PruneSessions_();
|
||||||
|
void HandleQuitOnIdle_();
|
||||||
|
|
||||||
|
struct ScanResultsEntryPriv_;
|
||||||
|
|
||||||
// Note: would use an unordered_map here but gcc doesn't seem to allow
|
// Note: would use an unordered_map here but gcc doesn't seem to allow
|
||||||
// forward declarations of their template params.
|
// forward declarations of their template params.
|
||||||
std::map<std::string, ScanResultsEntryPriv> scan_results_;
|
std::map<std::string, ScanResultsEntryPriv_> scan_results_;
|
||||||
std::mutex scan_results_mutex_;
|
std::mutex scan_results_mutex_;
|
||||||
uint32_t next_scan_query_id_{};
|
uint32_t next_scan_query_id_{};
|
||||||
int scan_socket_{-1};
|
int scan_socket_{-1};
|
||||||
|
int host_protocol_version_{-1};
|
||||||
|
|
||||||
std::list<std::string> chat_messages_;
|
std::list<std::string> chat_messages_;
|
||||||
bool chat_muted_{};
|
|
||||||
// *All* existing sessions (including old ones waiting to shut down).
|
// *All* existing sessions (including old ones waiting to shut down).
|
||||||
std::vector<Object::Ref<Session> > sessions_;
|
std::vector<Object::Ref<Session> > sessions_;
|
||||||
Object::WeakRef<Scene> foreground_scene_;
|
Object::WeakRef<Scene> foreground_scene_;
|
||||||
Object::WeakRef<Session> foreground_session_;
|
Object::WeakRef<Session> foreground_session_;
|
||||||
|
|
||||||
bool game_roster_dirty_{};
|
bool chat_muted_ : 1 {};
|
||||||
|
bool in_update_ : 1 {};
|
||||||
|
bool kick_idle_players_ : 1 {};
|
||||||
|
bool public_party_enabled_ : 1 {};
|
||||||
|
bool public_party_queue_enabled_ : 1 {true};
|
||||||
|
bool require_client_authentication_ : 1 {};
|
||||||
|
bool idle_exiting_ : 1 {};
|
||||||
|
bool game_roster_dirty_ : 1 {};
|
||||||
|
bool kick_vote_in_progress_ : 1 {};
|
||||||
|
bool kick_voting_enabled_ : 1 {true};
|
||||||
|
|
||||||
|
cJSON* game_roster_{};
|
||||||
millisecs_t last_game_roster_send_time_{};
|
millisecs_t last_game_roster_send_time_{};
|
||||||
std::unique_ptr<ConnectionSet> connections_;
|
std::unique_ptr<ConnectionSet> connections_;
|
||||||
cJSON* game_roster_{};
|
|
||||||
Object::WeakRef<ConnectionToClient> kick_vote_starter_;
|
Object::WeakRef<ConnectionToClient> kick_vote_starter_;
|
||||||
Object::WeakRef<ConnectionToClient> kick_vote_target_;
|
Object::WeakRef<ConnectionToClient> kick_vote_target_;
|
||||||
millisecs_t kick_vote_end_time_{};
|
millisecs_t kick_vote_end_time_{};
|
||||||
bool kick_vote_in_progress_{};
|
|
||||||
int last_kick_votes_needed_{-1};
|
int last_kick_votes_needed_{-1};
|
||||||
bool kick_voting_enabled_{true};
|
|
||||||
millisecs_t legacy_display_time_millisecs_{};
|
millisecs_t legacy_display_time_millisecs_{};
|
||||||
millisecs_t legacy_display_time_millisecs_prev_{-1};
|
millisecs_t legacy_display_time_millisecs_prev_{-1};
|
||||||
|
|
||||||
@ -229,28 +244,22 @@ class SceneV1AppMode : public base::AppMode {
|
|||||||
// it over the network.
|
// it over the network.
|
||||||
int buffer_time_{};
|
int buffer_time_{};
|
||||||
|
|
||||||
bool in_update_{};
|
|
||||||
millisecs_t next_long_update_report_time_{};
|
millisecs_t next_long_update_report_time_{};
|
||||||
int debug_speed_exponent_{};
|
int debug_speed_exponent_{};
|
||||||
float debug_speed_mult_{1.0f};
|
|
||||||
int replay_speed_exponent_{};
|
int replay_speed_exponent_{};
|
||||||
float replay_speed_mult_{1.0f};
|
|
||||||
bool kick_idle_players_{};
|
|
||||||
std::set<std::string> admin_public_ids_;
|
|
||||||
millisecs_t last_connection_to_client_join_time_{};
|
|
||||||
bool public_party_enabled_{};
|
|
||||||
int public_party_size_{1}; // Always count ourself (is that what we want?).
|
int public_party_size_{1}; // Always count ourself (is that what we want?).
|
||||||
int public_party_max_size_{8};
|
int public_party_max_size_{8};
|
||||||
bool public_party_queue_enabled_{true};
|
|
||||||
int public_party_player_count_{0};
|
int public_party_player_count_{0};
|
||||||
int public_party_max_player_count_{8};
|
int public_party_max_player_count_{8};
|
||||||
|
float debug_speed_mult_{1.0f};
|
||||||
|
float replay_speed_mult_{1.0f};
|
||||||
|
std::set<std::string> admin_public_ids_;
|
||||||
|
millisecs_t last_connection_to_client_join_time_{};
|
||||||
std::string public_party_name_;
|
std::string public_party_name_;
|
||||||
std::string public_party_min_league_;
|
std::string public_party_min_league_;
|
||||||
std::string public_party_stats_url_;
|
std::string public_party_stats_url_;
|
||||||
bool require_client_authentication_{};
|
|
||||||
std::list<std::pair<millisecs_t, PlayerSpec> > banned_players_;
|
std::list<std::pair<millisecs_t, PlayerSpec> > banned_players_;
|
||||||
std::optional<float> idle_exit_minutes_{};
|
std::optional<float> idle_exit_minutes_{};
|
||||||
bool idle_exiting_{};
|
|
||||||
std::optional<uint32_t> internal_music_play_id_{};
|
std::optional<uint32_t> internal_music_play_id_{};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -31,8 +31,9 @@ SessionStream::SessionStream(HostSession* host_session, bool save_replay)
|
|||||||
Log(LogLevel::kError,
|
Log(LogLevel::kError,
|
||||||
"g_replay_open true at replay start; shouldn't happen.");
|
"g_replay_open true at replay start; shouldn't happen.");
|
||||||
}
|
}
|
||||||
|
// We always write replays as the max protocol version we support.
|
||||||
assert(g_base->assets_server);
|
assert(g_base->assets_server);
|
||||||
g_base->assets_server->PushBeginWriteReplayCall(kProtocolVersion);
|
g_base->assets_server->PushBeginWriteReplayCall(kProtocolVersionMax);
|
||||||
writing_replay_ = true;
|
writing_replay_ = true;
|
||||||
g_core->replay_open = true;
|
g_core->replay_open = true;
|
||||||
}
|
}
|
||||||
@ -205,9 +206,6 @@ void SessionStream::Flush() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma clang diagnostic push
|
|
||||||
#pragma ide diagnostic ignored "ConstantParameter"
|
|
||||||
|
|
||||||
// Writes just a command.
|
// Writes just a command.
|
||||||
void SessionStream::WriteCommand(SessionCommand cmd) {
|
void SessionStream::WriteCommand(SessionCommand cmd) {
|
||||||
assert(out_command_.empty());
|
assert(out_command_.empty());
|
||||||
@ -219,8 +217,6 @@ void SessionStream::WriteCommand(SessionCommand cmd) {
|
|||||||
*ptr = static_cast<uint8_t>(cmd);
|
*ptr = static_cast<uint8_t>(cmd);
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma clang diagnostic pop
|
|
||||||
|
|
||||||
// Writes a command plus an int to the stream, using whatever size is optimal.
|
// Writes a command plus an int to the stream, using whatever size is optimal.
|
||||||
void SessionStream::WriteCommandInt32(SessionCommand cmd, int32_t value) {
|
void SessionStream::WriteCommandInt32(SessionCommand cmd, int32_t value) {
|
||||||
assert(out_command_.empty());
|
assert(out_command_.empty());
|
||||||
@ -1123,6 +1119,13 @@ void SessionStream::EmitBGDynamics(const base::BGDynamicsEmission& e) {
|
|||||||
EndCommand();
|
EndCommand();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SessionStream::EmitCameraShake(float intensity) {
|
||||||
|
WriteCommand(SessionCommand::kCameraShake);
|
||||||
|
// FIXME: We shouldn't need to be passing all these as full floats. :-(
|
||||||
|
WriteFloat(intensity);
|
||||||
|
EndCommand();
|
||||||
|
}
|
||||||
|
|
||||||
void SessionStream::PlaySound(SceneSound* sound, float volume) {
|
void SessionStream::PlaySound(SceneSound* sound, float volume) {
|
||||||
assert(IsValidSound(sound));
|
assert(IsValidSound(sound));
|
||||||
assert(IsValidScene(sound->scene()));
|
assert(IsValidScene(sound->scene()));
|
||||||
|
|||||||
@ -69,6 +69,7 @@ class SessionStream : public Object, public ClientControllerInterface {
|
|||||||
float z);
|
float z);
|
||||||
void PlaySound(SceneSound* sound, float volume);
|
void PlaySound(SceneSound* sound, float volume);
|
||||||
void EmitBGDynamics(const base::BGDynamicsEmission& e);
|
void EmitBGDynamics(const base::BGDynamicsEmission& e);
|
||||||
|
void EmitCameraShake(float intensity);
|
||||||
auto GetSoundID(SceneSound* s) -> int64_t;
|
auto GetSoundID(SceneSound* s) -> int64_t;
|
||||||
auto GetMaterialID(Material* m) -> int64_t;
|
auto GetMaterialID(Material* m) -> int64_t;
|
||||||
void ScreenMessageBottom(const std::string& val, float r, float g, float b);
|
void ScreenMessageBottom(const std::string& val, float r, float g, float b);
|
||||||
|
|||||||
@ -39,7 +39,7 @@ auto main(int argc, char** argv) -> int {
|
|||||||
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 kEngineBuildNumber = 21447;
|
const int kEngineBuildNumber = 21453;
|
||||||
const char* kEngineVersion = "1.7.28";
|
const char* kEngineVersion = "1.7.28";
|
||||||
const int kEngineApiVersion = 8;
|
const int kEngineApiVersion = 8;
|
||||||
|
|
||||||
|
|||||||
@ -248,24 +248,24 @@ void EventLoop::WaitForNextEvent_(bool single_cycle) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EventLoop::LoopUpkeep_(bool single_cycle) {
|
// void EventLoop::LoopUpkeep_(bool single_cycle) {
|
||||||
assert(g_core);
|
// assert(g_core);
|
||||||
// Keep our autorelease pool clean on mac/ios
|
// // Keep our autorelease pool clean on mac/ios
|
||||||
// FIXME: Should define a CorePlatform::ThreadHelper or something
|
// // FIXME: Should define a CorePlatform::ThreadHelper or something
|
||||||
// so we don't have platform-specific code here.
|
// // so we don't have platform-specific code here.
|
||||||
#if BA_XCODE_BUILD
|
// #if BA_XCODE_BUILD
|
||||||
// Let's not do autorelease pools when being called ad-hoc,
|
// // Let's not do autorelease pools when being called ad-hoc,
|
||||||
// since in that case we're part of another run loop
|
// // since in that case we're part of another run loop
|
||||||
// (and its crashing on drain for some reason)
|
// // (and its crashing on drain for some reason)
|
||||||
if (!single_cycle) {
|
// if (!single_cycle) {
|
||||||
if (auto_release_pool_) {
|
// if (auto_release_pool_) {
|
||||||
g_core->platform->DrainAutoReleasePool(auto_release_pool_);
|
// g_core->platform->DrainAutoReleasePool(auto_release_pool_);
|
||||||
auto_release_pool_ = nullptr;
|
// auto_release_pool_ = nullptr;
|
||||||
}
|
// }
|
||||||
auto_release_pool_ = g_core->platform->NewAutoReleasePool();
|
// auto_release_pool_ = g_core->platform->NewAutoReleasePool();
|
||||||
}
|
// }
|
||||||
#endif
|
// #endif
|
||||||
}
|
// }
|
||||||
|
|
||||||
void EventLoop::RunToCompletion() { Run_(false); }
|
void EventLoop::RunToCompletion() { Run_(false); }
|
||||||
void EventLoop::RunSingleCycle() { Run_(true); }
|
void EventLoop::RunSingleCycle() { Run_(true); }
|
||||||
@ -273,7 +273,7 @@ void EventLoop::RunSingleCycle() { Run_(true); }
|
|||||||
void EventLoop::Run_(bool single_cycle) {
|
void EventLoop::Run_(bool single_cycle) {
|
||||||
assert(g_core);
|
assert(g_core);
|
||||||
while (true) {
|
while (true) {
|
||||||
LoopUpkeep_(single_cycle);
|
// LoopUpkeep_(single_cycle);
|
||||||
|
|
||||||
WaitForNextEvent_(single_cycle);
|
WaitForNextEvent_(single_cycle);
|
||||||
|
|
||||||
|
|||||||
@ -118,7 +118,7 @@ class EventLoop {
|
|||||||
auto CheckPushRunnableSafety_() -> bool;
|
auto CheckPushRunnableSafety_() -> bool;
|
||||||
void SetInternalThreadName_(const std::string& name);
|
void SetInternalThreadName_(const std::string& name);
|
||||||
void WaitForNextEvent_(bool single_cycle);
|
void WaitForNextEvent_(bool single_cycle);
|
||||||
void LoopUpkeep_(bool single_cycle);
|
// void LoopUpkeep_(bool single_cycle);
|
||||||
void LogThreadMessageTally_(
|
void LogThreadMessageTally_(
|
||||||
std::vector<std::pair<LogLevel, std::string>>* log_entries);
|
std::vector<std::pair<LogLevel, std::string>>* log_entries);
|
||||||
void PushLocalRunnable_(Runnable* runnable, bool* completion_flag);
|
void PushLocalRunnable_(Runnable* runnable, bool* completion_flag);
|
||||||
@ -155,32 +155,26 @@ class EventLoop {
|
|||||||
|
|
||||||
void BootstrapThread_();
|
void BootstrapThread_();
|
||||||
|
|
||||||
|
bool bootstrapped_{};
|
||||||
bool writing_tally_{};
|
bool writing_tally_{};
|
||||||
bool suspended_{};
|
bool suspended_{};
|
||||||
|
bool done_{};
|
||||||
|
bool acquires_python_gil_{};
|
||||||
|
EventLoopID identifier_{EventLoopID::kInvalid};
|
||||||
millisecs_t last_suspend_time_{};
|
millisecs_t last_suspend_time_{};
|
||||||
|
int listen_sd_{};
|
||||||
int messages_since_suspended_{};
|
int messages_since_suspended_{};
|
||||||
millisecs_t last_suspended_message_report_time_{};
|
millisecs_t last_suspended_message_report_time_{};
|
||||||
bool done_{};
|
|
||||||
ThreadSource source_;
|
|
||||||
int listen_sd_{};
|
|
||||||
std::thread::id thread_id_{};
|
|
||||||
EventLoopID identifier_{EventLoopID::kInvalid};
|
|
||||||
millisecs_t last_complaint_time_{};
|
millisecs_t last_complaint_time_{};
|
||||||
bool acquires_python_gil_{};
|
ThreadSource source_;
|
||||||
|
std::thread::id thread_id_{};
|
||||||
// FIXME: Should generalize this to some sort of PlatformThreadData class.
|
|
||||||
#if BA_XCODE_BUILD
|
|
||||||
void* auto_release_pool_{};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
bool bootstrapped_{};
|
|
||||||
std::list<std::pair<Runnable*, bool*>> runnables_;
|
std::list<std::pair<Runnable*, bool*>> runnables_;
|
||||||
std::list<Runnable*> suspend_callbacks_;
|
std::list<Runnable*> suspend_callbacks_;
|
||||||
std::list<Runnable*> unsuspend_callbacks_;
|
std::list<Runnable*> unsuspend_callbacks_;
|
||||||
std::condition_variable thread_message_cv_;
|
std::condition_variable thread_message_cv_;
|
||||||
|
std::condition_variable client_listener_cv_;
|
||||||
std::mutex thread_message_mutex_;
|
std::mutex thread_message_mutex_;
|
||||||
std::list<ThreadMessage_> thread_messages_;
|
std::list<ThreadMessage_> thread_messages_;
|
||||||
std::condition_variable client_listener_cv_;
|
|
||||||
std::mutex client_listener_mutex_;
|
std::mutex client_listener_mutex_;
|
||||||
std::list<std::vector<char>> data_to_client_;
|
std::list<std::vector<char>> data_to_client_;
|
||||||
PyThreadState* py_thread_state_{};
|
PyThreadState* py_thread_state_{};
|
||||||
|
|||||||
@ -68,7 +68,7 @@ class Graphics;
|
|||||||
///
|
///
|
||||||
/// Category: Enums
|
/// Category: Enums
|
||||||
///
|
///
|
||||||
enum class InputType {
|
enum class InputType : uint8_t {
|
||||||
kUpDown = 2,
|
kUpDown = 2,
|
||||||
kLeftRight,
|
kLeftRight,
|
||||||
kJumpPress,
|
kJumpPress,
|
||||||
@ -111,7 +111,7 @@ enum class InputType {
|
|||||||
///
|
///
|
||||||
/// 'hard' leads to the process exiting. This generally should be avoided
|
/// 'hard' leads to the process exiting. This generally should be avoided
|
||||||
/// on platforms such as mobile.
|
/// on platforms such as mobile.
|
||||||
enum class QuitType {
|
enum class QuitType : uint8_t {
|
||||||
kSoft,
|
kSoft,
|
||||||
kBack,
|
kBack,
|
||||||
kHard,
|
kHard,
|
||||||
@ -139,7 +139,7 @@ typedef int64_t TimerMedium;
|
|||||||
/// 'small' is used primarily for phones or other small devices where
|
/// 'small' is used primarily for phones or other small devices where
|
||||||
/// content needs to be presented as large and clear in order to remain
|
/// content needs to be presented as large and clear in order to remain
|
||||||
/// readable from an average distance.
|
/// readable from an average distance.
|
||||||
enum class UIScale {
|
enum class UIScale : uint8_t {
|
||||||
kLarge,
|
kLarge,
|
||||||
kMedium,
|
kMedium,
|
||||||
kSmall,
|
kSmall,
|
||||||
@ -162,7 +162,7 @@ enum class UIScale {
|
|||||||
/// 'real' time is mostly based on clock time, with a few exceptions. It may
|
/// 'real' time is mostly based on clock time, with a few exceptions. It may
|
||||||
/// not advance while the app is backgrounded for instance. (the engine
|
/// not advance while the app is backgrounded for instance. (the engine
|
||||||
/// attempts to prevent single large time jumps from occurring)
|
/// attempts to prevent single large time jumps from occurring)
|
||||||
enum class TimeType {
|
enum class TimeType : uint8_t {
|
||||||
kSim,
|
kSim,
|
||||||
kBase,
|
kBase,
|
||||||
kReal,
|
kReal,
|
||||||
@ -173,7 +173,7 @@ enum class TimeType {
|
|||||||
/// Specifies the format time values are provided in.
|
/// Specifies the format time values are provided in.
|
||||||
///
|
///
|
||||||
/// Category: Enums
|
/// Category: Enums
|
||||||
enum class TimeFormat {
|
enum class TimeFormat : uint8_t {
|
||||||
kSeconds,
|
kSeconds,
|
||||||
kMilliseconds,
|
kMilliseconds,
|
||||||
kLast // Sentinel.
|
kLast // Sentinel.
|
||||||
@ -183,7 +183,7 @@ enum class TimeFormat {
|
|||||||
/// Permissions that can be requested from the OS.
|
/// Permissions that can be requested from the OS.
|
||||||
///
|
///
|
||||||
/// Category: Enums
|
/// Category: Enums
|
||||||
enum class Permission {
|
enum class Permission : uint8_t {
|
||||||
kStorage,
|
kStorage,
|
||||||
kLast // Sentinel.
|
kLast // Sentinel.
|
||||||
};
|
};
|
||||||
@ -192,7 +192,7 @@ enum class Permission {
|
|||||||
/// Special characters the game can print.
|
/// Special characters the game can print.
|
||||||
///
|
///
|
||||||
/// Category: Enums
|
/// Category: Enums
|
||||||
enum class SpecialChar {
|
enum class SpecialChar : uint8_t {
|
||||||
kDownArrow,
|
kDownArrow,
|
||||||
kUpArrow,
|
kUpArrow,
|
||||||
kLeftArrow,
|
kLeftArrow,
|
||||||
@ -288,7 +288,7 @@ enum class SpecialChar {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/// Python exception types we can raise from our own exceptions.
|
/// Python exception types we can raise from our own exceptions.
|
||||||
enum class PyExcType {
|
enum class PyExcType : uint8_t {
|
||||||
kRuntime,
|
kRuntime,
|
||||||
kAttribute,
|
kAttribute,
|
||||||
kIndex,
|
kIndex,
|
||||||
@ -306,7 +306,7 @@ enum class PyExcType {
|
|||||||
kWidgetNotFound
|
kWidgetNotFound
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class LogLevel {
|
enum class LogLevel : uint8_t {
|
||||||
kDebug,
|
kDebug,
|
||||||
kInfo,
|
kInfo,
|
||||||
kWarning,
|
kWarning,
|
||||||
@ -314,7 +314,7 @@ enum class LogLevel {
|
|||||||
kCritical,
|
kCritical,
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class ThreadSource {
|
enum class ThreadSource : uint8_t {
|
||||||
/// Spin up a new thread for the event loop.
|
/// Spin up a new thread for the event loop.
|
||||||
kCreate,
|
kCreate,
|
||||||
/// Wrap the event loop around the current thread.
|
/// Wrap the event loop around the current thread.
|
||||||
@ -323,7 +323,7 @@ enum class ThreadSource {
|
|||||||
|
|
||||||
/// Used for thread identification.
|
/// Used for thread identification.
|
||||||
/// Mostly just for debugging.
|
/// Mostly just for debugging.
|
||||||
enum class EventLoopID {
|
enum class EventLoopID : uint8_t {
|
||||||
kInvalid,
|
kInvalid,
|
||||||
kLogic,
|
kLogic,
|
||||||
kAssets,
|
kAssets,
|
||||||
|
|||||||
@ -143,6 +143,12 @@ class ServerConfig:
|
|||||||
# queue spamming attacks.
|
# queue spamming attacks.
|
||||||
enable_queue: bool = True
|
enable_queue: bool = True
|
||||||
|
|
||||||
|
# Protocol version we host with. Currently the default is 33 which
|
||||||
|
# still allows older 1.4 game clients to connect. Explicitly setting
|
||||||
|
# to 35 no longer allows those clients but adds/fixes a few things
|
||||||
|
# such as making camera shake properly work in net games.
|
||||||
|
protocol_version: int | None = None
|
||||||
|
|
||||||
# (internal) stress-testing mode.
|
# (internal) stress-testing mode.
|
||||||
stress_test_players: int | None = None
|
stress_test_players: int | None = None
|
||||||
|
|
||||||
|
|||||||
@ -601,6 +601,7 @@ def _get_server_config_raw_contents(projroot: str) -> str:
|
|||||||
|
|
||||||
def _get_server_config_template_yaml(projroot: str) -> str:
|
def _get_server_config_template_yaml(projroot: str) -> str:
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
|
# pylint: disable=too-many-statements
|
||||||
import yaml
|
import yaml
|
||||||
|
|
||||||
lines_in = _get_server_config_raw_contents(projroot).splitlines()
|
lines_in = _get_server_config_raw_contents(projroot).splitlines()
|
||||||
@ -664,6 +665,8 @@ def _get_server_config_template_yaml(projroot: str) -> str:
|
|||||||
vval = 'https://mystatssite.com/showstats?player=${ACCOUNT}'
|
vval = 'https://mystatssite.com/showstats?player=${ACCOUNT}'
|
||||||
elif vname == 'admins':
|
elif vname == 'admins':
|
||||||
vval = ['pb-yOuRAccOuNtIdHErE', 'pb-aNdMayBeAnotherHeRE']
|
vval = ['pb-yOuRAccOuNtIdHErE', 'pb-aNdMayBeAnotherHeRE']
|
||||||
|
elif vname == 'protocol_version':
|
||||||
|
vval = 35
|
||||||
lines_out += [
|
lines_out += [
|
||||||
'#' + l for l in yaml.dump({vname: vval}).strip().splitlines()
|
'#' + l for l in yaml.dump({vname: vval}).strip().splitlines()
|
||||||
]
|
]
|
||||||
|
|||||||
@ -59,6 +59,10 @@ def _gen_enums(infilename: str) -> str:
|
|||||||
|
|
||||||
def _parse_name(lines: list[str], lnum: int) -> str:
|
def _parse_name(lines: list[str], lnum: int) -> str:
|
||||||
bits = lines[lnum].split(' ')
|
bits = lines[lnum].split(' ')
|
||||||
|
|
||||||
|
# Special case: allow for specifying underlying type.
|
||||||
|
if len(bits) == 6 and bits[3] == ':' and bits[4] in {'uint8_t', 'uint16_t'}:
|
||||||
|
bits = [bits[0], bits[1], bits[2], bits[5]]
|
||||||
if (
|
if (
|
||||||
len(bits) != 4
|
len(bits) != 4
|
||||||
or bits[0] != 'enum'
|
or bits[0] != 'enum'
|
||||||
|
|||||||
@ -29,7 +29,7 @@ from batools.spinoff._state import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
if TYPE_CHECKING:
|
if TYPE_CHECKING:
|
||||||
from typing import Callable, Iterable
|
from typing import Callable, Iterable, Any
|
||||||
|
|
||||||
from batools.project import ProjectUpdater
|
from batools.project import ProjectUpdater
|
||||||
|
|
||||||
@ -54,6 +54,7 @@ class SpinoffContext:
|
|||||||
OVERRIDE = 'override'
|
OVERRIDE = 'override'
|
||||||
DIFF = 'diff'
|
DIFF = 'diff'
|
||||||
BACKPORT = 'backport'
|
BACKPORT = 'backport'
|
||||||
|
DESCRIBE_PATH = 'describe_path'
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
@ -66,6 +67,7 @@ class SpinoffContext:
|
|||||||
override_paths: list[str] | None = None,
|
override_paths: list[str] | None = None,
|
||||||
backport_file: str | None = None,
|
backport_file: str | None = None,
|
||||||
auto_backport: bool = False,
|
auto_backport: bool = False,
|
||||||
|
describe_path: str | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
# pylint: disable=too-many-statements
|
# pylint: disable=too-many-statements
|
||||||
|
|
||||||
@ -82,6 +84,7 @@ class SpinoffContext:
|
|||||||
self._override_paths = override_paths
|
self._override_paths = override_paths
|
||||||
self._backport_file = backport_file
|
self._backport_file = backport_file
|
||||||
self._auto_backport = auto_backport
|
self._auto_backport = auto_backport
|
||||||
|
self._describe_path = describe_path
|
||||||
|
|
||||||
self._project_updater: ProjectUpdater | None = None
|
self._project_updater: ProjectUpdater | None = None
|
||||||
|
|
||||||
@ -278,10 +281,10 @@ class SpinoffContext:
|
|||||||
self._src_omit_feature_sets,
|
self._src_omit_feature_sets,
|
||||||
) = self._calc_src_retain_omit_feature_sets()
|
) = self._calc_src_retain_omit_feature_sets()
|
||||||
|
|
||||||
# Generate a version of src_omit_paths that includes our feature-set
|
# Generate a version of src_omit_paths that includes some extra values
|
||||||
# omissions. Basically, omitting a feature set simply omits
|
|
||||||
# particular names at a few particular places.
|
|
||||||
self._src_omit_paths_expanded = self.src_omit_paths.copy()
|
self._src_omit_paths_expanded = self.src_omit_paths.copy()
|
||||||
|
# Include feature-set omissions. Basically, omitting a feature
|
||||||
|
# set simply omits particular names at a few particular places.
|
||||||
self._add_feature_set_omit_paths(self._src_omit_paths_expanded)
|
self._add_feature_set_omit_paths(self._src_omit_paths_expanded)
|
||||||
|
|
||||||
# Create a version of dst-write-paths that also includes filtered
|
# Create a version of dst-write-paths that also includes filtered
|
||||||
@ -362,6 +365,7 @@ class SpinoffContext:
|
|||||||
def run(self) -> None:
|
def run(self) -> None:
|
||||||
"""Do the thing."""
|
"""Do the thing."""
|
||||||
# pylint: disable=too-many-branches
|
# pylint: disable=too-many-branches
|
||||||
|
# pylint: disable=too-many-statements
|
||||||
|
|
||||||
self._read_state()
|
self._read_state()
|
||||||
|
|
||||||
@ -400,6 +404,20 @@ class SpinoffContext:
|
|||||||
# Ignore anything under omitted paths/names.
|
# Ignore anything under omitted paths/names.
|
||||||
self._filter_src_git_file_list()
|
self._filter_src_git_file_list()
|
||||||
|
|
||||||
|
# Go through the final set of files we're syncing to dst and
|
||||||
|
# make sure none of them fall under our unchecked-paths list.
|
||||||
|
# That would mean we are writing a file but we're also declaring
|
||||||
|
# that we don't care if anyone else writes that file, which
|
||||||
|
# could lead to ambiguous/dangerous situations where spinoff as
|
||||||
|
# well as some command on dst write to the same file.
|
||||||
|
for path in self._src_git_files:
|
||||||
|
if _any_path_contains(self.src_unchecked_paths, path):
|
||||||
|
self._src_error_entities[path] = (
|
||||||
|
'Synced file falls under src_unchecked_paths, which'
|
||||||
|
" is not allowed. Either don't sync the file or carve"
|
||||||
|
' it out from src_unchecked_paths.'
|
||||||
|
)
|
||||||
|
|
||||||
# Now map whatever is left to paths in dst.
|
# Now map whatever is left to paths in dst.
|
||||||
self._dst_git_files = set(
|
self._dst_git_files = set(
|
||||||
self._filter_path(s) for s in self._src_git_files
|
self._filter_path(s) for s in self._src_git_files
|
||||||
@ -454,8 +472,10 @@ class SpinoffContext:
|
|||||||
)
|
)
|
||||||
raise self.BackportInProgressError
|
raise self.BackportInProgressError
|
||||||
|
|
||||||
|
if self._mode is self.Mode.DESCRIBE_PATH:
|
||||||
|
self._do_describe_path()
|
||||||
# If anything is off, print errors; otherwise actually do the deed.
|
# If anything is off, print errors; otherwise actually do the deed.
|
||||||
if self._src_error_entities or self._dst_error_entities:
|
elif self._src_error_entities or self._dst_error_entities:
|
||||||
self._print_error_entities()
|
self._print_error_entities()
|
||||||
else:
|
else:
|
||||||
if (
|
if (
|
||||||
@ -511,6 +531,76 @@ class SpinoffContext:
|
|||||||
if self._mode is self.Mode.UPDATE or self._mode is self.Mode.OVERRIDE:
|
if self._mode is self.Mode.UPDATE or self._mode is self.Mode.OVERRIDE:
|
||||||
self._write_gitignore()
|
self._write_gitignore()
|
||||||
|
|
||||||
|
def _do_describe_path(self) -> None:
|
||||||
|
assert self._describe_path is not None
|
||||||
|
path = self._describe_path
|
||||||
|
|
||||||
|
# Currently operating only on dst paths.
|
||||||
|
if path.startswith('/') and not path.startswith(self._dst_root):
|
||||||
|
raise CleanError('Please supply a path in the dst dir.')
|
||||||
|
|
||||||
|
# Allow abs paths.
|
||||||
|
path = path.removeprefix(f'{self._dst_root}/')
|
||||||
|
|
||||||
|
if self._src_error_entities or self._dst_error_entities:
|
||||||
|
print(
|
||||||
|
f'{Clr.RED}Note: Errors are present;'
|
||||||
|
f' this info may not be fully accurate.{Clr.RST}'
|
||||||
|
)
|
||||||
|
print(f'{Clr.BLD}dstpath: {Clr.BLU}{path}{Clr.RST}')
|
||||||
|
|
||||||
|
def _printval(name: Any, val: Any) -> None:
|
||||||
|
print(f' {name}: {Clr.BLU}{val}{Clr.RST}')
|
||||||
|
|
||||||
|
_printval('exists', os.path.exists(os.path.join(self._dst_root, path)))
|
||||||
|
|
||||||
|
# Adapted from code in _check_spinoff_managed_dirs.
|
||||||
|
managed = False
|
||||||
|
unchecked = False
|
||||||
|
git_mirrored = False
|
||||||
|
|
||||||
|
dstrootsl = f'{self._dst_root}/'
|
||||||
|
assert self._spinoff_managed_dirs is not None
|
||||||
|
for rdir in self._spinoff_managed_dirs:
|
||||||
|
for root, dirnames, fnames in os.walk(
|
||||||
|
os.path.join(self._dst_root, rdir),
|
||||||
|
topdown=True,
|
||||||
|
):
|
||||||
|
# Completely ignore ignore-names in both dirs and files
|
||||||
|
# and cruft-file names in files.
|
||||||
|
for dirname in dirnames.copy():
|
||||||
|
if dirname in self.ignore_names:
|
||||||
|
dirnames.remove(dirname)
|
||||||
|
for fname in fnames.copy():
|
||||||
|
if (
|
||||||
|
fname in self.ignore_names
|
||||||
|
or fname in self.cruft_file_names
|
||||||
|
):
|
||||||
|
fnames.remove(fname)
|
||||||
|
|
||||||
|
for fname in fnames:
|
||||||
|
dst_path_full = os.path.join(root, fname)
|
||||||
|
assert dst_path_full.startswith(dstrootsl)
|
||||||
|
dst_path = dst_path_full.removeprefix(dstrootsl)
|
||||||
|
if dst_path == path:
|
||||||
|
managed = True
|
||||||
|
if _any_path_contains(self._dst_unchecked_paths, dst_path):
|
||||||
|
unchecked = True
|
||||||
|
if _any_path_contains(self.git_mirrored_paths, dst_path):
|
||||||
|
git_mirrored = True
|
||||||
|
_printval(
|
||||||
|
'spinoff-managed',
|
||||||
|
managed,
|
||||||
|
)
|
||||||
|
_printval(
|
||||||
|
'unchecked',
|
||||||
|
unchecked,
|
||||||
|
)
|
||||||
|
_printval(
|
||||||
|
'git-mirrored',
|
||||||
|
git_mirrored,
|
||||||
|
)
|
||||||
|
|
||||||
def _apply_project_configs(self) -> None:
|
def _apply_project_configs(self) -> None:
|
||||||
# pylint: disable=exec-used
|
# pylint: disable=exec-used
|
||||||
try:
|
try:
|
||||||
@ -744,9 +834,6 @@ class SpinoffContext:
|
|||||||
|
|
||||||
# Strip out any sections frames by our strip-begin/end tags.
|
# Strip out any sections frames by our strip-begin/end tags.
|
||||||
|
|
||||||
# strip_tag_pairs: list[tuple[str, str]] = []
|
|
||||||
# print('HELLO WORLD')
|
|
||||||
|
|
||||||
def _first_index_containing_string(
|
def _first_index_containing_string(
|
||||||
items: list[str], substring: str
|
items: list[str], substring: str
|
||||||
) -> int | None:
|
) -> int | None:
|
||||||
@ -795,7 +882,7 @@ class SpinoffContext:
|
|||||||
'make any edits in source project)'
|
'make any edits in source project)'
|
||||||
)
|
)
|
||||||
lines = self.default_filter_text(text).splitlines()
|
lines = self.default_filter_text(text).splitlines()
|
||||||
return '\n'.join(lines[:1] + ['', blurb] + lines[1:])
|
return '\n'.join([blurb, ' '] + lines)
|
||||||
if 'Jenkinsfile' in src_path:
|
if 'Jenkinsfile' in src_path:
|
||||||
blurb = (
|
blurb = (
|
||||||
'// THIS FILE IS AUTOGENERATED BY SPINOFF;'
|
'// THIS FILE IS AUTOGENERATED BY SPINOFF;'
|
||||||
@ -1028,8 +1115,10 @@ class SpinoffContext:
|
|||||||
"""Print info about entity errors encountered."""
|
"""Print info about entity errors encountered."""
|
||||||
print(
|
print(
|
||||||
'\nSpinoff Error(s) Found:\n'
|
'\nSpinoff Error(s) Found:\n'
|
||||||
" Tip: to resolve 'spinoff-managed file modified' errors,\n"
|
" Tips: To resolve 'spinoff-managed file modified' errors,\n"
|
||||||
" use the 'backport' subcommand.\n",
|
" use the 'backport' subcommand.\n"
|
||||||
|
" To debug other issues, try the 'describe-path'"
|
||||||
|
' subcommand.\n',
|
||||||
file=sys.stderr,
|
file=sys.stderr,
|
||||||
)
|
)
|
||||||
for key, val in sorted(self._src_error_entities.items()):
|
for key, val in sorted(self._src_error_entities.items()):
|
||||||
@ -1046,7 +1135,18 @@ class SpinoffContext:
|
|||||||
print('')
|
print('')
|
||||||
|
|
||||||
def _validate_final_lists(self) -> None:
|
def _validate_final_lists(self) -> None:
|
||||||
"""Make sure we never delete the few files we're letting git store."""
|
"""Check some last things on our entities lists before we update."""
|
||||||
|
|
||||||
|
# Go through the final set of files we're syncing to dst and
|
||||||
|
# make sure none of them fall under our unchecked-paths list.
|
||||||
|
# That would mean we are writing a file but we're also declaring
|
||||||
|
# that we don't care if anyone else writes that file, which
|
||||||
|
# could lead to ambiguous/dangerous situations where spinoff as
|
||||||
|
# well as some command on dst write to the same file.
|
||||||
|
# print('CHECKING', self._src_copy_entities)
|
||||||
|
# for ent in self._src_copy_entities:
|
||||||
|
# if _any_path_contains(self._dst_unchecked_paths, ent):
|
||||||
|
# raise CleanError('FOUND BAD PATH', ent)
|
||||||
|
|
||||||
for ent in self._dst_purge_entities.copy():
|
for ent in self._dst_purge_entities.copy():
|
||||||
if _any_path_contains(self.git_mirrored_paths, ent):
|
if _any_path_contains(self.git_mirrored_paths, ent):
|
||||||
@ -1806,7 +1906,8 @@ class SpinoffContext:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def _filter_src_git_file_list(self) -> None:
|
def _filter_src_git_file_list(self) -> None:
|
||||||
# Crate a filtered version of src git files based on our omit entries.
|
# Create a filtered version of src git files based on our omit
|
||||||
|
# entries.
|
||||||
out = set[str]()
|
out = set[str]()
|
||||||
assert self._src_git_files is not None
|
assert self._src_git_files is not None
|
||||||
for gitpath in self._src_git_files:
|
for gitpath in self._src_git_files:
|
||||||
@ -1958,7 +2059,7 @@ class SpinoffContext:
|
|||||||
|
|
||||||
# In strict mode we want it to always be an error if dst mod-time
|
# In strict mode we want it to always be an error if dst mod-time
|
||||||
# varies from the version we wrote (we want to track down anyone
|
# varies from the version we wrote (we want to track down anyone
|
||||||
# writing to our files who is not us).
|
# writing to our managed files who is not us).
|
||||||
# Note that we need to ignore git-mirrored-paths because git might
|
# Note that we need to ignore git-mirrored-paths because git might
|
||||||
# be mucking with modtimes itself.
|
# be mucking with modtimes itself.
|
||||||
if (
|
if (
|
||||||
|
|||||||
@ -32,6 +32,7 @@ class Command(Enum):
|
|||||||
CLEAN_CHECK = 'cleancheck'
|
CLEAN_CHECK = 'cleancheck'
|
||||||
OVERRIDE = 'override'
|
OVERRIDE = 'override'
|
||||||
DIFF = 'diff'
|
DIFF = 'diff'
|
||||||
|
DESCRIBE_PATH = 'describe-path'
|
||||||
BACKPORT = 'backport'
|
BACKPORT = 'backport'
|
||||||
CREATE = 'create'
|
CREATE = 'create'
|
||||||
ADD_SUBMODULE_PARENT = 'add-submodule-parent'
|
ADD_SUBMODULE_PARENT = 'add-submodule-parent'
|
||||||
@ -94,6 +95,8 @@ def _main() -> None:
|
|||||||
single_run_mode = SpinoffContext.Mode.CLEAN_CHECK
|
single_run_mode = SpinoffContext.Mode.CLEAN_CHECK
|
||||||
elif cmd is Command.DIFF:
|
elif cmd is Command.DIFF:
|
||||||
single_run_mode = SpinoffContext.Mode.DIFF
|
single_run_mode = SpinoffContext.Mode.DIFF
|
||||||
|
elif cmd is Command.DESCRIBE_PATH:
|
||||||
|
single_run_mode = SpinoffContext.Mode.DESCRIBE_PATH
|
||||||
elif cmd is Command.OVERRIDE:
|
elif cmd is Command.OVERRIDE:
|
||||||
_do_override(src_root, dst_root)
|
_do_override(src_root, dst_root)
|
||||||
elif cmd is Command.BACKPORT:
|
elif cmd is Command.BACKPORT:
|
||||||
@ -115,6 +118,12 @@ def _main() -> None:
|
|||||||
assert_never(cmd)
|
assert_never(cmd)
|
||||||
|
|
||||||
if single_run_mode is not None:
|
if single_run_mode is not None:
|
||||||
|
from efrotools import extract_flag
|
||||||
|
|
||||||
|
args = sys.argv[2:]
|
||||||
|
force = extract_flag(args, '--force')
|
||||||
|
verbose = extract_flag(args, '--verbose')
|
||||||
|
print_full_lists = extract_flag(args, '--full')
|
||||||
if src_root is None:
|
if src_root is None:
|
||||||
if '--soft' in sys.argv:
|
if '--soft' in sys.argv:
|
||||||
return
|
return
|
||||||
@ -123,6 +132,15 @@ def _main() -> None:
|
|||||||
' you appear to be in a src project.'
|
' you appear to be in a src project.'
|
||||||
" To silently no-op in this case, pass '--soft'."
|
" To silently no-op in this case, pass '--soft'."
|
||||||
)
|
)
|
||||||
|
|
||||||
|
describe_path: str | None
|
||||||
|
if single_run_mode is SpinoffContext.Mode.DESCRIBE_PATH:
|
||||||
|
if len(args) != 1:
|
||||||
|
raise CleanError(f'Expected a single path arg; got {args}.')
|
||||||
|
describe_path = args[0]
|
||||||
|
else:
|
||||||
|
describe_path = None
|
||||||
|
|
||||||
# SpinoffContext should never be relying on relative paths, so let's
|
# SpinoffContext should never be relying on relative paths, so let's
|
||||||
# keep ourself honest by making sure.
|
# keep ourself honest by making sure.
|
||||||
os.chdir('/')
|
os.chdir('/')
|
||||||
@ -130,9 +148,10 @@ def _main() -> None:
|
|||||||
src_root,
|
src_root,
|
||||||
dst_root,
|
dst_root,
|
||||||
single_run_mode,
|
single_run_mode,
|
||||||
force='--force' in sys.argv,
|
force=force,
|
||||||
verbose='--verbose' in sys.argv,
|
verbose=verbose,
|
||||||
print_full_lists='--full' in sys.argv,
|
print_full_lists=print_full_lists,
|
||||||
|
describe_path=describe_path,
|
||||||
).run()
|
).run()
|
||||||
|
|
||||||
|
|
||||||
@ -581,6 +600,8 @@ def _print_available_commands() -> None:
|
|||||||
'Remove files from spinoff, leaving local copies in place.\n'
|
'Remove files from spinoff, leaving local copies in place.\n'
|
||||||
f' {bgn}backport [file]{end} '
|
f' {bgn}backport [file]{end} '
|
||||||
'Help get changes to spinoff dst files back to src.\n'
|
'Help get changes to spinoff dst files back to src.\n'
|
||||||
|
f' {bgn}describe-path [path]{end}'
|
||||||
|
' Tells whether a path is spinoff-managed/etc.\n'
|
||||||
f' {bgn}create [name, path]{end} '
|
f' {bgn}create [name, path]{end} '
|
||||||
'Create a new spinoff project based on this src one.\n'
|
'Create a new spinoff project based on this src one.\n'
|
||||||
' Name should be passed in CamelCase form.\n'
|
' Name should be passed in CamelCase form.\n'
|
||||||
|
|||||||
@ -165,7 +165,6 @@ class _Outputter:
|
|||||||
)
|
)
|
||||||
return value if self._create else None
|
return value if self._create else None
|
||||||
|
|
||||||
# noinspection PyPep8
|
|
||||||
if origin is typing.Union or origin is types.UnionType:
|
if origin is typing.Union or origin is types.UnionType:
|
||||||
# Currently, the only unions we support are None/Value
|
# Currently, the only unions we support are None/Value
|
||||||
# (translated from Optional), which we verified on prep.
|
# (translated from Optional), which we verified on prep.
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user