mirror of
https://github.com/RYDE-WORK/ballistica.git
synced 2026-01-28 10:03:15 +08:00
Separating out ads functionality
This commit is contained in:
parent
d098291948
commit
eba70f0c81
@ -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/72/93/a41a9777570bee533ab3259f1597",
|
||||
"build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/2d/40/964c6b36393b12b459433dcda36c",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/e3/d6/e286d413e60a2e6a43b5773d6441",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/20/97/d4a3ccde682ec984a67b3d683f54",
|
||||
"build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/82/25/c1d9b277444a9aa46be5f1eec44a",
|
||||
"build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/9c/05/36d55f280f9676e3098ed9aa6a78",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/8a/8c/0a3d0b30186fec5b189a5f0407cb",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/30/1c/f554338c290026fb45bd52e9e1a7",
|
||||
"build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/fa/e5/e624e868b0fc00a2413ae4b9432c",
|
||||
"build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/e8/1e/418931e11d12072c869808579592",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/21/21/de5c6e124de4675b11d0c73ceb28",
|
||||
"build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/11/f3/037c95d8bbb74730bdef686526a5",
|
||||
"build/prefab/lib/linux_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/1a/4a/bd980abb5c7078d1e144d19fa63d",
|
||||
"build/prefab/lib/linux_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/fd/cd/3397d744c7405740df4d4ae567f0",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/34/c2/24a9ab7d513acdb8ebaa3251611f",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/54/40/8e0cc564f49f8963803834ec7995",
|
||||
"build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/62/55/765d22f045d0d4d31a02aeffec7b",
|
||||
"build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/4b/76/aa0554648b65da797eb30b9c4699",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/91/dc/c415fc19cfa0395cc200a5a72e2c",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/cd/d8/ec9c1c0955cc62ea574a4b42a707"
|
||||
"build/prefab/full/linux_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/dd/d1/e12c5331256ecffcd7684d6d2963",
|
||||
"build/prefab/full/linux_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/c5/35/8d811eec1a47f4e70d4b27eb3bf5",
|
||||
"build/prefab/full/linux_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/6f/43/d0f61fb34a76e11b9ebd4334038d",
|
||||
"build/prefab/full/linux_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/a4/56/614e09d8ab86355f0d65c1ce76fe",
|
||||
"build/prefab/full/mac_x86_64/debug/ballisticacore": "https://files.ballistica.net/cache/ba1/f7/c5/f695953295c2d79dfb01555aea89",
|
||||
"build/prefab/full/mac_x86_64/release/ballisticacore": "https://files.ballistica.net/cache/ba1/7b/19/ff1bba3a148b6e0f0823d7d7c7bf",
|
||||
"build/prefab/full/mac_x86_64_server/debug/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/f7/dd/d5e4d872192060d46821d6911c13",
|
||||
"build/prefab/full/mac_x86_64_server/release/dist/ballisticacore_headless": "https://files.ballistica.net/cache/ba1/64/58/962c2e707ee66d310848f375796a",
|
||||
"build/prefab/full/windows_x86/debug/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/db/58/65c487facba1de6ba8ce65f19f01",
|
||||
"build/prefab/full/windows_x86/release/BallisticaCore.exe": "https://files.ballistica.net/cache/ba1/40/cb/afd71350fdc9827ca99baab73001",
|
||||
"build/prefab/full/windows_x86_server/debug/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/f2/05/e9252b68a96ee418da35dd4d2530",
|
||||
"build/prefab/full/windows_x86_server/release/dist/ballisticacore_headless.exe": "https://files.ballistica.net/cache/ba1/bb/a2/e1f5b3f561a08bb09cc3ffb2492f",
|
||||
"build/prefab/lib/linux_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/66/c9/3b04209f599dea8b8ca4be7d3404",
|
||||
"build/prefab/lib/linux_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/0d/3b/b7b46c3131cff8a40dfaa001af38",
|
||||
"build/prefab/lib/linux_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/57/40/0c1d88af3ce14e0f8870ab9ac7ad",
|
||||
"build/prefab/lib/linux_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/8a/72/02b4eddf662001f05f98288d4ad4",
|
||||
"build/prefab/lib/mac_x86_64/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/fc/bc/51529aac7531d1a62cf13eb79153",
|
||||
"build/prefab/lib/mac_x86_64/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/ea/7b/de9ce5284627cc77b2fffc354a66",
|
||||
"build/prefab/lib/mac_x86_64_server/debug/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/96/00/78b64146e33ec35dcde9c278328a",
|
||||
"build/prefab/lib/mac_x86_64_server/release/libballisticacore_internal.a": "https://files.ballistica.net/cache/ba1/6d/25/0e5918fa1eb7285538d14051761c"
|
||||
}
|
||||
12
.idea/dictionaries/ericf.xml
generated
12
.idea/dictionaries/ericf.xml
generated
@ -29,8 +29,8 @@
|
||||
<w>achname</w>
|
||||
<w>achs</w>
|
||||
<w>acinstance</w>
|
||||
<w>ack'ed</w>
|
||||
<w>ack</w>
|
||||
<w>ack'ed</w>
|
||||
<w>acked</w>
|
||||
<w>acks</w>
|
||||
<w>acnt</w>
|
||||
@ -152,8 +152,8 @@
|
||||
<w>bacommon</w>
|
||||
<w>badguy</w>
|
||||
<w>bafoundation</w>
|
||||
<w>ballistica's</w>
|
||||
<w>ballistica</w>
|
||||
<w>ballistica's</w>
|
||||
<w>ballisticacore</w>
|
||||
<w>ballisticacorecb</w>
|
||||
<w>bamaster</w>
|
||||
@ -800,8 +800,8 @@
|
||||
<w>gamedata</w>
|
||||
<w>gameinstance</w>
|
||||
<w>gamemap</w>
|
||||
<w>gamepad's</w>
|
||||
<w>gamepad</w>
|
||||
<w>gamepad's</w>
|
||||
<w>gamepadadvanced</w>
|
||||
<w>gamepads</w>
|
||||
<w>gamepadselect</w>
|
||||
@ -1189,8 +1189,8 @@
|
||||
<w>lsqlite</w>
|
||||
<w>lssl</w>
|
||||
<w>lstart</w>
|
||||
<w>lstr's</w>
|
||||
<w>lstr</w>
|
||||
<w>lstr's</w>
|
||||
<w>lstrs</w>
|
||||
<w>lsval</w>
|
||||
<w>ltex</w>
|
||||
@ -1823,8 +1823,8 @@
|
||||
<w>sessionname</w>
|
||||
<w>sessionplayer</w>
|
||||
<w>sessionplayers</w>
|
||||
<w>sessionteam's</w>
|
||||
<w>sessionteam</w>
|
||||
<w>sessionteam's</w>
|
||||
<w>sessionteams</w>
|
||||
<w>sessiontype</w>
|
||||
<w>setactivity</w>
|
||||
@ -2156,8 +2156,8 @@
|
||||
<w>txtw</w>
|
||||
<w>typeargs</w>
|
||||
<w>typecheck</w>
|
||||
<w>typechecker's</w>
|
||||
<w>typechecker</w>
|
||||
<w>typechecker's</w>
|
||||
<w>typedval</w>
|
||||
<w>typeshed</w>
|
||||
<w>typestr</w>
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
- Plugin functionality has been consolidated into a PluginSubsystem obj at ba.app.plugins
|
||||
- Ditto with AccountSubsystem and ba.app.accounts
|
||||
- Ditto with MetadataSubsystem and ba.app.meta
|
||||
- Ditto with AdsSubsystem and ba.app.ads
|
||||
|
||||
### 1.5.26 (20217)
|
||||
- Simplified licensing header on python scripts.
|
||||
|
||||
@ -6,6 +6,7 @@
|
||||
"ba_data/python/ba/__pycache__/_activity.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/ba/__pycache__/_activitytypes.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/ba/__pycache__/_actor.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/ba/__pycache__/_ads.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/ba/__pycache__/_analytics.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/ba/__pycache__/_app.cpython-38.opt-1.pyc",
|
||||
"ba_data/python/ba/__pycache__/_appconfig.cpython-38.opt-1.pyc",
|
||||
@ -67,6 +68,7 @@
|
||||
"ba_data/python/ba/_activity.py",
|
||||
"ba_data/python/ba/_activitytypes.py",
|
||||
"ba_data/python/ba/_actor.py",
|
||||
"ba_data/python/ba/_ads.py",
|
||||
"ba_data/python/ba/_analytics.py",
|
||||
"ba_data/python/ba/_app.py",
|
||||
"ba_data/python/ba/_appconfig.py",
|
||||
|
||||
@ -137,6 +137,7 @@ SCRIPT_TARGETS_PY_PUBLIC = \
|
||||
build/ba_data/python/ba/_activity.py \
|
||||
build/ba_data/python/ba/_activitytypes.py \
|
||||
build/ba_data/python/ba/_actor.py \
|
||||
build/ba_data/python/ba/_ads.py \
|
||||
build/ba_data/python/ba/_analytics.py \
|
||||
build/ba_data/python/ba/_app.py \
|
||||
build/ba_data/python/ba/_appconfig.py \
|
||||
@ -375,6 +376,7 @@ SCRIPT_TARGETS_PYC_PUBLIC = \
|
||||
build/ba_data/python/ba/__pycache__/_activity.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/ba/__pycache__/_activitytypes.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/ba/__pycache__/_actor.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/ba/__pycache__/_ads.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/ba/__pycache__/_analytics.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/ba/__pycache__/_app.cpython-38.opt-1.pyc \
|
||||
build/ba_data/python/ba/__pycache__/_appconfig.cpython-38.opt-1.pyc \
|
||||
|
||||
@ -40,11 +40,10 @@ class EndSessionActivity(Activity[EmptyPlayer, EmptyTeam]):
|
||||
def on_begin(self) -> None:
|
||||
# pylint: disable=cyclic-import
|
||||
from bastd.mainmenu import MainMenuSession
|
||||
from ba._apputils import call_after_ad
|
||||
from ba._general import Call
|
||||
super().on_begin()
|
||||
_ba.unlock_all_input()
|
||||
call_after_ad(Call(_ba.new_host_session, MainMenuSession))
|
||||
_ba.app.ads.call_after_ad(Call(_ba.new_host_session, MainMenuSession))
|
||||
|
||||
|
||||
class JoinActivity(Activity[EmptyPlayer, EmptyTeam]):
|
||||
|
||||
186
assets/src/ba_data/python/ba/_ads.py
Normal file
186
assets/src/ba_data/python/ba/_ads.py
Normal file
@ -0,0 +1,186 @@
|
||||
# Released under the MIT License. See LICENSE for details.
|
||||
#
|
||||
"""Functionality related to ads."""
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import _ba
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from typing import Optional, Callable, Any
|
||||
|
||||
|
||||
class AdsSubsystem:
|
||||
"""Subsystem for ads functionality in the app.
|
||||
|
||||
Category: App Classes
|
||||
|
||||
Access the single shared instance of this class at 'ba.app.ads'.
|
||||
"""
|
||||
|
||||
def __init__(self) -> None:
|
||||
self.last_ad_network = 'unknown'
|
||||
self.last_ad_network_set_time = time.time()
|
||||
self.ad_amt: Optional[float] = None
|
||||
self.last_ad_purpose = 'invalid'
|
||||
self.attempted_first_ad = False
|
||||
self.last_in_game_ad_remove_message_show_time: Optional[float] = None
|
||||
self.last_ad_completion_time: Optional[float] = None
|
||||
self.last_ad_was_short = False
|
||||
|
||||
def do_remove_in_game_ads_message(self) -> None:
|
||||
"""(internal)"""
|
||||
from ba._language import Lstr
|
||||
from ba._enums import TimeType
|
||||
|
||||
# Print this message once every 10 minutes at most.
|
||||
tval = _ba.time(TimeType.REAL)
|
||||
if (self.last_in_game_ad_remove_message_show_time is None or
|
||||
(tval - self.last_in_game_ad_remove_message_show_time > 60 * 10)):
|
||||
self.last_in_game_ad_remove_message_show_time = tval
|
||||
with _ba.Context('ui'):
|
||||
_ba.timer(
|
||||
1.0,
|
||||
lambda: _ba.screenmessage(Lstr(
|
||||
resource='removeInGameAdsText',
|
||||
subs=[('${PRO}',
|
||||
Lstr(resource='store.bombSquadProNameText')),
|
||||
('${APP_NAME}', Lstr(resource='titleText'))]),
|
||||
color=(1, 1, 0)),
|
||||
timetype=TimeType.REAL)
|
||||
|
||||
def show_ad(self,
|
||||
purpose: str,
|
||||
on_completion_call: Callable[[], Any] = None) -> None:
|
||||
"""(internal)"""
|
||||
self.last_ad_purpose = purpose
|
||||
_ba.show_ad(purpose, on_completion_call)
|
||||
|
||||
def show_ad_2(self,
|
||||
purpose: str,
|
||||
on_completion_call: Callable[[bool], Any] = None) -> None:
|
||||
"""(internal)"""
|
||||
self.last_ad_purpose = purpose
|
||||
_ba.show_ad_2(purpose, on_completion_call)
|
||||
|
||||
def call_after_ad(self, call: Callable[[], Any]) -> None:
|
||||
"""Run a call after potentially showing an ad."""
|
||||
# pylint: disable=too-many-statements
|
||||
# pylint: disable=too-many-branches
|
||||
# pylint: disable=too-many-locals
|
||||
from ba._enums import TimeType
|
||||
app = _ba.app
|
||||
show = True
|
||||
|
||||
# No ads without net-connections, etc.
|
||||
if not _ba.can_show_ad():
|
||||
show = False
|
||||
if app.accounts.have_pro():
|
||||
show = False # Pro disables interstitials.
|
||||
try:
|
||||
session = _ba.get_foreground_host_session()
|
||||
assert session is not None
|
||||
is_tournament = session.tournament_id is not None
|
||||
except Exception:
|
||||
is_tournament = False
|
||||
if is_tournament:
|
||||
show = False # Never show ads during tournaments.
|
||||
|
||||
if show:
|
||||
interval: Optional[float]
|
||||
launch_count = app.config.get('launchCount', 0)
|
||||
|
||||
# If we're seeing short ads we may want to space them differently.
|
||||
interval_mult = (_ba.get_account_misc_read_val(
|
||||
'ads.shortIntervalMult', 1.0)
|
||||
if self.last_ad_was_short else 1.0)
|
||||
if self.ad_amt is None:
|
||||
if launch_count <= 1:
|
||||
self.ad_amt = _ba.get_account_misc_read_val(
|
||||
'ads.startVal1', 0.99)
|
||||
else:
|
||||
self.ad_amt = _ba.get_account_misc_read_val(
|
||||
'ads.startVal2', 1.0)
|
||||
interval = None
|
||||
else:
|
||||
# So far we're cleared to show; now calc our
|
||||
# ad-show-threshold and see if we should *actually* show
|
||||
# (we reach our threshold faster the longer we've been
|
||||
# playing).
|
||||
base = 'ads' if _ba.has_video_ads() else 'ads2'
|
||||
min_lc = _ba.get_account_misc_read_val(base + '.minLC', 0.0)
|
||||
max_lc = _ba.get_account_misc_read_val(base + '.maxLC', 5.0)
|
||||
min_lc_scale = (_ba.get_account_misc_read_val(
|
||||
base + '.minLCScale', 0.25))
|
||||
max_lc_scale = (_ba.get_account_misc_read_val(
|
||||
base + '.maxLCScale', 0.34))
|
||||
min_lc_interval = (_ba.get_account_misc_read_val(
|
||||
base + '.minLCInterval', 360))
|
||||
max_lc_interval = (_ba.get_account_misc_read_val(
|
||||
base + '.maxLCInterval', 300))
|
||||
if launch_count < min_lc:
|
||||
lc_amt = 0.0
|
||||
elif launch_count > max_lc:
|
||||
lc_amt = 1.0
|
||||
else:
|
||||
lc_amt = ((float(launch_count) - min_lc) /
|
||||
(max_lc - min_lc))
|
||||
incr = (1.0 - lc_amt) * min_lc_scale + lc_amt * max_lc_scale
|
||||
interval = ((1.0 - lc_amt) * min_lc_interval +
|
||||
lc_amt * max_lc_interval)
|
||||
self.ad_amt += incr
|
||||
assert self.ad_amt is not None
|
||||
if self.ad_amt >= 1.0:
|
||||
self.ad_amt = self.ad_amt % 1.0
|
||||
self.attempted_first_ad = True
|
||||
|
||||
# After we've reached the traditional show-threshold once,
|
||||
# try again whenever its been INTERVAL since our last successful
|
||||
# show.
|
||||
elif (
|
||||
self.attempted_first_ad and
|
||||
(self.last_ad_completion_time is None or
|
||||
(interval is not None
|
||||
and _ba.time(TimeType.REAL) - self.last_ad_completion_time >
|
||||
(interval * interval_mult)))):
|
||||
# Reset our other counter too in this case.
|
||||
self.ad_amt = 0.0
|
||||
else:
|
||||
show = False
|
||||
|
||||
# If we're *still* cleared to show, actually tell the system to show.
|
||||
if show:
|
||||
# As a safety-check, set up an object that will run
|
||||
# the completion callback if we've returned and sat for 10 seconds
|
||||
# (in case some random ad network doesn't properly deliver its
|
||||
# completion callback).
|
||||
class _Payload:
|
||||
|
||||
def __init__(self, pcall: Callable[[], Any]):
|
||||
self._call = pcall
|
||||
self._ran = False
|
||||
|
||||
def run(self, fallback: bool = False) -> None:
|
||||
"""Run fallback call (and issue a warning about it)."""
|
||||
if not self._ran:
|
||||
if fallback:
|
||||
print(
|
||||
('ERROR: relying on fallback ad-callback! '
|
||||
'last network: ' + app.ads.last_ad_network +
|
||||
' (set ' + str(
|
||||
int(time.time() -
|
||||
app.ads.last_ad_network_set_time)) +
|
||||
's ago); purpose=' + app.ads.last_ad_purpose))
|
||||
_ba.pushcall(self._call)
|
||||
self._ran = True
|
||||
|
||||
payload = _Payload(call)
|
||||
with _ba.Context('ui'):
|
||||
_ba.timer(5.0,
|
||||
lambda: payload.run(fallback=True),
|
||||
timetype=TimeType.REAL)
|
||||
self.show_ad('between_game', on_completion_call=payload.run)
|
||||
else:
|
||||
_ba.pushcall(call) # Just run the callback without the ad.
|
||||
@ -3,7 +3,6 @@
|
||||
"""Functionality related to the high level state of the app."""
|
||||
from __future__ import annotations
|
||||
|
||||
import time
|
||||
import random
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
@ -173,6 +172,7 @@ class App:
|
||||
from ba._plugin import PluginSubsystem
|
||||
from ba._account import AccountSubsystem
|
||||
from ba._meta import MetadataSubsystem
|
||||
from ba._ads import AdsSubsystem
|
||||
|
||||
# Config.
|
||||
self.config_file_healthy = False
|
||||
@ -199,12 +199,9 @@ class App:
|
||||
# Misc.
|
||||
self.tips: List[str] = []
|
||||
self.stress_test_reset_timer: Optional[ba.Timer] = None
|
||||
self.last_ad_completion_time: Optional[float] = None
|
||||
self.last_ad_was_short = False
|
||||
self.did_weak_call_warning = False
|
||||
self.ran_on_app_launch = False
|
||||
|
||||
self.last_in_game_ad_remove_message_show_time: Optional[float] = None
|
||||
self.log_have_new = False
|
||||
self.log_upload_timer_started = False
|
||||
self._config: Optional[ba.AppConfig] = None
|
||||
@ -223,13 +220,6 @@ class App:
|
||||
# Server Mode.
|
||||
self.server: Optional[ba.ServerController] = None
|
||||
|
||||
# Ads.
|
||||
self.last_ad_network = 'unknown'
|
||||
self.last_ad_network_set_time = time.time()
|
||||
self.ad_amt: Optional[float] = None
|
||||
self.last_ad_purpose = 'invalid'
|
||||
self.attempted_first_ad = False
|
||||
|
||||
self.meta = MetadataSubsystem()
|
||||
self.accounts = AccountSubsystem()
|
||||
self.plugins = PluginSubsystem()
|
||||
@ -237,6 +227,7 @@ class App:
|
||||
self.lang = LanguageSubsystem()
|
||||
self.ach = AchievementSubsystem()
|
||||
self.ui = UISubsystem()
|
||||
self.ads = AdsSubsystem()
|
||||
|
||||
# Lobby.
|
||||
self.lobby_random_profile_index: int = 1
|
||||
@ -552,27 +543,6 @@ class App:
|
||||
_ba.fade_screen(False, endcall=_fade_end)
|
||||
return True
|
||||
|
||||
def do_remove_in_game_ads_message(self) -> None:
|
||||
"""(internal)"""
|
||||
from ba._language import Lstr
|
||||
from ba._enums import TimeType
|
||||
|
||||
# Print this message once every 10 minutes at most.
|
||||
tval = _ba.time(TimeType.REAL)
|
||||
if (self.last_in_game_ad_remove_message_show_time is None or
|
||||
(tval - self.last_in_game_ad_remove_message_show_time > 60 * 10)):
|
||||
self.last_in_game_ad_remove_message_show_time = tval
|
||||
with _ba.Context('ui'):
|
||||
_ba.timer(
|
||||
1.0,
|
||||
lambda: _ba.screenmessage(Lstr(
|
||||
resource='removeInGameAdsText',
|
||||
subs=[('${PRO}',
|
||||
Lstr(resource='store.bombSquadProNameText')),
|
||||
('${APP_NAME}', Lstr(resource='titleText'))]),
|
||||
color=(1, 1, 0)),
|
||||
timetype=TimeType.REAL)
|
||||
|
||||
def on_app_shutdown(self) -> None:
|
||||
"""(internal)"""
|
||||
self.music.on_app_shutdown()
|
||||
|
||||
@ -225,133 +225,3 @@ def print_corrupt_file_error() -> None:
|
||||
_ba.timer(2.0,
|
||||
Call(_ba.playsound, _ba.getsound('error')),
|
||||
timetype=TimeType.REAL)
|
||||
|
||||
|
||||
def show_ad(purpose: str,
|
||||
on_completion_call: Callable[[], Any] = None) -> None:
|
||||
"""(internal)"""
|
||||
_ba.app.last_ad_purpose = purpose
|
||||
_ba.show_ad(purpose, on_completion_call)
|
||||
|
||||
|
||||
def show_ad_2(purpose: str,
|
||||
on_completion_call: Callable[[bool], Any] = None) -> None:
|
||||
"""(internal)"""
|
||||
_ba.app.last_ad_purpose = purpose
|
||||
_ba.show_ad_2(purpose, on_completion_call)
|
||||
|
||||
|
||||
def call_after_ad(call: Callable[[], Any]) -> None:
|
||||
"""Run a call after potentially showing an ad."""
|
||||
# pylint: disable=too-many-statements
|
||||
# pylint: disable=too-many-branches
|
||||
# pylint: disable=too-many-locals
|
||||
from ba._enums import TimeType
|
||||
import time
|
||||
app = _ba.app
|
||||
show = True
|
||||
|
||||
# No ads without net-connections, etc.
|
||||
if not _ba.can_show_ad():
|
||||
show = False
|
||||
if app.accounts.have_pro():
|
||||
show = False # Pro disables interstitials.
|
||||
try:
|
||||
session = _ba.get_foreground_host_session()
|
||||
assert session is not None
|
||||
is_tournament = session.tournament_id is not None
|
||||
except Exception:
|
||||
is_tournament = False
|
||||
if is_tournament:
|
||||
show = False # Never show ads during tournaments.
|
||||
|
||||
if show:
|
||||
interval: Optional[float]
|
||||
launch_count = app.config.get('launchCount', 0)
|
||||
|
||||
# If we're seeing short ads we may want to space them differently.
|
||||
interval_mult = (_ba.get_account_misc_read_val(
|
||||
'ads.shortIntervalMult', 1.0) if app.last_ad_was_short else 1.0)
|
||||
if app.ad_amt is None:
|
||||
if launch_count <= 1:
|
||||
app.ad_amt = _ba.get_account_misc_read_val(
|
||||
'ads.startVal1', 0.99)
|
||||
else:
|
||||
app.ad_amt = _ba.get_account_misc_read_val(
|
||||
'ads.startVal2', 1.0)
|
||||
interval = None
|
||||
else:
|
||||
# So far we're cleared to show; now calc our ad-show-threshold and
|
||||
# see if we should *actually* show (we reach our threshold faster
|
||||
# the longer we've been playing).
|
||||
base = 'ads' if _ba.has_video_ads() else 'ads2'
|
||||
min_lc = _ba.get_account_misc_read_val(base + '.minLC', 0.0)
|
||||
max_lc = _ba.get_account_misc_read_val(base + '.maxLC', 5.0)
|
||||
min_lc_scale = (_ba.get_account_misc_read_val(
|
||||
base + '.minLCScale', 0.25))
|
||||
max_lc_scale = (_ba.get_account_misc_read_val(
|
||||
base + '.maxLCScale', 0.34))
|
||||
min_lc_interval = (_ba.get_account_misc_read_val(
|
||||
base + '.minLCInterval', 360))
|
||||
max_lc_interval = (_ba.get_account_misc_read_val(
|
||||
base + '.maxLCInterval', 300))
|
||||
if launch_count < min_lc:
|
||||
lc_amt = 0.0
|
||||
elif launch_count > max_lc:
|
||||
lc_amt = 1.0
|
||||
else:
|
||||
lc_amt = ((float(launch_count) - min_lc) / (max_lc - min_lc))
|
||||
incr = (1.0 - lc_amt) * min_lc_scale + lc_amt * max_lc_scale
|
||||
interval = ((1.0 - lc_amt) * min_lc_interval +
|
||||
lc_amt * max_lc_interval)
|
||||
app.ad_amt += incr
|
||||
assert app.ad_amt is not None
|
||||
if app.ad_amt >= 1.0:
|
||||
app.ad_amt = app.ad_amt % 1.0
|
||||
app.attempted_first_ad = True
|
||||
|
||||
# After we've reached the traditional show-threshold once,
|
||||
# try again whenever its been INTERVAL since our last successful show.
|
||||
elif (app.attempted_first_ad
|
||||
and (app.last_ad_completion_time is None or
|
||||
(interval is not None
|
||||
and _ba.time(TimeType.REAL) - app.last_ad_completion_time >
|
||||
(interval * interval_mult)))):
|
||||
# Reset our other counter too in this case.
|
||||
app.ad_amt = 0.0
|
||||
else:
|
||||
show = False
|
||||
|
||||
# If we're *still* cleared to show, actually tell the system to show.
|
||||
if show:
|
||||
# As a safety-check, set up an object that will run
|
||||
# the completion callback if we've returned and sat for 10 seconds
|
||||
# (in case some random ad network doesn't properly deliver its
|
||||
# completion callback).
|
||||
class _Payload:
|
||||
|
||||
def __init__(self, pcall: Callable[[], Any]):
|
||||
self._call = pcall
|
||||
self._ran = False
|
||||
|
||||
def run(self, fallback: bool = False) -> None:
|
||||
"""Run the fallback call (and issues a warning about it)."""
|
||||
if not self._ran:
|
||||
if fallback:
|
||||
print((
|
||||
'ERROR: relying on fallback ad-callback! '
|
||||
'last network: ' + app.last_ad_network + ' (set ' +
|
||||
str(int(time.time() -
|
||||
app.last_ad_network_set_time)) +
|
||||
's ago); purpose=' + app.last_ad_purpose))
|
||||
_ba.pushcall(self._call)
|
||||
self._ran = True
|
||||
|
||||
payload = _Payload(call)
|
||||
with _ba.Context('ui'):
|
||||
_ba.timer(5.0,
|
||||
lambda: payload.run(fallback=True),
|
||||
timetype=TimeType.REAL)
|
||||
show_ad('between_game', on_completion_call=payload.run)
|
||||
else:
|
||||
_ba.pushcall(call) # Just run the callback without the ad.
|
||||
|
||||
@ -178,8 +178,8 @@ def submit_analytics_counts(sval: str) -> None:
|
||||
|
||||
def set_last_ad_network(sval: str) -> None:
|
||||
import time
|
||||
_ba.app.last_ad_network = sval
|
||||
_ba.app.last_ad_network_set_time = time.time()
|
||||
_ba.app.ads.last_ad_network = sval
|
||||
_ba.app.ads.last_ad_network_set_time = time.time()
|
||||
|
||||
|
||||
def no_game_circle_message() -> None:
|
||||
@ -263,7 +263,7 @@ def quit_window() -> None:
|
||||
|
||||
|
||||
def remove_in_game_ads_message() -> None:
|
||||
_ba.app.do_remove_in_game_ads_message()
|
||||
_ba.app.ads.do_remove_in_game_ads_message()
|
||||
|
||||
|
||||
def telnet_access_request() -> None:
|
||||
|
||||
@ -612,7 +612,7 @@ class Session:
|
||||
def transitioning_out_activity_was_freed(
|
||||
self, can_show_ad_on_death: bool) -> None:
|
||||
"""(internal)"""
|
||||
from ba._apputils import garbage_collect, call_after_ad
|
||||
from ba._apputils import garbage_collect
|
||||
|
||||
# Since things should be generally still right now, it's a good time
|
||||
# to run garbage collection to clear out any circular dependency
|
||||
@ -622,7 +622,7 @@ class Session:
|
||||
|
||||
with _ba.Context(self):
|
||||
if can_show_ad_on_death:
|
||||
call_after_ad(self.begin_next_activity)
|
||||
_ba.app.ads.call_after_ad(self.begin_next_activity)
|
||||
else:
|
||||
_ba.pushcall(self.begin_next_activity)
|
||||
|
||||
|
||||
@ -18,7 +18,7 @@ from ba._input import (get_device_value, get_input_map_hash,
|
||||
from ba._general import getclass, json_prep, get_type_name
|
||||
from ba._activitytypes import JoinActivity, ScoreScreenActivity
|
||||
from ba._apputils import (is_browser_likely_available, get_remote_app_name,
|
||||
should_submit_debug_info, show_ad, show_ad_2)
|
||||
should_submit_debug_info)
|
||||
from ba._benchmark import (run_gpu_benchmark, run_cpu_benchmark,
|
||||
run_media_reload_benchmark, run_stress_test)
|
||||
from ba._campaign import getcampaign
|
||||
|
||||
@ -551,7 +551,6 @@ class GetCurrencyWindow(ba.Window):
|
||||
|
||||
# actually start the purchase locally..
|
||||
def _do_purchase(self, item: str) -> None:
|
||||
from ba.internal import show_ad
|
||||
if item == 'ad':
|
||||
import datetime
|
||||
# if ads are disabled until some time, error..
|
||||
@ -568,7 +567,7 @@ class GetCurrencyWindow(ba.Window):
|
||||
resource='getTicketsWindow.unavailableTemporarilyText'),
|
||||
color=(1, 0, 0))
|
||||
elif self._enable_ad_button:
|
||||
show_ad('tickets')
|
||||
_ba.app.ads.show_ad('tickets')
|
||||
else:
|
||||
_ba.purchase(item)
|
||||
|
||||
|
||||
@ -434,8 +434,9 @@ def show_offer() -> bool:
|
||||
# Space things out a bit so we don't hit the poor user with an ad and
|
||||
# then an in-game offer.
|
||||
has_been_long_enough_since_ad = True
|
||||
if (app.last_ad_completion_time is not None and
|
||||
(ba.time(ba.TimeType.REAL) - app.last_ad_completion_time < 30.0)):
|
||||
if (app.ads.last_ad_completion_time is not None and
|
||||
(ba.time(ba.TimeType.REAL) - app.ads.last_ad_completion_time <
|
||||
30.0)):
|
||||
has_been_long_enough_since_ad = False
|
||||
|
||||
if app.special_offer is not None and has_been_long_enough_since_ad:
|
||||
|
||||
@ -525,7 +525,6 @@ class TournamentEntryWindow(popup.PopupWindow):
|
||||
self._launch()
|
||||
|
||||
def _on_pay_with_ad_press(self) -> None:
|
||||
from ba.internal import show_ad_2
|
||||
|
||||
# If we're already entering, ignore.
|
||||
if self._entering:
|
||||
@ -547,8 +546,9 @@ class TournamentEntryWindow(popup.PopupWindow):
|
||||
cur_time = ba.time(ba.TimeType.REAL)
|
||||
if cur_time - self._last_ad_press_time > 5.0:
|
||||
self._last_ad_press_time = cur_time
|
||||
show_ad_2('tournament_entry',
|
||||
on_completion_call=ba.WeakCall(self._on_ad_complete))
|
||||
_ba.app.ads.show_ad_2('tournament_entry',
|
||||
on_completion_call=ba.WeakCall(
|
||||
self._on_ad_complete))
|
||||
|
||||
def _on_ad_complete(self, actually_showed: bool) -> None:
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
|
||||
<h4><em>last updated on 2020-10-16 for Ballistica version 1.5.27 build 20219</em></h4>
|
||||
<h4><em>last updated on 2020-10-17 for Ballistica version 1.5.27 build 20223</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>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user