mousefox.app.serverframe
Home of ServerFrame
.
1"""Home of `ServerFrame`.""" 2 3from typing import Optional 4from loguru import logger 5import asyncio 6import kvex as kx 7import pgnet 8 9 10def _info_text(subtheme) -> str: 11 warn = "Please consider your network security before running a server." 12 lh = subtheme.fg2.markup("localhost") 13 return ( 14 "[size=20dp][b][u]Hosting a server[/u][/b][/size]" 15 "\n\n" 16 f"[b][i]{subtheme.fg2.markup(warn)}[/i][/b]" 17 "\n\n" 18 "For best performance, run the server in a separate instance." 19 " You may be required to configure port forwarding on your network device" 20 " before remote clients can discover the server." 21 "\n\n\n" 22 "The running server is available at address" 23 f" [font=RobotoMono-Regular]{lh}[/font] and can" 24 " be connected to normally. To manage a server, connect as admin and use" 25 " the admin panel." 26 ) 27 28 29class ServerFrame(kx.XThemed, kx.XAnchor): 30 """Widget for launching server.""" 31 32 _conpath = "server" 33 34 def __init__(self, app_config): 35 """Initialize the class with an `AppConfig`.""" 36 self.info_label = kx.XLabel( 37 halign="left", 38 valign="top", 39 padding=(10, 10), 40 ) 41 super().__init__() 42 self._running_server: Optional[pgnet.Server] = None 43 self._game_class = app_config.game_class 44 self._make_widgets(app_config) 45 self.app.controller.set_active_callback(self._conpath, self.set_focus) 46 self.app.controller.bind(f"{self._conpath}.focus", self.set_focus) 47 self.app.controller.bind(f"{self._conpath}.shutdown", self._shutdown_server) 48 49 def on_subtheme(self, subtheme): 50 """Highlight text.""" 51 self.info_label.text = _info_text(subtheme) 52 53 def _make_widgets(self, app_config): 54 # Left frame 55 self.info_label.set_size(hy=5) 56 return_btn = kx.XButton( 57 text="Return to client", 58 on_release=self._return_to_client, 59 ) 60 return_btn.set_size(x="250dp", y="40dp") 61 left_frame = kx.XBox(orientation="vertical") 62 left_frame.add_widgets( 63 self.info_label, 64 kx.wrap(return_btn), 65 ) 66 # Right frame 67 config_panel_widgets = { 68 "admin_password": kx.XInputPanelWidget( 69 "Admin password:", 70 default=pgnet.util.DEFAULT_ADMIN_PASSWORD, 71 ), 72 "save_file": kx.XInputPanelWidget("Save file:", 'str'), 73 "port": kx.XInputPanelWidget("Port:", 'int', pgnet.util.DEFAULT_PORT), 74 "open_registration": kx.XInputPanelWidget( 75 "Open user registration:", 76 widget="bool", 77 default=True, 78 ), 79 "require_user_password": kx.XInputPanelWidget( 80 "Require user password:", 81 widget="bool", 82 default=False, 83 ), 84 } 85 with self.app.subtheme_context("secondary"): 86 self._config_panel = kx.XInputPanel( 87 config_panel_widgets, 88 invoke_text="Launch server", 89 ) 90 self._config_panel.bind(on_invoke=self._on_config_invoke) 91 config_frame = kx.pwrap(self._config_panel) 92 with self.app.subtheme_context("accent"): 93 self.pubkey_label = kx.XInput( 94 text="No server running.", 95 readonly=True, 96 disabled=True, 97 select_on_focus=True, 98 halign="center", 99 subtheme_name="secondary", 100 ) 101 self.pubkey_label.set_size(y="40dp") 102 pubkey_label_hint = kx.XLabel(text="Server pubkey:") 103 pubkey_label_hint.set_size(y="40dp") 104 self.shutdown_btn = kx.XButton( 105 text="Shutdown server", 106 on_release=self._shutdown_server, 107 disabled=True, 108 ) 109 self.shutdown_btn.set_size(x="250dp", y="40dp") 110 running_frame = kx.XBox(orientation="vertical") 111 running_frame.add_widgets( 112 pubkey_label_hint, 113 kx.pwrap(self.pubkey_label), 114 kx.wrap(self.shutdown_btn), 115 ) 116 running_frame = kx.fpwrap(running_frame) 117 running_frame.set_size(y="200sp") 118 right_frame = kx.XBox(orientation="vertical") 119 right_frame.add_widgets(config_frame, running_frame) 120 right_frame = kx.fpwrap(right_frame) 121 main_frame = kx.XBox() 122 main_frame.add_widgets(left_frame, right_frame) 123 self.add_widget(kx.pwrap(kx.fpwrap(main_frame))) 124 125 def _on_config_invoke(self, w, values): 126 self.set_focus() 127 server_kwargs = dict( 128 listen_globally=True, 129 admin_password=values["admin_password"], 130 save_file=values["save_file"] or None, 131 port=values["port"], 132 registration_enabled=values["open_registration"], 133 require_user_password=values["require_user_password"], 134 ) 135 asyncio.create_task(self._run_server(server_kwargs)) 136 137 async def _run_server(self, server_kwargs: dict): 138 if self._running_server is not None: 139 self.app.set_feedback("Server already running.", pgnet.Status.UNEXPECTED) 140 return 141 try: 142 server = pgnet.Server(self._game_class, **server_kwargs) 143 except Exception as e: 144 logger.warning(e) 145 self.app.set_feedback(str(e), pgnet.Status.UNEXPECTED) 146 return 147 self._running_server = server 148 self.shutdown_btn.disabled = False 149 self.pubkey_label.text = server.pubkey 150 self.pubkey_label.disabled = False 151 try: 152 logger.debug(f"Running {server=}") 153 exit_code: int = await server.async_run(on_start=self._on_server_start) 154 logger.debug(f"Shutdown {server=}") 155 shutdown_message = f"Server shutdown with exit code {exit_code}" 156 status = pgnet.Status.UNEXPECTED 157 except Exception as e: 158 logger.warning(e) 159 shutdown_message = "Server failed. Perhaps one is already running?" 160 status = pgnet.Status.BAD 161 self.shutdown_btn.disabled = True 162 self.pubkey_label.text = "No server running." 163 self.pubkey_label.disabled = True 164 self._running_server = None 165 self.app.set_feedback(shutdown_message, status) 166 167 def _on_server_start(self, *args): 168 self.app.set_feedback("Server running.") 169 170 def _shutdown_server(self, *args): 171 if self._running_server: 172 self._running_server.shutdown() 173 174 def _return_to_client(self, *args): 175 self.app.controller.invoke("show_client") 176 177 def set_focus(self, *args): 178 """Focus input widgets.""" 179 self._config_panel.set_focus("admin_password")
30class ServerFrame(kx.XThemed, kx.XAnchor): 31 """Widget for launching server.""" 32 33 _conpath = "server" 34 35 def __init__(self, app_config): 36 """Initialize the class with an `AppConfig`.""" 37 self.info_label = kx.XLabel( 38 halign="left", 39 valign="top", 40 padding=(10, 10), 41 ) 42 super().__init__() 43 self._running_server: Optional[pgnet.Server] = None 44 self._game_class = app_config.game_class 45 self._make_widgets(app_config) 46 self.app.controller.set_active_callback(self._conpath, self.set_focus) 47 self.app.controller.bind(f"{self._conpath}.focus", self.set_focus) 48 self.app.controller.bind(f"{self._conpath}.shutdown", self._shutdown_server) 49 50 def on_subtheme(self, subtheme): 51 """Highlight text.""" 52 self.info_label.text = _info_text(subtheme) 53 54 def _make_widgets(self, app_config): 55 # Left frame 56 self.info_label.set_size(hy=5) 57 return_btn = kx.XButton( 58 text="Return to client", 59 on_release=self._return_to_client, 60 ) 61 return_btn.set_size(x="250dp", y="40dp") 62 left_frame = kx.XBox(orientation="vertical") 63 left_frame.add_widgets( 64 self.info_label, 65 kx.wrap(return_btn), 66 ) 67 # Right frame 68 config_panel_widgets = { 69 "admin_password": kx.XInputPanelWidget( 70 "Admin password:", 71 default=pgnet.util.DEFAULT_ADMIN_PASSWORD, 72 ), 73 "save_file": kx.XInputPanelWidget("Save file:", 'str'), 74 "port": kx.XInputPanelWidget("Port:", 'int', pgnet.util.DEFAULT_PORT), 75 "open_registration": kx.XInputPanelWidget( 76 "Open user registration:", 77 widget="bool", 78 default=True, 79 ), 80 "require_user_password": kx.XInputPanelWidget( 81 "Require user password:", 82 widget="bool", 83 default=False, 84 ), 85 } 86 with self.app.subtheme_context("secondary"): 87 self._config_panel = kx.XInputPanel( 88 config_panel_widgets, 89 invoke_text="Launch server", 90 ) 91 self._config_panel.bind(on_invoke=self._on_config_invoke) 92 config_frame = kx.pwrap(self._config_panel) 93 with self.app.subtheme_context("accent"): 94 self.pubkey_label = kx.XInput( 95 text="No server running.", 96 readonly=True, 97 disabled=True, 98 select_on_focus=True, 99 halign="center", 100 subtheme_name="secondary", 101 ) 102 self.pubkey_label.set_size(y="40dp") 103 pubkey_label_hint = kx.XLabel(text="Server pubkey:") 104 pubkey_label_hint.set_size(y="40dp") 105 self.shutdown_btn = kx.XButton( 106 text="Shutdown server", 107 on_release=self._shutdown_server, 108 disabled=True, 109 ) 110 self.shutdown_btn.set_size(x="250dp", y="40dp") 111 running_frame = kx.XBox(orientation="vertical") 112 running_frame.add_widgets( 113 pubkey_label_hint, 114 kx.pwrap(self.pubkey_label), 115 kx.wrap(self.shutdown_btn), 116 ) 117 running_frame = kx.fpwrap(running_frame) 118 running_frame.set_size(y="200sp") 119 right_frame = kx.XBox(orientation="vertical") 120 right_frame.add_widgets(config_frame, running_frame) 121 right_frame = kx.fpwrap(right_frame) 122 main_frame = kx.XBox() 123 main_frame.add_widgets(left_frame, right_frame) 124 self.add_widget(kx.pwrap(kx.fpwrap(main_frame))) 125 126 def _on_config_invoke(self, w, values): 127 self.set_focus() 128 server_kwargs = dict( 129 listen_globally=True, 130 admin_password=values["admin_password"], 131 save_file=values["save_file"] or None, 132 port=values["port"], 133 registration_enabled=values["open_registration"], 134 require_user_password=values["require_user_password"], 135 ) 136 asyncio.create_task(self._run_server(server_kwargs)) 137 138 async def _run_server(self, server_kwargs: dict): 139 if self._running_server is not None: 140 self.app.set_feedback("Server already running.", pgnet.Status.UNEXPECTED) 141 return 142 try: 143 server = pgnet.Server(self._game_class, **server_kwargs) 144 except Exception as e: 145 logger.warning(e) 146 self.app.set_feedback(str(e), pgnet.Status.UNEXPECTED) 147 return 148 self._running_server = server 149 self.shutdown_btn.disabled = False 150 self.pubkey_label.text = server.pubkey 151 self.pubkey_label.disabled = False 152 try: 153 logger.debug(f"Running {server=}") 154 exit_code: int = await server.async_run(on_start=self._on_server_start) 155 logger.debug(f"Shutdown {server=}") 156 shutdown_message = f"Server shutdown with exit code {exit_code}" 157 status = pgnet.Status.UNEXPECTED 158 except Exception as e: 159 logger.warning(e) 160 shutdown_message = "Server failed. Perhaps one is already running?" 161 status = pgnet.Status.BAD 162 self.shutdown_btn.disabled = True 163 self.pubkey_label.text = "No server running." 164 self.pubkey_label.disabled = True 165 self._running_server = None 166 self.app.set_feedback(shutdown_message, status) 167 168 def _on_server_start(self, *args): 169 self.app.set_feedback("Server running.") 170 171 def _shutdown_server(self, *args): 172 if self._running_server: 173 self._running_server.shutdown() 174 175 def _return_to_client(self, *args): 176 self.app.controller.invoke("show_client") 177 178 def set_focus(self, *args): 179 """Focus input widgets.""" 180 self._config_panel.set_focus("admin_password")
Widget for launching server.
ServerFrame(app_config)
35 def __init__(self, app_config): 36 """Initialize the class with an `AppConfig`.""" 37 self.info_label = kx.XLabel( 38 halign="left", 39 valign="top", 40 padding=(10, 10), 41 ) 42 super().__init__() 43 self._running_server: Optional[pgnet.Server] = None 44 self._game_class = app_config.game_class 45 self._make_widgets(app_config) 46 self.app.controller.set_active_callback(self._conpath, self.set_focus) 47 self.app.controller.bind(f"{self._conpath}.focus", self.set_focus) 48 self.app.controller.bind(f"{self._conpath}.shutdown", self._shutdown_server)
Initialize the class with an AppConfig
.
def
on_subtheme(self, subtheme):
50 def on_subtheme(self, subtheme): 51 """Highlight text.""" 52 self.info_label.text = _info_text(subtheme)
Highlight text.
def
set_focus(self, *args):
178 def set_focus(self, *args): 179 """Focus input widgets.""" 180 self._config_panel.set_focus("admin_password")
Focus input widgets.
Inherited Members
- kivy.uix.anchorlayout.AnchorLayout
- padding
- anchor_x
- anchor_y
- do_layout
- kivy.uix.layout.Layout
- add_widget
- remove_widget
- layout_hint_with_bounds
- kivy.uix.widget.Widget
- proxy_ref
- apply_class_lang_rules
- collide_point
- collide_widget
- on_motion
- on_touch_down
- on_touch_move
- on_touch_up
- on_kv_post
- clear_widgets
- register_for_motion_event
- unregister_for_motion_event
- export_to_png
- export_as_image
- get_root_window
- get_parent_window
- walk
- walk_reverse
- to_widget
- to_window
- to_parent
- to_local
- get_window_matrix
- x
- y
- width
- height
- pos
- size
- get_right
- set_right
- right
- get_top
- set_top
- top
- get_center_x
- set_center_x
- center_x
- get_center_y
- set_center_y
- center_y
- center
- cls
- children
- parent
- size_hint_x
- size_hint_y
- size_hint
- pos_hint
- size_hint_min_x
- size_hint_min_y
- size_hint_min
- size_hint_max_x
- size_hint_max_y
- size_hint_max
- ids
- opacity
- on_opacity
- canvas
- get_disabled
- set_disabled
- inc_disabled
- dec_disabled
- disabled
- motion_filter
- kivy._event.EventDispatcher
- register_event_type
- unregister_event_types
- unregister_event_type
- is_event_type
- bind
- unbind
- fbind
- funbind
- unbind_uid
- get_property_observers
- events
- dispatch
- dispatch_generic
- dispatch_children
- setter
- getter
- property
- properties
- create_property
- apply_property