mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-19 21:37:57 +08:00
Latest public/internal sync.
This commit is contained in:
parent
0ab52dc448
commit
08b9d07e5e
@ -420,7 +420,7 @@
|
||||
"assets/build/ba_data/audio/zoeOw.ogg": "https://files.ballistica.net/cache/ba1/60/ad/38269b7f1c7dc20cb9a506cd0681",
|
||||
"assets/build/ba_data/audio/zoePickup01.ogg": "https://files.ballistica.net/cache/ba1/72/85/d6fc4d16b7081d91fba2850b5b10",
|
||||
"assets/build/ba_data/audio/zoeScream01.ogg": "https://files.ballistica.net/cache/ba1/e9/ae/1d674d0c086eaa0bd1c3b1db0505",
|
||||
"assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/7d/d4/6a32da2a6a5d1f8d71f65ac65792",
|
||||
"assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/36/57/76a60b261de061886357dbf2c0db",
|
||||
"assets/build/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/e2/24/5e7ea9ca5c9de4d3b7a28e53564d",
|
||||
"assets/build/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/61/03/89070ca765e06da3a419a579f503",
|
||||
"assets/build/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/f8/15/e1a2fa38697417bcf2cf19cd34ef",
|
||||
@ -454,7 +454,7 @@
|
||||
"assets/build/ba_data/data/languages/thai.json": "https://files.ballistica.net/cache/ba1/f7/df/7ba5f99c5c2c4c86fc0503fcf0b7",
|
||||
"assets/build/ba_data/data/languages/turkish.json": "https://files.ballistica.net/cache/ba1/9a/90/8e2ed626def09f88c3b9ab5215a3",
|
||||
"assets/build/ba_data/data/languages/ukrainian.json": "https://files.ballistica.net/cache/ba1/ab/35/644e4239cfa62a597a905412b90c",
|
||||
"assets/build/ba_data/data/languages/venetian.json": "https://files.ballistica.net/cache/ba1/53/9e/068074156b38bab7f732977a4031",
|
||||
"assets/build/ba_data/data/languages/venetian.json": "https://files.ballistica.net/cache/ba1/22/8e/3fccd3a8c9761c9e60ee4f5ecd85",
|
||||
"assets/build/ba_data/data/languages/vietnamese.json": "https://files.ballistica.net/cache/ba1/1f/ae/abe3f105b3c4b51f6b7942773305",
|
||||
"assets/build/ba_data/data/maps/big_g.json": "https://files.ballistica.net/cache/ba1/47/0a/a617cc85d927b576c4e6fc1091ed",
|
||||
"assets/build/ba_data/data/maps/bridgit.json": "https://files.ballistica.net/cache/ba1/03/4b/57ee9b42854b26f23f81bd8c58ef",
|
||||
@ -3995,50 +3995,50 @@
|
||||
"assets/src/ba_data/python/ba/_generated/__init__.py": "https://files.ballistica.net/cache/ba1/ee/e8/cad05aa531c7faf7ff7b96db7f6e",
|
||||
"assets/src/ba_data/python/ba/_generated/enums.py": "https://files.ballistica.net/cache/ba1/b2/e5/0ee0561e16257a32830645239f34",
|
||||
"ballisticacore-windows/Generic/BallisticaCore.ico": "https://files.ballistica.net/cache/ba1/89/c0/e32c7d2a35dc9aef57cc73b0911a",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/b4/b2/d9d81b227c329f77198d96ee2ae1",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/06/0d/c5503c7e9b4e8c5465b7df36c3ab",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/2e/57/6c8e84496af8bcfc60b6030f9008",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f1/37/e421b0f64743dd33ff9e3db54838",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/3a/83/153f01e88ee01db4e2dd234ce189",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/0f/9b/66b3b2c089137e81496868f9c829",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/5a/2e/0b387964a1cbe7658f353b0c3d58",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/77/52/ae35cb8cfea02c60417968702b3f",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/cf/55/d502cfa9bef1142b7cc240759c07",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/c7/59/4d6c76a8e57478a81804b446cec7",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/ed/5c/68871f87dd6353ca6693b24c895c",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/69/34/d573542acf00286337a8a7d4070e",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/54/21/ad891fc900eb06eb307401887479",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/10/6d/b079707d97db261651539cf42be4",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/9f/ec/d6f090057a354d6f27778a79151d",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/2f/8d/7076055efd990269b20af15d092a",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/b6/9b/04419fdb2911f107ba2cf3d1daef",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/89/c0/d3b177978d11d0283b0290e88960",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/32/23/ce5919233ff0438442f6e6e66a62",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/99/51/13dbd2e177dd17314b4bc86fe200",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e2/88/53757bc9fd92d49bd35dc6d3be0e",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/06/69/078f7eef49a1127a1492db4703f6",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/82/f1/2b13fe77164f72d2bf57453bb8e5",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/6a/b0/a853b61ab794706bbf395ecd2a80",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/76/3d/b0d2913a1650bdc35b2ca0d81154",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/cb/b3/39a0642a376e1f131172f9500353",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7a/07/4804a222e0d92f0fab8b279ce4c1",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/55/09/bf8e7d6ce41962163411c6bbd884",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/1d/be/5e0b2be7272c4e443cc974d5b182",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/5e/ab/075e9137d21e6110d29b67210533",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/54/2a/c5e1d5ed4328c40821695db2cd84",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b7/dd/55210b3f3c075d9b237e0c6aa733",
|
||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a2/00/d01842e8b0777f7e6ea47c912b16",
|
||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a6/91/f9cb15d0876750e28abe3b0d221c",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b7/11/1ecfe322ae997772b71538664cad",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8d/d6/1e83dba73d581cfb2b2f6eb31f22",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/50/6a/ca5e49b3cad047b541648dc9914d",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/6f/f7/b38282cfbd3cb0cda89d5a458176",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/97/e5/1d2e76fcadbe022d4c522c7d2135",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/54/d6/2d642477837c34c946b70e27014d",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/79/2b/f17b81bdf03719098a558305f0a3",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/9a/5a/3ca5679187f8dd5f89d8bb68ed84",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/4b/88/d016d4059ea7b334e95d1e6ef258",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/1b/35/b11d851fc912b7bcd57766981fa2",
|
||||
"build/prefab/full/linux_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/7c/40/4d11b867dbfdab90a1601f31b792",
|
||||
"build/prefab/full/linux_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/c1/64/71a9087e53146cc5377135b42b73",
|
||||
"build/prefab/full/linux_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/32/bd/f82f8059a47fd84e5d86d8814ecb",
|
||||
"build/prefab/full/linux_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/68/a5/24e48c50d5325869c9ce56b691ba",
|
||||
"build/prefab/full/linux_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/f5/0b/9694a6942b8f6580eb041040b7f4",
|
||||
"build/prefab/full/linux_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/a8/53/ab21700e2be712f046b6c11e6061",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a5/bd/6a29dc921daec7c8508fbf17b24a",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/24/71/80d7091f262bff97ff18a4bb8948",
|
||||
"build/prefab/full/mac_arm64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/f1/0e/c3738bcfce8a0ff51140f95ec9a5",
|
||||
"build/prefab/full/mac_arm64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/d8/7e/3ecd464aa666da920625a8336ccf",
|
||||
"build/prefab/full/mac_arm64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/c4/12/244ec02b1e210441832b9265d96b",
|
||||
"build/prefab/full/mac_arm64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/1a/42/7ff1c8244886f3a8a3772ffe82e1",
|
||||
"build/prefab/full/mac_x86_64_gui/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/25/50/b37e83853bc4a5549686f79d4346",
|
||||
"build/prefab/full/mac_x86_64_gui/release/ballisticacore": "https://files.ballistica.net/cache/ba1/fa/80/4e12c28bd617bcee0b3402f6bfcc",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/dd/64/9c8a9a811af36110da0d875ef694",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/43/ba/6d7956bc670c75758e74615a9f3f",
|
||||
"build/prefab/full/windows_x86_gui/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/7c/29/68fdb792ec55c6ba404b09bba49c",
|
||||
"build/prefab/full/windows_x86_gui/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/bb/1b/2d3c1944086c8e70fe396aeb73ff",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/b4/e5/a46580be7e047d2f0d12587b3e30",
|
||||
"build/prefab/full/windows_x86_server/release/dist/BallisticaCoreHeadless.exe": "https://files.ballistica.net/cache/ba1/f5/a5/98b120cfcc070679e032ee684ca3",
|
||||
"build/prefab/lib/linux_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/9d/a6/c963859c531bb19f507f405cf589",
|
||||
"build/prefab/lib/linux_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/df/dc/79349a169d3b00964d9f35853f84",
|
||||
"build/prefab/lib/linux_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/a7/63/d057e19cb7806302b9570b91c573",
|
||||
"build/prefab/lib/linux_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/17/c0/3da5a81581aa9275b6c32bb03fc8",
|
||||
"build/prefab/lib/linux_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/3f/84/d66202e8a15d5518f876dccdd6d6",
|
||||
"build/prefab/lib/linux_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/5a/ba/3a6f95b9e4a9c310ac63d0bc0a8c",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/2f/58/9462e34601e2f442eb429185ee35",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/50/09/6e8feb718cb60ea80ecaab4ba9a0",
|
||||
"build/prefab/lib/mac_arm64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7d/a3/f8fd8ad1037e5f5b47b72a6d1edf",
|
||||
"build/prefab/lib/mac_arm64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/9d/c6/3902a717b71f9f8781d724c8ca23",
|
||||
"build/prefab/lib/mac_arm64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e4/5a/3b25cbbca51ef2a6036d8d1805aa",
|
||||
"build/prefab/lib/mac_arm64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/fd/11/f0fb88f01753350f88f068b6c6a0",
|
||||
"build/prefab/lib/mac_x86_64_gui/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/e4/22/d3700b99ec02b9bd99b8801ad69b",
|
||||
"build/prefab/lib/mac_x86_64_gui/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8b/d8/4b2e840ace5be8dd8fc9d6841cdd",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/46/07/545eec0e6bde25bba8b3857d7e9b",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/b4/ae/d9a2b38dc9824ac6acc79d520404",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/4a/d3/b01764e89eafd5b9a3ffc2e2e540",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/9a/0e/ce412ed759cb5275b647f19b33ff",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/96/6d/d2aa5bbb9d9f2d3f5fd51042d50b",
|
||||
"build/prefab/lib/windows/Debug_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/bc/ed/250948d00952c6e690e3e24511f6",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.lib": "https://files.ballistica.net/cache/ba1/59/2e/2b4b7140eb237e6ce00b7ec975db",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaCoreGenericInternal.pdb": "https://files.ballistica.net/cache/ba1/7f/0a/1ee8835881fb9af7a3c3576de59a",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.lib": "https://files.ballistica.net/cache/ba1/b6/3d/6a800860c35bd41f4aad2199b10f",
|
||||
"build/prefab/lib/windows/Release_Win32/BallisticaCoreHeadlessInternal.pdb": "https://files.ballistica.net/cache/ba1/d3/7d/3fee0a9e0b2d4ad3601722def1c8",
|
||||
"src/ballistica/generated/python_embedded/binding.inc": "https://files.ballistica.net/cache/ba1/c0/32/b7907e3859a5c5013a3d97b6b523",
|
||||
"src/ballistica/generated/python_embedded/bootstrap.inc": "https://files.ballistica.net/cache/ba1/2d/4f/f4fe67827f36cd59cd5193333a02",
|
||||
"src/ballistica/generated/python_embedded/bootstrap_monolithic.inc": "https://files.ballistica.net/cache/ba1/ef/c1/aa5f1aa10af89f5c0b1e616355fd"
|
||||
|
||||
12
.idea/dictionaries/ericf.xml
generated
12
.idea/dictionaries/ericf.xml
generated
@ -153,6 +153,7 @@
|
||||
<w>assigninput</w>
|
||||
<w>astc</w>
|
||||
<w>astcenc</w>
|
||||
<w>astr</w>
|
||||
<w>astroid</w>
|
||||
<w>asus</w>
|
||||
<w>asynchat</w>
|
||||
@ -261,6 +262,8 @@
|
||||
<w>blocksize</w>
|
||||
<w>bluetooth</w>
|
||||
<w>bmag</w>
|
||||
<w>bmas</w>
|
||||
<w>bmasl</w>
|
||||
<w>bname</w>
|
||||
<w>bndl</w>
|
||||
<w>boffs</w>
|
||||
@ -343,6 +346,7 @@
|
||||
<w>campaignname</w>
|
||||
<w>cancelbtn</w>
|
||||
<w>capb</w>
|
||||
<w>caplog</w>
|
||||
<w>capturetheflag</w>
|
||||
<w>carentity</w>
|
||||
<w>cashregistersound</w>
|
||||
@ -604,6 +608,8 @@
|
||||
<w>depsets</w>
|
||||
<w>depsval</w>
|
||||
<w>dereferencing</w>
|
||||
<w>deregistering</w>
|
||||
<w>deregistration</w>
|
||||
<w>descpos</w>
|
||||
<w>desctype</w>
|
||||
<w>dest</w>
|
||||
@ -679,6 +685,7 @@
|
||||
<w>dropdir</w>
|
||||
<w>drumroll</w>
|
||||
<w>dsqlite</w>
|
||||
<w>dstabs</w>
|
||||
<w>dstattr</w>
|
||||
<w>dstbase</w>
|
||||
<w>dstdata</w>
|
||||
@ -1725,6 +1732,7 @@
|
||||
<w>nvidia</w>
|
||||
<w>nyko</w>
|
||||
<w>obj's</w>
|
||||
<w>objb</w>
|
||||
<w>objid</w>
|
||||
<w>objname</w>
|
||||
<w>objs</w>
|
||||
@ -2375,6 +2383,7 @@
|
||||
<w>splitlen</w>
|
||||
<w>splitnumstr</w>
|
||||
<w>squadcore</w>
|
||||
<w>srcabs</w>
|
||||
<w>srcattr</w>
|
||||
<w>srcdata</w>
|
||||
<w>srcdir</w>
|
||||
@ -2498,6 +2507,7 @@
|
||||
<w>sysconfigdata</w>
|
||||
<w>sysctl</w>
|
||||
<w>syslogmodule</w>
|
||||
<w>sysresponse</w>
|
||||
<w>tabdefs</w>
|
||||
<w>tabtype</w>
|
||||
<w>tabtypes</w>
|
||||
@ -2594,6 +2604,7 @@
|
||||
<w>this'll</w>
|
||||
<w>thislinelen</w>
|
||||
<w>thismodule</w>
|
||||
<w>threadlocals</w>
|
||||
<w>threadpool</w>
|
||||
<w>threadtype</w>
|
||||
<w>throwiness</w>
|
||||
@ -2925,6 +2936,7 @@
|
||||
<w>zlib</w>
|
||||
<w>zlibmodule</w>
|
||||
<w>zoneid</w>
|
||||
<w>zoneids</w>
|
||||
<w>zoneinfo</w>
|
||||
<w>zoomtext</w>
|
||||
<w>zpings</w>
|
||||
|
||||
@ -1,9 +1,16 @@
|
||||
### 1.7.10 (build 20887, api 7, 2022-09-29)
|
||||
### 1.7.10 (build 20889, api 7, 2022-10-07)
|
||||
- Added eval support for cloud-console. This means you can type something like '1+1' in the console and see '2' printed. This is how Python behaves in the stdin console or in-game console or the standard Python interpreter.
|
||||
- Exceptions in the cloud-console now print to stderr instead of logging.exception(). This means they aren't a pretty red color anymore, but this will keep cloud-console behaving well with things like servers where logging.exception() might trigger alarms or otherwise. This is also consistent with standard interactive Python behavior.
|
||||
- Cloud console now shows the device name at the top instead of simply 'Console' while connected.
|
||||
- Moved the function that actually runs cloud console code to `ba._cloud.cloud_console_exec()`.
|
||||
- Added efro.debug which contains useful functionality for debugging object reference issues and memory leaks on live app instances (via cloud shell or whatever).
|
||||
- Lots of reworking/polishing in general on communication between the game and v2 regional/master servers in preparation of upgrading Google Play accounts to V2. Please holler if anything is not working smoothly with a V2 account.
|
||||
- When establishing V2 master-server communication, if the closest regional server is down or too busy, will now fall back to farther ones instead of giving up. You can follow this process by setting env var `BA_DEBUG_PRINT_V2_TRANSPORT` to 1 when running the app.
|
||||
- Network testing now skips the alternate v1 master server addr if the primary succeeded. The alternate often fails which makes things look broken even though the game is ok as long as primary works.
|
||||
- The v2-transport system will now properly reestablish account connectivity when asked to refresh its connection (the cloud does this periodically so regional cloud servers can be restarted as needed). Practically this means your app won't stop showing up under the ballistica.net devices section after its been running for a while; a problem previous builds had.
|
||||
- The v2-transport system can now establish more than one connection at a time, which allows the app to gracefully transition to a new connection when the old is about to expire without any period of no connectivity. To test this functionality, set env var `BA_DEBUG_PRINT_V2_TRANSPORT=1` to see transport debug messages and `BA_DEBUG_V2_TRANSPORT_SHORT_DURATION=1` to cause the cloud to request a connection-refresh every 30 seconds or so.
|
||||
- V2 accounts now consider themselves instantly signed in if they were signed in when the app last ran. They still need to contact the master-server before anything important can happen, but this should help keep things feel faster in general.
|
||||
- Due to v2-transport improvements, pressing the 'End Session Now' button in ballistica.net account settings should now instantly log you out of all apps using that session.
|
||||
|
||||
### 1.7.9 (build 20880, api 7, 2022-09-24)
|
||||
- Cleaned up the efro.message system to isolate response types that are used purely internally (via a new SysResponse type).
|
||||
|
||||
@ -9,7 +9,7 @@ from typing import TYPE_CHECKING
|
||||
import _ba
|
||||
|
||||
if TYPE_CHECKING:
|
||||
pass
|
||||
from typing import Any
|
||||
|
||||
|
||||
class AccountV2Subsystem:
|
||||
@ -111,10 +111,21 @@ class AccountV2Subsystem:
|
||||
|
||||
|
||||
class AccountV2Handle:
|
||||
"""Handle for interacting with a v2 account."""
|
||||
"""Handle for interacting with a V2 account.
|
||||
|
||||
This class supports the 'with' statement, which is how it is
|
||||
used with some operations such as cloud messaging.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.tag = '?'
|
||||
|
||||
self.workspacename: str | None = None
|
||||
self.workspaceid: str | None = None
|
||||
|
||||
def __enter__(self) -> None:
|
||||
"""Support for "with" statement.
|
||||
"""
|
||||
|
||||
def __exit__(self, exc_type: Any, exc_value: Any, traceback: Any) -> Any:
|
||||
"""Support for "with" statement."""
|
||||
|
||||
@ -45,7 +45,7 @@ def bootstrap() -> None:
|
||||
|
||||
# Give a soft warning if we're being used with a different binary
|
||||
# version than we expect.
|
||||
expected_build = 20887
|
||||
expected_build = 20889
|
||||
running_build: int = env['build_number']
|
||||
if running_build != expected_build:
|
||||
print(
|
||||
|
||||
@ -56,10 +56,11 @@ class MainMenuWindow(ba.Window):
|
||||
self._gather_button: ba.Widget | None = None
|
||||
self._start_button: ba.Widget | None = None
|
||||
self._watch_button: ba.Widget | None = None
|
||||
self._gc_button: ba.Widget | None = None
|
||||
self._account_button: ba.Widget | None = None
|
||||
self._how_to_play_button: ba.Widget | None = None
|
||||
self._credits_button: ba.Widget | None = None
|
||||
self._settings_button: ba.Widget | None = None
|
||||
self._next_refresh_allow_time = 0.0
|
||||
|
||||
self._store_char_tex = self._get_store_char_tex()
|
||||
|
||||
@ -71,7 +72,7 @@ class MainMenuWindow(ba.Window):
|
||||
self._account_state_num = ba.internal.get_v1_account_state_num()
|
||||
self._account_type = (ba.internal.get_v1_account_type()
|
||||
if self._account_state == 'signed_in' else None)
|
||||
self._refresh_timer = ba.Timer(1.0,
|
||||
self._refresh_timer = ba.Timer(0.27,
|
||||
ba.WeakCall(self._check_refresh),
|
||||
repeat=True,
|
||||
timetype=ba.TimeType.REAL)
|
||||
@ -132,11 +133,15 @@ class MainMenuWindow(ba.Window):
|
||||
if not self._root_widget:
|
||||
return
|
||||
|
||||
now = ba.time(ba.TimeType.REAL)
|
||||
if now < self._next_refresh_allow_time:
|
||||
return
|
||||
|
||||
# Don't refresh for the first few seconds the game is up so we don't
|
||||
# interrupt the transition in.
|
||||
ba.app.main_menu_window_refresh_check_count += 1
|
||||
if ba.app.main_menu_window_refresh_check_count < 4:
|
||||
return
|
||||
# ba.app.main_menu_window_refresh_check_count += 1
|
||||
# if ba.app.main_menu_window_refresh_check_count < 4:
|
||||
# return
|
||||
|
||||
store_char_tex = self._get_store_char_tex()
|
||||
account_state_num = ba.internal.get_v1_account_state_num()
|
||||
@ -251,11 +256,11 @@ class MainMenuWindow(ba.Window):
|
||||
size=(self._button_width, self._button_height),
|
||||
autoselect=self._use_autoselect,
|
||||
label=ba.Lstr(resource=self._r +
|
||||
('.endTestText' if self._is_benchmark()
|
||||
else '.endGameText')),
|
||||
('.endTestText' if self._is_benchmark(
|
||||
) else '.endGameText')),
|
||||
on_activate_call=(self._confirm_end_test
|
||||
if self._is_benchmark()
|
||||
else self._confirm_end_game))
|
||||
if self._is_benchmark() else
|
||||
self._confirm_end_game))
|
||||
# Assume we're in a client-session.
|
||||
else:
|
||||
ba.buttonwidget(
|
||||
@ -428,6 +433,7 @@ class MainMenuWindow(ba.Window):
|
||||
self._tdelay = 2.0
|
||||
self._t_delay_inc = 0.02
|
||||
self._t_delay_play = 1.7
|
||||
self._next_refresh_allow_time = ba.time(ba.TimeType.REAL) + 2.01
|
||||
ba.app.did_menu_intro = True
|
||||
self._width = 400.0
|
||||
self._height = 200.0
|
||||
@ -609,7 +615,7 @@ class MainMenuWindow(ba.Window):
|
||||
this_b_width = self._button_width
|
||||
h, v, scale = positions[self._p_index]
|
||||
self._p_index += 1
|
||||
self._gc_button = ba.buttonwidget(
|
||||
self._account_button = ba.buttonwidget(
|
||||
parent=self._root_widget,
|
||||
position=(h - this_b_width * 0.5 * scale, v),
|
||||
size=(this_b_width, self._button_height),
|
||||
@ -637,7 +643,7 @@ class MainMenuWindow(ba.Window):
|
||||
tilt_scale=0.0)
|
||||
self._tdelay += self._t_delay_inc
|
||||
else:
|
||||
self._gc_button = None
|
||||
self._account_button = None
|
||||
|
||||
# How-to-play button.
|
||||
h, v, scale = positions[self._p_index]
|
||||
@ -852,7 +858,7 @@ class MainMenuWindow(ba.Window):
|
||||
ba.containerwidget(edit=self._root_widget, transition='out_left')
|
||||
ba.app.ui.set_main_menu_window(
|
||||
AccountSettingsWindow(
|
||||
origin_widget=self._gc_button).get_root_widget())
|
||||
origin_widget=self._account_button).get_root_widget())
|
||||
|
||||
def _on_store_pressed(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
@ -870,7 +876,7 @@ class MainMenuWindow(ba.Window):
|
||||
def _is_benchmark(self) -> bool:
|
||||
session = ba.internal.get_foreground_host_session()
|
||||
return (getattr(session, 'benchmark_type', None) == 'cpu'
|
||||
or ba.app.stress_test_reset_timer is not None)
|
||||
or ba.app.stress_test_reset_timer is not None)
|
||||
|
||||
def _confirm_end_game(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
@ -984,8 +990,8 @@ class MainMenuWindow(ba.Window):
|
||||
ba.app.ui.main_menu_selection = 'Credits'
|
||||
elif sel == self._settings_button:
|
||||
ba.app.ui.main_menu_selection = 'Settings'
|
||||
elif sel == self._gc_button:
|
||||
ba.app.ui.main_menu_selection = 'GameService'
|
||||
elif sel == self._account_button:
|
||||
ba.app.ui.main_menu_selection = 'Account'
|
||||
elif sel == self._store_button:
|
||||
ba.app.ui.main_menu_selection = 'Store'
|
||||
elif sel == self._quit_button:
|
||||
@ -1016,8 +1022,8 @@ class MainMenuWindow(ba.Window):
|
||||
sel = self._credits_button
|
||||
elif sel_name == 'Settings':
|
||||
sel = self._settings_button
|
||||
elif sel_name == 'GameService':
|
||||
sel = self._gc_button
|
||||
elif sel_name == 'Account':
|
||||
sel = self._account_button
|
||||
elif sel_name == 'Store':
|
||||
sel = self._store_button
|
||||
elif sel_name == 'Quit':
|
||||
|
||||
@ -146,13 +146,14 @@ def _run_diagnostics(weakwin: weakref.ref[NetTestingWindow]) -> None:
|
||||
|
||||
ba.pushcall(_print_in_logic_thread, from_other_thread=True)
|
||||
|
||||
def _print_test_results(call: Callable[[], Any]) -> None:
|
||||
"""Run the provided call; return success/fail text & color."""
|
||||
def _print_test_results(call: Callable[[], Any]) -> bool:
|
||||
"""Run the provided call, print result, & return success."""
|
||||
starttime = time.monotonic()
|
||||
try:
|
||||
call()
|
||||
duration = time.monotonic() - starttime
|
||||
_print(f'Succeeded in {duration:.2f}s.', color=(0, 1, 0))
|
||||
return True
|
||||
except Exception as exc:
|
||||
import traceback
|
||||
duration = time.monotonic() - starttime
|
||||
@ -161,6 +162,7 @@ def _run_diagnostics(weakwin: weakref.ref[NetTestingWindow]) -> None:
|
||||
_print(msg, color=(1.0, 1.0, 0.3))
|
||||
_print(f'Failed in {duration:.2f}s.', color=(1, 0, 0))
|
||||
have_error[0] = True
|
||||
return False
|
||||
|
||||
try:
|
||||
_print(f'Running network diagnostics...\n'
|
||||
@ -177,14 +179,23 @@ def _run_diagnostics(weakwin: weakref.ref[NetTestingWindow]) -> None:
|
||||
# V1 ping
|
||||
baseaddr = ba.internal.get_master_server_address(source=0, version=1)
|
||||
_print(f'\nContacting V1 master-server src0 ({baseaddr})...')
|
||||
_print_test_results(lambda: _test_fetch(baseaddr))
|
||||
v1worked = _print_test_results(lambda: _test_fetch(baseaddr))
|
||||
|
||||
# V1 alternate ping
|
||||
baseaddr = ba.internal.get_master_server_address(source=1, version=1)
|
||||
_print(f'\nContacting V1 master-server src1 ({baseaddr})...')
|
||||
_print_test_results(lambda: _test_fetch(baseaddr))
|
||||
# V1 alternate ping (only if primary fails since this often fails).
|
||||
if v1worked:
|
||||
_print('\nSkipping V1 master-server src1 test since src0 worked.')
|
||||
else:
|
||||
baseaddr = ba.internal.get_master_server_address(source=1,
|
||||
version=1)
|
||||
_print(f'\nContacting V1 master-server src1 ({baseaddr})...')
|
||||
_print_test_results(lambda: _test_fetch(baseaddr))
|
||||
|
||||
_print(f'\nV1-test-log: {ba.app.net.v1_test_log}')
|
||||
if 'none succeeded' in ba.app.net.v1_test_log:
|
||||
_print(f'\nV1-test-log failed: {ba.app.net.v1_test_log}',
|
||||
color=(1, 0, 0))
|
||||
have_error[0] = True
|
||||
else:
|
||||
_print(f'\nV1-test-log ok: {ba.app.net.v1_test_log}')
|
||||
|
||||
for srcid, result in sorted(ba.app.net.v1_ctest_results.items()):
|
||||
_print(f'\nV1 src{srcid} result: {result}')
|
||||
|
||||
12
ballisticacore-cmake/.idea/dictionaries/ericf.xml
generated
12
ballisticacore-cmake/.idea/dictionaries/ericf.xml
generated
@ -79,6 +79,7 @@
|
||||
<w>asci</w>
|
||||
<w>assetsmakefile</w>
|
||||
<w>assigninput</w>
|
||||
<w>astr</w>
|
||||
<w>atest</w>
|
||||
<w>athome</w>
|
||||
<w>attrobj</w>
|
||||
@ -144,6 +145,8 @@
|
||||
<w>blockwidth</w>
|
||||
<w>bluetooth</w>
|
||||
<w>blurscale</w>
|
||||
<w>bmas</w>
|
||||
<w>bmasl</w>
|
||||
<w>bname</w>
|
||||
<w>bodyid</w>
|
||||
<w>bodypart</w>
|
||||
@ -197,6 +200,7 @@
|
||||
<w>camerashake</w>
|
||||
<w>cancelbtn</w>
|
||||
<w>capitan</w>
|
||||
<w>caplog</w>
|
||||
<w>cargs</w>
|
||||
<w>cbegin</w>
|
||||
<w>cbgn</w>
|
||||
@ -329,6 +333,8 @@
|
||||
<w>demangled</w>
|
||||
<w>demangling</w>
|
||||
<w>denom</w>
|
||||
<w>deregistering</w>
|
||||
<w>deregistration</w>
|
||||
<w>dernit</w>
|
||||
<w>desctype</w>
|
||||
<w>destdir</w>
|
||||
@ -364,6 +370,7 @@
|
||||
<w>drpt</w>
|
||||
<w>dsize</w>
|
||||
<w>dsound</w>
|
||||
<w>dstabs</w>
|
||||
<w>dstattr</w>
|
||||
<w>dstnode</w>
|
||||
<w>dstpath</w>
|
||||
@ -906,6 +913,7 @@
|
||||
<w>nval</w>
|
||||
<w>nvidia</w>
|
||||
<w>nyffenegger</w>
|
||||
<w>objb</w>
|
||||
<w>objexists</w>
|
||||
<w>objid</w>
|
||||
<w>objtoyaml</w>
|
||||
@ -1234,6 +1242,7 @@
|
||||
<w>sphrand</w>
|
||||
<w>spinup</w>
|
||||
<w>spivak</w>
|
||||
<w>srcabs</w>
|
||||
<w>srcattr</w>
|
||||
<w>srcfolder</w>
|
||||
<w>srcid</w>
|
||||
@ -1303,6 +1312,7 @@
|
||||
<w>swiftc</w>
|
||||
<w>symbolification</w>
|
||||
<w>syscalls</w>
|
||||
<w>sysresponse</w>
|
||||
<w>tabdefs</w>
|
||||
<w>tabtype</w>
|
||||
<w>tabtypes</w>
|
||||
@ -1335,6 +1345,7 @@
|
||||
<w>theres</w>
|
||||
<w>thislinelen</w>
|
||||
<w>thismodule</w>
|
||||
<w>threadlocals</w>
|
||||
<w>threadname</w>
|
||||
<w>threadpool</w>
|
||||
<w>threadtype</w>
|
||||
@ -1522,6 +1533,7 @@
|
||||
<w>zoffset</w>
|
||||
<w>zomg</w>
|
||||
<w>zoneid</w>
|
||||
<w>zoneids</w>
|
||||
<w>zoneinfo</w>
|
||||
<w>zoomable</w>
|
||||
<w>zpings</w>
|
||||
|
||||
@ -32,7 +32,7 @@
|
||||
namespace ballistica {
|
||||
|
||||
// These are set automatically via script; don't modify them here.
|
||||
const int kAppBuildNumber = 20887;
|
||||
const int kAppBuildNumber = 20889;
|
||||
const char* kAppVersion = "1.7.10";
|
||||
|
||||
// Our standalone globals.
|
||||
|
||||
@ -152,6 +152,8 @@ void V1Account::SetLogin(V1AccountType account_type, V1LoginState login_state,
|
||||
|
||||
// We call out to Python so need to be in logic thread.
|
||||
assert(InLogicThread());
|
||||
|
||||
// We want redundant sets to be no-ops.
|
||||
if (login_state_ != login_state || g_app->account_type != account_type
|
||||
|| login_id_ != login_id || login_name_ != login_name) {
|
||||
// Special case: if they sent a sign-out for an account type that is
|
||||
|
||||
@ -1,10 +1,12 @@
|
||||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Testing message functionality."""
|
||||
# pylint: disable=too-many-lines
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import logging
|
||||
import asyncio
|
||||
from typing import TYPE_CHECKING, overload
|
||||
from dataclasses import dataclass
|
||||
@ -198,7 +200,7 @@ class _TestMessageSenderBBoth(MessageSender):
|
||||
"""Protocol-specific sender."""
|
||||
|
||||
def __init__(self) -> None:
|
||||
protocol = TEST_PROTOCOL_B
|
||||
protocol = TEST_PROTOCOL_EVOLVED
|
||||
super().__init__(protocol)
|
||||
|
||||
def __get__(self,
|
||||
@ -427,12 +429,14 @@ TEST_PROTOCOL = MessageProtocol(
|
||||
1: _TResp2,
|
||||
},
|
||||
forward_clean_errors=True,
|
||||
forward_communication_errors=True,
|
||||
remote_errors_include_stack_traces=True,
|
||||
)
|
||||
|
||||
# Represents an 'evolved' TEST_PROTOCOL (one extra message type added).
|
||||
# (so we can test communication failures talking to older protocols)
|
||||
TEST_PROTOCOL_B = MessageProtocol(
|
||||
# Represents an 'evolved' TEST_PROTOCOL (the same as TEST_PROTOCOL; just
|
||||
# one extra message type added).
|
||||
# This way we can test communication failures talking to older protocols.
|
||||
TEST_PROTOCOL_EVOLVED = MessageProtocol(
|
||||
message_types={
|
||||
0: _TMsg1,
|
||||
1: _TMsg2,
|
||||
@ -444,6 +448,7 @@ TEST_PROTOCOL_B = MessageProtocol(
|
||||
1: _TResp2,
|
||||
},
|
||||
forward_clean_errors=True,
|
||||
forward_communication_errors=True,
|
||||
remote_errors_include_stack_traces=True,
|
||||
)
|
||||
|
||||
@ -581,9 +586,9 @@ def test_sender_module_both_emb() -> None:
|
||||
# here, but it requires us to pass code which imports this test module
|
||||
# to get at the protocol, and that currently fails in our static mypy
|
||||
# tests.
|
||||
smod = TEST_PROTOCOL_B.do_create_sender_module(
|
||||
smod = TEST_PROTOCOL_EVOLVED.do_create_sender_module(
|
||||
'TestMessageSenderBBoth',
|
||||
protocol_create_code='protocol = TEST_PROTOCOL_B',
|
||||
protocol_create_code='protocol = TEST_PROTOCOL_EVOLVED',
|
||||
enable_sync_sends=True,
|
||||
enable_async_sends=True,
|
||||
private=True,
|
||||
@ -741,7 +746,7 @@ def test_receiver_creation() -> None:
|
||||
receiver.validate()
|
||||
|
||||
|
||||
def test_full_pipeline() -> None:
|
||||
def test_full_pipeline(caplog: pytest.LogCaptureFixture) -> None:
|
||||
"""Test the full pipeline."""
|
||||
|
||||
# pylint: disable=too-many-locals
|
||||
@ -816,6 +821,45 @@ def test_full_pipeline() -> None:
|
||||
if self.test_sidecar:
|
||||
setattr(response, '_sidecar_data', indata['_sidecar_data'])
|
||||
|
||||
# Alternate sender for testing other protocol options.
|
||||
class TestClassSAlt:
|
||||
"""Test class incorporating send functionality."""
|
||||
|
||||
msg = _TestMessageSenderSingle()
|
||||
|
||||
test_handling_unregistered = False
|
||||
test_send_method_exceptions = False
|
||||
test_send_method_exceptions_comm = False
|
||||
|
||||
def __init__(self, target: TestClassRAlt) -> None:
|
||||
self.test_sidecar = False
|
||||
self._target = target
|
||||
|
||||
@msg.send_method
|
||||
def _send_raw_message(self, data: str) -> str:
|
||||
"""Handle synchronous sending of raw json message data."""
|
||||
|
||||
# Test throwing exceptions in send methods.
|
||||
if self.test_send_method_exceptions:
|
||||
raise (CommunicationError()
|
||||
if self.test_send_method_exceptions_comm else
|
||||
RuntimeError())
|
||||
|
||||
# Just talk directly to the receiver for this example.
|
||||
# (currently only support synchronous receivers)
|
||||
assert isinstance(self._target, TestClassRAlt)
|
||||
try:
|
||||
return self._target.receiver.handle_raw_message(
|
||||
data, raise_unregistered=self.test_handling_unregistered)
|
||||
except UnregisteredMessageIDError:
|
||||
if self.test_handling_unregistered:
|
||||
# Emulate forwarding unregistered messages on to some
|
||||
# other handler...
|
||||
response_dict = self.msg.protocol.response_to_dict(
|
||||
EmptySysResponse())
|
||||
return self.msg.protocol.encode_dict(response_dict)
|
||||
raise
|
||||
|
||||
class TestClassRSync:
|
||||
"""Test class incorporating synchronous receive functionality."""
|
||||
|
||||
@ -831,6 +875,8 @@ def test_full_pipeline() -> None:
|
||||
raise CleanError('Testing Clean Error')
|
||||
if msg.ival == 2:
|
||||
raise RuntimeError('Testing Runtime Error')
|
||||
if msg.ival == 3:
|
||||
raise CommunicationError('Testing Communication Error')
|
||||
out = _TResp1(bval=True)
|
||||
if self.test_sidecar:
|
||||
setattr(out, '_sidecar_data', getattr(msg, '_sidecar_data'))
|
||||
@ -864,6 +910,30 @@ def test_full_pipeline() -> None:
|
||||
|
||||
receiver.validate()
|
||||
|
||||
class TestClassRAlt:
|
||||
"""Test class incorporating synchronous receive functionality."""
|
||||
|
||||
receiver = _TestSingleMessageReceiver()
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.test_sidecar = False
|
||||
|
||||
@receiver.handler
|
||||
def handle_test_message_1(self, msg: _TMsg1) -> _TResp1:
|
||||
"""Test."""
|
||||
if msg.ival == 1:
|
||||
raise CleanError('Testing Clean Error')
|
||||
if msg.ival == 2:
|
||||
raise RuntimeError('Testing Runtime Error')
|
||||
if msg.ival == 3:
|
||||
raise CommunicationError('Testing Communication Error')
|
||||
out = _TResp1(bval=True)
|
||||
if self.test_sidecar:
|
||||
setattr(out, '_sidecar_data', getattr(msg, '_sidecar_data'))
|
||||
return out
|
||||
|
||||
receiver.validate()
|
||||
|
||||
class TestClassRAsync:
|
||||
"""Test class incorporating asynchronous receive functionality."""
|
||||
|
||||
@ -876,6 +946,8 @@ def test_full_pipeline() -> None:
|
||||
raise CleanError('Testing Clean Error')
|
||||
if msg.ival == 2:
|
||||
raise RuntimeError('Testing Runtime Error')
|
||||
if msg.ival == 3:
|
||||
raise CommunicationError('Testing Communication Error')
|
||||
return _TResp1(bval=True)
|
||||
|
||||
@receiver.handler
|
||||
@ -897,35 +969,76 @@ def test_full_pipeline() -> None:
|
||||
obj = TestClassS(target=obj_r_sync)
|
||||
obj2 = TestClassS(target=obj_r_async)
|
||||
|
||||
obj_rb = TestClassRAlt()
|
||||
objb = TestClassSAlt(target=obj_rb)
|
||||
|
||||
# Test sends (of sync and async varieties).
|
||||
response1 = obj.msg.send(_TMsg1(ival=0))
|
||||
response2 = obj.msg.send(_TMsg2(sval='rah'))
|
||||
response3 = obj.msg.send(_TMsg3(sval='rah'))
|
||||
response4 = asyncio.run(obj.msg.send_async(_TMsg1(ival=0)))
|
||||
|
||||
# Make sure static typing lines up with what we expect.
|
||||
if os.environ.get('EFRO_TEST_MESSAGE_FAST') != '1':
|
||||
# assert static_type_equals(response1, _TResp1)
|
||||
assert_type(response1, _TResp1)
|
||||
# assert static_type_equals(response3, None)
|
||||
assert_type(response3, None)
|
||||
|
||||
assert_type(response1, _TResp1)
|
||||
assert isinstance(response1, _TResp1)
|
||||
|
||||
response1b = objb.msg.send(_TMsg1(ival=0))
|
||||
assert_type(response1b, _TResp1)
|
||||
|
||||
response2 = obj.msg.send(_TMsg2(sval='rah'))
|
||||
assert isinstance(response2, (_TResp1, _TResp2))
|
||||
|
||||
response3 = obj.msg.send(_TMsg3(sval='rah'))
|
||||
assert_type(response3, None)
|
||||
assert response3 is None
|
||||
|
||||
response4 = asyncio.run(obj.msg.send_async(_TMsg1(ival=0)))
|
||||
assert isinstance(response4, _TResp1)
|
||||
|
||||
# Remote CleanErrors should come across locally as the same
|
||||
# (provided our protocol has enabled support for them).
|
||||
# Nothing up to this point should have logged any warnings/errors/etc.
|
||||
assert not any(r.levelno >= logging.WARNING for r in caplog.records)
|
||||
|
||||
# Remote CleanErrors should come across locally as the same and
|
||||
# no errors should be logged.
|
||||
# (since our protocol has forward_clean_errors enabled).
|
||||
caplog.clear()
|
||||
try:
|
||||
_response5 = obj.msg.send(_TMsg1(ival=1))
|
||||
except Exception as exc:
|
||||
assert isinstance(exc, CleanError)
|
||||
assert str(exc) == 'Testing Clean Error'
|
||||
assert not caplog.records
|
||||
|
||||
# Other remote errors should result in RemoteError.
|
||||
# Same using a protocol *without* forward_clean_errors should
|
||||
# give us a generic RemoteError and log the error.
|
||||
caplog.clear()
|
||||
with pytest.raises(RemoteError):
|
||||
_response5 = objb.msg.send(_TMsg1(ival=1))
|
||||
assert (len(caplog.records) == 1
|
||||
and caplog.records[0].levelno == logging.ERROR)
|
||||
|
||||
# Same with CommunicationErrors occurring on the peer; they should
|
||||
# come back to us intact if forward_communication_errors is enabled
|
||||
# and no errors should have been logged.
|
||||
caplog.clear()
|
||||
try:
|
||||
_response5 = obj.msg.send(_TMsg1(ival=3))
|
||||
except Exception as exc:
|
||||
assert isinstance(exc, CommunicationError)
|
||||
assert str(exc) == 'Testing Communication Error'
|
||||
assert not caplog.records
|
||||
|
||||
# Same using a protocol *without* forward_clean_errors should
|
||||
# give us a generic RemoteError and log an error.
|
||||
caplog.clear()
|
||||
with pytest.raises(RemoteError):
|
||||
_response5 = objb.msg.send(_TMsg1(ival=3))
|
||||
assert (len(caplog.records) == 1
|
||||
and caplog.records[0].levelno == logging.ERROR)
|
||||
|
||||
# Misc other error types happening on peer should result in
|
||||
# RemoteError and log message.
|
||||
caplog.clear()
|
||||
with pytest.raises(RemoteError):
|
||||
_response5 = obj.msg.send(_TMsg1(ival=2))
|
||||
# This should have logged a single error message.
|
||||
assert (len(caplog.records) == 1
|
||||
and caplog.records[0].levelno == logging.ERROR)
|
||||
|
||||
# Now test sends to async handlers.
|
||||
response6 = asyncio.run(obj2.msg.send_async(_TMsg1(ival=0)))
|
||||
|
||||
@ -71,11 +71,14 @@ class _ServerClientCommon:
|
||||
|
||||
async def send_message(self,
|
||||
message: _Message,
|
||||
timeout: float | None = None) -> _Message:
|
||||
timeout: float | None = None,
|
||||
close_on_error: bool = True) -> _Message:
|
||||
"""Send high level messages."""
|
||||
assert self._endpoint is not None
|
||||
response = await self._endpoint.send_message(
|
||||
dataclass_to_json(message).encode(), timeout=timeout)
|
||||
dataclass_to_json(message).encode(),
|
||||
timeout=timeout,
|
||||
close_on_error=close_on_error)
|
||||
return dataclass_from_json(_Message, response.decode())
|
||||
|
||||
async def handle_message(self, msg: _Message) -> _Message:
|
||||
@ -364,12 +367,23 @@ def test_message_timeout() -> None:
|
||||
_Message(_MessageType.TEST_SLOW))
|
||||
assert resp.messagetype is _MessageType.RESPONSE_SLOW
|
||||
|
||||
# This message should time out.
|
||||
# This message should time out but not close the connection.
|
||||
with pytest.raises(CommunicationError):
|
||||
resp = await tester.server.send_message(
|
||||
_Message(_MessageType.TEST_SLOW),
|
||||
timeout=0.5,
|
||||
close_on_error=False,
|
||||
)
|
||||
assert not tester.server.endpoint.is_closing()
|
||||
|
||||
# This message should time out and close the connection as a result.
|
||||
with pytest.raises(CommunicationError):
|
||||
resp = await tester.server.send_message(
|
||||
_Message(_MessageType.TEST_SLOW),
|
||||
timeout=0.5,
|
||||
close_on_error=True,
|
||||
)
|
||||
assert tester.server.endpoint.is_closing()
|
||||
|
||||
tester.run(_do_it())
|
||||
|
||||
|
||||
@ -61,6 +61,7 @@ class ErrorSysResponse(SysResponse):
|
||||
REMOTE_CLEAN = 1
|
||||
LOCAL = 2
|
||||
COMMUNICATION = 3
|
||||
REMOTE_COMMUNICATION = 4
|
||||
|
||||
error_message: Annotated[str, IOAttrs('m')]
|
||||
error_type: Annotated[ErrorType, IOAttrs('e')] = ErrorType.REMOTE
|
||||
|
||||
@ -8,10 +8,9 @@ from __future__ import annotations
|
||||
|
||||
from typing import TYPE_CHECKING
|
||||
import traceback
|
||||
import logging
|
||||
import json
|
||||
|
||||
from efro.error import CleanError
|
||||
from efro.error import CleanError, CommunicationError
|
||||
from efro.dataclassio import (is_ioprepped_dataclass, dataclass_to_dict,
|
||||
dataclass_from_dict)
|
||||
from efro.message._message import (Message, Response, SysResponse,
|
||||
@ -34,24 +33,49 @@ class MessageProtocol:
|
||||
def __init__(self,
|
||||
message_types: dict[int, type[Message]],
|
||||
response_types: dict[int, type[Response]],
|
||||
forward_communication_errors: bool = False,
|
||||
forward_clean_errors: bool = False,
|
||||
remote_errors_include_stack_traces: bool = False) -> None:
|
||||
remote_errors_include_stack_traces: bool = False,
|
||||
log_remote_errors: bool = True) -> None:
|
||||
"""Create a protocol with a given configuration.
|
||||
|
||||
Note that common response types are automatically registered
|
||||
with (unchanging negative ids) so they don't need to be passed
|
||||
explicitly (but can be if a different id is desired).
|
||||
If 'forward_communication_errors' is True,
|
||||
efro.error.CommunicationErrors raised on the receiver end will
|
||||
result in a matching error raised back on the sender. This can
|
||||
be useful if the receiver will be in some way forwarding
|
||||
messages along and the sender doesn't need to know where
|
||||
communication breakdowns occurred; only that they did.
|
||||
|
||||
If 'forward_clean_errors' is True, efro.error.CleanError
|
||||
exceptions raised on the receiver end will result in a matching
|
||||
CleanError raised back on the sender. All other Exception types
|
||||
come across as efro.error.RemoteError.
|
||||
CleanError raised back on the sender.
|
||||
|
||||
If 'remote_errors_include_stack_traces' is True, stringified stack
|
||||
traces will be returned to the sender for exceptions occurring
|
||||
on the receiver end. This can make debugging easier but should
|
||||
only be used when the client is trusted to see such info.
|
||||
When an exception is not covered by the optional forwarding
|
||||
mechanisms above, it will come across as efro.error.RemoteError
|
||||
and the exception will be logged on the receiver end.
|
||||
|
||||
If 'remote_errors_include_stack_traces' is True, stringified
|
||||
stack traces will be returned with efro.error.RemoteError
|
||||
exceptions. This is useful for debugging but should only be
|
||||
enabled in cases where the sender is trusted to see internal
|
||||
details of the receiver.
|
||||
|
||||
By default, when a message-handling exception will result in an
|
||||
efro.error.RemoteError being returned to the sender, the
|
||||
exception will be logged on the receiver. This is because the
|
||||
goal is usually to avoid returning opaque RemoteErrors and to
|
||||
instead return something meaningful as part of an expected
|
||||
response type (even if that value itself represents a logical
|
||||
error state). If 'log_remote_errors' is False, however, such
|
||||
exceptions will not be logged on the receiver. This can be
|
||||
useful in combination with 'remote_errors_include_stack_traces'
|
||||
and 'forward_clean_errors' in situations where all error
|
||||
logging/management will be happening on the sender end. Be
|
||||
aware, however, that in that case
|
||||
efro.error.CommunicationErrors can possibly prevent such error
|
||||
messages from ever being seen.
|
||||
"""
|
||||
# pylint: disable=too-many-locals
|
||||
self.message_types_by_id: dict[int, type[Message]] = {}
|
||||
self.message_ids_by_type: dict[type[Message], int] = {}
|
||||
self.response_types_by_id: dict[int, type[Response]
|
||||
@ -123,8 +147,10 @@ class MessageProtocol:
|
||||
' all types are required to have unique names.')
|
||||
|
||||
self.forward_clean_errors = forward_clean_errors
|
||||
self.forward_communication_errors = forward_communication_errors
|
||||
self.remote_errors_include_stack_traces = (
|
||||
remote_errors_include_stack_traces)
|
||||
self.log_remote_errors = log_remote_errors
|
||||
|
||||
@staticmethod
|
||||
def encode_dict(obj: dict) -> str:
|
||||
@ -139,23 +165,31 @@ class MessageProtocol:
|
||||
"""Encode a response to a json ready dict."""
|
||||
return self._to_dict(response, self.response_ids_by_type, 'response')
|
||||
|
||||
def error_to_response(self, exc: Exception) -> SysResponse:
|
||||
"""Translate an error to a response."""
|
||||
def error_to_response(self, exc: Exception) -> tuple[SysResponse, bool]:
|
||||
"""Translate an Exception to a SysResponse.
|
||||
|
||||
# Log any errors we got during handling.
|
||||
logging.exception('Error in efro.message handling.')
|
||||
Also returns whether the error should be logged if this happened
|
||||
within handle_raw_message().
|
||||
"""
|
||||
|
||||
# If anything goes wrong, return a ErrorSysResponse instead.
|
||||
# (either CLEAN or generic REMOTE)
|
||||
if isinstance(exc, CleanError) and self.forward_clean_errors:
|
||||
return ErrorSysResponse(
|
||||
if self.forward_clean_errors and isinstance(exc, CleanError):
|
||||
return (ErrorSysResponse(
|
||||
error_message=str(exc),
|
||||
error_type=ErrorSysResponse.ErrorType.REMOTE_CLEAN)
|
||||
return ErrorSysResponse(
|
||||
error_type=ErrorSysResponse.ErrorType.REMOTE_CLEAN), False)
|
||||
if self.forward_communication_errors and isinstance(
|
||||
exc, CommunicationError):
|
||||
return (ErrorSysResponse(
|
||||
error_message=str(exc),
|
||||
error_type=ErrorSysResponse.ErrorType.REMOTE_COMMUNICATION),
|
||||
False)
|
||||
return (ErrorSysResponse(
|
||||
error_message=(traceback.format_exc()
|
||||
if self.remote_errors_include_stack_traces else
|
||||
'An internal error has occurred.'),
|
||||
error_type=ErrorSysResponse.ErrorType.REMOTE)
|
||||
error_type=ErrorSysResponse.ErrorType.REMOTE),
|
||||
self.log_remote_errors)
|
||||
|
||||
def _to_dict(self, message: Any, ids_by_type: dict[type, int],
|
||||
opname: str) -> dict:
|
||||
|
||||
@ -260,14 +260,14 @@ class MessageReceiver:
|
||||
return self.protocol.encode_dict(response_dict)
|
||||
|
||||
def encode_error_response(self, bound_obj: Any, message: Message | None,
|
||||
exc: Exception) -> str:
|
||||
"""Given an error, return a response ready for sending."""
|
||||
response = self.protocol.error_to_response(exc)
|
||||
exc: Exception) -> tuple[str, bool]:
|
||||
"""Given an error, return sysresponse str and whether to log."""
|
||||
response, dolog = self.protocol.error_to_response(exc)
|
||||
response_dict = self.protocol.response_to_dict(response)
|
||||
if self._encode_filter_call is not None:
|
||||
self._encode_filter_call(bound_obj, message, response,
|
||||
response_dict)
|
||||
return self.protocol.encode_dict(response_dict)
|
||||
return self.protocol.encode_dict(response_dict), dolog
|
||||
|
||||
def handle_raw_message(self,
|
||||
bound_obj: Any,
|
||||
@ -296,7 +296,11 @@ class MessageReceiver:
|
||||
if (raise_unregistered
|
||||
and isinstance(exc, UnregisteredMessageIDError)):
|
||||
raise
|
||||
return self.encode_error_response(bound_obj, msg_decoded, exc)
|
||||
rstr, dolog = self.encode_error_response(bound_obj, msg_decoded,
|
||||
exc)
|
||||
if dolog:
|
||||
logging.exception('Error in efro.message handling.')
|
||||
return rstr
|
||||
|
||||
async def handle_raw_message_async(
|
||||
self,
|
||||
@ -324,7 +328,11 @@ class MessageReceiver:
|
||||
if (raise_unregistered
|
||||
and isinstance(exc, UnregisteredMessageIDError)):
|
||||
raise
|
||||
return self.encode_error_response(bound_obj, msg_decoded, exc)
|
||||
rstr, dolog = self.encode_error_response(bound_obj, msg_decoded,
|
||||
exc)
|
||||
if dolog:
|
||||
logging.exception('Error in efro.message handling.')
|
||||
return rstr
|
||||
|
||||
|
||||
class BoundMessageReceiver:
|
||||
@ -348,9 +356,9 @@ class BoundMessageReceiver:
|
||||
"""Given an error, return a response ready to send.
|
||||
|
||||
This should be used for any errors that happen outside of
|
||||
of standard handle_raw_message calls. Any errors within those
|
||||
calls should be automatically returned as encoded strings.
|
||||
standard handle_raw_message calls. Any errors within those
|
||||
calls will be automatically returned as encoded strings.
|
||||
"""
|
||||
# Passing None for Message here; we would only have that available
|
||||
# for things going wrong in the handler (which this is not for).
|
||||
return self._receiver.encode_error_response(self._obj, None, exc)
|
||||
return self._receiver.encode_error_response(self._obj, None, exc)[0]
|
||||
|
||||
@ -272,6 +272,11 @@ class MessageSender:
|
||||
is ErrorSysResponse.ErrorType.REMOTE_CLEAN):
|
||||
raise CleanError(raw_response.error_message)
|
||||
|
||||
if (self.protocol.forward_communication_errors
|
||||
and raw_response.error_type is
|
||||
ErrorSysResponse.ErrorType.REMOTE_COMMUNICATION):
|
||||
raise CommunicationError(raw_response.error_message)
|
||||
|
||||
# Everything else gets lumped in as a remote error.
|
||||
raise RemoteError(raw_response.error_message,
|
||||
peer_desc=('peer' if self._peer_desc_call is None
|
||||
|
||||
@ -290,17 +290,24 @@ class RPCEndpoint:
|
||||
|
||||
async def send_message(self,
|
||||
message: bytes,
|
||||
timeout: float | None = None) -> bytes:
|
||||
timeout: float | None = None,
|
||||
close_on_error: bool = True) -> bytes:
|
||||
"""Send a message to the peer and return a response.
|
||||
|
||||
If timeout is not provided, the default will be used.
|
||||
Raises a CommunicationError if the round trip is not completed
|
||||
for any reason.
|
||||
|
||||
By default, the entire endpoint will go down in the case of
|
||||
errors. This allows messages to be treated as 'reliable' with
|
||||
respect to a given endpoint. Pass close_on_error=False to
|
||||
override this for a particular message.
|
||||
"""
|
||||
# pylint: disable=too-many-branches
|
||||
self._check_env()
|
||||
|
||||
if self._closing:
|
||||
raise CommunicationError('Endpoint is closed')
|
||||
raise CommunicationError('Endpoint is closed.')
|
||||
|
||||
# We need to know their protocol, so if we haven't gotten a handshake
|
||||
# from them yet, just wait.
|
||||
@ -358,6 +365,8 @@ class RPCEndpoint:
|
||||
if self._debug_print:
|
||||
self._debug_print_call(
|
||||
f'{self._label}: message {message_id} was cancelled.')
|
||||
if close_on_error:
|
||||
self.close()
|
||||
raise CommunicationError() from exc
|
||||
except asyncio.TimeoutError as exc:
|
||||
if self._debug_print:
|
||||
@ -370,6 +379,9 @@ class RPCEndpoint:
|
||||
# Remove the record of this message.
|
||||
del self._in_flight_messages[message_id]
|
||||
|
||||
if close_on_error:
|
||||
self.close()
|
||||
|
||||
# Let the user know something went wrong.
|
||||
raise CommunicationError() from exc
|
||||
|
||||
@ -407,7 +419,11 @@ class RPCEndpoint:
|
||||
return self._closing
|
||||
|
||||
async def wait_closed(self) -> None:
|
||||
"""I said seagulls; mmmm; stop it now."""
|
||||
"""I said seagulls; mmmm; stop it now.
|
||||
|
||||
Wait for the endpoint to finish closing. This is called by run()
|
||||
so generally does not need to be explicitly called.
|
||||
"""
|
||||
# pylint: disable=too-many-branches
|
||||
self._check_env()
|
||||
|
||||
@ -729,8 +745,9 @@ class RPCEndpoint:
|
||||
|
||||
def _check_env(self) -> None:
|
||||
# I was seeing that asyncio stuff wasn't working as expected if
|
||||
# created in one thread and used in another, so let's enforce
|
||||
# a single thread for all use of an instance.
|
||||
# created in one thread and used in another (and have verified
|
||||
# that this is part of the design), so let's enforce a single
|
||||
# thread for all use of an instance.
|
||||
if current_thread() is not self._thread:
|
||||
raise RuntimeError('This must be called from the same thread'
|
||||
' that the endpoint was created in.')
|
||||
|
||||
@ -433,11 +433,11 @@ def _filter_tool_config(cfg: str) -> str:
|
||||
|
||||
# Short project name.
|
||||
short_names = {
|
||||
'ballistica-internal': 'ba-int',
|
||||
'ballistica-internal': 'ba-i',
|
||||
'ballistica': 'ba',
|
||||
'ballistica-master-server': 'bamaster',
|
||||
'ballistica-master-server-legacy': 'bamasterlegacy',
|
||||
'ballistica-server-node': 'baservnode',
|
||||
'ballistica-master-server': 'bmas',
|
||||
'ballistica-master-server-legacy': 'bmasl',
|
||||
'ballistica-server-node': 'basn',
|
||||
}
|
||||
shortname = short_names.get(PROJROOT.name, PROJROOT.name)
|
||||
cfg = cfg.replace('__EFRO_PROJECT_SHORTNAME__', shortname)
|
||||
|
||||
@ -205,8 +205,10 @@ def sync_paths(src_proj: str, src: Path, dst: Path, mode: Mode) -> int:
|
||||
continue
|
||||
# Src/dst hashes don't match and marker doesn't match either.
|
||||
# We give up.
|
||||
srcabs = os.path.abspath(srcfile)
|
||||
dstabs = os.path.abspath(dstfile)
|
||||
raise RuntimeError(
|
||||
f'both src and dst sync files changed: {srcfile} {dstfile}'
|
||||
f'both src and dst sync files changed: {srcabs} {dstabs}'
|
||||
'; this must be resolved manually.')
|
||||
|
||||
# (if we got here this file should be healthy..)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user