| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  | from __future__ import annotations | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-14 21:14:22 +01:00
										 |  |  | import sys | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  | import threading | 
					
						
							| 
									
										
										
										
											2020-04-24 20:07:28 -07:00
										 |  |  | import time | 
					
						
							| 
									
										
											  
											
												WebUI (#100)
* Object-Oriented base changes for web-ui prep
* remove debug raise
* optimize broadcast to serialize once
* Implement WebUI socket, static assets, and classes
- Still need to wrap logging functions and send output to UI
- UI commands are successfully being sent to the server
* GUI operational. Wrap logging functions, implement server address selection on GUI, automatically launch web browser when client websocket is served
* Update MultiServer status when a user disconnects / reconnects
* Implement colored item and hint checks, improve GUI readability
* Fix improper formatting on received items
* Update SNES connection status on disconnect / reconnect. Implement itemFound, prevent accidentally printing JS objects
* Minor text change for itemFound
* Fixed a very wrong comment
* Fixed client commands not working, fixed un-helpful error messages appearing in GUI
* Fix a bug causing a failure to connect to a multiworld server if a previously existing cached address was present and the client was loaded without an address passed in
* Convert WebUI to React /w Redux. WebSocket communications not yet operational.
* WebUI fully converted to React / Redux.
- Websocket communication operational
- Added a button to connect to the multiserver which appears only when a SNES is connected and a server connection is not active
* Restore some features lost in WebUI
- Restore (found) notification on hints if the item has already been obtained
- Restore (x/y) indicator on received items, which indicates the number of items the client is waiting to receive from the client in a queue
* Fix a grammatical UI big causing player names to show only an apostrophe when possessive
* Add support for multiple SNES Devices, and switching between them
* freeze support for client
* make sure flask works when frozen
* UI Improvements
- Hint messages now actually show a found status via ✔ and ❌ emoji
- Active player name is always a different color than other players (orange for now)
- Add a toggle to show only entries relevant to the active player
- Added a WidgetArea
- Added a notes widget
* Received items now marked as relevant
* Include production build for deployment
* Notes now survive a browser close. Minimum width applied to monitor to prevent CSS issues.
* include webUi folder in setup.py
* Bugfixes for Monitor
- Fix a bug causing the monitor window to grow beyond it's intended content limit
- Reduced monitor content limit to 200 items
- Ensured each monitor entry has a unique key
* Prevent eslint from yelling at me about stupid things
* Add button to collapse sidebar, press enter on empty server input to disconnect on purpose
* WebUI is now aware of client disconnect, message log limit increased to 350, fix !missing output
* Update WebUI to v2.2.1
- Added color to WebUI for entrance-span
- Make !missing show total count at bottom of list to match /missing behavior
* Fix a bug causing clients version <= 2.2.0 to crash when anyone asks for a hint
- Also fix a bug in the WebUI causing the entrance location to always show as "somewhere"
* Update WebUI color palette (this cost me $50)
* allow text console input alongside web-ui
* remove Flask
a bit overkill for what we're doing
* remove jinja2
* Update WebUI to work with new hosting mechanism
* with flask gone, we no longer need subprocess shenanigans
* If multiple web ui clients try to run, at least present a working console
* Update MultiClient and WebUI to handle multiple clients simultaneously.
- The port on which the websocket for the WebUI is hosted is not chosen randomly from 5000 - 5999. This port is passed to the browser so it knows which MultiClient to connect to
- Removed failure condition if a web server is already running, as there is no need to run more than one web server on a single system. If an exception is thrown while attempting to launch a web server, a check is made for the port being unavailable. If the port is unavailable, it probably means the user is launching a second MultiClient. A web browser is then opened with a connection to the correct webui_socket_port.
- Add a /web command to the MultiClient to repoen the appropriate browser window and get params in case a user accidentally closes the tab
* Use proper name for WebUI
* move webui into /data with other data files
* make web ui optional
This is mostly for laptop users wanting to preserve some battery, should not be needed outside of that.
* fix direct server start
* re-add connection timer
* fix indentation
Co-authored-by: Chris <chris@legendserver.info>
											
										 
											2020-06-03 21:29:43 +02:00
										 |  |  | import multiprocessing | 
					
						
							| 
									
										
										
										
											2020-07-15 17:19:16 +02:00
										 |  |  | import os | 
					
						
							|  |  |  | import subprocess | 
					
						
							| 
									
										
										
										
											2020-10-19 08:26:31 +02:00
										 |  |  | import base64 | 
					
						
							| 
									
										
										
										
											2021-08-29 17:38:35 +02:00
										 |  |  | import logging | 
					
						
							|  |  |  | import asyncio | 
					
						
							| 
									
										
										
										
											2022-06-09 05:18:39 +02:00
										 |  |  | import enum | 
					
						
							|  |  |  | import typing | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-21 23:54:08 +01:00
										 |  |  | from json import loads, dumps | 
					
						
							| 
									
										
										
										
											2020-06-04 21:27:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-09-30 00:36:30 +02:00
										 |  |  | # CommonClient import first to trigger ModuleUpdater | 
					
						
							|  |  |  | from CommonClient import CommonContext, server_loop, ClientCommandProcessor, gui_enabled, get_base_parser | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import Utils | 
					
						
							| 
									
										
										
										
											2022-11-02 07:51:35 -07:00
										 |  |  | from Utils import async_start | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | from MultiServer import mark_raw | 
					
						
							|  |  |  | if typing.TYPE_CHECKING: | 
					
						
							|  |  |  |     from worlds.AutoSNIClient import SNIClient | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-10 15:35:43 +01:00
										 |  |  | if __name__ == "__main__": | 
					
						
							| 
									
										
										
										
											2022-09-30 00:36:30 +02:00
										 |  |  |     Utils.init_logging("SNIClient", exception_logger="Client") | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-18 15:04:39 +01:00
										 |  |  | import colorama | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | from websockets.client import connect as websockets_connect, WebSocketClientProtocol | 
					
						
							|  |  |  | from websockets.exceptions import WebSocketException, ConnectionClosed | 
					
						
							| 
									
										
										
										
											2022-09-30 00:36:30 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  | snes_logger = logging.getLogger("SNES") | 
					
						
							| 
									
										
										
										
											2021-01-19 06:37:35 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-11 16:09:08 +01:00
										 |  |  | class DeathState(enum.IntEnum): | 
					
						
							|  |  |  |     killing_player = 1 | 
					
						
							|  |  |  |     alive = 2 | 
					
						
							|  |  |  |     dead = 3 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-04 21:36:18 +01:00
										 |  |  | class SNIClientCommandProcessor(ClientCommandProcessor): | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |     ctx: SNIContext | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |     def _cmd_slow_mode(self, toggle: str = "") -> None: | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |         """Toggle slow mode, which limits how fast you send / receive items.""" | 
					
						
							|  |  |  |         if toggle: | 
					
						
							|  |  |  |             self.ctx.slow_mode = toggle.lower() in {"1", "true", "on"} | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             self.ctx.slow_mode = not self.ctx.slow_mode | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.output(f"Setting slow mode to {self.ctx.slow_mode}") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @mark_raw | 
					
						
							| 
									
										
										
										
											2021-08-28 21:20:45 -05:00
										 |  |  |     def _cmd_snes(self, snes_options: str = "") -> bool: | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  |         """Connect to a snes. Optionally include network address of a snes to connect to,
 | 
					
						
							| 
									
										
										
										
											2022-02-20 04:16:34 +01:00
										 |  |  |         otherwise show available devices; and a SNES device number if more than one SNES is detected. | 
					
						
							| 
									
										
										
										
											2022-07-08 09:36:14 -05:00
										 |  |  |         Examples: "/snes", "/snes 1", "/snes localhost:23074 1" """
 | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         return self.connect_to_snes(snes_options) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def connect_to_snes(self, snes_options: str = "") -> bool: | 
					
						
							| 
									
										
										
										
											2021-08-28 21:20:45 -05:00
										 |  |  |         snes_address = self.ctx.snes_address | 
					
						
							|  |  |  |         snes_device_number = -1 | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-28 21:20:45 -05:00
										 |  |  |         options = snes_options.split() | 
					
						
							|  |  |  |         num_options = len(options) | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-28 21:20:45 -05:00
										 |  |  |         if num_options > 0: | 
					
						
							| 
									
										
										
										
											2022-02-20 04:16:34 +01:00
										 |  |  |             snes_device_number = int(options[0]) | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-28 21:20:45 -05:00
										 |  |  |         if num_options > 1: | 
					
						
							| 
									
										
										
										
											2022-02-20 04:16:34 +01:00
										 |  |  |             snes_address = options[0] | 
					
						
							|  |  |  |             snes_device_number = int(options[1]) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |         self.ctx.snes_reconnect_address = None | 
					
						
							| 
									
										
										
										
											2022-06-12 03:20:03 +02:00
										 |  |  |         if self.ctx.snes_connect_task: | 
					
						
							|  |  |  |             self.ctx.snes_connect_task.cancel() | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         self.ctx.snes_connect_task = asyncio.create_task(snes_connect(self.ctx, snes_address, snes_device_number), | 
					
						
							|  |  |  |                                                          name="SNES Connect") | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |         return True | 
					
						
							| 
									
										
										
										
											2021-01-21 23:37:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |     def _cmd_snes_close(self) -> bool: | 
					
						
							|  |  |  |         """Close connection to a currently connected snes""" | 
					
						
							|  |  |  |         self.ctx.snes_reconnect_address = None | 
					
						
							| 
									
										
										
										
											2022-11-04 17:57:58 +01:00
										 |  |  |         self.ctx.cancel_snes_autoreconnect() | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |         if self.ctx.snes_socket is not None and not self.ctx.snes_socket.closed: | 
					
						
							| 
									
										
										
										
											2022-11-02 07:51:35 -07:00
										 |  |  |             async_start(self.ctx.snes_socket.close()) | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |             return True | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-13 23:05:39 +01:00
										 |  |  |     # Left here for quick re-addition for debugging. | 
					
						
							|  |  |  |     # def _cmd_snes_write(self, address, data): | 
					
						
							|  |  |  |     #     """Write the specified byte (base10) to the SNES' memory address (base16).""" | 
					
						
							|  |  |  |     #     if self.ctx.snes_state != SNESState.SNES_ATTACHED: | 
					
						
							|  |  |  |     #         self.output("No attached SNES Device.") | 
					
						
							|  |  |  |     #         return False | 
					
						
							|  |  |  |     #     snes_buffered_write(self.ctx, int(address, 16), bytes([int(data)])) | 
					
						
							| 
									
										
										
										
											2022-11-02 07:51:35 -07:00
										 |  |  |     #     async_start(snes_flush_writes(self.ctx)) | 
					
						
							| 
									
										
										
										
											2021-11-13 23:05:39 +01:00
										 |  |  |     #     self.output("Data Sent") | 
					
						
							|  |  |  |     #     return True | 
					
						
							| 
									
										
										
										
											2021-11-11 16:09:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-04 21:36:18 +01:00
										 |  |  |     # def _cmd_snes_read(self, address, size=1): | 
					
						
							|  |  |  |     #     """Read the SNES' memory address (base16).""" | 
					
						
							|  |  |  |     #     if self.ctx.snes_state != SNESState.SNES_ATTACHED: | 
					
						
							|  |  |  |     #         self.output("No attached SNES Device.") | 
					
						
							|  |  |  |     #         return False | 
					
						
							|  |  |  |     #     data = await snes_read(self.ctx, int(address, 16), size) | 
					
						
							|  |  |  |     #     self.output(f"Data Read: {data}") | 
					
						
							|  |  |  |     #     return True | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-02 11:11:57 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | class SNIContext(CommonContext): | 
					
						
							|  |  |  |     command_processor: typing.Type[SNIClientCommandProcessor] = SNIClientCommandProcessor | 
					
						
							|  |  |  |     game = None  # set in validate_rom | 
					
						
							| 
									
										
										
										
											2022-01-23 06:38:46 +01:00
										 |  |  |     items_handling = None  # set in game_watcher | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |     snes_connect_task: "typing.Optional[asyncio.Task[None]]" = None | 
					
						
							| 
									
										
										
										
											2022-11-04 17:57:58 +01:00
										 |  |  |     snes_autoreconnect_task: typing.Optional["asyncio.Task[None]"] = None | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |     snes_address: str | 
					
						
							|  |  |  |     snes_socket: typing.Optional[WebSocketClientProtocol] | 
					
						
							|  |  |  |     snes_state: SNESState | 
					
						
							|  |  |  |     snes_attached_device: typing.Optional[typing.Tuple[int, str]] | 
					
						
							|  |  |  |     snes_reconnect_address: typing.Optional[str] | 
					
						
							|  |  |  |     snes_recv_queue: "asyncio.Queue[bytes]" | 
					
						
							|  |  |  |     snes_request_lock: asyncio.Lock | 
					
						
							|  |  |  |     snes_write_buffer: typing.List[typing.Tuple[int, bytes]] | 
					
						
							|  |  |  |     snes_connector_lock: threading.Lock | 
					
						
							|  |  |  |     death_state: DeathState | 
					
						
							|  |  |  |     killing_player_task: "typing.Optional[asyncio.Task[None]]" | 
					
						
							|  |  |  |     allow_collect: bool | 
					
						
							|  |  |  |     slow_mode: bool | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     client_handler: typing.Optional[SNIClient] | 
					
						
							|  |  |  |     awaiting_rom: bool | 
					
						
							|  |  |  |     rom: typing.Optional[bytes] | 
					
						
							|  |  |  |     prev_rom: typing.Optional[bytes] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     hud_message_queue: typing.List[str]  # TODO: str is a guess, is this right? | 
					
						
							|  |  |  |     death_link_allow_survive: bool | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __init__(self, snes_address: str, server_address: str, password: str) -> None: | 
					
						
							|  |  |  |         super(SNIContext, self).__init__(server_address, password) | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |         # snes stuff | 
					
						
							|  |  |  |         self.snes_address = snes_address | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |         self.snes_socket = None | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |         self.snes_state = SNESState.SNES_DISCONNECTED | 
					
						
							| 
									
										
										
										
											2020-01-13 03:55:33 +01:00
										 |  |  |         self.snes_attached_device = None | 
					
						
							|  |  |  |         self.snes_reconnect_address = None | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |         self.snes_recv_queue = asyncio.Queue() | 
					
						
							|  |  |  |         self.snes_request_lock = asyncio.Lock() | 
					
						
							|  |  |  |         self.snes_write_buffer = [] | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |         self.snes_connector_lock = threading.Lock() | 
					
						
							| 
									
										
										
										
											2021-11-11 16:09:08 +01:00
										 |  |  |         self.death_state = DeathState.alive  # for death link flop behaviour | 
					
						
							| 
									
										
										
										
											2021-11-11 12:32:42 -08:00
										 |  |  |         self.killing_player_task = None | 
					
						
							| 
									
										
										
										
											2022-04-04 18:54:49 -07:00
										 |  |  |         self.allow_collect = False | 
					
						
							| 
									
										
										
										
											2022-06-08 00:34:45 +02:00
										 |  |  |         self.slow_mode = False | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         self.client_handler = None | 
					
						
							| 
									
										
										
										
											2020-01-14 10:42:27 +01:00
										 |  |  |         self.awaiting_rom = False | 
					
						
							|  |  |  |         self.rom = None | 
					
						
							| 
									
										
										
										
											2020-06-02 07:38:23 -07:00
										 |  |  |         self.prev_rom = None | 
					
						
							| 
									
										
											  
											
												WebUI (#100)
* Object-Oriented base changes for web-ui prep
* remove debug raise
* optimize broadcast to serialize once
* Implement WebUI socket, static assets, and classes
- Still need to wrap logging functions and send output to UI
- UI commands are successfully being sent to the server
* GUI operational. Wrap logging functions, implement server address selection on GUI, automatically launch web browser when client websocket is served
* Update MultiServer status when a user disconnects / reconnects
* Implement colored item and hint checks, improve GUI readability
* Fix improper formatting on received items
* Update SNES connection status on disconnect / reconnect. Implement itemFound, prevent accidentally printing JS objects
* Minor text change for itemFound
* Fixed a very wrong comment
* Fixed client commands not working, fixed un-helpful error messages appearing in GUI
* Fix a bug causing a failure to connect to a multiworld server if a previously existing cached address was present and the client was loaded without an address passed in
* Convert WebUI to React /w Redux. WebSocket communications not yet operational.
* WebUI fully converted to React / Redux.
- Websocket communication operational
- Added a button to connect to the multiserver which appears only when a SNES is connected and a server connection is not active
* Restore some features lost in WebUI
- Restore (found) notification on hints if the item has already been obtained
- Restore (x/y) indicator on received items, which indicates the number of items the client is waiting to receive from the client in a queue
* Fix a grammatical UI big causing player names to show only an apostrophe when possessive
* Add support for multiple SNES Devices, and switching between them
* freeze support for client
* make sure flask works when frozen
* UI Improvements
- Hint messages now actually show a found status via ✔ and ❌ emoji
- Active player name is always a different color than other players (orange for now)
- Add a toggle to show only entries relevant to the active player
- Added a WidgetArea
- Added a notes widget
* Received items now marked as relevant
* Include production build for deployment
* Notes now survive a browser close. Minimum width applied to monitor to prevent CSS issues.
* include webUi folder in setup.py
* Bugfixes for Monitor
- Fix a bug causing the monitor window to grow beyond it's intended content limit
- Reduced monitor content limit to 200 items
- Ensured each monitor entry has a unique key
* Prevent eslint from yelling at me about stupid things
* Add button to collapse sidebar, press enter on empty server input to disconnect on purpose
* WebUI is now aware of client disconnect, message log limit increased to 350, fix !missing output
* Update WebUI to v2.2.1
- Added color to WebUI for entrance-span
- Make !missing show total count at bottom of list to match /missing behavior
* Fix a bug causing clients version <= 2.2.0 to crash when anyone asks for a hint
- Also fix a bug in the WebUI causing the entrance location to always show as "somewhere"
* Update WebUI color palette (this cost me $50)
* allow text console input alongside web-ui
* remove Flask
a bit overkill for what we're doing
* remove jinja2
* Update WebUI to work with new hosting mechanism
* with flask gone, we no longer need subprocess shenanigans
* If multiple web ui clients try to run, at least present a working console
* Update MultiClient and WebUI to handle multiple clients simultaneously.
- The port on which the websocket for the WebUI is hosted is not chosen randomly from 5000 - 5999. This port is passed to the browser so it knows which MultiClient to connect to
- Removed failure condition if a web server is already running, as there is no need to run more than one web server on a single system. If an exception is thrown while attempting to launch a web server, a check is made for the port being unavailable. If the port is unavailable, it probably means the user is launching a second MultiClient. A web browser is then opened with a connection to the correct webui_socket_port.
- Add a /web command to the MultiClient to repoen the appropriate browser window and get params in case a user accidentally closes the tab
* Use proper name for WebUI
* move webui into /data with other data files
* make web ui optional
This is mostly for laptop users wanting to preserve some battery, should not be needed outside of that.
* fix direct server start
* re-add connection timer
* fix indentation
Co-authored-by: Chris <chris@legendserver.info>
											
										 
											2020-06-03 21:29:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |     async def connection_closed(self) -> None: | 
					
						
							|  |  |  |         await super(SNIContext, self).connection_closed() | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |         self.awaiting_rom = False | 
					
						
							| 
									
										
											  
											
												WebUI (#100)
* Object-Oriented base changes for web-ui prep
* remove debug raise
* optimize broadcast to serialize once
* Implement WebUI socket, static assets, and classes
- Still need to wrap logging functions and send output to UI
- UI commands are successfully being sent to the server
* GUI operational. Wrap logging functions, implement server address selection on GUI, automatically launch web browser when client websocket is served
* Update MultiServer status when a user disconnects / reconnects
* Implement colored item and hint checks, improve GUI readability
* Fix improper formatting on received items
* Update SNES connection status on disconnect / reconnect. Implement itemFound, prevent accidentally printing JS objects
* Minor text change for itemFound
* Fixed a very wrong comment
* Fixed client commands not working, fixed un-helpful error messages appearing in GUI
* Fix a bug causing a failure to connect to a multiworld server if a previously existing cached address was present and the client was loaded without an address passed in
* Convert WebUI to React /w Redux. WebSocket communications not yet operational.
* WebUI fully converted to React / Redux.
- Websocket communication operational
- Added a button to connect to the multiserver which appears only when a SNES is connected and a server connection is not active
* Restore some features lost in WebUI
- Restore (found) notification on hints if the item has already been obtained
- Restore (x/y) indicator on received items, which indicates the number of items the client is waiting to receive from the client in a queue
* Fix a grammatical UI big causing player names to show only an apostrophe when possessive
* Add support for multiple SNES Devices, and switching between them
* freeze support for client
* make sure flask works when frozen
* UI Improvements
- Hint messages now actually show a found status via ✔ and ❌ emoji
- Active player name is always a different color than other players (orange for now)
- Add a toggle to show only entries relevant to the active player
- Added a WidgetArea
- Added a notes widget
* Received items now marked as relevant
* Include production build for deployment
* Notes now survive a browser close. Minimum width applied to monitor to prevent CSS issues.
* include webUi folder in setup.py
* Bugfixes for Monitor
- Fix a bug causing the monitor window to grow beyond it's intended content limit
- Reduced monitor content limit to 200 items
- Ensured each monitor entry has a unique key
* Prevent eslint from yelling at me about stupid things
* Add button to collapse sidebar, press enter on empty server input to disconnect on purpose
* WebUI is now aware of client disconnect, message log limit increased to 350, fix !missing output
* Update WebUI to v2.2.1
- Added color to WebUI for entrance-span
- Make !missing show total count at bottom of list to match /missing behavior
* Fix a bug causing clients version <= 2.2.0 to crash when anyone asks for a hint
- Also fix a bug in the WebUI causing the entrance location to always show as "somewhere"
* Update WebUI color palette (this cost me $50)
* allow text console input alongside web-ui
* remove Flask
a bit overkill for what we're doing
* remove jinja2
* Update WebUI to work with new hosting mechanism
* with flask gone, we no longer need subprocess shenanigans
* If multiple web ui clients try to run, at least present a working console
* Update MultiClient and WebUI to handle multiple clients simultaneously.
- The port on which the websocket for the WebUI is hosted is not chosen randomly from 5000 - 5999. This port is passed to the browser so it knows which MultiClient to connect to
- Removed failure condition if a web server is already running, as there is no need to run more than one web server on a single system. If an exception is thrown while attempting to launch a web server, a check is made for the port being unavailable. If the port is unavailable, it probably means the user is launching a second MultiClient. A web browser is then opened with a connection to the correct webui_socket_port.
- Add a /web command to the MultiClient to repoen the appropriate browser window and get params in case a user accidentally closes the tab
* Use proper name for WebUI
* move webui into /data with other data files
* make web ui optional
This is mostly for laptop users wanting to preserve some battery, should not be needed outside of that.
* fix direct server start
* re-add connection timer
* fix indentation
Co-authored-by: Chris <chris@legendserver.info>
											
										 
											2020-06-03 21:29:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |     def event_invalid_slot(self) -> typing.NoReturn: | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |         if self.snes_socket is not None and not self.snes_socket.closed: | 
					
						
							| 
									
										
										
										
											2022-11-02 07:51:35 -07:00
										 |  |  |             async_start(self.snes_socket.close()) | 
					
						
							| 
									
										
										
										
											2022-08-30 08:16:21 -07:00
										 |  |  |         raise Exception("Invalid ROM detected, " | 
					
						
							|  |  |  |                         "please verify that you have loaded the correct rom and reconnect your snes (/snes)") | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |     async def server_auth(self, password_requested: bool = False) -> None: | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |         if password_requested and not self.password: | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |             await super(SNIContext, self).server_auth(password_requested) | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |         if self.rom is None: | 
					
						
							|  |  |  |             self.awaiting_rom = True | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |             snes_logger.info( | 
					
						
							| 
									
										
										
										
											2022-08-30 08:16:21 -07:00
										 |  |  |                 "No ROM detected, awaiting snes connection to authenticate to the multiworld server (/snes)") | 
					
						
							| 
									
										
											  
											
												WebUI (#100)
* Object-Oriented base changes for web-ui prep
* remove debug raise
* optimize broadcast to serialize once
* Implement WebUI socket, static assets, and classes
- Still need to wrap logging functions and send output to UI
- UI commands are successfully being sent to the server
* GUI operational. Wrap logging functions, implement server address selection on GUI, automatically launch web browser when client websocket is served
* Update MultiServer status when a user disconnects / reconnects
* Implement colored item and hint checks, improve GUI readability
* Fix improper formatting on received items
* Update SNES connection status on disconnect / reconnect. Implement itemFound, prevent accidentally printing JS objects
* Minor text change for itemFound
* Fixed a very wrong comment
* Fixed client commands not working, fixed un-helpful error messages appearing in GUI
* Fix a bug causing a failure to connect to a multiworld server if a previously existing cached address was present and the client was loaded without an address passed in
* Convert WebUI to React /w Redux. WebSocket communications not yet operational.
* WebUI fully converted to React / Redux.
- Websocket communication operational
- Added a button to connect to the multiserver which appears only when a SNES is connected and a server connection is not active
* Restore some features lost in WebUI
- Restore (found) notification on hints if the item has already been obtained
- Restore (x/y) indicator on received items, which indicates the number of items the client is waiting to receive from the client in a queue
* Fix a grammatical UI big causing player names to show only an apostrophe when possessive
* Add support for multiple SNES Devices, and switching between them
* freeze support for client
* make sure flask works when frozen
* UI Improvements
- Hint messages now actually show a found status via ✔ and ❌ emoji
- Active player name is always a different color than other players (orange for now)
- Add a toggle to show only entries relevant to the active player
- Added a WidgetArea
- Added a notes widget
* Received items now marked as relevant
* Include production build for deployment
* Notes now survive a browser close. Minimum width applied to monitor to prevent CSS issues.
* include webUi folder in setup.py
* Bugfixes for Monitor
- Fix a bug causing the monitor window to grow beyond it's intended content limit
- Reduced monitor content limit to 200 items
- Ensured each monitor entry has a unique key
* Prevent eslint from yelling at me about stupid things
* Add button to collapse sidebar, press enter on empty server input to disconnect on purpose
* WebUI is now aware of client disconnect, message log limit increased to 350, fix !missing output
* Update WebUI to v2.2.1
- Added color to WebUI for entrance-span
- Make !missing show total count at bottom of list to match /missing behavior
* Fix a bug causing clients version <= 2.2.0 to crash when anyone asks for a hint
- Also fix a bug in the WebUI causing the entrance location to always show as "somewhere"
* Update WebUI color palette (this cost me $50)
* allow text console input alongside web-ui
* remove Flask
a bit overkill for what we're doing
* remove jinja2
* Update WebUI to work with new hosting mechanism
* with flask gone, we no longer need subprocess shenanigans
* If multiple web ui clients try to run, at least present a working console
* Update MultiClient and WebUI to handle multiple clients simultaneously.
- The port on which the websocket for the WebUI is hosted is not chosen randomly from 5000 - 5999. This port is passed to the browser so it knows which MultiClient to connect to
- Removed failure condition if a web server is already running, as there is no need to run more than one web server on a single system. If an exception is thrown while attempting to launch a web server, a check is made for the port being unavailable. If the port is unavailable, it probably means the user is launching a second MultiClient. A web browser is then opened with a connection to the correct webui_socket_port.
- Add a /web command to the MultiClient to repoen the appropriate browser window and get params in case a user accidentally closes the tab
* Use proper name for WebUI
* move webui into /data with other data files
* make web ui optional
This is mostly for laptop users wanting to preserve some battery, should not be needed outside of that.
* fix direct server start
* re-add connection timer
* fix indentation
Co-authored-by: Chris <chris@legendserver.info>
											
										 
											2020-06-03 21:29:43 +02:00
										 |  |  |             return | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |         self.awaiting_rom = False | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         # TODO: This looks kind of hacky... | 
					
						
							|  |  |  |         # Context.auth is meant to be the "name" parameter in send_connect, | 
					
						
							|  |  |  |         # which has to be a str (bytes is not json serializable). | 
					
						
							|  |  |  |         # But here, Context.auth is being used for something else | 
					
						
							|  |  |  |         # (where it has to be bytes because it is compared with rom elsewhere). | 
					
						
							|  |  |  |         # If we need to save something to compare with rom elsewhere, | 
					
						
							|  |  |  |         # it should probably be in a different variable, | 
					
						
							|  |  |  |         # and let auth be used for what it's meant for. | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |         self.auth = self.rom | 
					
						
							|  |  |  |         auth = base64.b64encode(self.rom).decode() | 
					
						
							| 
									
										
										
										
											2021-11-21 02:50:24 +01:00
										 |  |  |         await self.send_connect(name=auth) | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-04 17:57:58 +01:00
										 |  |  |     def cancel_snes_autoreconnect(self) -> bool: | 
					
						
							|  |  |  |         if self.snes_autoreconnect_task: | 
					
						
							|  |  |  |             self.snes_autoreconnect_task.cancel() | 
					
						
							|  |  |  |             self.snes_autoreconnect_task = None | 
					
						
							|  |  |  |             return True | 
					
						
							|  |  |  |         return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |     def on_deathlink(self, data: typing.Dict[str, typing.Any]) -> None: | 
					
						
							| 
									
										
										
										
											2021-11-11 12:32:42 -08:00
										 |  |  |         if not self.killing_player_task or self.killing_player_task.done(): | 
					
						
							|  |  |  |             self.killing_player_task = asyncio.create_task(deathlink_kill_player(self)) | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         super(SNIContext, self).on_deathlink(data) | 
					
						
							| 
									
										
										
										
											2021-02-21 20:17:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |     async def handle_deathlink_state(self, currently_dead: bool) -> None: | 
					
						
							| 
									
										
										
										
											2021-11-12 14:58:48 +01:00
										 |  |  |         # in this state we only care about triggering a death send | 
					
						
							|  |  |  |         if self.death_state == DeathState.alive: | 
					
						
							|  |  |  |             if currently_dead: | 
					
						
							|  |  |  |                 self.death_state = DeathState.dead | 
					
						
							|  |  |  |                 await self.send_death() | 
					
						
							|  |  |  |         # in this state we care about confirming a kill, to move state to dead | 
					
						
							|  |  |  |         elif self.death_state == DeathState.killing_player: | 
					
						
							|  |  |  |             # this is being handled in deathlink_kill_player(ctx) already | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  |         # in this state we wait until the player is alive again | 
					
						
							|  |  |  |         elif self.death_state == DeathState.dead: | 
					
						
							|  |  |  |             if not currently_dead: | 
					
						
							|  |  |  |                 self.death_state = DeathState.alive | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |     async def shutdown(self) -> None: | 
					
						
							|  |  |  |         await super(SNIContext, self).shutdown() | 
					
						
							| 
									
										
										
										
											2022-11-04 17:57:58 +01:00
										 |  |  |         self.cancel_snes_autoreconnect() | 
					
						
							| 
									
										
										
										
											2022-06-12 03:20:03 +02:00
										 |  |  |         if self.snes_connect_task: | 
					
						
							| 
									
										
										
										
											2022-07-26 16:44:32 +02:00
										 |  |  |             try: | 
					
						
							|  |  |  |                 await asyncio.wait_for(self.snes_connect_task, 1) | 
					
						
							|  |  |  |             except asyncio.TimeoutError: | 
					
						
							|  |  |  |                 self.snes_connect_task.cancel() | 
					
						
							| 
									
										
										
										
											2022-06-12 03:20:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |     def on_package(self, cmd: str, args: typing.Dict[str, typing.Any]) -> None: | 
					
						
							| 
									
										
										
										
											2022-03-07 14:10:07 -08:00
										 |  |  |         if cmd in {"Connected", "RoomUpdate"}: | 
					
						
							|  |  |  |             if "checked_locations" in args and args["checked_locations"]: | 
					
						
							|  |  |  |                 new_locations = set(args["checked_locations"]) | 
					
						
							|  |  |  |                 self.checked_locations |= new_locations | 
					
						
							|  |  |  |                 self.locations_scouted |= new_locations | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |                 # Items belonging to the player should not be marked as checked in game, | 
					
						
							|  |  |  |                 # since the player will likely need that item. | 
					
						
							|  |  |  |                 # Once the games handled by SNIClient gets made to be remote items, | 
					
						
							|  |  |  |                 # this will no longer be needed. | 
					
						
							| 
									
										
										
										
											2022-11-02 07:51:35 -07:00
										 |  |  |                 async_start(self.send_msgs([{"cmd": "LocationScouts", "locations": list(new_locations)}])) | 
					
						
							| 
									
										
										
										
											2022-03-07 14:10:07 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |     def run_gui(self) -> None: | 
					
						
							| 
									
										
										
										
											2022-04-27 22:11:11 +02:00
										 |  |  |         from kvui import GameManager | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         class SNIManager(GameManager): | 
					
						
							|  |  |  |             logging_pairs = [ | 
					
						
							|  |  |  |                 ("Client", "Archipelago"), | 
					
						
							|  |  |  |                 ("SNES", "SNES"), | 
					
						
							|  |  |  |             ] | 
					
						
							|  |  |  |             base_title = "Archipelago SNI Client" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         self.ui = SNIManager(self) | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         self.ui_task = asyncio.create_task(self.ui.async_run(), name="UI")  # type: ignore | 
					
						
							| 
									
										
										
										
											2022-04-27 22:11:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-19 06:37:35 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | async def deathlink_kill_player(ctx: SNIContext) -> None: | 
					
						
							| 
									
										
										
										
											2021-11-11 12:07:17 -08:00
										 |  |  |     ctx.death_state = DeathState.killing_player | 
					
						
							| 
									
										
										
										
											2021-11-11 12:32:42 -08:00
										 |  |  |     while ctx.death_state == DeathState.killing_player and \ | 
					
						
							|  |  |  |             ctx.snes_state == SNESState.SNES_ATTACHED: | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if ctx.client_handler is None: | 
					
						
							|  |  |  |             continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         await ctx.client_handler.deathlink_kill_player(ctx) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-11 12:07:17 -08:00
										 |  |  |         ctx.last_death_link = time.time() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-11 16:09:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | _global_snes_reconnect_delay = 5 | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | class SNESState(enum.IntEnum): | 
					
						
							|  |  |  |     SNES_DISCONNECTED = 0 | 
					
						
							|  |  |  |     SNES_CONNECTING = 1 | 
					
						
							|  |  |  |     SNES_CONNECTED = 2 | 
					
						
							|  |  |  |     SNES_ATTACHED = 3 | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												WebUI (#100)
* Object-Oriented base changes for web-ui prep
* remove debug raise
* optimize broadcast to serialize once
* Implement WebUI socket, static assets, and classes
- Still need to wrap logging functions and send output to UI
- UI commands are successfully being sent to the server
* GUI operational. Wrap logging functions, implement server address selection on GUI, automatically launch web browser when client websocket is served
* Update MultiServer status when a user disconnects / reconnects
* Implement colored item and hint checks, improve GUI readability
* Fix improper formatting on received items
* Update SNES connection status on disconnect / reconnect. Implement itemFound, prevent accidentally printing JS objects
* Minor text change for itemFound
* Fixed a very wrong comment
* Fixed client commands not working, fixed un-helpful error messages appearing in GUI
* Fix a bug causing a failure to connect to a multiworld server if a previously existing cached address was present and the client was loaded without an address passed in
* Convert WebUI to React /w Redux. WebSocket communications not yet operational.
* WebUI fully converted to React / Redux.
- Websocket communication operational
- Added a button to connect to the multiserver which appears only when a SNES is connected and a server connection is not active
* Restore some features lost in WebUI
- Restore (found) notification on hints if the item has already been obtained
- Restore (x/y) indicator on received items, which indicates the number of items the client is waiting to receive from the client in a queue
* Fix a grammatical UI big causing player names to show only an apostrophe when possessive
* Add support for multiple SNES Devices, and switching between them
* freeze support for client
* make sure flask works when frozen
* UI Improvements
- Hint messages now actually show a found status via ✔ and ❌ emoji
- Active player name is always a different color than other players (orange for now)
- Add a toggle to show only entries relevant to the active player
- Added a WidgetArea
- Added a notes widget
* Received items now marked as relevant
* Include production build for deployment
* Notes now survive a browser close. Minimum width applied to monitor to prevent CSS issues.
* include webUi folder in setup.py
* Bugfixes for Monitor
- Fix a bug causing the monitor window to grow beyond it's intended content limit
- Reduced monitor content limit to 200 items
- Ensured each monitor entry has a unique key
* Prevent eslint from yelling at me about stupid things
* Add button to collapse sidebar, press enter on empty server input to disconnect on purpose
* WebUI is now aware of client disconnect, message log limit increased to 350, fix !missing output
* Update WebUI to v2.2.1
- Added color to WebUI for entrance-span
- Make !missing show total count at bottom of list to match /missing behavior
* Fix a bug causing clients version <= 2.2.0 to crash when anyone asks for a hint
- Also fix a bug in the WebUI causing the entrance location to always show as "somewhere"
* Update WebUI color palette (this cost me $50)
* allow text console input alongside web-ui
* remove Flask
a bit overkill for what we're doing
* remove jinja2
* Update WebUI to work with new hosting mechanism
* with flask gone, we no longer need subprocess shenanigans
* If multiple web ui clients try to run, at least present a working console
* Update MultiClient and WebUI to handle multiple clients simultaneously.
- The port on which the websocket for the WebUI is hosted is not chosen randomly from 5000 - 5999. This port is passed to the browser so it knows which MultiClient to connect to
- Removed failure condition if a web server is already running, as there is no need to run more than one web server on a single system. If an exception is thrown while attempting to launch a web server, a check is made for the port being unavailable. If the port is unavailable, it probably means the user is launching a second MultiClient. A web browser is then opened with a connection to the correct webui_socket_port.
- Add a /web command to the MultiClient to repoen the appropriate browser window and get params in case a user accidentally closes the tab
* Use proper name for WebUI
* move webui into /data with other data files
* make web ui optional
This is mostly for laptop users wanting to preserve some battery, should not be needed outside of that.
* fix direct server start
* re-add connection timer
* fix indentation
Co-authored-by: Chris <chris@legendserver.info>
											
										 
											2020-06-03 21:29:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | def launch_sni() -> None: | 
					
						
							|  |  |  |     sni_path = Utils.get_options()["sni_options"]["sni_path"] | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-07 03:45:27 +02:00
										 |  |  |     if not os.path.isdir(sni_path): | 
					
						
							|  |  |  |         sni_path = Utils.local_path(sni_path) | 
					
						
							|  |  |  |     if os.path.isdir(sni_path): | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         dir_entry: "os.DirEntry[str]" | 
					
						
							| 
									
										
										
										
											2022-01-01 15:46:08 +01:00
										 |  |  |         for dir_entry in os.scandir(sni_path): | 
					
						
							|  |  |  |             if dir_entry.is_file(): | 
					
						
							|  |  |  |                 lower_file = dir_entry.name.lower() | 
					
						
							|  |  |  |                 if (lower_file.startswith("sni.") and not lower_file.endswith(".proto")) or (lower_file == "sni"): | 
					
						
							|  |  |  |                     sni_path = dir_entry.path | 
					
						
							|  |  |  |                     break | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-07 03:45:27 +02:00
										 |  |  |     if os.path.isfile(sni_path): | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |         snes_logger.info(f"Attempting to start {sni_path}") | 
					
						
							| 
									
										
										
										
											2021-11-09 12:53:05 +01:00
										 |  |  |         import sys | 
					
						
							|  |  |  |         if not sys.stdout:  # if it spawns a visible console, may as well populate it | 
					
						
							| 
									
										
										
										
											2021-12-27 15:29:09 +01:00
										 |  |  |             subprocess.Popen(os.path.abspath(sni_path), cwd=os.path.dirname(sni_path)) | 
					
						
							| 
									
										
										
										
											2021-07-31 01:40:27 +02:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2022-03-31 05:08:15 +02:00
										 |  |  |             proc = subprocess.Popen(os.path.abspath(sni_path), cwd=os.path.dirname(sni_path), | 
					
						
							|  |  |  |                                     stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 proc.wait(.1)  # wait a bit to see if startup fails (missing dependencies) | 
					
						
							|  |  |  |                 snes_logger.info('Failed to start SNI. Try running it externally for error output.') | 
					
						
							|  |  |  |             except subprocess.TimeoutExpired: | 
					
						
							|  |  |  |                 pass  # seems to be running | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |         snes_logger.info( | 
					
						
							| 
									
										
										
										
											2021-07-07 03:45:27 +02:00
										 |  |  |             f"Attempt to start SNI was aborted as path {sni_path} was not found, " | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  |             f"please start it yourself if it is not running") | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | async def _snes_connect(ctx: SNIContext, address: str) -> WebSocketClientProtocol: | 
					
						
							| 
									
										
										
										
											2020-01-13 03:55:33 +01:00
										 |  |  |     address = f"ws://{address}" if "://" not in address else address | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |     snes_logger.info("Connecting to SNI at %s ..." % address) | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |     seen_problems: typing.Set[str] = set() | 
					
						
							|  |  |  |     while True: | 
					
						
							| 
									
										
										
										
											2020-03-07 00:07:32 +01:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |             snes_socket = await websockets_connect(address, ping_timeout=None, ping_interval=None) | 
					
						
							| 
									
										
										
										
											2020-03-07 00:07:32 +01:00
										 |  |  |         except Exception as e: | 
					
						
							|  |  |  |             problem = "%s" % e | 
					
						
							|  |  |  |             # only tell the user about new problems, otherwise silently lay in wait for a working connection | 
					
						
							|  |  |  |             if problem not in seen_problems: | 
					
						
							|  |  |  |                 seen_problems.add(problem) | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |                 snes_logger.error(f"Error connecting to SNI ({problem})") | 
					
						
							| 
									
										
											  
											
												WebUI (#100)
* Object-Oriented base changes for web-ui prep
* remove debug raise
* optimize broadcast to serialize once
* Implement WebUI socket, static assets, and classes
- Still need to wrap logging functions and send output to UI
- UI commands are successfully being sent to the server
* GUI operational. Wrap logging functions, implement server address selection on GUI, automatically launch web browser when client websocket is served
* Update MultiServer status when a user disconnects / reconnects
* Implement colored item and hint checks, improve GUI readability
* Fix improper formatting on received items
* Update SNES connection status on disconnect / reconnect. Implement itemFound, prevent accidentally printing JS objects
* Minor text change for itemFound
* Fixed a very wrong comment
* Fixed client commands not working, fixed un-helpful error messages appearing in GUI
* Fix a bug causing a failure to connect to a multiworld server if a previously existing cached address was present and the client was loaded without an address passed in
* Convert WebUI to React /w Redux. WebSocket communications not yet operational.
* WebUI fully converted to React / Redux.
- Websocket communication operational
- Added a button to connect to the multiserver which appears only when a SNES is connected and a server connection is not active
* Restore some features lost in WebUI
- Restore (found) notification on hints if the item has already been obtained
- Restore (x/y) indicator on received items, which indicates the number of items the client is waiting to receive from the client in a queue
* Fix a grammatical UI big causing player names to show only an apostrophe when possessive
* Add support for multiple SNES Devices, and switching between them
* freeze support for client
* make sure flask works when frozen
* UI Improvements
- Hint messages now actually show a found status via ✔ and ❌ emoji
- Active player name is always a different color than other players (orange for now)
- Add a toggle to show only entries relevant to the active player
- Added a WidgetArea
- Added a notes widget
* Received items now marked as relevant
* Include production build for deployment
* Notes now survive a browser close. Minimum width applied to monitor to prevent CSS issues.
* include webUi folder in setup.py
* Bugfixes for Monitor
- Fix a bug causing the monitor window to grow beyond it's intended content limit
- Reduced monitor content limit to 200 items
- Ensured each monitor entry has a unique key
* Prevent eslint from yelling at me about stupid things
* Add button to collapse sidebar, press enter on empty server input to disconnect on purpose
* WebUI is now aware of client disconnect, message log limit increased to 350, fix !missing output
* Update WebUI to v2.2.1
- Added color to WebUI for entrance-span
- Make !missing show total count at bottom of list to match /missing behavior
* Fix a bug causing clients version <= 2.2.0 to crash when anyone asks for a hint
- Also fix a bug in the WebUI causing the entrance location to always show as "somewhere"
* Update WebUI color palette (this cost me $50)
* allow text console input alongside web-ui
* remove Flask
a bit overkill for what we're doing
* remove jinja2
* Update WebUI to work with new hosting mechanism
* with flask gone, we no longer need subprocess shenanigans
* If multiple web ui clients try to run, at least present a working console
* Update MultiClient and WebUI to handle multiple clients simultaneously.
- The port on which the websocket for the WebUI is hosted is not chosen randomly from 5000 - 5999. This port is passed to the browser so it knows which MultiClient to connect to
- Removed failure condition if a web server is already running, as there is no need to run more than one web server on a single system. If an exception is thrown while attempting to launch a web server, a check is made for the port being unavailable. If the port is unavailable, it probably means the user is launching a second MultiClient. A web browser is then opened with a connection to the correct webui_socket_port.
- Add a /web command to the MultiClient to repoen the appropriate browser window and get params in case a user accidentally closes the tab
* Use proper name for WebUI
* move webui into /data with other data files
* make web ui optional
This is mostly for laptop users wanting to preserve some battery, should not be needed outside of that.
* fix direct server start
* re-add connection timer
* fix indentation
Co-authored-by: Chris <chris@legendserver.info>
											
										 
											2020-06-03 21:29:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-07 04:18:26 +02:00
										 |  |  |                 if len(seen_problems) == 1: | 
					
						
							| 
									
										
										
										
											2021-07-07 03:45:27 +02:00
										 |  |  |                     # this is the first problem. Let's try launching SNI if it isn't already running | 
					
						
							| 
									
										
										
										
											2022-07-26 16:44:32 +02:00
										 |  |  |                     launch_sni() | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             await asyncio.sleep(1) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return snes_socket | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-12 04:44:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | class SNESRequest(typing.TypedDict): | 
					
						
							|  |  |  |     Opcode: str | 
					
						
							|  |  |  |     Space: str | 
					
						
							|  |  |  |     Operands: typing.List[str] | 
					
						
							|  |  |  |     # TODO: When Python 3.11 is the lowest version supported, `Operands` can use `typing.NotRequired` (pep-0655) | 
					
						
							|  |  |  |     # Then the `Operands` key doesn't need to be given for opcodes that don't use it. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async def get_snes_devices(ctx: SNIContext) -> typing.List[str]: | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  |     socket = await _snes_connect(ctx, ctx.snes_address)  # establish new connection to poll | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |     DeviceList_Request: SNESRequest = { | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  |         "Opcode": "DeviceList", | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         "Space": "SNES", | 
					
						
							|  |  |  |         "Operands": [] | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-10-19 08:26:31 +02:00
										 |  |  |     await socket.send(dumps(DeviceList_Request)) | 
					
						
							| 
									
										
										
										
											2020-04-12 04:44:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |     reply: typing.Dict[str, typing.Any] = loads(await socket.recv()) | 
					
						
							| 
									
										
										
										
											2022-06-12 03:20:03 +02:00
										 |  |  |     devices: typing.List[str] = reply['Results'] if 'Results' in reply and len(reply['Results']) > 0 else [] | 
					
						
							| 
									
										
										
										
											2020-04-07 04:18:26 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  |     if not devices: | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |         snes_logger.info('No SNES device found. Please connect a SNES device to SNI.') | 
					
						
							| 
									
										
										
										
											2022-06-12 03:20:03 +02:00
										 |  |  |         while not devices and not ctx.exit_event.is_set(): | 
					
						
							|  |  |  |             await asyncio.sleep(0.1) | 
					
						
							| 
									
										
										
										
											2020-10-19 08:26:31 +02:00
										 |  |  |             await socket.send(dumps(DeviceList_Request)) | 
					
						
							|  |  |  |             reply = loads(await socket.recv()) | 
					
						
							| 
									
										
										
										
											2022-06-12 03:20:03 +02:00
										 |  |  |             devices = reply['Results'] if 'Results' in reply and len(reply['Results']) > 0 else [] | 
					
						
							|  |  |  |     if devices: | 
					
						
							|  |  |  |         await verify_snes_app(socket) | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  |     await socket.close() | 
					
						
							| 
									
										
										
										
											2022-06-09 05:18:39 +02:00
										 |  |  |     return sorted(devices) | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | async def verify_snes_app(socket: WebSocketClientProtocol) -> None: | 
					
						
							| 
									
										
										
										
											2021-11-03 19:58:40 +01:00
										 |  |  |     AppVersion_Request = { | 
					
						
							|  |  |  |         "Opcode": "AppVersion", | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     await socket.send(dumps(AppVersion_Request)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     app: str = loads(await socket.recv())["Results"][0] | 
					
						
							| 
									
										
										
										
											2021-11-12 14:58:48 +01:00
										 |  |  |     if "SNI" not in app: | 
					
						
							| 
									
										
										
										
											2021-11-03 19:58:40 +01:00
										 |  |  |         snes_logger.warning(f"Warning: Did not find SNI as the endpoint, instead {app} was found.") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | async def snes_connect(ctx: SNIContext, address: str, deviceIndex: int = -1) -> None: | 
					
						
							|  |  |  |     global _global_snes_reconnect_delay | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |     if ctx.snes_socket is not None and ctx.snes_state == SNESState.SNES_CONNECTED: | 
					
						
							| 
									
										
										
										
											2021-07-31 01:40:27 +02:00
										 |  |  |         if ctx.rom: | 
					
						
							|  |  |  |             snes_logger.error('Already connected to SNES, with rom loaded.') | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             snes_logger.error('Already connected to SNI, likely awaiting a device.') | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  |         return | 
					
						
							| 
									
										
											  
											
												WebUI (#100)
* Object-Oriented base changes for web-ui prep
* remove debug raise
* optimize broadcast to serialize once
* Implement WebUI socket, static assets, and classes
- Still need to wrap logging functions and send output to UI
- UI commands are successfully being sent to the server
* GUI operational. Wrap logging functions, implement server address selection on GUI, automatically launch web browser when client websocket is served
* Update MultiServer status when a user disconnects / reconnects
* Implement colored item and hint checks, improve GUI readability
* Fix improper formatting on received items
* Update SNES connection status on disconnect / reconnect. Implement itemFound, prevent accidentally printing JS objects
* Minor text change for itemFound
* Fixed a very wrong comment
* Fixed client commands not working, fixed un-helpful error messages appearing in GUI
* Fix a bug causing a failure to connect to a multiworld server if a previously existing cached address was present and the client was loaded without an address passed in
* Convert WebUI to React /w Redux. WebSocket communications not yet operational.
* WebUI fully converted to React / Redux.
- Websocket communication operational
- Added a button to connect to the multiserver which appears only when a SNES is connected and a server connection is not active
* Restore some features lost in WebUI
- Restore (found) notification on hints if the item has already been obtained
- Restore (x/y) indicator on received items, which indicates the number of items the client is waiting to receive from the client in a queue
* Fix a grammatical UI big causing player names to show only an apostrophe when possessive
* Add support for multiple SNES Devices, and switching between them
* freeze support for client
* make sure flask works when frozen
* UI Improvements
- Hint messages now actually show a found status via ✔ and ❌ emoji
- Active player name is always a different color than other players (orange for now)
- Add a toggle to show only entries relevant to the active player
- Added a WidgetArea
- Added a notes widget
* Received items now marked as relevant
* Include production build for deployment
* Notes now survive a browser close. Minimum width applied to monitor to prevent CSS issues.
* include webUi folder in setup.py
* Bugfixes for Monitor
- Fix a bug causing the monitor window to grow beyond it's intended content limit
- Reduced monitor content limit to 200 items
- Ensured each monitor entry has a unique key
* Prevent eslint from yelling at me about stupid things
* Add button to collapse sidebar, press enter on empty server input to disconnect on purpose
* WebUI is now aware of client disconnect, message log limit increased to 350, fix !missing output
* Update WebUI to v2.2.1
- Added color to WebUI for entrance-span
- Make !missing show total count at bottom of list to match /missing behavior
* Fix a bug causing clients version <= 2.2.0 to crash when anyone asks for a hint
- Also fix a bug in the WebUI causing the entrance location to always show as "somewhere"
* Update WebUI color palette (this cost me $50)
* allow text console input alongside web-ui
* remove Flask
a bit overkill for what we're doing
* remove jinja2
* Update WebUI to work with new hosting mechanism
* with flask gone, we no longer need subprocess shenanigans
* If multiple web ui clients try to run, at least present a working console
* Update MultiClient and WebUI to handle multiple clients simultaneously.
- The port on which the websocket for the WebUI is hosted is not chosen randomly from 5000 - 5999. This port is passed to the browser so it knows which MultiClient to connect to
- Removed failure condition if a web server is already running, as there is no need to run more than one web server on a single system. If an exception is thrown while attempting to launch a web server, a check is made for the port being unavailable. If the port is unavailable, it probably means the user is launching a second MultiClient. A web browser is then opened with a connection to the correct webui_socket_port.
- Add a /web command to the MultiClient to repoen the appropriate browser window and get params in case a user accidentally closes the tab
* Use proper name for WebUI
* move webui into /data with other data files
* make web ui optional
This is mostly for laptop users wanting to preserve some battery, should not be needed outside of that.
* fix direct server start
* re-add connection timer
* fix indentation
Co-authored-by: Chris <chris@legendserver.info>
											
										 
											2020-06-03 21:29:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-11-04 17:57:58 +01:00
										 |  |  |     ctx.cancel_snes_autoreconnect() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-28 21:20:45 -05:00
										 |  |  |     device = None | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  |     recv_task = None | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |     ctx.snes_state = SNESState.SNES_CONNECTING | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  |     socket = await _snes_connect(ctx, address) | 
					
						
							|  |  |  |     ctx.snes_socket = socket | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |     ctx.snes_state = SNESState.SNES_CONNECTED | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |     try: | 
					
						
							|  |  |  |         devices = await get_snes_devices(ctx) | 
					
						
							| 
									
										
										
										
											2022-03-22 19:13:04 +01:00
										 |  |  |         device_count = len(devices) | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-22 19:13:04 +01:00
										 |  |  |         if device_count == 1: | 
					
						
							| 
									
										
										
										
											2020-01-13 03:55:33 +01:00
										 |  |  |             device = devices[0] | 
					
						
							|  |  |  |         elif ctx.snes_reconnect_address: | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |             assert ctx.snes_attached_device | 
					
						
							| 
									
										
										
										
											2020-01-13 03:55:33 +01:00
										 |  |  |             if ctx.snes_attached_device[1] in devices: | 
					
						
							|  |  |  |                 device = ctx.snes_attached_device[1] | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 device = devices[ctx.snes_attached_device[0]] | 
					
						
							| 
									
										
										
										
											2022-03-22 19:13:04 +01:00
										 |  |  |         elif device_count > 1: | 
					
						
							| 
									
										
										
										
											2021-08-28 21:20:45 -05:00
										 |  |  |             if deviceIndex == -1: | 
					
						
							| 
									
										
										
										
											2022-03-22 19:13:04 +01:00
										 |  |  |                 snes_logger.info(f"Found {device_count} SNES devices. " | 
					
						
							|  |  |  |                                  f"Connect to one with /snes <address> <device number>. For example /snes {address} 1") | 
					
						
							| 
									
										
										
										
											2021-08-28 21:20:45 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |                 for idx, availableDevice in enumerate(devices): | 
					
						
							| 
									
										
										
										
											2021-08-28 21:47:19 -05:00
										 |  |  |                     snes_logger.info(str(idx + 1) + ": " + availableDevice) | 
					
						
							| 
									
										
										
										
											2021-08-28 21:20:45 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-22 19:13:04 +01:00
										 |  |  |             elif (deviceIndex < 0) or (deviceIndex - 1) > device_count: | 
					
						
							| 
									
										
										
										
											2021-08-28 21:47:19 -05:00
										 |  |  |                 snes_logger.warning("SNES device number out of range") | 
					
						
							| 
									
										
										
										
											2021-08-28 21:20:45 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 device = devices[deviceIndex - 1] | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-28 21:20:45 -05:00
										 |  |  |         if device is None: | 
					
						
							| 
									
										
											  
											
												WebUI (#100)
* Object-Oriented base changes for web-ui prep
* remove debug raise
* optimize broadcast to serialize once
* Implement WebUI socket, static assets, and classes
- Still need to wrap logging functions and send output to UI
- UI commands are successfully being sent to the server
* GUI operational. Wrap logging functions, implement server address selection on GUI, automatically launch web browser when client websocket is served
* Update MultiServer status when a user disconnects / reconnects
* Implement colored item and hint checks, improve GUI readability
* Fix improper formatting on received items
* Update SNES connection status on disconnect / reconnect. Implement itemFound, prevent accidentally printing JS objects
* Minor text change for itemFound
* Fixed a very wrong comment
* Fixed client commands not working, fixed un-helpful error messages appearing in GUI
* Fix a bug causing a failure to connect to a multiworld server if a previously existing cached address was present and the client was loaded without an address passed in
* Convert WebUI to React /w Redux. WebSocket communications not yet operational.
* WebUI fully converted to React / Redux.
- Websocket communication operational
- Added a button to connect to the multiserver which appears only when a SNES is connected and a server connection is not active
* Restore some features lost in WebUI
- Restore (found) notification on hints if the item has already been obtained
- Restore (x/y) indicator on received items, which indicates the number of items the client is waiting to receive from the client in a queue
* Fix a grammatical UI big causing player names to show only an apostrophe when possessive
* Add support for multiple SNES Devices, and switching between them
* freeze support for client
* make sure flask works when frozen
* UI Improvements
- Hint messages now actually show a found status via ✔ and ❌ emoji
- Active player name is always a different color than other players (orange for now)
- Add a toggle to show only entries relevant to the active player
- Added a WidgetArea
- Added a notes widget
* Received items now marked as relevant
* Include production build for deployment
* Notes now survive a browser close. Minimum width applied to monitor to prevent CSS issues.
* include webUi folder in setup.py
* Bugfixes for Monitor
- Fix a bug causing the monitor window to grow beyond it's intended content limit
- Reduced monitor content limit to 200 items
- Ensured each monitor entry has a unique key
* Prevent eslint from yelling at me about stupid things
* Add button to collapse sidebar, press enter on empty server input to disconnect on purpose
* WebUI is now aware of client disconnect, message log limit increased to 350, fix !missing output
* Update WebUI to v2.2.1
- Added color to WebUI for entrance-span
- Make !missing show total count at bottom of list to match /missing behavior
* Fix a bug causing clients version <= 2.2.0 to crash when anyone asks for a hint
- Also fix a bug in the WebUI causing the entrance location to always show as "somewhere"
* Update WebUI color palette (this cost me $50)
* allow text console input alongside web-ui
* remove Flask
a bit overkill for what we're doing
* remove jinja2
* Update WebUI to work with new hosting mechanism
* with flask gone, we no longer need subprocess shenanigans
* If multiple web ui clients try to run, at least present a working console
* Update MultiClient and WebUI to handle multiple clients simultaneously.
- The port on which the websocket for the WebUI is hosted is not chosen randomly from 5000 - 5999. This port is passed to the browser so it knows which MultiClient to connect to
- Removed failure condition if a web server is already running, as there is no need to run more than one web server on a single system. If an exception is thrown while attempting to launch a web server, a check is made for the port being unavailable. If the port is unavailable, it probably means the user is launching a second MultiClient. A web browser is then opened with a connection to the correct webui_socket_port.
- Add a /web command to the MultiClient to repoen the appropriate browser window and get params in case a user accidentally closes the tab
* Use proper name for WebUI
* move webui into /data with other data files
* make web ui optional
This is mostly for laptop users wanting to preserve some battery, should not be needed outside of that.
* fix direct server start
* re-add connection timer
* fix indentation
Co-authored-by: Chris <chris@legendserver.info>
											
										 
											2020-06-03 21:29:43 +02:00
										 |  |  |             await snes_disconnect(ctx) | 
					
						
							|  |  |  |             return | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |         snes_logger.info("Attaching to " + device) | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         Attach_Request: SNESRequest = { | 
					
						
							| 
									
										
											  
											
												WebUI (#100)
* Object-Oriented base changes for web-ui prep
* remove debug raise
* optimize broadcast to serialize once
* Implement WebUI socket, static assets, and classes
- Still need to wrap logging functions and send output to UI
- UI commands are successfully being sent to the server
* GUI operational. Wrap logging functions, implement server address selection on GUI, automatically launch web browser when client websocket is served
* Update MultiServer status when a user disconnects / reconnects
* Implement colored item and hint checks, improve GUI readability
* Fix improper formatting on received items
* Update SNES connection status on disconnect / reconnect. Implement itemFound, prevent accidentally printing JS objects
* Minor text change for itemFound
* Fixed a very wrong comment
* Fixed client commands not working, fixed un-helpful error messages appearing in GUI
* Fix a bug causing a failure to connect to a multiworld server if a previously existing cached address was present and the client was loaded without an address passed in
* Convert WebUI to React /w Redux. WebSocket communications not yet operational.
* WebUI fully converted to React / Redux.
- Websocket communication operational
- Added a button to connect to the multiserver which appears only when a SNES is connected and a server connection is not active
* Restore some features lost in WebUI
- Restore (found) notification on hints if the item has already been obtained
- Restore (x/y) indicator on received items, which indicates the number of items the client is waiting to receive from the client in a queue
* Fix a grammatical UI big causing player names to show only an apostrophe when possessive
* Add support for multiple SNES Devices, and switching between them
* freeze support for client
* make sure flask works when frozen
* UI Improvements
- Hint messages now actually show a found status via ✔ and ❌ emoji
- Active player name is always a different color than other players (orange for now)
- Add a toggle to show only entries relevant to the active player
- Added a WidgetArea
- Added a notes widget
* Received items now marked as relevant
* Include production build for deployment
* Notes now survive a browser close. Minimum width applied to monitor to prevent CSS issues.
* include webUi folder in setup.py
* Bugfixes for Monitor
- Fix a bug causing the monitor window to grow beyond it's intended content limit
- Reduced monitor content limit to 200 items
- Ensured each monitor entry has a unique key
* Prevent eslint from yelling at me about stupid things
* Add button to collapse sidebar, press enter on empty server input to disconnect on purpose
* WebUI is now aware of client disconnect, message log limit increased to 350, fix !missing output
* Update WebUI to v2.2.1
- Added color to WebUI for entrance-span
- Make !missing show total count at bottom of list to match /missing behavior
* Fix a bug causing clients version <= 2.2.0 to crash when anyone asks for a hint
- Also fix a bug in the WebUI causing the entrance location to always show as "somewhere"
* Update WebUI color palette (this cost me $50)
* allow text console input alongside web-ui
* remove Flask
a bit overkill for what we're doing
* remove jinja2
* Update WebUI to work with new hosting mechanism
* with flask gone, we no longer need subprocess shenanigans
* If multiple web ui clients try to run, at least present a working console
* Update MultiClient and WebUI to handle multiple clients simultaneously.
- The port on which the websocket for the WebUI is hosted is not chosen randomly from 5000 - 5999. This port is passed to the browser so it knows which MultiClient to connect to
- Removed failure condition if a web server is already running, as there is no need to run more than one web server on a single system. If an exception is thrown while attempting to launch a web server, a check is made for the port being unavailable. If the port is unavailable, it probably means the user is launching a second MultiClient. A web browser is then opened with a connection to the correct webui_socket_port.
- Add a /web command to the MultiClient to repoen the appropriate browser window and get params in case a user accidentally closes the tab
* Use proper name for WebUI
* move webui into /data with other data files
* make web ui optional
This is mostly for laptop users wanting to preserve some battery, should not be needed outside of that.
* fix direct server start
* re-add connection timer
* fix indentation
Co-authored-by: Chris <chris@legendserver.info>
											
										 
											2020-06-03 21:29:43 +02:00
										 |  |  |             "Opcode": "Attach", | 
					
						
							|  |  |  |             "Space": "SNES", | 
					
						
							|  |  |  |             "Operands": [device] | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2020-10-19 08:26:31 +02:00
										 |  |  |         await ctx.snes_socket.send(dumps(Attach_Request)) | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |         ctx.snes_state = SNESState.SNES_ATTACHED | 
					
						
							| 
									
										
										
										
											2020-01-13 03:55:33 +01:00
										 |  |  |         ctx.snes_attached_device = (devices.index(device), device) | 
					
						
							|  |  |  |         ctx.snes_reconnect_address = address | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |         recv_task = asyncio.create_task(snes_recv_loop(ctx)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     except Exception as e: | 
					
						
							|  |  |  |         if recv_task is not None: | 
					
						
							|  |  |  |             if not ctx.snes_socket.closed: | 
					
						
							|  |  |  |                 await ctx.snes_socket.close() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             if ctx.snes_socket is not None: | 
					
						
							|  |  |  |                 if not ctx.snes_socket.closed: | 
					
						
							|  |  |  |                     await ctx.snes_socket.close() | 
					
						
							|  |  |  |                 ctx.snes_socket = None | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |             ctx.snes_state = SNESState.SNES_DISCONNECTED | 
					
						
							| 
									
										
										
										
											2020-01-13 03:55:33 +01:00
										 |  |  |         if not ctx.snes_reconnect_address: | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |             snes_logger.error("Error connecting to snes (%s)" % e) | 
					
						
							| 
									
										
										
										
											2020-01-13 03:55:33 +01:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2022-11-04 17:57:58 +01:00
										 |  |  |             snes_logger.error(f"Error connecting to snes, retrying in {_global_snes_reconnect_delay} seconds") | 
					
						
							|  |  |  |             assert ctx.snes_autoreconnect_task is None | 
					
						
							|  |  |  |             ctx.snes_autoreconnect_task = asyncio.create_task(snes_autoreconnect(ctx), name="snes auto-reconnect") | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         _global_snes_reconnect_delay *= 2 | 
					
						
							| 
									
										
										
										
											2020-01-13 03:55:33 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-22 19:13:04 +01:00
										 |  |  |     else: | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         _global_snes_reconnect_delay = ctx.starting_reconnect_delay | 
					
						
							| 
									
										
										
										
											2022-03-22 19:13:04 +01:00
										 |  |  |         snes_logger.info(f"Attached to {device}") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-13 03:53:20 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | async def snes_disconnect(ctx: SNIContext) -> None: | 
					
						
							| 
									
										
											  
											
												WebUI (#100)
* Object-Oriented base changes for web-ui prep
* remove debug raise
* optimize broadcast to serialize once
* Implement WebUI socket, static assets, and classes
- Still need to wrap logging functions and send output to UI
- UI commands are successfully being sent to the server
* GUI operational. Wrap logging functions, implement server address selection on GUI, automatically launch web browser when client websocket is served
* Update MultiServer status when a user disconnects / reconnects
* Implement colored item and hint checks, improve GUI readability
* Fix improper formatting on received items
* Update SNES connection status on disconnect / reconnect. Implement itemFound, prevent accidentally printing JS objects
* Minor text change for itemFound
* Fixed a very wrong comment
* Fixed client commands not working, fixed un-helpful error messages appearing in GUI
* Fix a bug causing a failure to connect to a multiworld server if a previously existing cached address was present and the client was loaded without an address passed in
* Convert WebUI to React /w Redux. WebSocket communications not yet operational.
* WebUI fully converted to React / Redux.
- Websocket communication operational
- Added a button to connect to the multiserver which appears only when a SNES is connected and a server connection is not active
* Restore some features lost in WebUI
- Restore (found) notification on hints if the item has already been obtained
- Restore (x/y) indicator on received items, which indicates the number of items the client is waiting to receive from the client in a queue
* Fix a grammatical UI big causing player names to show only an apostrophe when possessive
* Add support for multiple SNES Devices, and switching between them
* freeze support for client
* make sure flask works when frozen
* UI Improvements
- Hint messages now actually show a found status via ✔ and ❌ emoji
- Active player name is always a different color than other players (orange for now)
- Add a toggle to show only entries relevant to the active player
- Added a WidgetArea
- Added a notes widget
* Received items now marked as relevant
* Include production build for deployment
* Notes now survive a browser close. Minimum width applied to monitor to prevent CSS issues.
* include webUi folder in setup.py
* Bugfixes for Monitor
- Fix a bug causing the monitor window to grow beyond it's intended content limit
- Reduced monitor content limit to 200 items
- Ensured each monitor entry has a unique key
* Prevent eslint from yelling at me about stupid things
* Add button to collapse sidebar, press enter on empty server input to disconnect on purpose
* WebUI is now aware of client disconnect, message log limit increased to 350, fix !missing output
* Update WebUI to v2.2.1
- Added color to WebUI for entrance-span
- Make !missing show total count at bottom of list to match /missing behavior
* Fix a bug causing clients version <= 2.2.0 to crash when anyone asks for a hint
- Also fix a bug in the WebUI causing the entrance location to always show as "somewhere"
* Update WebUI color palette (this cost me $50)
* allow text console input alongside web-ui
* remove Flask
a bit overkill for what we're doing
* remove jinja2
* Update WebUI to work with new hosting mechanism
* with flask gone, we no longer need subprocess shenanigans
* If multiple web ui clients try to run, at least present a working console
* Update MultiClient and WebUI to handle multiple clients simultaneously.
- The port on which the websocket for the WebUI is hosted is not chosen randomly from 5000 - 5999. This port is passed to the browser so it knows which MultiClient to connect to
- Removed failure condition if a web server is already running, as there is no need to run more than one web server on a single system. If an exception is thrown while attempting to launch a web server, a check is made for the port being unavailable. If the port is unavailable, it probably means the user is launching a second MultiClient. A web browser is then opened with a connection to the correct webui_socket_port.
- Add a /web command to the MultiClient to repoen the appropriate browser window and get params in case a user accidentally closes the tab
* Use proper name for WebUI
* move webui into /data with other data files
* make web ui optional
This is mostly for laptop users wanting to preserve some battery, should not be needed outside of that.
* fix direct server start
* re-add connection timer
* fix indentation
Co-authored-by: Chris <chris@legendserver.info>
											
										 
											2020-06-03 21:29:43 +02:00
										 |  |  |     if ctx.snes_socket: | 
					
						
							|  |  |  |         if not ctx.snes_socket.closed: | 
					
						
							|  |  |  |             await ctx.snes_socket.close() | 
					
						
							|  |  |  |         ctx.snes_socket = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | async def snes_autoreconnect(ctx: SNIContext) -> None: | 
					
						
							|  |  |  |     await asyncio.sleep(_global_snes_reconnect_delay) | 
					
						
							| 
									
										
										
										
											2022-11-04 17:57:58 +01:00
										 |  |  |     if ctx.snes_reconnect_address and not ctx.snes_socket and not ctx.snes_connect_task: | 
					
						
							|  |  |  |         ctx.snes_connect_task = asyncio.create_task(snes_connect(ctx, ctx.snes_reconnect_address), name="SNES Connect") | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												WebUI (#100)
* Object-Oriented base changes for web-ui prep
* remove debug raise
* optimize broadcast to serialize once
* Implement WebUI socket, static assets, and classes
- Still need to wrap logging functions and send output to UI
- UI commands are successfully being sent to the server
* GUI operational. Wrap logging functions, implement server address selection on GUI, automatically launch web browser when client websocket is served
* Update MultiServer status when a user disconnects / reconnects
* Implement colored item and hint checks, improve GUI readability
* Fix improper formatting on received items
* Update SNES connection status on disconnect / reconnect. Implement itemFound, prevent accidentally printing JS objects
* Minor text change for itemFound
* Fixed a very wrong comment
* Fixed client commands not working, fixed un-helpful error messages appearing in GUI
* Fix a bug causing a failure to connect to a multiworld server if a previously existing cached address was present and the client was loaded without an address passed in
* Convert WebUI to React /w Redux. WebSocket communications not yet operational.
* WebUI fully converted to React / Redux.
- Websocket communication operational
- Added a button to connect to the multiserver which appears only when a SNES is connected and a server connection is not active
* Restore some features lost in WebUI
- Restore (found) notification on hints if the item has already been obtained
- Restore (x/y) indicator on received items, which indicates the number of items the client is waiting to receive from the client in a queue
* Fix a grammatical UI big causing player names to show only an apostrophe when possessive
* Add support for multiple SNES Devices, and switching between them
* freeze support for client
* make sure flask works when frozen
* UI Improvements
- Hint messages now actually show a found status via ✔ and ❌ emoji
- Active player name is always a different color than other players (orange for now)
- Add a toggle to show only entries relevant to the active player
- Added a WidgetArea
- Added a notes widget
* Received items now marked as relevant
* Include production build for deployment
* Notes now survive a browser close. Minimum width applied to monitor to prevent CSS issues.
* include webUi folder in setup.py
* Bugfixes for Monitor
- Fix a bug causing the monitor window to grow beyond it's intended content limit
- Reduced monitor content limit to 200 items
- Ensured each monitor entry has a unique key
* Prevent eslint from yelling at me about stupid things
* Add button to collapse sidebar, press enter on empty server input to disconnect on purpose
* WebUI is now aware of client disconnect, message log limit increased to 350, fix !missing output
* Update WebUI to v2.2.1
- Added color to WebUI for entrance-span
- Make !missing show total count at bottom of list to match /missing behavior
* Fix a bug causing clients version <= 2.2.0 to crash when anyone asks for a hint
- Also fix a bug in the WebUI causing the entrance location to always show as "somewhere"
* Update WebUI color palette (this cost me $50)
* allow text console input alongside web-ui
* remove Flask
a bit overkill for what we're doing
* remove jinja2
* Update WebUI to work with new hosting mechanism
* with flask gone, we no longer need subprocess shenanigans
* If multiple web ui clients try to run, at least present a working console
* Update MultiClient and WebUI to handle multiple clients simultaneously.
- The port on which the websocket for the WebUI is hosted is not chosen randomly from 5000 - 5999. This port is passed to the browser so it knows which MultiClient to connect to
- Removed failure condition if a web server is already running, as there is no need to run more than one web server on a single system. If an exception is thrown while attempting to launch a web server, a check is made for the port being unavailable. If the port is unavailable, it probably means the user is launching a second MultiClient. A web browser is then opened with a connection to the correct webui_socket_port.
- Add a /web command to the MultiClient to repoen the appropriate browser window and get params in case a user accidentally closes the tab
* Use proper name for WebUI
* move webui into /data with other data files
* make web ui optional
This is mostly for laptop users wanting to preserve some battery, should not be needed outside of that.
* fix direct server start
* re-add connection timer
* fix indentation
Co-authored-by: Chris <chris@legendserver.info>
											
										 
											2020-06-03 21:29:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | async def snes_recv_loop(ctx: SNIContext) -> None: | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |     try: | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         if ctx.snes_socket is None: | 
					
						
							|  |  |  |             raise Exception("invalid context state - snes_socket not connected") | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |         async for msg in ctx.snes_socket: | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |             ctx.snes_recv_queue.put_nowait(typing.cast(bytes, msg)) | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |         snes_logger.warning("Snes disconnected") | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |     except Exception as e: | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         if not isinstance(e, WebSocketException): | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |             snes_logger.exception(e) | 
					
						
							|  |  |  |         snes_logger.error("Lost connection to the snes, type /snes to reconnect") | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |     finally: | 
					
						
							|  |  |  |         socket, ctx.snes_socket = ctx.snes_socket, None | 
					
						
							|  |  |  |         if socket is not None and not socket.closed: | 
					
						
							|  |  |  |             await socket.close() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |         ctx.snes_state = SNESState.SNES_DISCONNECTED | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |         ctx.snes_recv_queue = asyncio.Queue() | 
					
						
							|  |  |  |         ctx.hud_message_queue = [] | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-14 10:42:27 +01:00
										 |  |  |         ctx.rom = None | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-13 03:55:33 +01:00
										 |  |  |         if ctx.snes_reconnect_address: | 
					
						
							| 
									
										
										
										
											2022-11-04 17:57:58 +01:00
										 |  |  |             snes_logger.info(f"... automatically reconnecting to snes in {_global_snes_reconnect_delay} seconds") | 
					
						
							|  |  |  |             assert ctx.snes_autoreconnect_task is None | 
					
						
							|  |  |  |             ctx.snes_autoreconnect_task = asyncio.create_task(snes_autoreconnect(ctx), name="snes auto-reconnect") | 
					
						
							| 
									
										
										
										
											2020-01-13 03:55:33 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												WebUI (#100)
* Object-Oriented base changes for web-ui prep
* remove debug raise
* optimize broadcast to serialize once
* Implement WebUI socket, static assets, and classes
- Still need to wrap logging functions and send output to UI
- UI commands are successfully being sent to the server
* GUI operational. Wrap logging functions, implement server address selection on GUI, automatically launch web browser when client websocket is served
* Update MultiServer status when a user disconnects / reconnects
* Implement colored item and hint checks, improve GUI readability
* Fix improper formatting on received items
* Update SNES connection status on disconnect / reconnect. Implement itemFound, prevent accidentally printing JS objects
* Minor text change for itemFound
* Fixed a very wrong comment
* Fixed client commands not working, fixed un-helpful error messages appearing in GUI
* Fix a bug causing a failure to connect to a multiworld server if a previously existing cached address was present and the client was loaded without an address passed in
* Convert WebUI to React /w Redux. WebSocket communications not yet operational.
* WebUI fully converted to React / Redux.
- Websocket communication operational
- Added a button to connect to the multiserver which appears only when a SNES is connected and a server connection is not active
* Restore some features lost in WebUI
- Restore (found) notification on hints if the item has already been obtained
- Restore (x/y) indicator on received items, which indicates the number of items the client is waiting to receive from the client in a queue
* Fix a grammatical UI big causing player names to show only an apostrophe when possessive
* Add support for multiple SNES Devices, and switching between them
* freeze support for client
* make sure flask works when frozen
* UI Improvements
- Hint messages now actually show a found status via ✔ and ❌ emoji
- Active player name is always a different color than other players (orange for now)
- Add a toggle to show only entries relevant to the active player
- Added a WidgetArea
- Added a notes widget
* Received items now marked as relevant
* Include production build for deployment
* Notes now survive a browser close. Minimum width applied to monitor to prevent CSS issues.
* include webUi folder in setup.py
* Bugfixes for Monitor
- Fix a bug causing the monitor window to grow beyond it's intended content limit
- Reduced monitor content limit to 200 items
- Ensured each monitor entry has a unique key
* Prevent eslint from yelling at me about stupid things
* Add button to collapse sidebar, press enter on empty server input to disconnect on purpose
* WebUI is now aware of client disconnect, message log limit increased to 350, fix !missing output
* Update WebUI to v2.2.1
- Added color to WebUI for entrance-span
- Make !missing show total count at bottom of list to match /missing behavior
* Fix a bug causing clients version <= 2.2.0 to crash when anyone asks for a hint
- Also fix a bug in the WebUI causing the entrance location to always show as "somewhere"
* Update WebUI color palette (this cost me $50)
* allow text console input alongside web-ui
* remove Flask
a bit overkill for what we're doing
* remove jinja2
* Update WebUI to work with new hosting mechanism
* with flask gone, we no longer need subprocess shenanigans
* If multiple web ui clients try to run, at least present a working console
* Update MultiClient and WebUI to handle multiple clients simultaneously.
- The port on which the websocket for the WebUI is hosted is not chosen randomly from 5000 - 5999. This port is passed to the browser so it knows which MultiClient to connect to
- Removed failure condition if a web server is already running, as there is no need to run more than one web server on a single system. If an exception is thrown while attempting to launch a web server, a check is made for the port being unavailable. If the port is unavailable, it probably means the user is launching a second MultiClient. A web browser is then opened with a connection to the correct webui_socket_port.
- Add a /web command to the MultiClient to repoen the appropriate browser window and get params in case a user accidentally closes the tab
* Use proper name for WebUI
* move webui into /data with other data files
* make web ui optional
This is mostly for laptop users wanting to preserve some battery, should not be needed outside of that.
* fix direct server start
* re-add connection timer
* fix indentation
Co-authored-by: Chris <chris@legendserver.info>
											
										 
											2020-06-03 21:29:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | async def snes_read(ctx: SNIContext, address: int, size: int) -> typing.Optional[bytes]: | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |     try: | 
					
						
							|  |  |  |         await ctx.snes_request_lock.acquire() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         if ( | 
					
						
							|  |  |  |             ctx.snes_state != SNESState.SNES_ATTACHED or | 
					
						
							|  |  |  |             ctx.snes_socket is None or | 
					
						
							|  |  |  |             not ctx.snes_socket.open or | 
					
						
							|  |  |  |             ctx.snes_socket.closed | 
					
						
							|  |  |  |         ): | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |             return None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         GetAddress_Request: SNESRequest = { | 
					
						
							| 
									
										
										
										
											2021-01-21 23:37:58 +01:00
										 |  |  |             "Opcode": "GetAddress", | 
					
						
							|  |  |  |             "Space": "SNES", | 
					
						
							|  |  |  |             "Operands": [hex(address)[2:], hex(size)[2:]] | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |         } | 
					
						
							|  |  |  |         try: | 
					
						
							| 
									
										
										
										
											2020-10-19 08:26:31 +02:00
										 |  |  |             await ctx.snes_socket.send(dumps(GetAddress_Request)) | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         except ConnectionClosed: | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |             return None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         data: bytes = bytes() | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |         while len(data) < size: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 data += await asyncio.wait_for(ctx.snes_recv_queue.get(), 5) | 
					
						
							|  |  |  |             except asyncio.TimeoutError: | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         if len(data) != size: | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |             snes_logger.error('Error reading %s, requested %d bytes, received %d' % (hex(address), size, len(data))) | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |             if len(data): | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |                 snes_logger.error(str(data)) | 
					
						
							|  |  |  |                 snes_logger.warning('Communication Failure with SNI') | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |             if ctx.snes_socket is not None and not ctx.snes_socket.closed: | 
					
						
							|  |  |  |                 await ctx.snes_socket.close() | 
					
						
							|  |  |  |             return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         return data | 
					
						
							|  |  |  |     finally: | 
					
						
							|  |  |  |         ctx.snes_request_lock.release() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												WebUI (#100)
* Object-Oriented base changes for web-ui prep
* remove debug raise
* optimize broadcast to serialize once
* Implement WebUI socket, static assets, and classes
- Still need to wrap logging functions and send output to UI
- UI commands are successfully being sent to the server
* GUI operational. Wrap logging functions, implement server address selection on GUI, automatically launch web browser when client websocket is served
* Update MultiServer status when a user disconnects / reconnects
* Implement colored item and hint checks, improve GUI readability
* Fix improper formatting on received items
* Update SNES connection status on disconnect / reconnect. Implement itemFound, prevent accidentally printing JS objects
* Minor text change for itemFound
* Fixed a very wrong comment
* Fixed client commands not working, fixed un-helpful error messages appearing in GUI
* Fix a bug causing a failure to connect to a multiworld server if a previously existing cached address was present and the client was loaded without an address passed in
* Convert WebUI to React /w Redux. WebSocket communications not yet operational.
* WebUI fully converted to React / Redux.
- Websocket communication operational
- Added a button to connect to the multiserver which appears only when a SNES is connected and a server connection is not active
* Restore some features lost in WebUI
- Restore (found) notification on hints if the item has already been obtained
- Restore (x/y) indicator on received items, which indicates the number of items the client is waiting to receive from the client in a queue
* Fix a grammatical UI big causing player names to show only an apostrophe when possessive
* Add support for multiple SNES Devices, and switching between them
* freeze support for client
* make sure flask works when frozen
* UI Improvements
- Hint messages now actually show a found status via ✔ and ❌ emoji
- Active player name is always a different color than other players (orange for now)
- Add a toggle to show only entries relevant to the active player
- Added a WidgetArea
- Added a notes widget
* Received items now marked as relevant
* Include production build for deployment
* Notes now survive a browser close. Minimum width applied to monitor to prevent CSS issues.
* include webUi folder in setup.py
* Bugfixes for Monitor
- Fix a bug causing the monitor window to grow beyond it's intended content limit
- Reduced monitor content limit to 200 items
- Ensured each monitor entry has a unique key
* Prevent eslint from yelling at me about stupid things
* Add button to collapse sidebar, press enter on empty server input to disconnect on purpose
* WebUI is now aware of client disconnect, message log limit increased to 350, fix !missing output
* Update WebUI to v2.2.1
- Added color to WebUI for entrance-span
- Make !missing show total count at bottom of list to match /missing behavior
* Fix a bug causing clients version <= 2.2.0 to crash when anyone asks for a hint
- Also fix a bug in the WebUI causing the entrance location to always show as "somewhere"
* Update WebUI color palette (this cost me $50)
* allow text console input alongside web-ui
* remove Flask
a bit overkill for what we're doing
* remove jinja2
* Update WebUI to work with new hosting mechanism
* with flask gone, we no longer need subprocess shenanigans
* If multiple web ui clients try to run, at least present a working console
* Update MultiClient and WebUI to handle multiple clients simultaneously.
- The port on which the websocket for the WebUI is hosted is not chosen randomly from 5000 - 5999. This port is passed to the browser so it knows which MultiClient to connect to
- Removed failure condition if a web server is already running, as there is no need to run more than one web server on a single system. If an exception is thrown while attempting to launch a web server, a check is made for the port being unavailable. If the port is unavailable, it probably means the user is launching a second MultiClient. A web browser is then opened with a connection to the correct webui_socket_port.
- Add a /web command to the MultiClient to repoen the appropriate browser window and get params in case a user accidentally closes the tab
* Use proper name for WebUI
* move webui into /data with other data files
* make web ui optional
This is mostly for laptop users wanting to preserve some battery, should not be needed outside of that.
* fix direct server start
* re-add connection timer
* fix indentation
Co-authored-by: Chris <chris@legendserver.info>
											
										 
											2020-06-03 21:29:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | async def snes_write(ctx: SNIContext, write_list: typing.List[typing.Tuple[int, bytes]]) -> bool: | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |     try: | 
					
						
							|  |  |  |         await ctx.snes_request_lock.acquire() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |         if ctx.snes_state != SNESState.SNES_ATTACHED or ctx.snes_socket is None or \ | 
					
						
							|  |  |  |                 not ctx.snes_socket.open or ctx.snes_socket.closed: | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |             return False | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         PutAddress_Request: SNESRequest = {"Opcode": "PutAddress", "Operands": [], 'Space': 'SNES'} | 
					
						
							| 
									
										
										
										
											2021-07-07 03:45:27 +02:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |             for address, data in write_list: | 
					
						
							| 
									
										
										
										
											2021-07-07 03:45:27 +02:00
										 |  |  |                 PutAddress_Request['Operands'] = [hex(address)[2:], hex(len(data))[2:]] | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |                 # REVIEW: above: `if snes_socket is None: return False` | 
					
						
							|  |  |  |                 # Does it need to be checked again? | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                 if ctx.snes_socket is not None: | 
					
						
							| 
									
										
										
										
											2020-10-19 08:26:31 +02:00
										 |  |  |                     await ctx.snes_socket.send(dumps(PutAddress_Request)) | 
					
						
							| 
									
										
										
										
											2021-07-07 03:45:27 +02:00
										 |  |  |                     await ctx.snes_socket.send(data) | 
					
						
							| 
									
										
										
										
											2020-12-01 21:57:18 +01:00
										 |  |  |                 else: | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |                     snes_logger.warning(f"Could not send data to SNES: {data}") | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         except ConnectionClosed: | 
					
						
							| 
									
										
										
										
											2021-07-07 03:45:27 +02:00
										 |  |  |             return False | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         return True | 
					
						
							|  |  |  |     finally: | 
					
						
							|  |  |  |         ctx.snes_request_lock.release() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												WebUI (#100)
* Object-Oriented base changes for web-ui prep
* remove debug raise
* optimize broadcast to serialize once
* Implement WebUI socket, static assets, and classes
- Still need to wrap logging functions and send output to UI
- UI commands are successfully being sent to the server
* GUI operational. Wrap logging functions, implement server address selection on GUI, automatically launch web browser when client websocket is served
* Update MultiServer status when a user disconnects / reconnects
* Implement colored item and hint checks, improve GUI readability
* Fix improper formatting on received items
* Update SNES connection status on disconnect / reconnect. Implement itemFound, prevent accidentally printing JS objects
* Minor text change for itemFound
* Fixed a very wrong comment
* Fixed client commands not working, fixed un-helpful error messages appearing in GUI
* Fix a bug causing a failure to connect to a multiworld server if a previously existing cached address was present and the client was loaded without an address passed in
* Convert WebUI to React /w Redux. WebSocket communications not yet operational.
* WebUI fully converted to React / Redux.
- Websocket communication operational
- Added a button to connect to the multiserver which appears only when a SNES is connected and a server connection is not active
* Restore some features lost in WebUI
- Restore (found) notification on hints if the item has already been obtained
- Restore (x/y) indicator on received items, which indicates the number of items the client is waiting to receive from the client in a queue
* Fix a grammatical UI big causing player names to show only an apostrophe when possessive
* Add support for multiple SNES Devices, and switching between them
* freeze support for client
* make sure flask works when frozen
* UI Improvements
- Hint messages now actually show a found status via ✔ and ❌ emoji
- Active player name is always a different color than other players (orange for now)
- Add a toggle to show only entries relevant to the active player
- Added a WidgetArea
- Added a notes widget
* Received items now marked as relevant
* Include production build for deployment
* Notes now survive a browser close. Minimum width applied to monitor to prevent CSS issues.
* include webUi folder in setup.py
* Bugfixes for Monitor
- Fix a bug causing the monitor window to grow beyond it's intended content limit
- Reduced monitor content limit to 200 items
- Ensured each monitor entry has a unique key
* Prevent eslint from yelling at me about stupid things
* Add button to collapse sidebar, press enter on empty server input to disconnect on purpose
* WebUI is now aware of client disconnect, message log limit increased to 350, fix !missing output
* Update WebUI to v2.2.1
- Added color to WebUI for entrance-span
- Make !missing show total count at bottom of list to match /missing behavior
* Fix a bug causing clients version <= 2.2.0 to crash when anyone asks for a hint
- Also fix a bug in the WebUI causing the entrance location to always show as "somewhere"
* Update WebUI color palette (this cost me $50)
* allow text console input alongside web-ui
* remove Flask
a bit overkill for what we're doing
* remove jinja2
* Update WebUI to work with new hosting mechanism
* with flask gone, we no longer need subprocess shenanigans
* If multiple web ui clients try to run, at least present a working console
* Update MultiClient and WebUI to handle multiple clients simultaneously.
- The port on which the websocket for the WebUI is hosted is not chosen randomly from 5000 - 5999. This port is passed to the browser so it knows which MultiClient to connect to
- Removed failure condition if a web server is already running, as there is no need to run more than one web server on a single system. If an exception is thrown while attempting to launch a web server, a check is made for the port being unavailable. If the port is unavailable, it probably means the user is launching a second MultiClient. A web browser is then opened with a connection to the correct webui_socket_port.
- Add a /web command to the MultiClient to repoen the appropriate browser window and get params in case a user accidentally closes the tab
* Use proper name for WebUI
* move webui into /data with other data files
* make web ui optional
This is mostly for laptop users wanting to preserve some battery, should not be needed outside of that.
* fix direct server start
* re-add connection timer
* fix indentation
Co-authored-by: Chris <chris@legendserver.info>
											
										 
											2020-06-03 21:29:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | def snes_buffered_write(ctx: SNIContext, address: int, data: bytes) -> None: | 
					
						
							| 
									
										
										
										
											2020-12-01 21:57:18 +01:00
										 |  |  |     if ctx.snes_write_buffer and (ctx.snes_write_buffer[-1][0] + len(ctx.snes_write_buffer[-1][1])) == address: | 
					
						
							|  |  |  |         # append to existing write command, bundling them | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |         ctx.snes_write_buffer[-1] = (ctx.snes_write_buffer[-1][0], ctx.snes_write_buffer[-1][1] + data) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         ctx.snes_write_buffer.append((address, data)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
											  
											
												WebUI (#100)
* Object-Oriented base changes for web-ui prep
* remove debug raise
* optimize broadcast to serialize once
* Implement WebUI socket, static assets, and classes
- Still need to wrap logging functions and send output to UI
- UI commands are successfully being sent to the server
* GUI operational. Wrap logging functions, implement server address selection on GUI, automatically launch web browser when client websocket is served
* Update MultiServer status when a user disconnects / reconnects
* Implement colored item and hint checks, improve GUI readability
* Fix improper formatting on received items
* Update SNES connection status on disconnect / reconnect. Implement itemFound, prevent accidentally printing JS objects
* Minor text change for itemFound
* Fixed a very wrong comment
* Fixed client commands not working, fixed un-helpful error messages appearing in GUI
* Fix a bug causing a failure to connect to a multiworld server if a previously existing cached address was present and the client was loaded without an address passed in
* Convert WebUI to React /w Redux. WebSocket communications not yet operational.
* WebUI fully converted to React / Redux.
- Websocket communication operational
- Added a button to connect to the multiserver which appears only when a SNES is connected and a server connection is not active
* Restore some features lost in WebUI
- Restore (found) notification on hints if the item has already been obtained
- Restore (x/y) indicator on received items, which indicates the number of items the client is waiting to receive from the client in a queue
* Fix a grammatical UI big causing player names to show only an apostrophe when possessive
* Add support for multiple SNES Devices, and switching between them
* freeze support for client
* make sure flask works when frozen
* UI Improvements
- Hint messages now actually show a found status via ✔ and ❌ emoji
- Active player name is always a different color than other players (orange for now)
- Add a toggle to show only entries relevant to the active player
- Added a WidgetArea
- Added a notes widget
* Received items now marked as relevant
* Include production build for deployment
* Notes now survive a browser close. Minimum width applied to monitor to prevent CSS issues.
* include webUi folder in setup.py
* Bugfixes for Monitor
- Fix a bug causing the monitor window to grow beyond it's intended content limit
- Reduced monitor content limit to 200 items
- Ensured each monitor entry has a unique key
* Prevent eslint from yelling at me about stupid things
* Add button to collapse sidebar, press enter on empty server input to disconnect on purpose
* WebUI is now aware of client disconnect, message log limit increased to 350, fix !missing output
* Update WebUI to v2.2.1
- Added color to WebUI for entrance-span
- Make !missing show total count at bottom of list to match /missing behavior
* Fix a bug causing clients version <= 2.2.0 to crash when anyone asks for a hint
- Also fix a bug in the WebUI causing the entrance location to always show as "somewhere"
* Update WebUI color palette (this cost me $50)
* allow text console input alongside web-ui
* remove Flask
a bit overkill for what we're doing
* remove jinja2
* Update WebUI to work with new hosting mechanism
* with flask gone, we no longer need subprocess shenanigans
* If multiple web ui clients try to run, at least present a working console
* Update MultiClient and WebUI to handle multiple clients simultaneously.
- The port on which the websocket for the WebUI is hosted is not chosen randomly from 5000 - 5999. This port is passed to the browser so it knows which MultiClient to connect to
- Removed failure condition if a web server is already running, as there is no need to run more than one web server on a single system. If an exception is thrown while attempting to launch a web server, a check is made for the port being unavailable. If the port is unavailable, it probably means the user is launching a second MultiClient. A web browser is then opened with a connection to the correct webui_socket_port.
- Add a /web command to the MultiClient to repoen the appropriate browser window and get params in case a user accidentally closes the tab
* Use proper name for WebUI
* move webui into /data with other data files
* make web ui optional
This is mostly for laptop users wanting to preserve some battery, should not be needed outside of that.
* fix direct server start
* re-add connection timer
* fix indentation
Co-authored-by: Chris <chris@legendserver.info>
											
										 
											2020-06-03 21:29:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | async def snes_flush_writes(ctx: SNIContext) -> None: | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |     if not ctx.snes_write_buffer: | 
					
						
							|  |  |  |         return | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-12-01 21:57:18 +01:00
										 |  |  |     # swap buffers | 
					
						
							|  |  |  |     ctx.snes_write_buffer, writes = [], ctx.snes_write_buffer | 
					
						
							|  |  |  |     await snes_write(ctx, writes) | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
											  
											
												WebUI (#100)
* Object-Oriented base changes for web-ui prep
* remove debug raise
* optimize broadcast to serialize once
* Implement WebUI socket, static assets, and classes
- Still need to wrap logging functions and send output to UI
- UI commands are successfully being sent to the server
* GUI operational. Wrap logging functions, implement server address selection on GUI, automatically launch web browser when client websocket is served
* Update MultiServer status when a user disconnects / reconnects
* Implement colored item and hint checks, improve GUI readability
* Fix improper formatting on received items
* Update SNES connection status on disconnect / reconnect. Implement itemFound, prevent accidentally printing JS objects
* Minor text change for itemFound
* Fixed a very wrong comment
* Fixed client commands not working, fixed un-helpful error messages appearing in GUI
* Fix a bug causing a failure to connect to a multiworld server if a previously existing cached address was present and the client was loaded without an address passed in
* Convert WebUI to React /w Redux. WebSocket communications not yet operational.
* WebUI fully converted to React / Redux.
- Websocket communication operational
- Added a button to connect to the multiserver which appears only when a SNES is connected and a server connection is not active
* Restore some features lost in WebUI
- Restore (found) notification on hints if the item has already been obtained
- Restore (x/y) indicator on received items, which indicates the number of items the client is waiting to receive from the client in a queue
* Fix a grammatical UI big causing player names to show only an apostrophe when possessive
* Add support for multiple SNES Devices, and switching between them
* freeze support for client
* make sure flask works when frozen
* UI Improvements
- Hint messages now actually show a found status via ✔ and ❌ emoji
- Active player name is always a different color than other players (orange for now)
- Add a toggle to show only entries relevant to the active player
- Added a WidgetArea
- Added a notes widget
* Received items now marked as relevant
* Include production build for deployment
* Notes now survive a browser close. Minimum width applied to monitor to prevent CSS issues.
* include webUi folder in setup.py
* Bugfixes for Monitor
- Fix a bug causing the monitor window to grow beyond it's intended content limit
- Reduced monitor content limit to 200 items
- Ensured each monitor entry has a unique key
* Prevent eslint from yelling at me about stupid things
* Add button to collapse sidebar, press enter on empty server input to disconnect on purpose
* WebUI is now aware of client disconnect, message log limit increased to 350, fix !missing output
* Update WebUI to v2.2.1
- Added color to WebUI for entrance-span
- Make !missing show total count at bottom of list to match /missing behavior
* Fix a bug causing clients version <= 2.2.0 to crash when anyone asks for a hint
- Also fix a bug in the WebUI causing the entrance location to always show as "somewhere"
* Update WebUI color palette (this cost me $50)
* allow text console input alongside web-ui
* remove Flask
a bit overkill for what we're doing
* remove jinja2
* Update WebUI to work with new hosting mechanism
* with flask gone, we no longer need subprocess shenanigans
* If multiple web ui clients try to run, at least present a working console
* Update MultiClient and WebUI to handle multiple clients simultaneously.
- The port on which the websocket for the WebUI is hosted is not chosen randomly from 5000 - 5999. This port is passed to the browser so it knows which MultiClient to connect to
- Removed failure condition if a web server is already running, as there is no need to run more than one web server on a single system. If an exception is thrown while attempting to launch a web server, a check is made for the port being unavailable. If the port is unavailable, it probably means the user is launching a second MultiClient. A web browser is then opened with a connection to the correct webui_socket_port.
- Add a /web command to the MultiClient to repoen the appropriate browser window and get params in case a user accidentally closes the tab
* Use proper name for WebUI
* move webui into /data with other data files
* make web ui optional
This is mostly for laptop users wanting to preserve some battery, should not be needed outside of that.
* fix direct server start
* re-add connection timer
* fix indentation
Co-authored-by: Chris <chris@legendserver.info>
											
										 
											2020-06-03 21:29:43 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | async def game_watcher(ctx: SNIContext) -> None: | 
					
						
							| 
									
										
										
										
											2020-04-24 20:07:28 -07:00
										 |  |  |     perf_counter = time.perf_counter() | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |     while not ctx.exit_event.is_set(): | 
					
						
							| 
									
										
										
										
											2020-01-18 11:28:08 +01:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2020-04-24 20:07:28 -07:00
										 |  |  |             await asyncio.wait_for(ctx.watcher_event.wait(), 0.125) | 
					
						
							| 
									
										
										
										
											2020-01-18 11:28:08 +01:00
										 |  |  |         except asyncio.TimeoutError: | 
					
						
							|  |  |  |             pass | 
					
						
							|  |  |  |         ctx.watcher_event.clear() | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         if not ctx.rom or not ctx.client_handler: | 
					
						
							| 
									
										
										
										
											2020-04-24 20:07:28 -07:00
										 |  |  |             ctx.finished_game = False | 
					
						
							| 
									
										
										
										
											2021-12-02 00:11:42 -05:00
										 |  |  |             ctx.death_link_allow_survive = False | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |             from worlds.AutoSNIClient import AutoSNIClientRegister | 
					
						
							|  |  |  |             ctx.client_handler = await AutoSNIClientRegister.get_handler(ctx) | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |             if not ctx.client_handler: | 
					
						
							| 
									
										
										
										
											2020-04-24 20:07:28 -07:00
										 |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |             if not ctx.rom: | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |             if not ctx.prev_rom or ctx.prev_rom != ctx.rom: | 
					
						
							|  |  |  |                 ctx.locations_checked = set() | 
					
						
							|  |  |  |                 ctx.locations_scouted = set() | 
					
						
							|  |  |  |                 ctx.locations_info = {} | 
					
						
							|  |  |  |             ctx.prev_rom = ctx.rom | 
					
						
							| 
									
										
										
										
											2022-03-15 08:55:57 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |             if ctx.awaiting_rom: | 
					
						
							|  |  |  |                 await ctx.server_auth(False) | 
					
						
							|  |  |  |             elif ctx.server is None: | 
					
						
							|  |  |  |                 snes_logger.warning("ROM detected but no active multiworld server connection. " + | 
					
						
							|  |  |  |                                     "Connect using command: /connect server:port") | 
					
						
							| 
									
										
										
										
											2022-03-15 08:55:57 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         if not ctx.client_handler: | 
					
						
							|  |  |  |             continue | 
					
						
							| 
									
										
										
										
											2022-03-15 08:55:57 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         rom_validated = await ctx.client_handler.validate_rom(ctx) | 
					
						
							| 
									
										
										
										
											2022-03-15 08:55:57 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         if not rom_validated or (ctx.auth and ctx.auth != ctx.rom): | 
					
						
							|  |  |  |             snes_logger.warning("ROM change detected, please reconnect to the multiworld server") | 
					
						
							| 
									
										
										
										
											2022-11-04 17:57:58 +01:00
										 |  |  |             await ctx.disconnect(allow_autoreconnect=True) | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |             ctx.client_handler = None | 
					
						
							|  |  |  |             ctx.rom = None | 
					
						
							|  |  |  |             ctx.command_processor(ctx).connect_to_snes() | 
					
						
							|  |  |  |             continue | 
					
						
							| 
									
										
										
										
											2022-03-15 08:55:57 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         delay = 7 if ctx.slow_mode else 0 | 
					
						
							|  |  |  |         if time.perf_counter() - perf_counter < delay: | 
					
						
							|  |  |  |             continue | 
					
						
							| 
									
										
										
										
											2022-03-15 08:55:57 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         perf_counter = time.perf_counter() | 
					
						
							| 
									
										
										
										
											2022-03-15 08:55:57 -04:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |         await ctx.client_handler.game_watcher(ctx) | 
					
						
							| 
									
										
										
										
											2022-03-15 08:55:57 -04:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | async def run_game(romfile: str) -> None: | 
					
						
							|  |  |  |     auto_start = typing.cast(typing.Union[bool, str], | 
					
						
							|  |  |  |                              Utils.get_options()["sni_options"].get("snes_rom_start", True)) | 
					
						
							| 
									
										
										
										
											2020-07-15 17:19:16 +02:00
										 |  |  |     if auto_start is True: | 
					
						
							|  |  |  |         import webbrowser | 
					
						
							|  |  |  |         webbrowser.open(romfile) | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |     elif isinstance(auto_start, str) and os.path.isfile(auto_start): | 
					
						
							| 
									
										
										
										
											2020-07-15 17:19:16 +02:00
										 |  |  |         subprocess.Popen([auto_start, romfile], | 
					
						
							|  |  |  |                          stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) | 
					
						
							| 
									
										
										
										
											2020-03-10 00:38:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  | async def main() -> None: | 
					
						
							| 
									
										
											  
											
												WebUI (#100)
* Object-Oriented base changes for web-ui prep
* remove debug raise
* optimize broadcast to serialize once
* Implement WebUI socket, static assets, and classes
- Still need to wrap logging functions and send output to UI
- UI commands are successfully being sent to the server
* GUI operational. Wrap logging functions, implement server address selection on GUI, automatically launch web browser when client websocket is served
* Update MultiServer status when a user disconnects / reconnects
* Implement colored item and hint checks, improve GUI readability
* Fix improper formatting on received items
* Update SNES connection status on disconnect / reconnect. Implement itemFound, prevent accidentally printing JS objects
* Minor text change for itemFound
* Fixed a very wrong comment
* Fixed client commands not working, fixed un-helpful error messages appearing in GUI
* Fix a bug causing a failure to connect to a multiworld server if a previously existing cached address was present and the client was loaded without an address passed in
* Convert WebUI to React /w Redux. WebSocket communications not yet operational.
* WebUI fully converted to React / Redux.
- Websocket communication operational
- Added a button to connect to the multiserver which appears only when a SNES is connected and a server connection is not active
* Restore some features lost in WebUI
- Restore (found) notification on hints if the item has already been obtained
- Restore (x/y) indicator on received items, which indicates the number of items the client is waiting to receive from the client in a queue
* Fix a grammatical UI big causing player names to show only an apostrophe when possessive
* Add support for multiple SNES Devices, and switching between them
* freeze support for client
* make sure flask works when frozen
* UI Improvements
- Hint messages now actually show a found status via ✔ and ❌ emoji
- Active player name is always a different color than other players (orange for now)
- Add a toggle to show only entries relevant to the active player
- Added a WidgetArea
- Added a notes widget
* Received items now marked as relevant
* Include production build for deployment
* Notes now survive a browser close. Minimum width applied to monitor to prevent CSS issues.
* include webUi folder in setup.py
* Bugfixes for Monitor
- Fix a bug causing the monitor window to grow beyond it's intended content limit
- Reduced monitor content limit to 200 items
- Ensured each monitor entry has a unique key
* Prevent eslint from yelling at me about stupid things
* Add button to collapse sidebar, press enter on empty server input to disconnect on purpose
* WebUI is now aware of client disconnect, message log limit increased to 350, fix !missing output
* Update WebUI to v2.2.1
- Added color to WebUI for entrance-span
- Make !missing show total count at bottom of list to match /missing behavior
* Fix a bug causing clients version <= 2.2.0 to crash when anyone asks for a hint
- Also fix a bug in the WebUI causing the entrance location to always show as "somewhere"
* Update WebUI color palette (this cost me $50)
* allow text console input alongside web-ui
* remove Flask
a bit overkill for what we're doing
* remove jinja2
* Update WebUI to work with new hosting mechanism
* with flask gone, we no longer need subprocess shenanigans
* If multiple web ui clients try to run, at least present a working console
* Update MultiClient and WebUI to handle multiple clients simultaneously.
- The port on which the websocket for the WebUI is hosted is not chosen randomly from 5000 - 5999. This port is passed to the browser so it knows which MultiClient to connect to
- Removed failure condition if a web server is already running, as there is no need to run more than one web server on a single system. If an exception is thrown while attempting to launch a web server, a check is made for the port being unavailable. If the port is unavailable, it probably means the user is launching a second MultiClient. A web browser is then opened with a connection to the correct webui_socket_port.
- Add a /web command to the MultiClient to repoen the appropriate browser window and get params in case a user accidentally closes the tab
* Use proper name for WebUI
* move webui into /data with other data files
* make web ui optional
This is mostly for laptop users wanting to preserve some battery, should not be needed outside of that.
* fix direct server start
* re-add connection timer
* fix indentation
Co-authored-by: Chris <chris@legendserver.info>
											
										 
											2020-06-03 21:29:43 +02:00
										 |  |  |     multiprocessing.freeze_support() | 
					
						
							| 
									
										
										
										
											2021-11-09 12:53:05 +01:00
										 |  |  |     parser = get_base_parser() | 
					
						
							| 
									
										
										
										
											2020-03-06 00:48:23 +01:00
										 |  |  |     parser.add_argument('diff_file', default="", type=str, nargs="?", | 
					
						
							| 
									
										
										
										
											2021-01-03 14:32:32 +01:00
										 |  |  |                         help='Path to a Archipelago Binary Patch file') | 
					
						
							| 
									
										
										
										
											2022-07-08 09:36:14 -05:00
										 |  |  |     parser.add_argument('--snes', default='localhost:23074', help='Address of the SNI server.') | 
					
						
							| 
									
										
										
										
											2020-01-18 12:21:57 +01:00
										 |  |  |     parser.add_argument('--loglevel', default='info', choices=['debug', 'info', 'warning', 'error', 'critical']) | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |     args = parser.parse_args() | 
					
						
							| 
									
										
										
										
											2021-11-09 12:53:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-06 00:48:23 +01:00
										 |  |  |     if args.diff_file: | 
					
						
							|  |  |  |         import Patch | 
					
						
							| 
									
										
										
										
											2020-04-26 15:14:30 +02:00
										 |  |  |         logging.info("Patch file was supplied. Creating sfc rom..") | 
					
						
							| 
									
										
										
										
											2022-06-23 19:26:30 +02:00
										 |  |  |         try: | 
					
						
							|  |  |  |             meta, romfile = Patch.create_rom_file(args.diff_file) | 
					
						
							|  |  |  |         except Exception as e: | 
					
						
							| 
									
										
										
										
											2022-09-30 00:36:30 +02:00
										 |  |  |             Utils.messagebox('Error', str(e), True) | 
					
						
							| 
									
										
										
										
											2022-06-23 19:26:30 +02:00
										 |  |  |             raise | 
					
						
							| 
									
										
										
										
											2022-09-30 00:36:30 +02:00
										 |  |  |         args.connect = meta["server"] | 
					
						
							| 
									
										
										
										
											2020-03-10 00:38:29 +01:00
										 |  |  |         logging.info(f"Wrote rom file to {romfile}") | 
					
						
							| 
									
										
										
										
											2021-11-14 21:14:22 +01:00
										 |  |  |         if args.diff_file.endswith(".apsoe"): | 
					
						
							|  |  |  |             import webbrowser | 
					
						
							| 
									
										
										
										
											2022-09-30 00:36:30 +02:00
										 |  |  |             webbrowser.open(f"http://www.evermizer.com/apclient/#server={meta['server']}") | 
					
						
							| 
									
										
										
										
											2021-11-14 21:14:22 +01:00
										 |  |  |             logging.info("Starting Evermizer Client in your Browser...") | 
					
						
							|  |  |  |             import time | 
					
						
							|  |  |  |             time.sleep(3) | 
					
						
							|  |  |  |             sys.exit() | 
					
						
							| 
									
										
										
										
											2022-09-30 00:36:30 +02:00
										 |  |  |         elif args.diff_file.endswith(".aplttp"): | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |             from worlds.alttp.Client import get_alttp_settings | 
					
						
							| 
									
										
										
										
											2022-01-20 04:19:58 +01:00
										 |  |  |             adjustedromfile, adjusted = get_alttp_settings(romfile) | 
					
						
							| 
									
										
										
										
											2022-11-02 07:51:35 -07:00
										 |  |  |             async_start(run_game(adjustedromfile if adjusted else romfile)) | 
					
						
							| 
									
										
										
										
											2021-11-14 21:14:22 +01:00
										 |  |  |         else: | 
					
						
							| 
									
										
										
										
											2022-11-02 07:51:35 -07:00
										 |  |  |             async_start(run_game(romfile)) | 
					
						
							| 
									
										
										
										
											2020-01-18 12:21:57 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-10-25 13:54:43 -04:00
										 |  |  |     ctx = SNIContext(args.snes, args.connect, args.password) | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |     if ctx.server_task is None: | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |         ctx.server_task = asyncio.create_task(server_loop(ctx), name="ServerLoop") | 
					
						
							| 
									
										
										
										
											2022-04-27 22:11:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-20 22:31:17 +02:00
										 |  |  |     if gui_enabled: | 
					
						
							| 
									
										
										
										
											2022-04-27 22:11:11 +02:00
										 |  |  |         ctx.run_gui() | 
					
						
							|  |  |  |     ctx.run_cli() | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-06-12 03:20:03 +02:00
										 |  |  |     ctx.snes_connect_task = asyncio.create_task(snes_connect(ctx, ctx.snes_address), name="SNES Connect") | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |     watcher_task = asyncio.create_task(game_watcher(ctx), name="GameWatcher") | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     await ctx.exit_event.wait() | 
					
						
							| 
									
										
										
										
											2021-11-21 02:02:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-14 10:42:27 +01:00
										 |  |  |     ctx.server_address = None | 
					
						
							| 
									
										
										
										
											2020-01-13 03:55:33 +01:00
										 |  |  |     ctx.snes_reconnect_address = None | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |     if ctx.snes_socket is not None and not ctx.snes_socket.closed: | 
					
						
							|  |  |  |         await ctx.snes_socket.close() | 
					
						
							| 
									
										
										
										
											2021-11-21 02:02:40 +01:00
										 |  |  |     await watcher_task | 
					
						
							|  |  |  |     await ctx.shutdown() | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-03-04 21:36:18 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | if __name__ == '__main__': | 
					
						
							| 
									
										
										
										
											2020-01-18 10:05:59 +01:00
										 |  |  |     colorama.init() | 
					
						
							| 
									
										
										
										
											2022-04-27 22:11:11 +02:00
										 |  |  |     asyncio.run(main()) | 
					
						
							| 
									
										
										
										
											2020-01-18 10:05:59 +01:00
										 |  |  |     colorama.deinit() |