Added a filter option to the public-party gather tab (and more general cleanup)

This commit is contained in:
Eric Froemling 2020-10-29 12:06:10 -05:00
parent e2126fa0f7
commit d3ec602329
16 changed files with 238 additions and 166 deletions

View File

@ -420,20 +420,20 @@
"assets/build/ba_data/audio/zoeOw.ogg": "https://files.ballistica.net/cache/ba1/04/0a/c4f7d2794b018593ab0b2bcb07f0",
"assets/build/ba_data/audio/zoePickup01.ogg": "https://files.ballistica.net/cache/ba1/06/4d/18777c9a2eb2207a2891a2837a70",
"assets/build/ba_data/audio/zoeScream01.ogg": "https://files.ballistica.net/cache/ba1/81/90/23ab1ecc8c55267bd904a9c05344",
"assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/48/27/1b912861ed8d8795add395df41b6",
"assets/build/ba_data/data/langdata.json": "https://files.ballistica.net/cache/ba1/05/20/767f42e4f0f289389cbdfb61eb10",
"assets/build/ba_data/data/languages/arabic.json": "https://files.ballistica.net/cache/ba1/de/25/74be4875c2a0e22b813a4e1a103b",
"assets/build/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/49/5f/b29bb65369040892fe6601801637",
"assets/build/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/c3/3f/c37ac3c65ac65f171af9313a502a",
"assets/build/ba_data/data/languages/chinesetraditional.json": "https://files.ballistica.net/cache/ba1/5a/9e/e8cad6f08b2b19803ab20fdc80d0",
"assets/build/ba_data/data/languages/belarussian.json": "https://files.ballistica.net/cache/ba1/c1/2b/54aeb92c709c4af443f4a9013b3d",
"assets/build/ba_data/data/languages/chinese.json": "https://files.ballistica.net/cache/ba1/69/cc/f8bdd1e83162481c6bf2a78cb5e0",
"assets/build/ba_data/data/languages/chinesetraditional.json": "https://files.ballistica.net/cache/ba1/7c/b5/ddf2aedf7a7821b134d3663ae320",
"assets/build/ba_data/data/languages/croatian.json": "https://files.ballistica.net/cache/ba1/56/02/c22deb7174aabdcbffe1da23e484",
"assets/build/ba_data/data/languages/czech.json": "https://files.ballistica.net/cache/ba1/97/6c/61de67c477c98b7c4fddd22e6ae0",
"assets/build/ba_data/data/languages/danish.json": "https://files.ballistica.net/cache/ba1/3f/46/e4da3c1d2b0ebf916df55c608b28",
"assets/build/ba_data/data/languages/dutch.json": "https://files.ballistica.net/cache/ba1/d1/07/37b7adc3dbec7328d26c5325f212",
"assets/build/ba_data/data/languages/english.json": "https://files.ballistica.net/cache/ba1/d4/5b/fbd64cf1846340db0606824568c2",
"assets/build/ba_data/data/languages/english.json": "https://files.ballistica.net/cache/ba1/3b/0d/eb66b073295cacde00e5d054dbae",
"assets/build/ba_data/data/languages/esperanto.json": "https://files.ballistica.net/cache/ba1/6e/fd/685a4e1da031474d47a1d9eb2731",
"assets/build/ba_data/data/languages/french.json": "https://files.ballistica.net/cache/ba1/e3/6d/63e78b12ae9d99e0929671f52c12",
"assets/build/ba_data/data/languages/german.json": "https://files.ballistica.net/cache/ba1/3f/62/4eedf8cfad2e5f7ff2136ec19277",
"assets/build/ba_data/data/languages/gibberish.json": "https://files.ballistica.net/cache/ba1/97/3a/b6746b7f89af424584c794c4d11f",
"assets/build/ba_data/data/languages/gibberish.json": "https://files.ballistica.net/cache/ba1/93/31/66df9784a606600b7933bcb8ffa1",
"assets/build/ba_data/data/languages/greek.json": "https://files.ballistica.net/cache/ba1/a4/2c/8c43b338be2367441b87378e19c1",
"assets/build/ba_data/data/languages/hindi.json": "https://files.ballistica.net/cache/ba1/d5/19/5e450e35b83fe68722330d03b896",
"assets/build/ba_data/data/languages/hungarian.json": "https://files.ballistica.net/cache/ba1/87/2d/027aa239eb66ea8f496562f4fd83",
@ -442,12 +442,12 @@
"assets/build/ba_data/data/languages/korean.json": "https://files.ballistica.net/cache/ba1/0a/84/bbb6ed2abf66509406f534cbbb52",
"assets/build/ba_data/data/languages/persian.json": "https://files.ballistica.net/cache/ba1/1e/67/f8d1d6579698c10af9da2ecb62d9",
"assets/build/ba_data/data/languages/polish.json": "https://files.ballistica.net/cache/ba1/88/4b/6745a1a58220772e259f0de51196",
"assets/build/ba_data/data/languages/portuguese.json": "https://files.ballistica.net/cache/ba1/42/b5/7612cce15fe4555889585108b3ef",
"assets/build/ba_data/data/languages/portuguese.json": "https://files.ballistica.net/cache/ba1/59/49/d75f2b9916541bd2be8c49a5171f",
"assets/build/ba_data/data/languages/romanian.json": "https://files.ballistica.net/cache/ba1/44/3c/7cc06ca8d5475e1687d0ed05bdbf",
"assets/build/ba_data/data/languages/russian.json": "https://files.ballistica.net/cache/ba1/d4/bb/69c09648f60e36f35bd38be20cf8",
"assets/build/ba_data/data/languages/serbian.json": "https://files.ballistica.net/cache/ba1/e7/d8/ace32888249fc8b8cca0e2edb48b",
"assets/build/ba_data/data/languages/slovak.json": "https://files.ballistica.net/cache/ba1/b7/0a/fab820b96e7aa587ee56427ecdc2",
"assets/build/ba_data/data/languages/spanish.json": "https://files.ballistica.net/cache/ba1/32/0e/ac0b8dcef065d7934a6bc30d7560",
"assets/build/ba_data/data/languages/spanish.json": "https://files.ballistica.net/cache/ba1/2a/35/eab086a95c41287fa0a856e59fb6",
"assets/build/ba_data/data/languages/swedish.json": "https://files.ballistica.net/cache/ba1/50/9f/be006ba19be6a69a57837eb6dca0",
"assets/build/ba_data/data/languages/turkish.json": "https://files.ballistica.net/cache/ba1/9a/8a/f7b2521c1904ffc83262dff1e11b",
"assets/build/ba_data/data/languages/ukrainian.json": "https://files.ballistica.net/cache/ba1/26/62/a072404c02c576a5c3f09059b582",
@ -3932,24 +3932,24 @@
"assets/build/windows/Win32/ucrtbased.dll": "https://files.ballistica.net/cache/ba1/b5/85/f8b6d0558ddb87267f34254b1450",
"assets/build/windows/Win32/vc_redist.x86.exe": "https://files.ballistica.net/cache/ba1/1c/e1/4a1a2eddda2f4aebd5f8b64ab08e",
"assets/build/windows/Win32/vcruntime140d.dll": "https://files.ballistica.net/cache/ba1/50/8d/bc2600ac9491f1b14d659709451f",
"build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/57/c4/65b46cdf99edf156347ed3381223",
"build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/24/06/49b1e54e764a57c0c925cd48d71e",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b1/e9/12e2965cd54d3cbc02f795222441",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/94/e8/4b12bc80273d1527e2a8210cf090",
"build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/b3/f2/25d7adfb561752522269fe0677e7",
"build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/e4/37/aa7613de3f87a18a5f981f73786a",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/de/7a/54548afb27b6027ff7c10c41838f",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/6b/f3/8e66e8bfdc5ca8fbe5b61838c7b8",
"build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/a0/af/d65a065f6da183970c71c2f479d7",
"build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/38/bc/dc1d1354f9a81f9e0120a3de63a7",
"build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/da/9a/0c9617ad9d98f912db70b91dfd20",
"build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/53/28/8e4c340f2accc1cf3dfb600a2981",
"build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/29/71/f22b8e8297b5fcda6a055501dd9a",
"build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/e3/4b/cd188b8da52207463fdeb6176704",
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/10/52/435cd568d1690c023b8c1aebafff",
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/6d/b7/57677f9d991f046f6b784bdd6321",
"build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/0c/77/4c9ff1fa0630936563f0f7c9fee4",
"build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/80/25/43012563e7b0db4a76c431fcbb40",
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/b3/e5/e2da42b1e5a6031487333c72da70",
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e1/89/0491751bb4d33a3d3ffc758a6b20",
"build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/3a/c5/623f2be3c9f061941c0ed1543588",
"build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/47/54/9f7775a0cdf008be499e80d62a8f",
"build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/2b/29/060806ab4ed3b47f3890b14a36e9",
"build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/e4/7e/943f5f6a581e4fccfe763b20fd30",
"build/prefab/lib/linux_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/80/d2/d1c6766cf07f4c4828bbd5899f79",
"build/prefab/lib/linux_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/de/49/2cfc34ac856737d903954db5571b",
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/17/9a/fa66aafdf31fc9bdd92ce382c619",
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/86/de/7d9c9a2b7bba34c630130ed759c9",
"build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/7c/71/59aa912540741d36fca95baf0cdf",
"build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/f7/0e/da229849278095b6dcf26432909a",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/58/e2/e78c6993c58dae2a94aa1c36139b",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8b/28/742e9c33d64ceeb5e1ff87da5577"
"build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/11/ca/ef8dc06a074606894db186954d49",
"build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/24/4d/f64b747dbea2588e6ffe8d3e24e8",
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/35/5f/e588c08ae05abeb233bb803c695b",
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/c6/a4/77a6648afb78e6b452bcccad2c8e"
}

View File

@ -30,8 +30,8 @@
<w>achname</w>
<w>achs</w>
<w>acinstance</w>
<w>ack</w>
<w>ack'ed</w>
<w>ack</w>
<w>acked</w>
<w>acks</w>
<w>acnt</w>
@ -153,8 +153,8 @@
<w>bacommon</w>
<w>badguy</w>
<w>bafoundation</w>
<w>ballistica</w>
<w>ballistica's</w>
<w>ballistica</w>
<w>ballisticacore</w>
<w>ballisticacorecb</w>
<w>bamaster</w>
@ -703,6 +703,7 @@
<w>filterlines</w>
<w>filterpath</w>
<w>filterpaths</w>
<w>filterval</w>
<w>finalhash</w>
<w>finalmaterials</w>
<w>finfo</w>
@ -801,8 +802,8 @@
<w>gamedata</w>
<w>gameinstance</w>
<w>gamemap</w>
<w>gamepad</w>
<w>gamepad's</w>
<w>gamepad</w>
<w>gamepadadvanced</w>
<w>gamepads</w>
<w>gamepadselect</w>
@ -1191,8 +1192,8 @@
<w>lsqlite</w>
<w>lssl</w>
<w>lstart</w>
<w>lstr</w>
<w>lstr's</w>
<w>lstr</w>
<w>lstrs</w>
<w>lsval</w>
<w>ltex</w>
@ -1828,8 +1829,8 @@
<w>sessionname</w>
<w>sessionplayer</w>
<w>sessionplayers</w>
<w>sessionteam</w>
<w>sessionteam's</w>
<w>sessionteam</w>
<w>sessionteams</w>
<w>sessiontype</w>
<w>setactivity</w>
@ -2164,8 +2165,8 @@
<w>txtw</w>
<w>typeargs</w>
<w>typecheck</w>
<w>typechecker</w>
<w>typechecker's</w>
<w>typechecker</w>
<w>typedval</w>
<w>typeshed</w>
<w>typestr</w>

View File

@ -1,4 +1,4 @@
### 1.5.27 (20224)
### 1.5.27 (20231)
- Language functionality has been consolidated into a LanguageSubsystem object at ba.app.lang
- ba.get_valid_languages() is now an attr: ba.app.lang.available_languages
- Achievement functionality has been consolidated into an AchievementSubsystem object at ba.app.ach
@ -9,6 +9,8 @@
- Revamped tab-button functionality into a cleaner type-safe class (bastd.ui.tabs.TabRow)
- Split Gather-Window tabs out into individual classes for future improvements (bastd.ui.gather.*)
- Added the ability to disable ticket-purchasing UIs for builds (ba.app.allow_ticket_purchases)
- Reworked the public party gather section to perform better; it should no longer have to rebuild the list from scratch each time the UI is visited.
- Added a filter option to the public party list (sorry it has taken so long).
### 1.5.26 (20217)
- Simplified licensing header on python scripts.

View File

@ -27,7 +27,7 @@ The Ballistica project is the foundation for the next generation of [BombSquad](
* A: No, BombSquad is still BombSquad. 'Ballistica' is simply the new name for the engine/app-framework. This way it can also be used for other game/app projects without causing confusion (though that is mostly theoretical at this point). As a modder, the biggest changes you will notice is 'ba' prefixes in the API instead of 'bs' and naming that follows Python PEP8 standards (underscores and lowercase instead of camel-case). So `bs.playSound(mySound)` in the old system might look like `ba.playsound(my_sound)` in the new. You may also see the word 'BallisticaCore' show up various places, which in actual releases gets replaced by 'BombSquad'.
* **Q: Does this mean BombSquad is open source?**
* A: Yes and no. All code contained in this repo is MIT licensed and free for use anywhere. This includes game scripts, pipeline tools, etc. Over time I hope to expand this to include at least some of the binary engine sources. Anything not directly contained in this repository, however, even if automatically downloaded by build scripts, is still proprietary and cannot be redistributed without explicit consent. This includes assets and game binaries. So in a nutshell: create and share mods to your heart's content, but please don't distribute your own complete copies of the game without permission. Please email support@froemling.net if you have any questions about this.
* A: Yes and no. All code contained in this repo is MIT licensed and free for use anywhere. This includes game scripts, pipeline tools, and most of the binary engine sources. Anything not directly contained in this repository, however, even if automatically downloaded by build scripts, is still proprietary and cannot be redistributed without explicit consent. This includes assets and game libraries/binaries. So in a nutshell: create and share mods to your heart's content, but please don't distribute your own complete copies of the game without permission. Please email support@froemling.net if you have any questions about this.
* **Q: Will my existing BombSquad 1.4.x mods still work?**
* A: Not 'out of the box'. All mods will need to be explicitly updated to work with the new ballistica apis in 1.5+. This may or may not be a significant amount of work depending on the mod. I would highly suggest tinkering around with some of the new features in 1.5 such as type-safe Python and dynamic assets before attempting to port any old mods, as some things are done significantly differently now. You may also want to consider simply sticking with 1.4 builds for a while longer, especially for server duties, since they will remain fully compatible with clients running 1.5. The new ballistica APIs may be changing significantly for at least a while as the dust settles, but they will be worth switching to in the end, I promise!

View File

@ -2622,6 +2622,7 @@ def hscrollwidget(edit: ba.Widget = None,
border_opacity: float = None,
simple_culling_h: float = None,
claims_left_right: bool = None,
claims_up_down: bool = None,
claims_tab: bool = None) -> ba.Widget:
"""hscrollwidget(edit: ba.Widget = None, parent: ba.Widget = None,
size: Sequence[float] = None, position: Sequence[float] = None,
@ -2632,6 +2633,7 @@ def hscrollwidget(edit: ba.Widget = None,
highlight: bool = None, border_opacity: float = None,
simple_culling_h: float = None,
claims_left_right: bool = None,
claims_up_down: bool = None,
claims_tab: bool = None) -> ba.Widget
Create or edit a horizontal scroll widget.
@ -3388,6 +3390,7 @@ def scrollwidget(edit: ba.Widget = None,
simple_culling_v: float = None,
selection_loops_to_parent: bool = None,
claims_left_right: bool = None,
claims_up_down: bool = None,
claims_tab: bool = None,
autoselect: bool = None) -> ba.Widget:
"""scrollwidget(edit: ba.Widget = None, parent: ba.Widget = None,
@ -3399,6 +3402,7 @@ def scrollwidget(edit: ba.Widget = None,
simple_culling_v: float = None,
selection_loops_to_parent: bool = None,
claims_left_right: bool = None,
claims_up_down: bool = None,
claims_tab: bool = None,
autoselect: bool = None) -> ba.Widget

View File

@ -45,6 +45,7 @@ class MainMenuActivity(ba.Activity[ba.Player, ba.Team]):
if not ba.app.toolbar_test:
color = ((1.0, 1.0, 1.0, 1.0) if vr_mode else (0.5, 0.6, 0.5, 0.6))
# FIXME: Need a node attr for vr-specific-scale.
scale = (0.9 if
(app.ui.uiscale is ba.UIScale.SMALL or vr_mode) else 0.7)

View File

@ -20,6 +20,7 @@ if TYPE_CHECKING:
from typing import Callable, Any, Optional, Dict, Union, Tuple, List
from bastd.ui.gather import GatherWindow
# Print a bit of info about pings, queries, etc.
DEBUG_SERVER_COMMUNICATION = False
@ -58,6 +59,7 @@ class State:
sub_tab: SubTabType = SubTabType.JOIN
parties: Optional[List[PartyEntry]] = None
next_entry_index: int = 0
filter_value: str = ''
class SelectionComponent(Enum):
@ -186,6 +188,7 @@ class PublicGatherTab(GatherTab):
self._container: Optional[ba.Widget] = None
self._join_text: Optional[ba.Widget] = None
self._host_text: Optional[ba.Widget] = None
self._filter_text: Optional[ba.Widget] = None
self._local_address: Optional[str] = None
self._last_connect_attempt_time: Optional[float] = None
self._sub_tab: SubTabType = SubTabType.JOIN
@ -207,7 +210,8 @@ class PublicGatherTab(GatherTab):
self._first_server_list_rebuild_time: Optional[float] = None
self._next_entry_index = 0
self._have_valid_server_list = False
self._built_join_list = False
self._server_list_dirty = True
self._filter_value = ''
def on_activate(
self,
@ -278,7 +282,6 @@ class PublicGatherTab(GatherTab):
if self._local_address is None:
AddrFetchThread(ba.WeakCall(self._fetch_local_addr_cb)).start()
assert self._sub_tab is not None
self._set_sub_tab(self._sub_tab, region_width, region_height)
self._update_timer = ba.Timer(0.2,
ba.WeakCall(self._update),
@ -292,13 +295,15 @@ class PublicGatherTab(GatherTab):
def save_state(self) -> None:
# Save off a small number of parties with the lowest ping; this
# should be most of the ones that matter and will keep things
# a reasonable size.
# Save off a small number of parties with the lowest ping; we'll
# display these immediately when our UI comes back up which should
# be enough to make things feel nice and crisp while we do a full
# server re-query or whatnot.
ba.app.ui.window_states[self.__class__.__name__] = State(
sub_tab=self._sub_tab,
parties=[copy.copy(p) for p in self._get_ordered_parties()[:20]],
next_entry_index=self._next_entry_index)
parties=[copy.copy(p) for p in self._get_ordered_parties()[:40]],
next_entry_index=self._next_entry_index,
filter_value=self._filter_value)
def restore_state(self) -> None:
state = ba.app.ui.window_states.get(self.__class__.__name__)
@ -307,7 +312,7 @@ class PublicGatherTab(GatherTab):
assert isinstance(state, State)
self._sub_tab = state.sub_tab
# Restore the parties we stored...
# Restore the parties we stored.
if state.parties:
self._parties = {
f'{p.address}_{p.port}': copy.copy(p)
@ -315,6 +320,7 @@ class PublicGatherTab(GatherTab):
}
self._next_entry_index = state.next_entry_index
self._have_valid_server_list = True
self._filter_value = state.filter_value
def _set_sub_tab(self,
value: SubTabType,
@ -346,16 +352,9 @@ class PublicGatherTab(GatherTab):
if widget and widget not in {self._host_text, self._join_text}:
widget.delete()
c_width = region_width
c_height = region_height - 20
sub_scroll_height = c_height - 90
sub_scroll_width = 830
v = c_height - 35
v -= 25
if value is SubTabType.JOIN:
self._build_join_tab(v, sub_scroll_width, sub_scroll_height,
c_width, c_height)
self._built_join_list = False
self._build_join_tab(region_width, region_height)
self._server_list_dirty = True
# If we've not yet successfully fetched a server list,
# force an attempt now and show the user a 'loading...' status.
@ -372,12 +371,36 @@ class PublicGatherTab(GatherTab):
ba.textwidget(edit=self._join_status_text, text=join_status_str)
if value is SubTabType.HOST:
self._build_host_tab(v, sub_scroll_width, sub_scroll_height,
c_width, c_height)
self._build_host_tab(region_width, region_height)
def _build_join_tab(self, region_width: float,
region_height: float) -> None:
c_width = region_width
c_height = region_height - 20
sub_scroll_height = c_height - 125
sub_scroll_width = 830
v = c_height - 35
v -= 60
self._filter_text = ba.textwidget(parent=self._container,
text=self._filter_value,
size=(350, 45),
position=(290, v - 10),
h_align='left',
v_align='center',
editable=True)
ba.widget(edit=self._filter_text, up_widget=self._join_text)
ba.textwidget(text=ba.Lstr(resource='filterText'),
parent=self._container,
size=(0, 0),
position=(270, v + 15),
maxwidth=100,
scale=0.8,
color=(0.5, 0.5, 0.5),
flatness=1.0,
shadow=0.0,
h_align='right',
v_align='center')
def _build_join_tab(self, v: float, sub_scroll_width: float,
sub_scroll_height: float, c_width: float,
c_height: float) -> None:
ba.textwidget(text=ba.Lstr(resource='nameText'),
parent=self._container,
size=(0, 0),
@ -412,19 +435,18 @@ class PublicGatherTab(GatherTab):
h_align='center',
v_align='center')
v -= sub_scroll_height + 23
self._host_scrollwidget = scrollw = ba.scrollwidget(
parent=self._container,
simple_culling_v=10,
position=((c_width - sub_scroll_width) * 0.5, v),
size=(sub_scroll_width, sub_scroll_height),
claims_up_down=False,
claims_left_right=True,
autoselect=True)
self._join_list_column = ba.containerwidget(parent=scrollw,
background=False,
size=(400, 400),
claims_left_right=True)
self._join_status_text = ba.textwidget(parent=self._container,
text='',
size=(0, 0),
@ -438,10 +460,12 @@ class PublicGatherTab(GatherTab):
position=(c_width * 0.5,
c_height * 0.5))
def _build_host_tab(self, v: float, sub_scroll_width: float,
sub_scroll_height: float, c_width: float,
c_height: float) -> None:
del sub_scroll_width, sub_scroll_height, c_height # Unused.
def _build_host_tab(self, region_width: float,
region_height: float) -> None:
c_width = region_width
c_height = region_height - 20
v = c_height - 35
v -= 25
is_public_enabled = _ba.get_public_party_enabled()
v -= 30
party_name_text = ba.Lstr(
@ -584,7 +608,7 @@ class PublicGatherTab(GatherTab):
since_first = cur_time - self._first_server_list_rebuild_time
wait_time = (1.0 if since_first < 2.0 else
2.5 if since_first < 10.0 else 5.0)
if (self._built_join_list
if (not self._server_list_dirty
and self._last_server_list_update_time is not None
and cur_time - self._last_server_list_update_time < wait_time):
return
@ -595,7 +619,7 @@ class PublicGatherTab(GatherTab):
return
self._last_server_list_update_time = cur_time
self._built_join_list = True
self._server_list_dirty = False
with ba.Context('ui'):
@ -605,6 +629,14 @@ class PublicGatherTab(GatherTab):
ordered_parties = self._get_ordered_parties()
# If we've got a filter, filter them.
if self._filter_value:
# Let's do case-insensitive searching.
filterval = self._filter_value.lower()
ordered_parties = [
p for p in ordered_parties if filterval in p.name.lower()
]
sub_scroll_width = 830
lineheight = 42
sub_scroll_height = lineheight * len(ordered_parties) + 50
@ -699,10 +731,10 @@ class PublicGatherTab(GatherTab):
if first:
if party.stats_button:
ba.widget(edit=party.stats_button,
up_widget=self._join_text)
up_widget=self._filter_text)
if party.name_widget:
ba.widget(edit=party.name_widget,
up_widget=self._join_text)
up_widget=self._filter_text)
first = False
party.size_widget = ba.textwidget(
@ -820,54 +852,70 @@ class PublicGatherTab(GatherTab):
_ba.set_public_party_name(name)
if self._sub_tab is SubTabType.JOIN:
now = ba.time(ba.TimeType.REAL)
# Fire off a new public-party query periodically.
if (self._last_server_list_query_time is None
or now - self._last_server_list_query_time > 0.001 *
_ba.get_account_misc_read_val('pubPartyRefreshMS', 10000)):
self._last_server_list_query_time = now
# If our filter value has changed, refresh the list
# using the new one.
text = self._filter_text
if text:
filter_value = cast(str, ba.textwidget(query=text))
if filter_value != self._filter_value:
self._filter_value = filter_value
self._server_list_dirty = True
self._update_server_list()
self._query_party_list_periodically()
self._ping_parties_periodically()
def _query_party_list_periodically(self) -> None:
now = ba.time(ba.TimeType.REAL)
# Fire off a new public-party query periodically.
if (self._last_server_list_query_time is None
or now - self._last_server_list_query_time > 0.001 *
_ba.get_account_misc_read_val('pubPartyRefreshMS', 10000)):
self._last_server_list_query_time = now
if DEBUG_SERVER_COMMUNICATION:
print('REQUESTING SERVER LIST')
_ba.add_transaction(
{
'type': 'PUBLIC_PARTY_QUERY',
'proto': ba.app.protocol_version,
'lang': ba.app.lang.language
},
callback=ba.WeakCall(self._on_public_party_query_result))
_ba.run_transactions()
def _ping_parties_periodically(self) -> None:
now = ba.time(ba.TimeType.REAL)
# Go through our existing public party entries firing off pings
# for any that have timed out.
for party in list(self._parties.values()):
if party.next_ping_time <= now and ba.app.ping_thread_count < 15:
# Crank the interval up for high-latency or non-responding
# parties to save us some useless work.
mult = 1
if party.ping_responses == 0:
if party.ping_attempts > 4:
mult = 10
elif party.ping_attempts > 2:
mult = 5
if party.ping is not None:
mult = (10 if party.ping > 300 else
5 if party.ping > 150 else 2)
interval = party.ping_interval * mult
if DEBUG_SERVER_COMMUNICATION:
print('REQUESTING SERVER LIST')
_ba.add_transaction(
{
'type': 'PUBLIC_PARTY_QUERY',
'proto': ba.app.protocol_version,
'lang': ba.app.lang.language
},
callback=ba.WeakCall(self._on_public_party_query_result))
_ba.run_transactions()
print(f'pinging #{party.index} cur={party.ping} '
f'interval={interval} '
f'({party.ping_responses}/{party.ping_attempts})')
# Go through our existing public party entries firing off pings
# for any that have timed out.
for party in list(self._parties.values()):
if (party.next_ping_time <= now
and ba.app.ping_thread_count < 15):
party.next_ping_time = now + party.ping_interval * mult
party.ping_attempts += 1
# Crank the interval up for high-latency or non-responding
# parties to save us some useless work.
mult = 1
if party.ping_responses == 0:
if party.ping_attempts > 4:
mult = 10
elif party.ping_attempts > 2:
mult = 5
if party.ping is not None:
mult = (10 if party.ping > 300 else
5 if party.ping > 150 else 2)
interval = party.ping_interval * mult
if DEBUG_SERVER_COMMUNICATION:
print(
f'pinging #{party.index} cur={party.ping} '
f'interval={interval} '
f'({party.ping_responses}/{party.ping_attempts})')
party.next_ping_time = now + party.ping_interval * mult
party.ping_attempts += 1
PingThread(party.address, party.port,
ba.WeakCall(self._ping_callback)).start()
PingThread(party.address, party.port,
ba.WeakCall(self._ping_callback)).start()
def _ping_callback(self, address: str, port: Optional[int],
result: Optional[int]) -> None:

View File

@ -38,8 +38,11 @@ class MainMenuWindow(ba.Window):
toolbar_visibility='menu_minimal_no_back' if self.
_in_game else 'menu_minimal_no_back'))
# Grab this stuff in case it changes.
self._is_demo = ba.app.demo_mode
self._is_arcade = ba.app.arcade_mode
self._is_iircade = ba.app.iircade_mode
self._tdelay = 0.0
self._t_delay_inc = 0.02
self._t_delay_play = 1.7
@ -179,7 +182,7 @@ class MainMenuWindow(ba.Window):
self._have_settings_button = (
(not self._in_game or not app.toolbar_test)
and not (self._is_demo or self._is_arcade))
and not (self._is_demo or self._is_arcade or self._is_iircade))
self._input_device = input_device = _ba.get_ui_input_device()
self._input_player = input_device.player if input_device else None
@ -436,7 +439,9 @@ class MainMenuWindow(ba.Window):
account_type_icon_color = (1.0, 1.0, 1.0)
account_type_call = self._show_account_window
account_type_enable_button_sound = True
b_count = 4 # play, help, credits, settings
b_count = 3 # play, help, credits
if self._have_settings_button:
b_count += 1
if enable_account_button:
b_count += 1
if self._have_quit_button:

View File

@ -209,15 +209,6 @@ class AllSettingsWindow(ba.Window):
# pylint: disable=cyclic-import
from bastd.ui.settings.controls import ControlsSettingsWindow
self._save_state()
# Disallow this on iircade.
if ba.app.iircade_mode:
ba.playsound(ba.getsound('error'))
ba.screenmessage(
ba.Lstr(resource='getTicketsWindow.unavailableText'),
color=(1, 0, 0))
return
ba.containerwidget(edit=self._root_widget, transition='out_left')
ba.app.ui.set_main_menu_window(
ControlsSettingsWindow(
@ -227,15 +218,6 @@ class AllSettingsWindow(ba.Window):
# pylint: disable=cyclic-import
from bastd.ui.settings.graphics import GraphicsSettingsWindow
self._save_state()
# Disallow this on iircade.
if ba.app.iircade_mode:
ba.playsound(ba.getsound('error'))
ba.screenmessage(
ba.Lstr(resource='getTicketsWindow.unavailableText'),
color=(1, 0, 0))
return
ba.containerwidget(edit=self._root_widget, transition='out_left')
ba.app.ui.set_main_menu_window(
GraphicsSettingsWindow(

View File

@ -13,8 +13,8 @@
<w>ack'ed</w>
<w>acked</w>
<w>acks</w>
<w>aclass</w>
<w>aclass's</w>
<w>aclass</w>
<w>activityplayer</w>
<w>addrs</w>
<w>adjoint</w>
@ -146,8 +146,8 @@
<w>cmath</w>
<w>cmds</w>
<w>cmdvals</w>
<w>codewarrior</w>
<w>codewarrior's</w>
<w>codewarrior</w>
<w>cofnodes</w>
<w>collapseable</w>
<w>collidable</w>
@ -287,6 +287,7 @@
<w>fffffffffifff</w>
<w>fgets</w>
<w>fifteenbits</w>
<w>filterval</w>
<w>finishedptr</w>
<w>fjco</w>
<w>fjcoiwef</w>

View File

@ -1,5 +1,5 @@
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
<h4><em>last updated on 2020-10-27 for Ballistica version 1.5.27 build 20228</em></h4>
<h4><em>last updated on 2020-10-29 for Ballistica version 1.5.27 build 20230</em></h4>
<p>This page documents the Python classes and functions in the 'ba' module,
which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p>
<hr>
@ -6760,6 +6760,7 @@ in the background if necessary.</p>
highlight: bool = None, border_opacity: float = None,
simple_culling_h: float = None,
claims_left_right: bool = None,
claims_up_down: bool = None,
claims_tab: bool = None) -&gt; <a href="#class_ba_Widget">ba.Widget</a></span></p>
<p>Create or edit a horizontal scroll widget.</p>
@ -7036,6 +7037,7 @@ Currently the 'clients' option only works for transient messages.</p>
simple_culling_v: float = None,
selection_loops_to_parent: bool = None,
claims_left_right: bool = None,
claims_up_down: bool = None,
claims_tab: bool = None,
autoselect: bool = None) -&gt; <a href="#class_ba_Widget">ba.Widget</a></span></p>

View File

@ -21,7 +21,7 @@
namespace ballistica {
// These are set automatically via script; don't change here.
const int kAppBuildNumber = 20229;
const int kAppBuildNumber = 20232;
const char* kAppVersion = "1.5.27";
// Our standalone globals.

View File

@ -1179,6 +1179,7 @@ auto PyScrollWidget(PyObject* self, PyObject* args, PyObject* keywds)
PyObject* simple_culling_v_obj{Py_None};
PyObject* selection_loops_to_parent_obj{Py_None};
PyObject* claims_left_right_obj{Py_None};
PyObject* claims_up_down_obj{Py_None};
PyObject* claims_tab_obj{Py_None};
PyObject* autoselect_obj{Py_None};
@ -1197,18 +1198,19 @@ auto PyScrollWidget(PyObject* self, PyObject* args, PyObject* keywds)
"simple_culling_v",
"selection_loops_to_parent",
"claims_left_right",
"claims_up_down",
"claims_tab",
"autoselect",
nullptr};
if (!PyArg_ParseTupleAndKeywords(
args, keywds, "|OOOOOOOOOOOOOOOOO", const_cast<char**>(kwlist),
args, keywds, "|OOOOOOOOOOOOOOOOOO", const_cast<char**>(kwlist),
&edit_obj, &parent_obj, &size_obj, &pos_obj, &background_obj,
&selected_child_obj, &capture_arrows_obj, &on_select_call_obj,
&center_small_content_obj, &color_obj, &highlight_obj,
&border_opacity_obj, &simple_culling_v_obj,
&selection_loops_to_parent_obj, &claims_left_right_obj,
&claims_tab_obj, &autoselect_obj))
&claims_up_down_obj, &claims_tab_obj, &autoselect_obj))
return nullptr;
if (!g_game->IsInUIContext()) {
@ -1287,6 +1289,9 @@ auto PyScrollWidget(PyObject* self, PyObject* args, PyObject* keywds)
if (claims_left_right_obj != Py_None) {
widget->set_claims_left_right(Python::GetPyBool(claims_left_right_obj));
}
if (claims_up_down_obj != Py_None) {
widget->set_claims_up_down(Python::GetPyBool(claims_up_down_obj));
}
if (claims_tab_obj != Py_None) {
widget->set_claims_tab(Python::GetPyBool(claims_tab_obj));
}
@ -1324,6 +1329,7 @@ auto PyHScrollWidget(PyObject* self, PyObject* args, PyObject* keywds)
PyObject* border_opacity_obj = Py_None;
PyObject* simple_culling_h_obj = Py_None;
PyObject* claims_left_right_obj = Py_None;
PyObject* claims_up_down_obj = Py_None;
PyObject* claims_tab_obj = Py_None;
PyObject* autoselect_obj = Py_None;
@ -1341,6 +1347,7 @@ auto PyHScrollWidget(PyObject* self, PyObject* args, PyObject* keywds)
"border_opacity",
"simple_culling_h",
"claims_left_right",
"claims_up_down",
"claims_tab",
"autoselect",
nullptr};
@ -1351,7 +1358,7 @@ auto PyHScrollWidget(PyObject* self, PyObject* args, PyObject* keywds)
&selected_child_obj, &capture_arrows_obj, &on_select_call_obj,
&center_small_content_obj, &color_obj, &highlight_obj,
&border_opacity_obj, &simple_culling_h_obj, &claims_left_right_obj,
&claims_tab_obj, &autoselect_obj))
&claims_up_down_obj, &claims_tab_obj, &autoselect_obj))
return nullptr;
if (!g_game->IsInUIContext()) {
@ -1425,6 +1432,9 @@ auto PyHScrollWidget(PyObject* self, PyObject* args, PyObject* keywds)
if (claims_left_right_obj != Py_None) {
widget->set_claims_left_right(Python::GetPyBool(claims_left_right_obj));
}
if (claims_up_down_obj != Py_None) {
widget->set_claims_up_down(Python::GetPyBool(claims_up_down_obj));
}
if (claims_tab_obj != Py_None) {
widget->set_claims_tab(Python::GetPyBool(claims_tab_obj));
}
@ -2643,6 +2653,7 @@ PyMethodDef PythonMethodsUI::methods_def[] = {
" simple_culling_v: float = None,\n"
" selection_loops_to_parent: bool = None,\n"
" claims_left_right: bool = None,\n"
" claims_up_down: bool = None,\n"
" claims_tab: bool = None,\n"
" autoselect: bool = None) -> ba.Widget\n"
"\n"
@ -2665,6 +2676,7 @@ PyMethodDef PythonMethodsUI::methods_def[] = {
" highlight: bool = None, border_opacity: float = None,\n"
" simple_culling_h: float = None,\n"
" claims_left_right: bool = None,\n"
" claims_up_down: bool = None,\n"
" claims_tab: bool = None) -> ba.Widget\n"
"\n"
"Create or edit a horizontal scroll widget.\n"

View File

@ -301,6 +301,7 @@ void TextWidget::Draw(RenderPass* pass, bool draw_transparent) {
if (text_group_dirty_) {
text_group_->SetText(text_translated_, align_h, align_v, big_, res_scale_);
text_width_ = g_text_graphics->GetStringWidth(text_translated_, big_);
// FIXME: doesnt support big.
text_height_ = g_text_graphics->GetStringHeight(text_translated_);
text_group_dirty_ = false;
@ -334,7 +335,9 @@ void TextWidget::Draw(RenderPass* pass, bool draw_transparent) {
for (int e = 0; e < elem_count; e++) {
// Gracefully skip unloaded textures..
TextureData* t2 = text_group_->GetElementTexture(e);
if (!t2->preloaded()) continue;
if (!t2->preloaded()) {
continue;
}
c.SetTexture(t2);
c.SetMaskUV2Texture(text_group_->GetElementMaskUV2Texture(e));
c.SetShadow(-0.004f * text_group_->GetElementUScale(e),
@ -607,23 +610,22 @@ auto TextWidget::HandleMessage(const WidgetMessage& m) -> bool {
return false;
case SDLK_RETURN:
case SDLK_KP_ENTER:
#if BA_OSTYPE_IOS_TVOS || BA_OSTYPE_ANDROID
// On iOS, return currently just deselects us.
g_audio->PlaySound(g_media->GetSound(SystemSoundID::kSwish));
parent_widget()->SelectWidget(nullptr);
return true;
#else
if (on_return_press_call_.exists()) {
claimed = true;
if (g_buildconfig.ostype_ios_tvos() || g_buildconfig.ostype_android()) {
// On mobile, return currently just deselects us.
g_audio->PlaySound(g_media->GetSound(SystemSoundID::kSwish));
parent_widget()->SelectWidget(nullptr);
return true;
} else {
if (on_return_press_call_.exists()) {
// Call this in the next cycle (don't wanna risk mucking with UI
// from within a UI loop)
g_game->PushPythonWeakCall(
Object::WeakRef<PythonContextCall>(on_return_press_call_));
claimed = true;
if (on_return_press_call_.exists()) {
// Call this in the next cycle (don't wanna risk mucking with UI
// from within a UI loop)
g_game->PushPythonWeakCall(
Object::WeakRef<PythonContextCall>(on_return_press_call_));
}
}
}
#endif // BA_OSTYPE_IOS_TVOS
break;
case SDLK_LEFT:
if (editable()) {

View File

@ -33,7 +33,7 @@ PIP_REQUIREMENTS = [
PipRequirement(modulename='mypy', minversion=[0, 790]),
PipRequirement(modulename='yapf', minversion=[0, 30, 0]),
PipRequirement(modulename='cpplint', minversion=[1, 5, 4]),
PipRequirement(modulename='pytest', minversion=[6, 1, 1]),
PipRequirement(modulename='pytest', minversion=[6, 1, 2]),
PipRequirement(modulename='typing_extensions'),
PipRequirement(modulename='pytz'),
PipRequirement(modulename='yaml', pipname='PyYAML'),
@ -739,41 +739,54 @@ def cmake_prep_dir(dirname: str) -> None:
else:
versions = {}
# Get version of installed cmake.
# Start fresh if cmake version changes.
cmake_ver_output = subprocess.run(['cmake', '--version'],
check=True,
capture_output=True).stdout.decode()
cmake_ver = cmake_ver_output.splitlines()[0].split('cmake version ')[1]
cmake_ver_existing = versions.get('cmake')
cmake_ver_existing = versions.get('cmake_version')
assert isinstance(cmake_ver_existing, (str, type(None)))
# Get specific version of our target python.
# ...or if python's version changes.
python_ver_output = subprocess.run([f'python{PYVER}', '--version'],
check=True,
capture_output=True).stdout.decode()
python_ver = python_ver_output.splitlines()[0].split('Python ')[1]
python_ver_existing = versions.get('python')
python_ver_existing = versions.get('python_version')
assert isinstance(python_ver_existing, (str, type(None)))
# If they don't match, blow away the dir and write the current version.
if cmake_ver_existing != cmake_ver or python_ver_existing != python_ver:
if (cmake_ver_existing != cmake_ver
and cmake_ver_existing is not None):
# ...or if the actual location of python on disk changes.
python_path = os.path.realpath(
subprocess.run(['which', f'python{PYVER}'],
check=True,
capture_output=True).stdout.decode())
python_path_existing = versions.get('python_path')
assert isinstance(python_path_existing, (str, type(None)))
# Blow away and start from scratch if any vals differ from existing.
if (cmake_ver_existing != cmake_ver or python_ver_existing != python_ver
or python_path != python_path_existing):
if (cmake_ver_existing is not None
and cmake_ver_existing != cmake_ver):
print(f'{Clr.BLU}CMake version changed from {cmake_ver_existing}'
f' to {cmake_ver}; clearing existing build at'
f' "{dirname}".{Clr.RST}')
if (python_ver_existing != python_ver
and python_ver_existing is not None):
if (python_ver_existing is not None
and python_ver_existing != python_ver):
print(f'{Clr.BLU}Python version changed from {python_ver_existing}'
f' to {python_ver}; clearing existing build at'
f' "{dirname}".{Clr.RST}')
if (python_path_existing is not None
and python_path_existing != python_path):
print(f'{Clr.BLU}Python path changed from {python_path_existing}'
f' to {python_path}; clearing existing build at'
f' "{dirname}".{Clr.RST}')
subprocess.run(['rm', '-rf', dirname], check=True)
os.makedirs(dirname, exist_ok=True)
with open(verfilename, 'w') as outfile:
outfile.write(
json.dumps({
'cmake': cmake_ver,
'python': python_ver
'cmake_version': cmake_ver,
'python_version': python_ver,
'python_path': python_path
}))

View File

@ -733,7 +733,6 @@ def cmake_prep_dir() -> None:
"""
from efro.error import CleanError
import batools.build
if len(sys.argv) != 3:
raise CleanError('Expected 1 arg (dir name)')
dirname = sys.argv[2]