Ë
    Š‰ŒjŸ*  ã                  ó    — d Z ddlmZ ddlZddlZddlmZ ddlmZm	Z	 dd„Z
dZdZd	Zd
ZdZddd„Zdd„Ze G d„ d«      «       Z G d„ d«      Zy)u‘  What the renderers say about themselves, kept for as long as they keep saying it.

Each renderer posts a small sample every few seconds (`core/web/client_beacon.js`). This holds the
newest one per renderer and forgets any that stops reporting. Deliberately small and deliberately
dumb: it stores what was said and computes nothing about the competition.

**It is a REGISTRY, not a controller.** Nothing here is ever read back by a renderer, and the server
has no way to answer a beacon with an instruction â€” the channel is one-way by construction, so it
cannot quietly become a route for telling the picture what to do. The broadcast presents; it never
decides, and that includes not being decided at.

Two properties worth stating because they are load-bearing:

* **A renderer is present because it is TALKING, not because it once connected.** Entries expire on
  age. A partner's box that was unplugged stops appearing, rather than being listed for ever as one
  of the three outputs that are fine.
* **Untrusted input.** The beacon is a public POST from a browser we do not control â€” under the
  World Sailing arrangement, from a browser on someone else's premises. Every field is clamped, the
  id is truncated, the roster is capped, and nothing posted is ever interpolated into a page without
  escaping. A monitor that can be filled with junk by anyone who can reach the producer is a monitor
  that gets ignored.
é    )ÚannotationsN)ÚCallable)Ú	dataclassÚfieldc                 óR   — t        j                  dt        j                  «       «      S )zŽThe venue operator's own date. Local, not UTC: the person reading the board asks "how many
    today", and their today ends at their midnight.z%Y-%m-%d)ÚtimeÚstrftimeÚ	localtime© ó    úD/srv/vr/releases/25f455cc4e75/v3/broadcast/live/renderer_registry.pyÚ
_local_dayr      s   € ô �=‰=˜¤T§^¡^Ó%5Ó6Ð6r   g      0@é@   i   )ÚroleÚlangÚworldÚraceÚaudioBackendÚworldRequested)ÚfpsÚ
longFramesÚframesÚwindowSÚupSÚwÚhÚ	feedAgeMsÚ	cursorSecÚfrontierSecÚpresentationLagSÚ
frameMaxMsÚ	workMaxMsÚhitch250Ú	hitch1000ÚraceAgeSÚ
raceFramesÚraceLongFramesÚraceMaxFrameMsÚraceHitch250ÚraceHitch1000ÚsceneGeometriesÚsceneTexturesÚ
sceneCallsÚsceneTrianglesÚjsHeapMBÚfeedHandoversÚfeedLastHandoverAgeMsc                ó‚   — t        | t        «      sy dj                  d„ | D «       «      d | j                  «       }|xs d S )NÚ c              3  óB   K  — | ]  }|j                  «       sŒ|–— Œ y ­w©N)Úisprintable)Ú.0Úchs     r   ú	<genexpr>z_clean_str.<locals>.<genexpr>M   s   è ø€ Ò9˜¨¯©Õ(8”"Ñ9ùs   ‚˜)Ú
isinstanceÚstrÚjoinÚstrip)ÚvalueÚlimitÚouts      r   Ú
_clean_strrA   J   s=   € Ü�eœSÔ!ØØ
�'‰'Ñ9˜uÔ9Ó
9¸&¸5Ð
A×
GÑ
GÓ
I€CØŠ;�$Ðr   c                óª   — t        | t        «      s| €y	 t        | «      }||k7  s|t        d«      t        d«      fv ry|S # t        t        f$ r Y yw xY w)zëA finite number, or `None`. Guards the boundary the same way the ingest does: a string, a null
    or a NaN must never reach an average or a threshold, where it would either raise or produce a
    plausible-looking figure from nothing.NÚinfz-inf)r:   ÚboolÚfloatÚ	TypeErrorÚ
ValueError)r>   r@   s     r   Ú
_clean_numrH   Q   sa   € ô �%œÔ % -ØðÜ�E‹lˆð ˆc‚z�SœU 5›\¬5°«=Ð9Ñ9ØØ€Jøô	 ”zÐ"ò Ùðús   •A  Á AÁAc                  óF   — e Zd ZU ded<   ded<    ee¬«      Zded<   d
d„Zy	)ÚRendererr;   ÚidrE   Úat_s)Údefault_factoryÚdictÚfieldsc                ó~   — | j                   t        t        d|| j                  z
  «      dz  «      dœ| j                  ¥S )Ng        iè  )rK   ÚageMs)rK   ÚintÚmaxrL   rO   )ÚselfÚnow_ss     r   Úto_wirezRenderer.to_wiref   s9   € ð —g‘g¬¬C°°U¸T¿Y¹YÑ5FÓ,GÈ$Ñ,NÓ(OÑ_ÐSW×S^ÑS^Ð_Ð_r   N)rU   rE   ÚreturnrN   )Ú__name__Ú
__module__Ú__qualname__Ú__annotations__r   rN   rO   rV   r   r   r   rJ   rJ   `   s!   … àƒGØ
ƒKÙ¨Ô.€FˆDÓ.ô`r   rJ   c                  óX   — e Zd ZdZeeddœ	 dd„Zddœdd„Zdd„Zdd„Z	ddœdd	„Z
dd
„Zy)ÚRendererRegistryz‡Newest sample per renderer, expiring on silence. Thread-safe: beacons arrive on handler
    threads while the monitor reads on another.N)Úexpiry_sÚmax_renderersÚday_fnc               óâ   — t        j                  «       | _        i | _        || _        || _        |xs t        | _        | j                  «       | _        t        «       | _
        i | _        d| _        y )NF)Ú	threadingÚLockÚ_lockÚ_rowsÚ	_expiry_sÚ_maxr   Ú_day_fnÚ_dayÚsetÚ_countedÚ_loadsÚ_counted_capped)rT   r^   r_   r`   s       r   Ú__init__zRendererRegistry.__init__q   sX   € ä—^‘^Ó%ˆŒ
Ø*,ˆŒ
Ø!ˆŒØ!ˆŒ	ð Ò+¤ˆŒØ—L‘L“NˆŒ	Ü"%£%ˆŒØ&(ˆŒØ$ˆÕr   )rU   c               óè  — t        |t        «      syt        |j                  d«      d«      }|sy|€t	        j
                  «       n|}i }t        D ]$  }t        |j                  |«      «      }|€Œ |||<   Œ& t        D ].  }t        |j                  |«      «      }|€Œ t        |d«      ||<   Œ0 t        |j                  d«      «      |d<   | j                  5  | j                  |«       || j                  vr,t        | j                  «      | j                  k\  r
	 ddd«       yt!        |||¬«      | j                  |<   | j#                  ||j                  d«      «       ddd«       y	# 1 sw Y   y	xY w)
zTake one beacon. Returns False for anything unusable, so the caller can answer 400 rather
        than silently accepting junk.FrK   é   Né   Ústale)rK   rL   rO   r   T)r:   rN   rA   Úgetr   Ú	monotonicÚ_STR_FIELDSÚ_NUM_FIELDSrH   ÚroundrD   rd   Ú_expirere   Úlenrg   rJ   Ú_count_load)rT   ÚpayloadrU   ÚridrO   Úkeyr>   s          r   ÚrecordzRendererRegistry.record„   sP  € ô ˜'¤4Ô(ØÜ˜Ÿ™ TÓ*¨BÓ/ˆÙØØ$) M”—‘Ô °uˆØˆÜò 	$ˆCÜ˜wŸ{™{¨3Ó/Ó0ˆEØÑ Ø#��s’ð	$ô ò 	.ˆCÜ˜wŸ{™{¨3Ó/Ó0ˆEØÑ Ü# E¨1›o��s’ð	.ô ˜wŸ{™{¨7Ó3Ó4ˆˆw‰Ø�Z‰Zñ 	7Ø�L‰L˜ÔØ˜$Ÿ*™*Ñ$¬¨T¯Z©Z«¸D¿I¹IÒ)EØ÷	7ð 	7ô '¨#°EÀ&ÔIˆD�J‰J�s‰OØ×Ñ˜S &§*¡*¨WÓ"5Ô6÷	7ð ÷	7ð ús   ÃAE(Ä#<E(Å(E1c                ó�  — | j                  «       }|| j                  k7  r'|t        «       i df\  | _        | _        | _        | _        || j                  v ryt        | j                  «      t        k\  rd| _        n| j                  j                  |«       |xs d}| j                  j                  |d«      dz   | j                  |<   y)uÝ  First sight of a renderer id is one page load, hence one ion session. Caller holds the lock.

        Only ACCEPTED beacons reach here, so a POST refused by `MAX_RENDERERS` is not counted. That is
        deliberate: this number is read by an operator, and a beacon we would not trust in the roster
        must not be able to move it. The cost is an undercount in a state the product does not have â€”
        the live roster is single digits against a ceiling of 64.
        FNTÚunknownr   é   )
rh   ri   rj   rk   rl   rm   ry   ÚMAX_COUNTED_IDSÚaddrs   )rT   r|   r   Údayr}   s        r   rz   zRendererRegistry._count_loadŸ   s¥   € ð �l‰l‹nˆØ�$—)‘)ÒØJMÌsËuÐVXÐZ_ÐJ_ÑGˆDŒI�t”} d¤k°4Ô3GØ�$—-‘-ÑØÜˆt�}‰}Ó¤Ò0Ø#'ˆDÕ à�M‰M×Ñ˜cÔ"ØÒ �yˆØŸ;™;Ÿ?™?¨3°Ó2°QÑ6ˆ�‰�CÒr   c                óD  — | j                   5  | j                  «       | j                  k7  r5| j                  «       t        «       i df\  | _        | _        | _        | _        | j                  t        | j
                  «      | j                  dœcddd«       S # 1 sw Y   yxY w)u   Page loads so far today, by world â€” the shape the ion bill is charged in.

        `approximate` says the id memory filled up, so a renderer that flapped may be counted twice.
        It is reported rather than hidden: an over-count that says so is usable, a silent one is not.
        F)r„   ÚbyWorldÚapproximateN)rd   rh   ri   rj   rk   rl   rm   rN   )rT   s    r   Úloads_todayzRendererRegistry.loads_today³   s~   € ð �Z‰Zñ 	9Ø�|‰|‹~ §¡Ò*ØNRÏlÉlËnÔ^aÓ^cÐegÐinÐNnÑK�”	˜4œ=¨$¬+°tÔ7KØŸ9™9´°d·k±kÓ1BØ#'×#7Ñ#7ñ9÷	9÷ 	9ò 	9ús   �A?BÂBc               ó6  — |€t        j                  «       n|}| j                  5  | j                  |«       | j                  j                  «       D �cg c]  }|j                  |«      ‘Œ }}d d d «       j                  d„ ¬«       |S c c}w # 1 sw Y   Œ#xY w)Nc                ó|   — t        | j                  d«      t        t        f«      r| j                  d«      nd| d   fS )Nr   g      ð¿rK   )r:   rs   rR   rE   )Úrs    r   ú<lambda>z+RendererRegistry.snapshot.<locals>.<lambda>Å   s3   € ´¸A¿E¹EÀ%»LÌ3ÔPUÈ,Ô1W §¡ u¤Ð]aØ!" 4¡ð!*€ r   )r}   )r   rt   rd   rx   re   ÚvaluesrV   Úsort)rT   rU   r‹   Úrowss       r   ÚsnapshotzRendererRegistry.snapshot¿   s‰   € Ø$) M”—‘Ô °uˆØ�Z‰Zñ 	CØ�L‰L˜ÔØ.2¯j©j×.?Ñ.?Ó.AÖB¨�A—I‘I˜eÕ$ÐBˆDÐB÷	Cð 	�	‰	ñ *ˆ	ô 	+àˆùò	 C÷	Cð 	Cús   ¥.BÁB
Á+BÂ
BÂBc                óÊ   — | j                   j                  «       D ��cg c]$  \  }}||j                  z
  | j                  kD  sŒ#|‘Œ& }}}|D ]  }| j                   |= Œ y c c}}w r5   )re   ÚitemsrL   rf   )rT   rU   r|   ÚrowÚdeads        r   rx   zRendererRegistry._expireÉ   sZ   € Ø$(§J¡J×$4Ñ$4Ó$6×\™˜˜S¸%À#Ç(Á(Ñ:JÈTÏ^É^Ó:[’Ð\ˆÑ\Øò 	 ˆCØ—
‘
˜3‘ñ	 ùó ]s   ž$AÁA)r^   rE   r_   rR   r`   zCallable[[], str] | None)r{   ÚobjectrU   úfloat | NonerW   rD   )r|   r;   r   ú
str | NonerW   ÚNone)rW   rN   )rU   r–   rW   z
list[dict])rU   rE   rW   r˜   )rX   rY   rZ   Ú__doc__ÚEXPIRY_SÚMAX_RENDERERSrn   r~   rz   rˆ   r�   rx   r   r   r   r]   r]   m   sC   „ ñ3ð -5È=Ø48ñ%Ø1ó%ð& @Dõ ó67ó(
9ð 15õ ô r   r]   )rW   r;   )é(   )r>   r•   r?   rR   rW   r—   )r>   r•   rW   r–   )r™   Ú
__future__r   rb   r   Úcollections.abcr   Údataclassesr   r   r   rš   r›   r‚   ru   rv   rA   rH   rJ   r]   r   r   r   ú<module>r       sv   ðñõ, #ã Û Ý $ß (ó7ð €ð €ð €ð R€ð€ôóð ÷	`ð 	`ó ð	`÷_ ò _ r   