
    Ii~                        S r SSKrSSKrSSKrSSKJrJr  SSKJr  SSKJ	r	  SSK
Jr  SSKJr  SSKr\" \5      R                   r\S:X  a,  \R&                  R)                  S\" \R                   5      5        SS	KJr  \R0                  " \S
-  5        SSKJrJr  SSKJrJr  SSKJ r J!r!J"r"J#r#  SSK$J%r%  SSK&J'r'  SSK(J)r)  SSK*J+r+  \ " S S\ 5      5       r, " S S\%5      r- " S S5      r.S r/S r0S r1 " S S5      r2S r3S\4S jr4S \Rj                  S\4S! jr6S"S#S#S$.S%\S&\S'\S(\S)\7S*\7S\4S+ jjr8S \Rj                  SS4S, jr9S- r:\S:X  a  \:" 5         gg).aY  
Command-line interface for AI agents to simulate and watch games.

All operations are parameter-based with no interactive input required.

Usage examples:
    # Simulate a Light Turret game with 2 bots
    python -m server.cli simulate lightturret --bots 2

    # Simulate with specific bot names
    python -m server.cli simulate lightturret --bots Alice,Bob,Charlie

    # Set game options
    python -m server.cli simulate lightturret --bots 3 -o starting_power=15 -o max_rounds=30

    # Output as JSON for machine parsing
    python -m server.cli simulate lightturret --bots 2 --json

    # Test serialization (save/restore after each tick)
    python -m server.cli simulate threes --bots 2 --test-serialization

    # List available games
    python -m server.cli list-games

    # Show game options
    python -m server.cli show-options lightturret
    N)	dataclassfield)getpass)StringIO)Path)Any__main__)Localizationlocales)GameRegistryget_game_class)Game	BOT_NAMES)UserMenuItem
TrustLevelgenerate_uuid)Bot)KeybindState)Database)AuthManagerc                   .   \ rS rSr% SrSr\\S'   Sr\\S'   \	" \
S9r\\S'   \	" \S9r\\S	'   \	" \S9r\\S
'   \	" \S9r\\S'   Sr\\S'   Sr\\S'   Sr\\S'   \S\4S j5       r\S\4S j5       r\S\4S j5       rS\SS4S jrS5S\S\SS4S jjrS6S\S\S\S\SS4
S jjrS7S\S\SS4S  jjrS8S! jrS9S"\S#\S$\SS4S% jjrS8S& jr S'\S(\SS4S) jr!  S:S'\S(\S*\S-  S+\S-  SS4
S, jjr"S'\SS4S- jr#S;S.\S/\S0\SS4S1 jjr$S.\SS4S2 jr%S8S3 jr&S4r'g)<SpectatorUser;   zb
A spectator user that captures all game output.
Used for CLI simulation to watch games play out.
 	_usernameen_locale)default_factory_uuid	_messages_menus_soundsF
_json_mode_quietr   _tickreturnc                     U R                   $ N)r   selfs    )C:\Users\dbart\PlayPalace11\server\cli.pyusernameSpectatorUser.usernameL   s    ~~    c                     U R                   $ r)   )r   r*   s    r,   localeSpectatorUser.localeP   s    ||r/   c                     U R                   $ r)   )r    r*   s    r,   uuidSpectatorUser.uuidT   s    zzr/   textNc                     SU;   a  gU R                   R                  U5        U R                  (       d!  U R                  (       d  [	        SU 35        ggg)zLog a message.__spectator__N  )r!   appendr%   r$   print)r+   r6   s     r,   _logSpectatorUser._logX   sC     d"d#{{4??Btf+ $3{r/   bufferc                 &    U R                  U5        g r)   )r<   r+   r6   r>   s      r,   speakSpectatorUser.speaka   s    		$r/   namevolumepanpitchc                 T    U R                   R                  U R                  US.5        g )N)ticksound)r#   r:   r&   r+   rC   rD   rE   rF   s        r,   
play_soundSpectatorUser.play_soundd   s    TZZ$?@r/   loopingc                     g r)    )r+   rC   rM   s      r,   
play_musicSpectatorUser.play_musicg       r/   c                     g r)   rO   r*   s    r,   
stop_musicSpectatorUser.stop_musicj   rR   r/   loopintrooutroc                     g r)   rO   )r+   rV   rW   rX   s       r,   play_ambienceSpectatorUser.play_ambiencem   rR   r/   c                     g r)   rO   r*   s    r,   stop_ambienceSpectatorUser.stop_ambiencep   rR   r/   menu_iditemsc           	      H   / nU H  n[        US5      (       a  UR                  UR                  5        M1  [        U[        5      (       a,  UR                  UR                  S[        U5      5      5        Mr  UR                  [        U5      5        M     X@R                  U'   g )Nr6   )hasattrr:   r6   
isinstancedictgetstrr"   r+   r_   r`   kwargs
item_textsitems         r,   	show_menuSpectatorUser.show_menus   sz    
DtV$$!!$)),D$''!!$((63t9"=>!!#d),   *Gr/   positionselection_idc                 &    U R                  X5        g r)   rk   )r+   r_   r`   rm   rn   s        r,   update_menuSpectatorUser.update_menu   s     	w&r/   c                 <    U R                   R                  US 5        g r)   )r"   pop)r+   r_   s     r,   remove_menuSpectatorUser.remove_menu   s    &r/   input_idpromptdefault_valuec                     g r)   rO   )r+   rw   rx   ry   rh   s        r,   show_editboxSpectatorUser.show_editbox   rR   r/   c                     g r)   rO   )r+   rw   s     r,   remove_editboxSpectatorUser.remove_editbox   rR   r/   c                 8    U R                   R                  5         g r)   )r"   clearr*   s    r,   clear_uiSpectatorUser.clear_ui   s    r/   rO   miscd   r   r   )T)r'   N)r   r   )NN)r   )(__name__
__module____qualname____firstlineno____doc__r   rf   __annotations__r   r   r   r    listr!   rd   r"   r#   r$   boolr%   r&   intpropertyr-   r1   r4   r<   rA   rK   rP   rT   rZ   r]   rk   rq   ru   r{   r~   r   __static_attributes__rO   r/   r,   r   r   ;   s   
 IsGS}5E35D1It1.FD.$/GT/JFDE3N#      c    # s  As AC AC AC AZ^ As T T # c s D 
* 
*T 
* 
*   $#''' ' *	'
 Dj' 
''3 '4 'S # c ]a s t r/   r   c                      ^  \ rS rSrSrSS\S\4U 4S jjjrSS\S\SS	4S
 jjrSS\S\S\S\SS	4
S jjr	S\S\
SS	4S jr   SS\S\
S\S	-  S\S	-  S\SS	4S jjrSrU =r$ )CapturingBot   z
A bot that captures menus, speech, and sound events for CLI inspection.
Used instead of plain Bot when the CLI needs to report what players see.
rC   r1   c                 X   > [         TU ]  XS9  / U l        / U l        / U l        SU l        g )Nr1   r   )super__init__captured_menuscaptured_speechcaptured_soundsr&   )r+   rC   r1   	__class__s      r,   r   CapturingBot.__init__   s2    -*,*,+-
r/   r6   r>   r'   Nc                 :    U R                   R                  U5        g r)   )r   r:   r@   s      r,   rA   CapturingBot.speak   s    ##D)r/   rD   rE   rF   c                 V    U R                   R                  U R                  XX4S.5        g )N)rH   rI   rD   rE   rF   )r   r:   r&   rJ   s        r,   rK   CapturingBot.play_sound   s$    ##ZZ$]	
r/   r_   r`   c           	      |   / nU H  n[        US5      (       a  UR                  UR                  5        M1  [        U[        5      (       a,  UR                  UR                  S[        U5      5      5        Mr  UR                  [        U5      5        M     U R                  R                  U R                  XS.5        g )Nr6   )rH   r_   r`   )	rb   r:   r6   rc   rd   re   rf   r   r&   rg   s         r,   rk   CapturingBot.show_menu   s    
DtV$$!!$)),D$''!!$((63t9"=>!!#d),  	""ZZGI	
r/   rm   rn   play_selection_soundc                 &    U R                  X5        g r)   rp   )r+   r_   r`   rm   rn   r   s         r,   rq   CapturingBot.update_menu   s     	w&r/   )r&   r   r   r   )r   r   r   )NNF)r   r   r   r   r   rf   r   rA   r   rK   r   rk   r   rq   r   __classcell__)r   s   @r,   r   r      s    
S #  *# *s * *
s 
C 
C 
C 
Z^ 


 
T 
 
"  $#'%*'' ' *	'
 Dj' #' 
' 'r/   r   c                       \ rS rSrSr      SS\S\\   S\\\4   S\	S\	S\
S	\	S
\S\	4S jjrS\	4S jrS\
SS4S jrS\\\4   4S jrS\\\4   4S jrSrg)GameSimulator   z1Runs a game simulation with bots and a spectator.	game_type	bot_namesoptions	json_modequiet	max_tickstest_serializationr1   validate_keybindsc
                     Xl         X l        X0l        X@l        XPl        X`l        Xpl        Xl        Xl        S U l	        S U l
        S U l        0 U l        g r)   )r   r   r   r   r   r   r   r1   r   game	spectator
game_classcapturing_bots)
r+   r   r   r   r   r   r   r   r1   r   s
             r,   r   GameSimulator.__init__   sP     #""
""4!2!%	/3'+79r/   r'   c                 X   [        U R                  5      U l        U R                  (       d6  U R                  (       d$  [	        SU R                   S35        [	        S5        gU R                  R                  5       nU R                  R                  5       n[        U R                  5      U:  a.  U R                  (       d  [	        SU R                   SU S35        g[        U R                  5      U:  a.  U R                  (       d  [	        SU R                   SU S35        gU R                  5       U l	        [        U R                  S	5      (       Ga$  U R                  R                  5        GH  u  p4[        U R                  R                  U5      (       a  [        U R                  R                  U5      n[        U[        5      (       a  UR!                  5       S
;   nOA[        U["        5      (       a  [#        U5      nO [        U[$        5      (       a  [%        U5      n['        U R                  R                  X45        M  U R                  (       a  M  [	        SU SU R                   35        GM     [)        SU R*                  U R                  U R,                  S9U l        U R                  S   U R                  l        U R                   H  n[3        X`R*                  S9nXpR4                  U'   U R                  R7                  UR8                  USS9nU R                  R:                  R=                  U5        U R                  R?                  UR@                  U5        U R                  RC                  U5        M     U R                  R7                  U R.                  R8                  SSS9n	SU	l"        U R                  R:                  R=                  U	5        U R                  R?                  U	R@                  U R.                  5        U R                  RC                  U	5        g)z)Set up the game. Returns True on success.Error: Unknown game type ''z(Use 'list-games' to see available games.FError: z requires at least z playersz allows at most r   )true1yeszWarning: Unknown option 'z' for r8   )r   r   r$   r%   r   r   T)is_bot)#r   r   r   r   r;   get_min_playersget_max_playerslenr   r   rb   r   r`   getattrrc   r   lowerr   floatsetattrr   r1   r   r   hostr   r   create_playerr4   playersr:   attach_useridsetup_player_actionsis_spectator)
r+   min_playersmax_playerskeyvaluecurrentrC   bot_userplayerspectator_players
             r,   setupGameSimulator.setup   s    )8>>24>>2B!DE@A oo557oo557t~~,>>//B;-xXYt~~,>>//?}HUV OO%	 499i(("ll002
499,,c22 &dii&7&7=G!'400 %1E E#GS11 #E
#GU33 %eDII--s:5cU&@PQR 3  '%KK~~::	
 *		 NND#D=H(0%YY,,X]]D,NFII$$V,II!!&))X6II**62 #  9922NN 3 
 )-%		  !12		.114>>B		&&'78r/   rH   Nc                    U R                   (       a  U R                  (       d  g[        U R                   R                  5      nU R                   R                  n[        U R                   R
                  5      n[        U R                   R                  5      n[        U R                   R                  5      n[        U R                   R                  5      nU R                   R                  n U R                   R                  5       n	 U R                  R                  U	5      U l         X R                   l        X0R                   l        X@R                   l        XPR                   l        X`R                   l        XpR                   l	        XR                   l
        U R                   R                  5         g! [         a  n
[        SU SU
 35      eSn
A
ff = f! [         a  n
[        SU SU
 35      eSn
A
ff = f)z8Save game to JSON and restore it, testing serialization.NzSerialization failed at tick z: zDeserialization failed at tick )r   r   rd   _users_table	_keybinds_pending_actionsset_status_box_open_actions_menu_open
turn_indexto_json	ExceptionRuntimeError	from_jsonrebuild_runtime_state)r+   rH   saved_userssaved_tablesaved_keybindssaved_pending_actionssaved_status_box_opensaved_actions_menu_opensaved_turn_index	game_jsones              r,   _save_and_restoreGameSimulator._save_and_restore+  s   yy 499++,ii&&dii112 $TYY%?%? @ #DII$>$> ?"%dii&B&B"C99//	L		))+I
	N11)<DI
 '		&		,		%:		"%:		"'>		$  0				'')'  	L!>tfBqcJKK	L  	N!@bLMM	Ns0   ,F2  G 2
G<GG
G5G00G5c           	      4	   U R                   (       a  U R                  (       d  SS0$ U R                  (       df  U R                  (       dU  U R                  (       a  SOSn[        SU R                   R                  5        S[        U R                  5       SU S35        U R                   R                  5       nU(       a  U HZ  n[        U[        5      (       a  Uu  pE[        R                  " S	U40 UD6nO[        R                  " S	U5      n[        S
U 35        M\     SU Vs/ s H  n[        U5      PM     snS.$ U R                   R                  5         U R                   R!                  5         SnSn	U R                   R"                  (       a  XR$                  :  a  XR                  l        U R(                  R+                  5        H	  n
Xl        M     U R                   R-                  5         US-  nU R                  (       a   U R/                  U5        U R                   R"                  (       a  XR$                  :  a  M  XR$                  :  nU(       a*  U R                  (       d  [        SU R$                   S35        U R                  R2                   Vs/ s H  nSU;  d  M  UPM     nnU R                  R4                  R                  S/ 5       Vs/ s H  nSU;  d  M  UPM     nnU R6                  U R                   R                  5       UU R                   R8                  UU R:                  UUS.nU R                  (       a  SUS'   U	(       a  U	US'   OSUS'   0 n0 nU R(                  R=                  5        HH  u  nn
U
R>                  (       a  U
R>                  UU'   U
R@                  (       d  M9  U
R@                  UU'   MJ     UUS'   UUS'   [        U R                  RB                  5      n[E        S UR+                  5        5       5      nUUUU-   S.US'   U RF                  (       a  U RI                  5       US'   U$ s  snf ! [0         a6  n[        U5      n	U R                  (       d  [        SU	 35         SnAGMC  SnAff = fs  snf s  snf ) z7Run the simulation to completion. Returns results dict.errorzGame not set upz [testing serialization]r   z
===  (z bots)z ===
r   r   zprestart validation failed)r   messagesr   N   z
Error: z
Warning: Game timed out after z ticksr8   	game_over)r   	game_nameticksrounds	timed_outr1   r   
final_menuTserialization_testedserialization_errorserialization_passedplayer_menusplayer_soundsc              3   8   #    U  H  n[        U5      v   M     g 7fr)   )r   ).0ss     r,   	<genexpr>$GameSimulator.run.<locals>.<genexpr>  s     !I2HQ#a&&2Hs   )r   r   totalsound_eventskeybind_validation)%r   r   r   r   r   r;   get_namer   r   prestart_validaterc   tupler
   re   rf   setup_keybindson_startgame_activer   r&   r   valueson_tickr   r   r!   r"   r   roundr1   r`   r   r   r#   sumr   _validate_keybinds)r+   mode_strerrorserrr   rh   msgr   rH   r   botr   mfiltered_messagesrj   filtered_menuresultsr  r  rC   spectator_sound_counttotal_player_soundss                         r,   runGameSimulator.runQ  s   yy.//~~djj595L5L1RTHF499--/03t~~3F2GvhZW]^_ ,,.c5))"%KC&**4??C&**45Cuo&  :X^G_X^STAX^G_`` 			  "		 "ii##~~(=#'NN **113 	 4 IIAID &&**40 ii##~~(=( NN*	T^^4T^^4DFKL )-(@(@](@1O[\D\Q(@] --11+rB
Bd* B 	 
 ++-iioo"kk)'	#
 "".2G*+"1D-.26./ /1/1,,224ID#!!%(%7%7T""""&)&9&9d#	 5
 #/#0  !$DNN$:$: ;!!I-2F2F2H!II.**-@@#
 !!,0,C,C,EG()i H`, $ *-a&'>>	*=)>?@	 ^
s6   $QQ 
R*R
R(R
R*RRc                    U R                   (       d  SS0$ / n/ n[        5       nU R                   R                   Hq  nUR                  (       a  M  U R                   R                  R                  UR                  / 5       H'  nUR                   H  nUR                  U5        M     M)     Ms     U R                   R                  R                  5        H  u  pxU H  n	UU	R                  U	R                  U	R                  R                  S.n
UR                  U
5        U	R                   H/  nX;  d  M
  UR                  SU SU	R                   SU S35        M1     M     M     / nSnU R                   R                   H  nUR                  (       a  M  Un  O   U(       a  U R                   R                  R                  5        H  u  pxS	US
.nUR                  S5      (       a  SUS'   UR!                  S5      US'   UR                  S5      (       a  SUS'   UR!                  S5      US'   UR                  S5      (       a  SUS'   UR!                  S5      US'    U R                   R#                  X5        UR                  USS.5        M     UUU[)        U5      S:H  S.$ ! [$         aA  nUR                  US['        U5      S.5        UR                  SU SU 35         SnAGM$  SnAff = f)z?Validate keybinds: check action IDs exist and fire smoke tests.r   zno game)r   rC   actionsstatez	Keybind 'z' (z): action 'z' not found in any action setNkeybind)typer   zctrl+Tcontrolr   zshift+shiftzalt+altok)r   status)r   r,  r   z' smoke test crashed: r   )keybindsissuessmoke_testspassed)r   r   r   r   player_action_setsre   r   _orderaddr   r`   rC   r$  r%  r:   
startswithremoveprefixhandle_eventr   rf   r   )r+   r.  keybind_summaryall_action_idsr   
action_setaidr   r-  r&  entry	action_idsmoke_resultstest_playerpeventr   s                    r,   r   GameSimulator._validate_keybinds  s   yyY''&( $'5ii''F"""ii::>>vyy"M
%,,C"&&s+ - N ( "YY00668MC##LL&$]]//	  &&u-!(I 6'uC~ >''0k1NP "1 $ 9$ %'""A>>> #
 !%!4!4!:!:!<!*37>>'**'+E)$#&#3#3G#<E%L>>(++%)E'N#&#3#3H#=E%L>>&))#'E%L#&#3#3F#;E%LNII**;>!(()EF "=* ((&kQ&	
 	
	 ! N!((RUVWRX)YZMMIcU2H"LMMNs   /J##
K.-5K))K.)r   r   r   r   r   r   r1   r   r   r   r   r   r   )FF逖 Fr   F)r   r   r   r   r   rf   r   rd   r   r   r   r   r   r   r!  r  r   rO   r/   r,   r   r      s    ;  !#("':: 9: c3h	:
 : : : !: :  :6It IV$*c $*d $*LgT#s(^ gRE
DcN E
r/   r   c           	         [         R                  " 5       nU R                  (       a  / nU H`  nUR                  UR	                  5       UR                  5       UR                  5       UR                  5       UR                  5       S.5        Mb     [        [        R                  " USS95        g
[        S5        U H  n[        SUR	                  5        35        [        SUR                  5        35        [        SUR                  5        35        [        SUR                  5        S	UR                  5        35        [        5         M     g
)zList all available games.)r'  rC   categoryr   r      indentzAvailable games:
r9   z
    Name: z    Category: z    Players: -N)r   get_alljsonr:   get_typer  get_categoryr   r   r;   dumps)argsgamesoutputr   s       r,   cmd_list_gamesrQ    s     "EyyJMM&//1&//1 * 7 7 9#-#=#=#?#-#=#=#?   	djj*+"#JBz**,-./Jz224567N:#:#:#<"=>?M*"<"<">!?qA[A[A]@^_`G  r/   c                 V   [        U R                  5      nU(       d/  [        SU R                   S35        [        R                  " S5        U" 5       n[        US5      (       dL  U R                  (       a"  [        [        R                  " S/ 05      5        g[        U R                   S35        gUR                  n/ nSSK	J
n  UR                   H  n[        X65      nU[        U5      R                  US	.nU" X65      n	U	(       a`  [        U	S
5      (       a  U	R                  US'   [        U	S5      (       a  U	R                   US'   [        U	S5      (       a  U	R"                  US'   UR%                  U5        M     U R                  (       a,  [        [        R                  " U R                  US.SS95        g[        SU R                   S35        U HS  n
[        SU
S    SU
S    S35        [        SU
S    35        SU
;   a  [        SU
S    SU
S    35        [        5         MU     g)z!Show options for a specific game.r   r   r   r   z has no configurable options.Nr   )get_option_meta)rC   r'  defaultmin_valminmax_valmaxlabel)r   r   rE  rF  zOptions for z:
r9   rC   r   r'  )z    Default: rT  z    Range: z - )r   r   r;   sysexitrb   rJ  rM  r   server.game_utils.optionsrS  __dataclass_fields__r   r'  r   rU  rW  rY  r:   )rN  r   r   options_objoptions_listrS  
field_namecurrent_valueoption_datametaopts              r,   cmd_show_optionsrf    s   /J*4>>*:!<= <D4##99$**i_-. 	 T^^$$ABC,,KL :!66
8 '00$
 {7tY''%)\\E"tY''%)\\E"tW%%'+zzG$K(' 7* yydjjt~~,OXYZ[T^^,C01CBs6{m2c&k]!45M#i.!123|CJ<s3u:,?@G  r/   c                 Z   U R                   (       a  [        5       n[        R                  n[	        X!5      [        l         [        U 5        U[        l        UR                  5       nU(       a"  [        R                  " U5        [        S5        gg[        U 5        g! U[        l        f = f)zSimulate a game with bots.z(Output copied to clipboard.)N)
clipr   r[  stdout
_TeeWriter_run_simulategetvalue	pyperclipcopyr;   )rN  captureoriginal_stdoutrP  s       r,   cmd_simulaterq  U  sy    yy***9
	)$(CJ!!#NN6"12  	d )CJs   B B*c                   *    \ rS rSrSrS rS rS rSrg)rj  ig  z$Write to two streams simultaneously.c                     Xl         g r)   streams)r+   ru  s     r,   r   _TeeWriter.__init__j  s    r/   c                 L    U R                    H  nUR                  U5        M     g r)   )ru  write)r+   r6   r  s      r,   rx  _TeeWriter.writem  s    AGGDM r/   c                 J    U R                    H  nUR                  5         M     g r)   )ru  flush)r+   r  s     r,   r{  _TeeWriter.flushq  s    AGGI r/   rt  N)	r   r   r   r   r   r   rx  r{  r   rO   r/   r,   rj  rj  g  s    .r/   rj  c                    U R                   R                  5       (       a  [        U R                   5      n[        SU nO8U R                   R	                  S5       Vs/ s H  o3R                  5       PM     nn0 nU R                  (       aO  U R                   H?  nSU;   d  M  UR	                  SS5      u  pgUR                  5       XFR                  5       '   MA     [        U R                  UUU R                  U R                  U R                  U R                  U R                  U R                  S9	nUR                  5       (       d  [         R"                  " S5        UR%                  5       n	U	R'                  S5      (       aE  U R                  (       a  [)        [        R*                  " U	SS95        [         R"                  " S5        U R                  (       a  [)        [        R*                  " U	SS95        gU R                  (       GdE  [)        S	U	S
    SU	S    S35        U	R'                  S5      S:w  a  [)        SU	S    35        U	R'                  S5      (       aT  [)        S5        U	S    H@  n
U
(       d  M  U
R-                  5       R/                  S5      (       a  M2  [)        SU
 35        MB     U	R'                  S0 5      nU(       a  [)        S5        UR1                  5        H  u  p<0 nU H  nUS   XS   '   M     UR1                  5        HX  u  nn[)        SU SU SSR3                  USS 5       35        [5        U5      S:  d  M=  [)        S[5        U5      S-
   S35        MZ     M     U	R'                  S0 5      nUR'                  S S!5      nUS!:X  a  [)        S"5        O5[)        S#U S$UR'                  S%S!5       S&UR'                  S'S!5       S(35        U	R'                  S)5      nU(       a[  US*   (       a+  [)        S+[5        US,   5       S-[5        US.   5       S/35        g[)        S05        US1    H  n[)        S2U 35        M     gggs  snf )3zCore simulate logic.N,=r   )	r   r   r   r   r   r   r   r1   r   r   rE  rF  z
=== Finished: r   z ticks, r   z rounds ===r1   r   zLocale: r   z
Final standings:leaver9   r  z
Player menus captured:r`   r_   z [z]: z,    z    ... and z more itemsr	  r  r   z1
Warning: 0 sound events fired. Silence is a bug.z
Sound events: z total (r   z broadcast, r   z per-player)r
  r0  z
Keybind validation: passed (r-  z keybinds, r/  z smoke tests)z
Keybind validation: FAILEDr.  z  - )botsisdigitr   r   splitstripoptionr   r   rJ  r   r   r   r1   r   r   r[  r\  r!  re   r;   rM  r   r4  r`   joinr   )rN  num_botsr   rC   r   re  r   r   	simulatorr  liner  menuslatestr  r_   r`   
sound_infototal_soundskbissues                        r,   rk  rk  v  s    yytyy>ix(	.2iiooc.BC.BdZZ\.B	C G{{;;Ccz YYsA.
',{{}		$  ..))jj..22{{00
I ??mmoG{{799$**WQ/0yydjj+,ZZZ !1 2(78;L:M[YZ;;x D(HWX./01;;|$$&'-4

 7 7 @ @Btf+& .
 {{>26,-+113/1A+,W:FY<( &,llnNGUBtfBwis499U2AY3G2HIJ5zA~SZ!^,<KHI '5  4 [[4
!~~gq11FG$\N(:>>+WX;Y:ZZfgqguguv  BC  hD  gE  EQ  R  S [[-.(|6s2j>7J6K;WZ[]^k[lWmVnn{|}46\ED.) * E G Ds   Qr'   c                       [        S5      n [        S5      nX:w  a  [        S5        M)  U (       d  [        S5        M=  U $ )z*Interactively prompt for a password twice.zNew owner password: zConfirm password: z"Passwords do not match. Try again.Password cannot be empty.)r   r;   )pwconfirms     r,   _prompt_for_passwordr    s@    
+,./=67-.	r/   rN  c                 P   U R                   b  U R                   $ U R                  (       a3  [        U R                  5      nUR                  SS9R	                  S5      $ U R
                  (       a/  [        R                  R                  5       nUR	                  S5      $ [        5       $ )z$Resolve password from CLI arguments.zutf-8)encodingz
)
passwordpassword_filer   	read_textrstrippassword_stdinr[  stdinreadr  )rN  pathdatas      r,   _resolve_bootstrap_passwordr    s|    }} }}D&&'~~w~/66v>>yy~~{{6""!!r/   r   F)r1   forcer   db_pathr-   r  r1   r  r   c                    U(       d  [        S5      e[        U 5      nUR                  5          [        U5      nUR	                  5       nUR                  U5      n	US:  a  U(       d  [        S5      eUR                  U5      (       a[  U(       d  [        SU S35      eUR                  X5        UR                  U[        R                  5        UR                  U5        Sn
O#UR                  UU	U[        R                  SS9  S	n
U(       d  [        U
 S
U SU  S35        U
UR                  5         $ ! UR                  5         f = f)z
Create or update the initial server owner account.

Returns a short status string describing the action performed.
Raises RuntimeError if the operation is not permitted.
r  r   zdDatabase already contains users. Use --force if you intend to replace or update an existing account.zUser 'z<' already exists. Use --force to elevate/update the account.UpdatedT)r-   password_hashr1   trust_levelapprovedCreatedz server owner 'z' in .)r   r   connectr   get_user_counthash_passworduser_existsupdate_user_passwordupdate_user_trust_levelr   SERVER_OWNERapprove_usercreate_userr;   close)r  r-   r  r1   r  r   databaseauth
user_countr  actions              r,   bootstrap_ownerr    s=    677 H!8$,,.
**84>%v  ))"XJ&bc  ))(B,,Xz7N7NO!!(+F  !+&33 !  FVHOH:U7)1EFs   C4D4 4Ec           	          [        U 5      n [        U R                  U R                  WU R                  U R                  U R                  S9  g! [         a;  n[        SU 3[        R                  S9  [        R
                  " S5         SnANSnAff = f! [         a;  n[        SU 3[        R                  S9  [        R
                  " S5         SnAgSnAff = f)z'Handle the bootstrap-owner CLI command.zError reading password: )filer   N)r  r-   r  r1   r  r   r   )r  r   r;   r[  stderrr\  r  r  r-   r1   r  r   r   )rN  r  excs      r,   cmd_bootstrap_ownerr    s    .t4
LL]];;****	
  (.SZZ@  uoCJJ/s/   A A B 
B1BB
C!1CCc                     [         R                  " S[         R                  [        S9n U R	                  SSS9nUR                  SSS9nUR                  S	S
SS9  UR                  SSS9nUR                  SSS9  UR                  S	S
SS9  UR                  SSS9nUR                  SSS9  UR                  SSSSS9  UR                  SSSSS9  UR                  S	S
SS9  UR                  SSS
SS9  UR                  S[        S S!S"9  UR                  S#S$S
S%S9  UR                  S&S'S(S)S*9  UR                  S+S,S
S-S9  UR                  S.S
S/S9  UR                  S0S1S9nUR                  S2SS3S9  UR                  S4S5S9  UR                  S6S7S9  UR                  S8S
S9S9  UR                  S:S;S<S*9  UR                  S&S(S=S*9  UR                  S>S
S?S9  UR                  SS
S@S9  U R                  5       nUR                  S:X  a  [        U5        g UR                  S:X  a  [        U5        g UR                  S:X  a  [        U5        g UR                  S0:X  a  [        U5        g U R                  5         [        R                   " SA5        g )BNzPlayPalace CLI for AI agents)descriptionformatter_classepilogcommandzAvailable commands)desthelpz
list-gameszList available games)r  z--json
store_truezOutput as JSON)r  r  zshow-optionszShow options for a gamer   z"Game type (e.g., lightturret, pig)simulatezSimulate a game with botsz--botsz-bTzCNumber of bots (e.g., 3) or comma-separated names (e.g., Alice,Bob))requiredr  z--optionz-or:   z,Set game option (e.g., -o starting_power=15)z--quietz-qzSuppress game outputz--max-ticksrB  z0Maximum ticks before timeout (default: 10000000))r'  rT  r  z--test-serializationz-szASave and restore game state after each tick to test serializationz--localez-lr   zHLocale for output (e.g., fr, es). Non-English exposes hardcoded strings.)rT  r  z--validate-keybindsz-kz:Validate keybind action IDs and run smoke tests after gamez--clipz*Copy all output to clipboard on completionzbootstrap-ownerz1Create or update the initial server owner accountz
--usernamez%Username for the server owner accountz
--passwordz^Password for the server owner (use with caution; prefer --password-file or interactive prompt)z--password-filez.File containing the password (first line used)z--password-stdinz0Read password from stdin (useful for automation)z	--db-pathzplaypalace.dbzJPath to the server database (default resolves to var/server/playpalace.db)z3Locale to assign to the owner account (default: en)z--forcezEAllow operation when users already exist or when replacing an accountz'Suppress success output (useful for CI)r   )argparseArgumentParserRawDescriptionHelpFormatterr   add_subparsers
add_parseradd_argumentr   
parse_argsr  rQ  rf  rq  r  
print_helpr[  r\  )parser
subparserslist_parseroptions_parser
sim_parserbootstrap_parserrN  s          r,   mainr  5  se   $$2 <<F &&I<P&QJ '';Q'RKXlAQR  **>@Y*ZN2VWDTU &&z8S&TJK.RSR	   ;	   H\@PQItLG]^?	   P	   W	   I	   9   ",,@ -  !!4 " 
 !!m "  !!= "  !!? " 
 !!Y " 
 !!B " 
 !!T " 
 !!6 "  D|||#t		'		#T	*	*D!r/   );r   r  rJ  r[  dataclassesr   r   r   ior   pathlibr   typingr   rm  __file__parent_MODULE_DIRr   r  insertrf   server.messages.localizationr
   initserver.games.registryr   r   server.games.baser   r   server.core.users.baser   r   r   r   server.core.users.botr   server.core.ui.keybindsr   server.persistence.databaser   server.auth.authr   r   r   r   rQ  rf  rq  rj  rk  r  	Namespacer  r   r  r  r  rO   r/   r,   <module>r     s  8   
 (      8n##zHHOOAs;--./ 6   +	) * > - L L % 0 0 ( VD V Vr*'3 *'Z}
 }
@
65p$ R*jc 
"h&8&8 
"S 
"$ 66 6 	6
 6 6 6 	6rh00 T ,yx zF r/   