[MouseFox logo]
The MouseFox Project
Join The Community Discord Server
[Discord logo]
Edit on GitHub

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")
class ServerFrame(kvex.behaviors.XThemed, kvex.widgets.layouts.XAnchor):
 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