Skip to main content

Module portal

Module portal 

Source
Expand description

Portal integration: ads, cloud saves, host pause, lifecycle mute and the host-facing gameplay notices, in one place.

A portal is a host a game runs inside (Playgama, Yandex Games, CrazyGames, and the rest). Six games in this family ship a byte-identical sim/platform.rs and an app/platform.rs that differs between two of them only in three log strings: ads with timeouts, cloud-save push and pull, the host’s pause, the lifecycle mute, and a time_scale gate copy-pasted into every frame loop that touches the clock. This module is that code, once.

§The three pieces

  • Portal is one backend: the adapter that speaks to a host. The Playgama one lives in portal::playgama behind the playgama feature and is off by default. NoPortal is the desktop backend, and it refuses every request with a typed reason instead of pretending.
  • PortalHost is the state machine the game owns. It asks the backend, absorbs what the backend reports, owns the ad deadline and the rate limit the host enforces, and answers one question per frame: does gameplay run, does the world tick, does sound play.
  • Suspend is that answer, and Suspend::apply is the only place Sim::time_scale is written.

§The no-fabrication rule

A host that cannot serve an ad or store a cloud save says so now, with a typed Refusal: nothing waits on a callback that will not come, and nothing reports a success it did not get. Every request is answered on the frame it was made, and everything that was taken ends in an AdOutcome or a cloud answer:

  • an ad that is not supported, or that another break is already in the way of, is refused before it starts (AdRefused);
  • an ad the host accepted and never ended is released by its deadline, and reported as AdOutcome::TimedOut: a break the host forgot cannot hold the player;
  • a rewarded ad pays only when the host reported the reward point, never on the strength of having asked (AdOutcome::pays);
  • an empty or non-text cloud payload is refused rather than stored, because the platform reads an empty value as a delete;
  • with no host at all, NoPortal still answers every request, with Refusal::NoPortal, rather than going quiet.

§What a game writes

ⓘ
// Once, at boot: the web after the SDK loaded, the desktop plain.
let mut portal = PortalHost::pull_first(Box::new(backend));
portal.loaded();

// Once per frame, in this order.
portal.update(dt);                                 // one clock for ad deadlines
let gate = portal.gate(GameState {
    playing: run.playing(),
    transitional: run.transitional(),
    modal: false,
    muted: save.data.muted,
});
gate.apply(&mut sim);                              // the one writer of time_scale
if gate.live() { schedule.run(&mut sim.world); }
portal.ready();                                    // first playable frame: game_ready
for report in portal.take_reports() { /* ad outcomes, cloud answers */ }

The gate replaces three hand-written lines that the games keep at every call site that touches the clock (live, suspended, time_scale), and Suspend::live replaces the fourth that decides whether the gameplay schedule runs at all.

repame-fx’s SimDriver::frame(step_fx, blocked, tick_sim, clear_edges) encodes the sequencing half of the same problem (step ungated fx first, then tick gameplay only when open, and roll the input edges when it is not). Suspend::suspended is exactly its blocked argument, so the two compose: the gate decides, the driver sequences. The driver has no opinion about run state, ads, modals, mute or lifecycle, and it does not own the clock, which is why the gate is here and not there.

Modules§

lifecycle
App lifecycle: foreground or background, as the single fact the gate reads.

Structs§

AdSchedule
The interstitial rate limit the host enforces, as the game’s bridge config declares it (minimumDelayBetweenInterstitial, initialInterstitialDelay).
GameState
What only the game knows, settled per frame.
NoPortal
Nothing is listening. Installed by PortalHost::none, which is also the default host.
PortalHost
The shell’s half of a portal: requests out, host facts in, one gate.
PortalStatus
What a portal can do, and the host facts it knows without being asked.
PortalView
What the portal knows, as a plain value so the truth table is testable without a backend.
Suspend
One frame’s answer to “does gameplay run, does the world tick, does sound play”.

Enums§

AdEnding
The host’s last word on an ad, before the shell turns it into an outcome.
AdKind
The two ads a portal serves.
AdOutcome
What an ad did, after the shell has matched the host’s events to the request that asked for it.
AdRefused
Why an ad the game asked for is not on its way.
Availability
Whether a portal can serve one feature, with the host’s own reason when it cannot.
Cause
Why the clock is stopped, outermost first.
CloudRefused
Why a cloud write did not go to the host.
HostNotice
A notice the host tracks gameplay by.
PortalEvent
One thing a portal learned from the host.
PortalReport
One thing the shell decided, for the game to drain once per frame.
PortalRequest
One request the shell hands a portal.
Refusal
Why a portal would not take a request, or cannot hand back what it has.

Constants§

INTERSTITIAL_TIMEOUT
How long the shell waits for an interstitial before releasing the game.
REWARDED_TIMEOUT
How long the shell waits for a rewarded video before releasing the game.

Traits§

Portal
One portal backend: the adapter between the shell and one host.