| 
									
										
										
										
											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-01-21 23:58:30 +01:00
										 |  |  | import shutil | 
					
						
							| 
									
										
										
										
											2021-08-29 17:38:35 +02:00
										 |  |  | import logging | 
					
						
							|  |  |  | import asyncio | 
					
						
							| 
									
										
										
										
											2021-02-21 23:54:08 +01:00
										 |  |  | from json import loads, dumps | 
					
						
							| 
									
										
										
										
											2020-06-04 21:27:29 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-10 15:35:43 +01:00
										 |  |  | from Utils import get_item_name_from_id, init_logging | 
					
						
							| 
									
										
										
										
											2021-11-03 19:58:40 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-10 15:35:43 +01:00
										 |  |  | if __name__ == "__main__": | 
					
						
							| 
									
										
										
										
											2021-11-17 22:46:32 +01:00
										 |  |  |     init_logging("SNIClient", exception_logger="Client") | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-18 15:04:39 +01:00
										 |  |  | import colorama | 
					
						
							| 
									
										
										
										
											2020-06-06 22:54:09 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-21 23:37:58 +01:00
										 |  |  | from NetUtils import * | 
					
						
							| 
									
										
										
										
											2021-01-30 23:43:15 +01:00
										 |  |  | from worlds.alttp import Regions, Shops | 
					
						
							| 
									
										
										
										
											2021-02-21 20:17:24 +01:00
										 |  |  | from worlds.alttp import Items | 
					
						
							| 
									
										
										
										
											2021-10-21 08:15:47 +02:00
										 |  |  | from worlds.alttp.Rom import ROM_PLAYER_LIMIT | 
					
						
							| 
									
										
										
										
											2021-11-13 09:40:20 -05:00
										 |  |  | from worlds.sm.Rom import ROM_PLAYER_LIMIT as SM_ROM_PLAYER_LIMIT | 
					
						
							| 
									
										
										
										
											2020-02-16 15:32:40 +01:00
										 |  |  | import Utils | 
					
						
							| 
									
										
										
										
											2021-11-10 15:35:43 +01:00
										 |  |  | from CommonClient import CommonContext, server_loop, console_loop, ClientCommandProcessor, gui_enabled, get_base_parser | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | from Patch import GAME_ALTTP, GAME_SM | 
					
						
							| 
									
										
										
										
											2021-09-30 09:09:21 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  | snes_logger = logging.getLogger("SNES") | 
					
						
							| 
									
										
										
										
											2021-01-19 06:37:35 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  | from MultiServer import mark_raw | 
					
						
							| 
									
										
										
										
											2020-02-22 19:42:44 +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 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  | class LttPCommandProcessor(ClientCommandProcessor): | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  |     ctx: Context | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |     def _cmd_slow_mode(self, toggle: str = ""): | 
					
						
							|  |  |  |         """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,
 | 
					
						
							|  |  |  |         otherwise show available devices; and a SNES device number if more than one SNES is detected"""
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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: | 
					
						
							|  |  |  |             snes_address = options[0] | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-28 21:20:45 -05:00
										 |  |  |         if num_options > 1: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 snes_device_number = int(options[1]) | 
					
						
							|  |  |  |             except: | 
					
						
							|  |  |  |                 pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |         self.ctx.snes_reconnect_address = None | 
					
						
							| 
									
										
										
										
											2021-11-21 02:02:40 +01:00
										 |  |  |         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 | 
					
						
							|  |  |  |         if self.ctx.snes_socket is not None and not self.ctx.snes_socket.closed: | 
					
						
							|  |  |  |             asyncio.create_task(self.ctx.snes_socket.close()) | 
					
						
							|  |  |  |             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)])) | 
					
						
							|  |  |  |     #     asyncio.create_task(snes_flush_writes(self.ctx)) | 
					
						
							|  |  |  |     #     self.output("Data Sent") | 
					
						
							|  |  |  |     #     return True | 
					
						
							| 
									
										
										
										
											2021-11-11 16:09:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-02 11:11:57 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  | class Context(CommonContext): | 
					
						
							|  |  |  |     command_processor = LttPCommandProcessor | 
					
						
							| 
									
										
										
										
											2021-07-12 20:07:02 +02:00
										 |  |  |     game = "A Link to the Past" | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-19 21:52:08 +02:00
										 |  |  |     def __init__(self, snes_address, server_address, password): | 
					
						
							|  |  |  |         super(Context, 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 | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |     async def connection_closed(self): | 
					
						
							|  |  |  |         await super(Context, self).connection_closed() | 
					
						
							|  |  |  |         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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |     def event_invalid_slot(self): | 
					
						
							|  |  |  |         if self.snes_socket is not None and not self.snes_socket.closed: | 
					
						
							|  |  |  |             asyncio.create_task(self.snes_socket.close()) | 
					
						
							|  |  |  |         raise Exception('Invalid ROM detected, ' | 
					
						
							|  |  |  |                         'please verify that you have loaded the correct rom and reconnect your snes (/snes)') | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  |     async def server_auth(self, password_requested: bool = False): | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |         if password_requested and not self.password: | 
					
						
							|  |  |  |             await super(Context, self).server_auth(password_requested) | 
					
						
							|  |  |  |         if self.rom is None: | 
					
						
							|  |  |  |             self.awaiting_rom = True | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |             snes_logger.info( | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02: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 | 
					
						
							|  |  |  |         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
										 |  |  | 
 | 
					
						
							|  |  |  |     def on_deathlink(self, data: dict): | 
					
						
							| 
									
										
										
										
											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)) | 
					
						
							| 
									
										
										
										
											2021-11-04 13:23:13 +01:00
										 |  |  |         super(Context, self).on_deathlink(data) | 
					
						
							| 
									
										
										
										
											2021-02-21 20:17:24 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 06:48:23 -08:00
										 |  |  |     async def handle_deathlink_state(self, currently_dead: bool): | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-19 06:37:35 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-11 16:09:08 +01:00
										 |  |  | async def deathlink_kill_player(ctx: Context): | 
					
						
							| 
									
										
										
										
											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: | 
					
						
							| 
									
										
										
										
											2021-11-12 14:36:34 +01:00
										 |  |  |         if ctx.game == GAME_ALTTP: | 
					
						
							|  |  |  |             snes_buffered_write(ctx, WRAM_START + 0xF36D, bytes([0]))  # set current health to 0 | 
					
						
							|  |  |  |             snes_buffered_write(ctx, WRAM_START + 0x0373, bytes([8]))  # deal 1 full heart of damage at next opportunity | 
					
						
							|  |  |  |         elif ctx.game == GAME_SM: | 
					
						
							|  |  |  |             snes_buffered_write(ctx, WRAM_START + 0x09C2, bytes([0, 0]))  # set current health to 0 | 
					
						
							| 
									
										
										
										
											2021-12-02 00:11:42 -05:00
										 |  |  |             if not ctx.death_link_allow_survive: | 
					
						
							|  |  |  |                 snes_buffered_write(ctx, WRAM_START + 0x09D6, bytes([0, 0]))  # set current reserve to 0 | 
					
						
							| 
									
										
										
										
											2021-11-11 12:07:17 -08:00
										 |  |  |         await snes_flush_writes(ctx) | 
					
						
							|  |  |  |         await asyncio.sleep(1) | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |         gamemode = None | 
					
						
							| 
									
										
										
										
											2021-11-12 14:36:34 +01:00
										 |  |  |         if ctx.game == GAME_ALTTP: | 
					
						
							|  |  |  |             gamemode = await snes_read(ctx, WRAM_START + 0x10, 1) | 
					
						
							| 
									
										
										
										
											2021-12-02 00:11:42 -05:00
										 |  |  |             if not gamemode or gamemode[0] in DEATH_MODES: | 
					
						
							|  |  |  |                 ctx.death_state = DeathState.dead | 
					
						
							| 
									
										
										
										
											2021-11-12 14:36:34 +01:00
										 |  |  |         elif ctx.game == GAME_SM: | 
					
						
							|  |  |  |             gamemode = await snes_read(ctx, WRAM_START + 0x0998, 1) | 
					
						
							| 
									
										
										
										
											2021-12-02 00:11:42 -05:00
										 |  |  |             health = await snes_read(ctx, WRAM_START + 0x09C2, 2) | 
					
						
							|  |  |  |             if health is not None: | 
					
						
							|  |  |  |                 health = health[0] | (health[1] << 8) | 
					
						
							|  |  |  |             if not gamemode or gamemode[0] in SM_DEATH_MODES or (ctx.death_link_allow_survive and health is not None and health > 0): | 
					
						
							|  |  |  |                 ctx.death_state = DeathState.dead | 
					
						
							| 
									
										
										
										
											2021-11-11 12:07:17 -08:00
										 |  |  |         ctx.last_death_link = time.time() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-11 16:09:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-03 03:19:25 +01:00
										 |  |  | def color_item(item_id: int, green: bool = False) -> str: | 
					
						
							|  |  |  |     item_name = get_item_name_from_id(item_id) | 
					
						
							|  |  |  |     item_colors = ['green' if green else 'cyan'] | 
					
						
							|  |  |  |     if item_name in Items.progression_items: | 
					
						
							|  |  |  |         item_colors.append("white_bg") | 
					
						
							|  |  |  |     return color(item_name, *item_colors) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-21 23:46:05 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-01 21:45:01 +02:00
										 |  |  | SNES_RECONNECT_DELAY = 5 | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | # LttP | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | ROM_START = 0x000000 | 
					
						
							|  |  |  | WRAM_START = 0xF50000 | 
					
						
							|  |  |  | WRAM_SIZE = 0x20000 | 
					
						
							|  |  |  | SRAM_START = 0xE00000 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | ROMNAME_START = SRAM_START + 0x2000 | 
					
						
							|  |  |  | ROMNAME_SIZE = 0x15 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | INGAME_MODES = {0x07, 0x09, 0x0b} | 
					
						
							| 
									
										
										
										
											2020-04-24 20:07:28 -07:00
										 |  |  | ENDGAME_MODES = {0x19, 0x1a} | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  | DEATH_MODES = {0x12} | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | SAVEDATA_START = WRAM_START + 0xF000 | 
					
						
							|  |  |  | SAVEDATA_SIZE = 0x500 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-18 09:50:12 +01:00
										 |  |  | RECV_PROGRESS_ADDR = SAVEDATA_START + 0x4D0         # 2 bytes | 
					
						
							|  |  |  | RECV_ITEM_ADDR = SAVEDATA_START + 0x4D2             # 1 byte | 
					
						
							|  |  |  | RECV_ITEM_PLAYER_ADDR = SAVEDATA_START + 0x4D3      # 1 byte | 
					
						
							|  |  |  | ROOMID_ADDR = SAVEDATA_START + 0x4D4                # 2 bytes | 
					
						
							|  |  |  | ROOMDATA_ADDR = SAVEDATA_START + 0x4D6              # 1 byte | 
					
						
							|  |  |  | SCOUT_LOCATION_ADDR = SAVEDATA_START + 0x4D7        # 1 byte | 
					
						
							|  |  |  | SCOUTREPLY_LOCATION_ADDR = SAVEDATA_START + 0x4D8   # 1 byte | 
					
						
							|  |  |  | SCOUTREPLY_ITEM_ADDR = SAVEDATA_START + 0x4D9       # 1 byte | 
					
						
							|  |  |  | SCOUTREPLY_PLAYER_ADDR = SAVEDATA_START + 0x4DA     # 1 byte | 
					
						
							| 
									
										
										
										
											2020-12-15 02:34:22 -06:00
										 |  |  | SHOP_ADDR = SAVEDATA_START + 0x302                  # 2 bytes | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | DEATH_LINK_ACTIVE_ADDR = ROMNAME_START + 0x15       # 1 byte | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # SM | 
					
						
							|  |  |  | SM_ROMNAME_START = 0x1C4F00 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | SM_INGAME_MODES = {0x07, 0x09, 0x0b} | 
					
						
							|  |  |  | SM_ENDGAME_MODES = {0x26, 0x27} | 
					
						
							|  |  |  | SM_DEATH_MODES = {0x15, 0x17, 0x18, 0x19, 0x1A} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | SM_RECV_PROGRESS_ADDR = SRAM_START + 0x2000         # 2 bytes | 
					
						
							|  |  |  | SM_RECV_ITEM_ADDR = SAVEDATA_START + 0x4D2          # 1 byte | 
					
						
							|  |  |  | SM_RECV_ITEM_PLAYER_ADDR = SAVEDATA_START + 0x4D3   # 1 byte | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | SM_DEATH_LINK_ACTIVE_ADDR = ROM_START + 0x277f04    # 1 byte | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-16 02:23:23 +01:00
										 |  |  | location_shop_ids = set([info[0] for name, info in Shops.shop_table.items()]) | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | location_table_uw = {"Blind's Hideout - Top": (0x11d, 0x10), | 
					
						
							|  |  |  |                      "Blind's Hideout - Left": (0x11d, 0x20), | 
					
						
							|  |  |  |                      "Blind's Hideout - Right": (0x11d, 0x40), | 
					
						
							|  |  |  |                      "Blind's Hideout - Far Left": (0x11d, 0x80), | 
					
						
							|  |  |  |                      "Blind's Hideout - Far Right": (0x11d, 0x100), | 
					
						
							|  |  |  |                      'Secret Passage': (0x55, 0x10), | 
					
						
							|  |  |  |                      'Waterfall Fairy - Left': (0x114, 0x10), | 
					
						
							|  |  |  |                      'Waterfall Fairy - Right': (0x114, 0x20), | 
					
						
							|  |  |  |                      "King's Tomb": (0x113, 0x10), | 
					
						
							|  |  |  |                      'Floodgate Chest': (0x10b, 0x10), | 
					
						
							|  |  |  |                      "Link's House": (0x104, 0x10), | 
					
						
							|  |  |  |                      'Kakariko Tavern': (0x103, 0x10), | 
					
						
							|  |  |  |                      'Chicken House': (0x108, 0x10), | 
					
						
							|  |  |  |                      "Aginah's Cave": (0x10a, 0x10), | 
					
						
							|  |  |  |                      "Sahasrahla's Hut - Left": (0x105, 0x10), | 
					
						
							|  |  |  |                      "Sahasrahla's Hut - Middle": (0x105, 0x20), | 
					
						
							|  |  |  |                      "Sahasrahla's Hut - Right": (0x105, 0x40), | 
					
						
							|  |  |  |                      'Kakariko Well - Top': (0x2f, 0x10), | 
					
						
							|  |  |  |                      'Kakariko Well - Left': (0x2f, 0x20), | 
					
						
							|  |  |  |                      'Kakariko Well - Middle': (0x2f, 0x40), | 
					
						
							|  |  |  |                      'Kakariko Well - Right': (0x2f, 0x80), | 
					
						
							|  |  |  |                      'Kakariko Well - Bottom': (0x2f, 0x100), | 
					
						
							|  |  |  |                      'Lost Woods Hideout': (0xe1, 0x200), | 
					
						
							|  |  |  |                      'Lumberjack Tree': (0xe2, 0x200), | 
					
						
							|  |  |  |                      'Cave 45': (0x11b, 0x400), | 
					
						
							|  |  |  |                      'Graveyard Cave': (0x11b, 0x200), | 
					
						
							|  |  |  |                      'Checkerboard Cave': (0x126, 0x200), | 
					
						
							|  |  |  |                      'Mini Moldorm Cave - Far Left': (0x123, 0x10), | 
					
						
							|  |  |  |                      'Mini Moldorm Cave - Left': (0x123, 0x20), | 
					
						
							|  |  |  |                      'Mini Moldorm Cave - Right': (0x123, 0x40), | 
					
						
							|  |  |  |                      'Mini Moldorm Cave - Far Right': (0x123, 0x80), | 
					
						
							|  |  |  |                      'Mini Moldorm Cave - Generous Guy': (0x123, 0x400), | 
					
						
							|  |  |  |                      'Ice Rod Cave': (0x120, 0x10), | 
					
						
							|  |  |  |                      'Bonk Rock Cave': (0x124, 0x10), | 
					
						
							|  |  |  |                      'Desert Palace - Big Chest': (0x73, 0x10), | 
					
						
							|  |  |  |                      'Desert Palace - Torch': (0x73, 0x400), | 
					
						
							|  |  |  |                      'Desert Palace - Map Chest': (0x74, 0x10), | 
					
						
							|  |  |  |                      'Desert Palace - Compass Chest': (0x85, 0x10), | 
					
						
							|  |  |  |                      'Desert Palace - Big Key Chest': (0x75, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Desert Palace - Desert Tiles 1 Pot Key': (0x63, 0x400), | 
					
						
							|  |  |  |                      'Desert Palace - Beamos Hall Pot Key': (0x53, 0x400), | 
					
						
							|  |  |  |                      'Desert Palace - Desert Tiles 2 Pot Key': (0x43, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Desert Palace - Boss': (0x33, 0x800), | 
					
						
							|  |  |  |                      'Eastern Palace - Compass Chest': (0xa8, 0x10), | 
					
						
							|  |  |  |                      'Eastern Palace - Big Chest': (0xa9, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Eastern Palace - Dark Square Pot Key': (0xba, 0x400), | 
					
						
							|  |  |  |                      'Eastern Palace - Dark Eyegore Key Drop': (0x99, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Eastern Palace - Cannonball Chest': (0xb9, 0x10), | 
					
						
							|  |  |  |                      'Eastern Palace - Big Key Chest': (0xb8, 0x10), | 
					
						
							|  |  |  |                      'Eastern Palace - Map Chest': (0xaa, 0x10), | 
					
						
							|  |  |  |                      'Eastern Palace - Boss': (0xc8, 0x800), | 
					
						
							|  |  |  |                      'Hyrule Castle - Boomerang Chest': (0x71, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Hyrule Castle - Boomerang Guard Key Drop': (0x71, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Hyrule Castle - Map Chest': (0x72, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Hyrule Castle - Map Guard Key Drop': (0x72, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      "Hyrule Castle - Zelda's Chest": (0x80, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Hyrule Castle - Big Key Drop': (0x80, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Sewers - Dark Cross': (0x32, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Hyrule Castle - Key Rat Key Drop': (0x21, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Sewers - Secret Room - Left': (0x11, 0x10), | 
					
						
							|  |  |  |                      'Sewers - Secret Room - Middle': (0x11, 0x20), | 
					
						
							|  |  |  |                      'Sewers - Secret Room - Right': (0x11, 0x40), | 
					
						
							|  |  |  |                      'Sanctuary': (0x12, 0x10), | 
					
						
							|  |  |  |                      'Castle Tower - Room 03': (0xe0, 0x10), | 
					
						
							|  |  |  |                      'Castle Tower - Dark Maze': (0xd0, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Castle Tower - Dark Archer Key Drop': (0xc0, 0x400), | 
					
						
							|  |  |  |                      'Castle Tower - Circle of Pots Key Drop': (0xb0, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Spectacle Rock Cave': (0xea, 0x400), | 
					
						
							|  |  |  |                      'Paradox Cave Lower - Far Left': (0xef, 0x10), | 
					
						
							|  |  |  |                      'Paradox Cave Lower - Left': (0xef, 0x20), | 
					
						
							|  |  |  |                      'Paradox Cave Lower - Right': (0xef, 0x40), | 
					
						
							|  |  |  |                      'Paradox Cave Lower - Far Right': (0xef, 0x80), | 
					
						
							|  |  |  |                      'Paradox Cave Lower - Middle': (0xef, 0x100), | 
					
						
							|  |  |  |                      'Paradox Cave Upper - Left': (0xff, 0x10), | 
					
						
							|  |  |  |                      'Paradox Cave Upper - Right': (0xff, 0x20), | 
					
						
							|  |  |  |                      'Spiral Cave': (0xfe, 0x10), | 
					
						
							|  |  |  |                      'Tower of Hera - Basement Cage': (0x87, 0x400), | 
					
						
							|  |  |  |                      'Tower of Hera - Map Chest': (0x77, 0x10), | 
					
						
							|  |  |  |                      'Tower of Hera - Big Key Chest': (0x87, 0x10), | 
					
						
							|  |  |  |                      'Tower of Hera - Compass Chest': (0x27, 0x20), | 
					
						
							|  |  |  |                      'Tower of Hera - Big Chest': (0x27, 0x10), | 
					
						
							|  |  |  |                      'Tower of Hera - Boss': (0x7, 0x800), | 
					
						
							|  |  |  |                      'Hype Cave - Top': (0x11e, 0x10), | 
					
						
							|  |  |  |                      'Hype Cave - Middle Right': (0x11e, 0x20), | 
					
						
							|  |  |  |                      'Hype Cave - Middle Left': (0x11e, 0x40), | 
					
						
							|  |  |  |                      'Hype Cave - Bottom': (0x11e, 0x80), | 
					
						
							|  |  |  |                      'Hype Cave - Generous Guy': (0x11e, 0x400), | 
					
						
							|  |  |  |                      'Peg Cave': (0x127, 0x400), | 
					
						
							|  |  |  |                      'Pyramid Fairy - Left': (0x116, 0x10), | 
					
						
							|  |  |  |                      'Pyramid Fairy - Right': (0x116, 0x20), | 
					
						
							|  |  |  |                      'Brewery': (0x106, 0x10), | 
					
						
							|  |  |  |                      'C-Shaped House': (0x11c, 0x10), | 
					
						
							|  |  |  |                      'Chest Game': (0x106, 0x400), | 
					
						
							|  |  |  |                      'Mire Shed - Left': (0x10d, 0x10), | 
					
						
							|  |  |  |                      'Mire Shed - Right': (0x10d, 0x20), | 
					
						
							|  |  |  |                      'Superbunny Cave - Top': (0xf8, 0x10), | 
					
						
							|  |  |  |                      'Superbunny Cave - Bottom': (0xf8, 0x20), | 
					
						
							|  |  |  |                      'Spike Cave': (0x117, 0x10), | 
					
						
							|  |  |  |                      'Hookshot Cave - Top Right': (0x3c, 0x10), | 
					
						
							|  |  |  |                      'Hookshot Cave - Top Left': (0x3c, 0x20), | 
					
						
							|  |  |  |                      'Hookshot Cave - Bottom Right': (0x3c, 0x80), | 
					
						
							|  |  |  |                      'Hookshot Cave - Bottom Left': (0x3c, 0x40), | 
					
						
							|  |  |  |                      'Mimic Cave': (0x10c, 0x10), | 
					
						
							|  |  |  |                      'Swamp Palace - Entrance': (0x28, 0x10), | 
					
						
							|  |  |  |                      'Swamp Palace - Map Chest': (0x37, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Swamp Palace - Pot Row Pot Key': (0x38, 0x400), | 
					
						
							|  |  |  |                      'Swamp Palace - Trench 1 Pot Key': (0x37, 0x400), | 
					
						
							|  |  |  |                      'Swamp Palace - Hookshot Pot Key': (0x36, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Swamp Palace - Big Chest': (0x36, 0x10), | 
					
						
							|  |  |  |                      'Swamp Palace - Compass Chest': (0x46, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Swamp Palace - Trench 2 Pot Key': (0x35, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Swamp Palace - Big Key Chest': (0x35, 0x10), | 
					
						
							|  |  |  |                      'Swamp Palace - West Chest': (0x34, 0x10), | 
					
						
							|  |  |  |                      'Swamp Palace - Flooded Room - Left': (0x76, 0x10), | 
					
						
							|  |  |  |                      'Swamp Palace - Flooded Room - Right': (0x76, 0x20), | 
					
						
							|  |  |  |                      'Swamp Palace - Waterfall Room': (0x66, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Swamp Palace - Waterway Pot Key': (0x16, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Swamp Palace - Boss': (0x6, 0x800), | 
					
						
							|  |  |  |                      "Thieves' Town - Big Key Chest": (0xdb, 0x20), | 
					
						
							|  |  |  |                      "Thieves' Town - Map Chest": (0xdb, 0x10), | 
					
						
							|  |  |  |                      "Thieves' Town - Compass Chest": (0xdc, 0x10), | 
					
						
							|  |  |  |                      "Thieves' Town - Ambush Chest": (0xcb, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      "Thieves' Town - Hallway Pot Key": (0xbc, 0x400), | 
					
						
							|  |  |  |                      "Thieves' Town - Spike Switch Pot Key": (0xab, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      "Thieves' Town - Attic": (0x65, 0x10), | 
					
						
							|  |  |  |                      "Thieves' Town - Big Chest": (0x44, 0x10), | 
					
						
							|  |  |  |                      "Thieves' Town - Blind's Cell": (0x45, 0x10), | 
					
						
							|  |  |  |                      "Thieves' Town - Boss": (0xac, 0x800), | 
					
						
							|  |  |  |                      'Skull Woods - Compass Chest': (0x67, 0x10), | 
					
						
							|  |  |  |                      'Skull Woods - Map Chest': (0x58, 0x20), | 
					
						
							|  |  |  |                      'Skull Woods - Big Chest': (0x58, 0x10), | 
					
						
							|  |  |  |                      'Skull Woods - Pot Prison': (0x57, 0x20), | 
					
						
							|  |  |  |                      'Skull Woods - Pinball Room': (0x68, 0x10), | 
					
						
							|  |  |  |                      'Skull Woods - Big Key Chest': (0x57, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Skull Woods - West Lobby Pot Key': (0x56, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Skull Woods - Bridge Room': (0x59, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Skull Woods - Spike Corner Key Drop': (0x39, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Skull Woods - Boss': (0x29, 0x800), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Ice Palace - Jelly Key Drop': (0x0e, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Ice Palace - Compass Chest': (0x2e, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Ice Palace - Conveyor Key Drop': (0x3e, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Ice Palace - Freezor Chest': (0x7e, 0x10), | 
					
						
							|  |  |  |                      'Ice Palace - Big Chest': (0x9e, 0x10), | 
					
						
							|  |  |  |                      'Ice Palace - Iced T Room': (0xae, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Ice Palace - Many Pots Pot Key': (0x9f, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Ice Palace - Spike Room': (0x5f, 0x10), | 
					
						
							|  |  |  |                      'Ice Palace - Big Key Chest': (0x1f, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Ice Palace - Hammer Block Key Drop': (0x3f, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Ice Palace - Map Chest': (0x3f, 0x10), | 
					
						
							|  |  |  |                      'Ice Palace - Boss': (0xde, 0x800), | 
					
						
							|  |  |  |                      'Misery Mire - Big Chest': (0xc3, 0x10), | 
					
						
							|  |  |  |                      'Misery Mire - Map Chest': (0xc3, 0x20), | 
					
						
							|  |  |  |                      'Misery Mire - Main Lobby': (0xc2, 0x10), | 
					
						
							|  |  |  |                      'Misery Mire - Bridge Chest': (0xa2, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Misery Mire - Spikes Pot Key': (0xb3, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Misery Mire - Spike Chest': (0xb3, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Misery Mire - Fishbone Pot Key': (0xa1, 0x400), | 
					
						
							|  |  |  |                      'Misery Mire - Conveyor Crystal Key Drop': (0xc1, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Misery Mire - Compass Chest': (0xc1, 0x10), | 
					
						
							|  |  |  |                      'Misery Mire - Big Key Chest': (0xd1, 0x10), | 
					
						
							|  |  |  |                      'Misery Mire - Boss': (0x90, 0x800), | 
					
						
							|  |  |  |                      'Turtle Rock - Compass Chest': (0xd6, 0x10), | 
					
						
							|  |  |  |                      'Turtle Rock - Roller Room - Left': (0xb7, 0x10), | 
					
						
							|  |  |  |                      'Turtle Rock - Roller Room - Right': (0xb7, 0x20), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Turtle Rock - Pokey 1 Key Drop': (0xb6, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Turtle Rock - Chain Chomps': (0xb6, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Turtle Rock - Pokey 2 Key Drop': (0x13, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Turtle Rock - Big Key Chest': (0x14, 0x10), | 
					
						
							|  |  |  |                      'Turtle Rock - Big Chest': (0x24, 0x10), | 
					
						
							|  |  |  |                      'Turtle Rock - Crystaroller Room': (0x4, 0x10), | 
					
						
							|  |  |  |                      'Turtle Rock - Eye Bridge - Bottom Left': (0xd5, 0x80), | 
					
						
							|  |  |  |                      'Turtle Rock - Eye Bridge - Bottom Right': (0xd5, 0x40), | 
					
						
							|  |  |  |                      'Turtle Rock - Eye Bridge - Top Left': (0xd5, 0x20), | 
					
						
							|  |  |  |                      'Turtle Rock - Eye Bridge - Top Right': (0xd5, 0x10), | 
					
						
							|  |  |  |                      'Turtle Rock - Boss': (0xa4, 0x800), | 
					
						
							|  |  |  |                      'Palace of Darkness - Shooter Room': (0x9, 0x10), | 
					
						
							|  |  |  |                      'Palace of Darkness - The Arena - Bridge': (0x2a, 0x20), | 
					
						
							|  |  |  |                      'Palace of Darkness - Stalfos Basement': (0xa, 0x10), | 
					
						
							|  |  |  |                      'Palace of Darkness - Big Key Chest': (0x3a, 0x10), | 
					
						
							|  |  |  |                      'Palace of Darkness - The Arena - Ledge': (0x2a, 0x10), | 
					
						
							|  |  |  |                      'Palace of Darkness - Map Chest': (0x2b, 0x10), | 
					
						
							|  |  |  |                      'Palace of Darkness - Compass Chest': (0x1a, 0x20), | 
					
						
							|  |  |  |                      'Palace of Darkness - Dark Basement - Left': (0x6a, 0x10), | 
					
						
							|  |  |  |                      'Palace of Darkness - Dark Basement - Right': (0x6a, 0x20), | 
					
						
							|  |  |  |                      'Palace of Darkness - Dark Maze - Top': (0x19, 0x10), | 
					
						
							|  |  |  |                      'Palace of Darkness - Dark Maze - Bottom': (0x19, 0x20), | 
					
						
							|  |  |  |                      'Palace of Darkness - Big Chest': (0x1a, 0x10), | 
					
						
							|  |  |  |                      'Palace of Darkness - Harmless Hellway': (0x1a, 0x40), | 
					
						
							|  |  |  |                      'Palace of Darkness - Boss': (0x5a, 0x800), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Ganons Tower - Conveyor Cross Pot Key': (0x8b, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      "Ganons Tower - Bob's Torch": (0x8c, 0x400), | 
					
						
							|  |  |  |                      'Ganons Tower - Hope Room - Left': (0x8c, 0x20), | 
					
						
							|  |  |  |                      'Ganons Tower - Hope Room - Right': (0x8c, 0x40), | 
					
						
							|  |  |  |                      'Ganons Tower - Tile Room': (0x8d, 0x10), | 
					
						
							|  |  |  |                      'Ganons Tower - Compass Room - Top Left': (0x9d, 0x10), | 
					
						
							|  |  |  |                      'Ganons Tower - Compass Room - Top Right': (0x9d, 0x20), | 
					
						
							|  |  |  |                      'Ganons Tower - Compass Room - Bottom Left': (0x9d, 0x40), | 
					
						
							|  |  |  |                      'Ganons Tower - Compass Room - Bottom Right': (0x9d, 0x80), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Ganons Tower - Conveyor Star Pits Pot Key': (0x7b, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Ganons Tower - DMs Room - Top Left': (0x7b, 0x10), | 
					
						
							|  |  |  |                      'Ganons Tower - DMs Room - Top Right': (0x7b, 0x20), | 
					
						
							|  |  |  |                      'Ganons Tower - DMs Room - Bottom Left': (0x7b, 0x40), | 
					
						
							|  |  |  |                      'Ganons Tower - DMs Room - Bottom Right': (0x7b, 0x80), | 
					
						
							|  |  |  |                      'Ganons Tower - Map Chest': (0x8b, 0x10), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Ganons Tower - Double Switch Pot Key': (0x9b, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Ganons Tower - Firesnake Room': (0x7d, 0x10), | 
					
						
							|  |  |  |                      'Ganons Tower - Randomizer Room - Top Left': (0x7c, 0x10), | 
					
						
							|  |  |  |                      'Ganons Tower - Randomizer Room - Top Right': (0x7c, 0x20), | 
					
						
							|  |  |  |                      'Ganons Tower - Randomizer Room - Bottom Left': (0x7c, 0x40), | 
					
						
							|  |  |  |                      'Ganons Tower - Randomizer Room - Bottom Right': (0x7c, 0x80), | 
					
						
							|  |  |  |                      "Ganons Tower - Bob's Chest": (0x8c, 0x80), | 
					
						
							|  |  |  |                      'Ganons Tower - Big Chest': (0x8c, 0x10), | 
					
						
							|  |  |  |                      'Ganons Tower - Big Key Room - Left': (0x1c, 0x20), | 
					
						
							|  |  |  |                      'Ganons Tower - Big Key Room - Right': (0x1c, 0x40), | 
					
						
							|  |  |  |                      'Ganons Tower - Big Key Chest': (0x1c, 0x10), | 
					
						
							|  |  |  |                      'Ganons Tower - Mini Helmasaur Room - Left': (0x3d, 0x10), | 
					
						
							|  |  |  |                      'Ganons Tower - Mini Helmasaur Room - Right': (0x3d, 0x20), | 
					
						
							| 
									
										
										
										
											2020-10-28 01:48:22 -07:00
										 |  |  |                      'Ganons Tower - Mini Helmasaur Key Drop': (0x3d, 0x400), | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                      'Ganons Tower - Pre-Moldorm Chest': (0x3d, 0x40), | 
					
						
							|  |  |  |                      'Ganons Tower - Validation Chest': (0x4d, 0x10)} | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  | location_table_uw_id = {Regions.lookup_name_to_id[name]: data for name, data in location_table_uw.items()} | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | location_table_npc = {'Mushroom': 0x1000, | 
					
						
							|  |  |  |                       'King Zora': 0x2, | 
					
						
							|  |  |  |                       'Sahasrahla': 0x10, | 
					
						
							|  |  |  |                       'Blacksmith': 0x400, | 
					
						
							|  |  |  |                       'Magic Bat': 0x8000, | 
					
						
							|  |  |  |                       'Sick Kid': 0x4, | 
					
						
							|  |  |  |                       'Library': 0x80, | 
					
						
							|  |  |  |                       'Potion Shop': 0x2000, | 
					
						
							|  |  |  |                       'Old Man': 0x1, | 
					
						
							|  |  |  |                       'Ether Tablet': 0x100, | 
					
						
							|  |  |  |                       'Catfish': 0x20, | 
					
						
							|  |  |  |                       'Stumpy': 0x8, | 
					
						
							|  |  |  |                       'Bombos Tablet': 0x200} | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  | location_table_npc_id = {Regions.lookup_name_to_id[name]: data for name, data in location_table_npc.items()} | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | location_table_ow = {'Flute Spot': 0x2a, | 
					
						
							|  |  |  |                      'Sunken Treasure': 0x3b, | 
					
						
							|  |  |  |                      "Zora's Ledge": 0x81, | 
					
						
							|  |  |  |                      'Lake Hylia Island': 0x35, | 
					
						
							|  |  |  |                      'Maze Race': 0x28, | 
					
						
							|  |  |  |                      'Desert Ledge': 0x30, | 
					
						
							|  |  |  |                      'Master Sword Pedestal': 0x80, | 
					
						
							|  |  |  |                      'Spectacle Rock': 0x3, | 
					
						
							|  |  |  |                      'Pyramid': 0x5b, | 
					
						
							|  |  |  |                      'Digging Game': 0x68, | 
					
						
							|  |  |  |                      'Bumper Cave Ledge': 0x4a, | 
					
						
							|  |  |  |                      'Floating Island': 0x5} | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  | location_table_ow_id = {Regions.lookup_name_to_id[name]: data for name, data in location_table_ow.items()} | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | location_table_misc = {'Bottle Merchant': (0x3c9, 0x2), | 
					
						
							|  |  |  |                        'Purple Chest': (0x3c9, 0x10), | 
					
						
							|  |  |  |                        "Link's Uncle": (0x3c6, 0x1), | 
					
						
							|  |  |  |                        'Hobo': (0x3c9, 0x1)} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  | location_table_misc_id = {Regions.lookup_name_to_id[name]: data for name, data in location_table_misc.items()} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-07 03:45:27 +02:00
										 |  |  | def launch_sni(ctx: Context): | 
					
						
							|  |  |  |     sni_path = Utils.get_options()["lttp_options"]["sni"] | 
					
						
							| 
									
										
										
										
											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): | 
					
						
							|  |  |  |         for file in os.listdir(sni_path): | 
					
						
							| 
									
										
										
										
											2021-10-21 06:43:42 +02:00
										 |  |  |             lower_file = file.lower() | 
					
						
							|  |  |  |             if (lower_file.startswith("sni.") and not lower_file.endswith(".proto")) or lower_file == "sni": | 
					
						
							| 
									
										
										
										
											2021-07-07 03:45:27 +02:00
										 |  |  |                 sni_path = os.path.join(sni_path, file) | 
					
						
							| 
									
										
										
										
											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-07-31 01:40:27 +02:00
										 |  |  |             subprocess.Popen(sni_path, cwd=os.path.dirname(sni_path)) | 
					
						
							|  |  |  |         else: | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  |             subprocess.Popen(sni_path, cwd=os.path.dirname(sni_path), stdout=subprocess.DEVNULL, | 
					
						
							|  |  |  |                              stderr=subprocess.DEVNULL) | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							|  |  |  | async def _snes_connect(ctx: Context, address: str): | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							| 
									
										
										
										
											2020-03-07 00:07:32 +01:00
										 |  |  |     seen_problems = set() | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  |     succesful = False | 
					
						
							|  |  |  |     while not succesful: | 
					
						
							| 
									
										
										
										
											2020-03-07 00:07:32 +01:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  |             snes_socket = await websockets.connect(address, ping_timeout=None, ping_interval=None) | 
					
						
							|  |  |  |             succesful = True | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  |                     launch_sni(ctx) | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  |             await asyncio.sleep(1) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             return snes_socket | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-04-12 04:44:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  | async def get_snes_devices(ctx: Context): | 
					
						
							|  |  |  |     socket = await _snes_connect(ctx, ctx.snes_address)  # establish new connection to poll | 
					
						
							|  |  |  |     DeviceList_Request = { | 
					
						
							|  |  |  |         "Opcode": "DeviceList", | 
					
						
							|  |  |  |         "Space": "SNES" | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2020-10-19 08:26:31 +02:00
										 |  |  |     await socket.send(dumps(DeviceList_Request)) | 
					
						
							| 
									
										
										
										
											2020-04-12 04:44:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-19 08:26:31 +02:00
										 |  |  |     reply = loads(await socket.recv()) | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  |     devices = reply['Results'] if 'Results' in reply and len(reply['Results']) > 0 else None | 
					
						
							| 
									
										
										
										
											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.') | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  |         while not devices: | 
					
						
							| 
									
										
										
										
											2020-03-07 00:07:32 +01:00
										 |  |  |             await asyncio.sleep(1) | 
					
						
							| 
									
										
										
										
											2020-10-19 08:26:31 +02:00
										 |  |  |             await socket.send(dumps(DeviceList_Request)) | 
					
						
							|  |  |  |             reply = loads(await socket.recv()) | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  |             devices = reply['Results'] if 'Results' in reply and len(reply['Results']) > 0 else None | 
					
						
							| 
									
										
										
										
											2021-11-03 19:58:40 +01:00
										 |  |  |     await verify_snes_app(socket) | 
					
						
							| 
									
										
										
										
											2020-06-06 23:46:28 +02:00
										 |  |  |     await socket.close() | 
					
						
							|  |  |  |     return devices | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-03 19:58:40 +01:00
										 |  |  | async def verify_snes_app(socket): | 
					
						
							|  |  |  |     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.") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  | async def snes_connect(ctx: Context, address, deviceIndex=-1): | 
					
						
							| 
									
										
										
										
											2020-09-01 21:45:01 +02:00
										 |  |  |     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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											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) | 
					
						
							| 
									
										
										
										
											2021-08-28 21:20:45 -05:00
										 |  |  |         numDevices = len(devices) | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-08-28 21:20:45 -05:00
										 |  |  |         if numDevices == 1: | 
					
						
							| 
									
										
										
										
											2020-01-13 03:55:33 +01:00
										 |  |  |             device = devices[0] | 
					
						
							|  |  |  |         elif ctx.snes_reconnect_address: | 
					
						
							|  |  |  |             if ctx.snes_attached_device[1] in devices: | 
					
						
							|  |  |  |                 device = ctx.snes_attached_device[1] | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 device = devices[ctx.snes_attached_device[0]] | 
					
						
							| 
									
										
										
										
											2021-08-28 21:20:45 -05:00
										 |  |  |         elif numDevices > 1: | 
					
						
							|  |  |  |             if deviceIndex == -1: | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  |                 snes_logger.info( | 
					
						
							|  |  |  |                     "Found " + str(numDevices) + " SNES devices; connect to one with /snes <address> <device number>:") | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							|  |  |  |             elif (deviceIndex < 0) or (deviceIndex - 1) > numDevices: | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							|  |  |  |         Attach_Request = { | 
					
						
							| 
									
										
											  
											
												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)) | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |         SNES_RECONNECT_DELAY = ctx.starting_reconnect_delay | 
					
						
							| 
									
										
										
										
											2021-07-31 01:40:27 +02:00
										 |  |  |         snes_logger.info(f"Attached to {device}") | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     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: | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |             snes_logger.error(f"Error connecting to snes, attempt again in {SNES_RECONNECT_DELAY}s") | 
					
						
							| 
									
										
										
										
											2020-01-14 10:42:27 +01:00
										 |  |  |             asyncio.create_task(snes_autoreconnect(ctx)) | 
					
						
							| 
									
										
										
										
											2020-09-01 21:45:01 +02:00
										 |  |  |         SNES_RECONNECT_DELAY *= 2 | 
					
						
							| 
									
										
										
										
											2020-01-13 03:55:33 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-13 03:53:20 +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
										 |  |  | async def snes_disconnect(ctx: Context): | 
					
						
							|  |  |  |     if ctx.snes_socket: | 
					
						
							|  |  |  |         if not ctx.snes_socket.closed: | 
					
						
							|  |  |  |             await ctx.snes_socket.close() | 
					
						
							|  |  |  |         ctx.snes_socket = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-14 10:42:27 +01:00
										 |  |  | async def snes_autoreconnect(ctx: Context): | 
					
						
							| 
									
										
										
										
											2020-09-01 21:45:01 +02:00
										 |  |  |     await asyncio.sleep(SNES_RECONNECT_DELAY) | 
					
						
							| 
									
										
										
										
											2020-01-13 03:55:33 +01:00
										 |  |  |     if ctx.snes_reconnect_address and ctx.snes_socket is None: | 
					
						
							|  |  |  |         await snes_connect(ctx, ctx.snes_reconnect_address) | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							|  |  |  | async def snes_recv_loop(ctx: Context): | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |     try: | 
					
						
							|  |  |  |         async for msg in ctx.snes_socket: | 
					
						
							|  |  |  |             ctx.snes_recv_queue.put_nowait(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: | 
					
						
							| 
									
										
										
										
											2019-12-16 18:39:00 +01:00
										 |  |  |         if not isinstance(e, websockets.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: | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |             snes_logger.info(f"...reconnecting in {SNES_RECONNECT_DELAY}s") | 
					
						
							| 
									
										
										
										
											2020-01-14 10:42:27 +01:00
										 |  |  |             asyncio.create_task(snes_autoreconnect(ctx)) | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-21 23:37:58 +01:00
										 |  |  | async def snes_read(ctx: Context, address, size): | 
					
						
							| 
									
										
										
										
											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 None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         GetAddress_Request = { | 
					
						
							| 
									
										
										
										
											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)) | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |         except websockets.ConnectionClosed: | 
					
						
							|  |  |  |             return None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         data = bytes() | 
					
						
							|  |  |  |         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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-21 23:37:58 +01:00
										 |  |  | async def snes_write(ctx: Context, write_list): | 
					
						
							| 
									
										
										
										
											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 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-11-11 12:36:02 +01:00
										 |  |  |         PutAddress_Request = {"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:]] | 
					
						
							| 
									
										
										
										
											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}") | 
					
						
							| 
									
										
										
										
											2021-07-07 03:45:27 +02:00
										 |  |  |         except websockets.ConnectionClosed: | 
					
						
							|  |  |  |             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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-21 23:37:58 +01:00
										 |  |  | def snes_buffered_write(ctx: Context, address, data): | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-21 23:37:58 +01:00
										 |  |  | async def snes_flush_writes(ctx: Context): | 
					
						
							| 
									
										
										
										
											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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-21 23:37:58 +01:00
										 |  |  | async def track_locations(ctx: Context, roomid, roomdata): | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |     new_locations = [] | 
					
						
							| 
									
										
											  
											
												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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |     def new_check(location_id): | 
					
						
							|  |  |  |         new_locations.append(location_id) | 
					
						
							|  |  |  |         ctx.locations_checked.add(location_id) | 
					
						
							|  |  |  |         location = ctx.location_name_getter(location_id) | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  |         snes_logger.info( | 
					
						
							|  |  |  |             f'New Check: {location} ({len(ctx.locations_checked)}/{len(ctx.missing_locations) + len(ctx.checked_locations)})') | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-20 03:17:08 +01:00
										 |  |  |     try: | 
					
						
							| 
									
										
										
										
											2020-12-15 02:34:22 -06:00
										 |  |  |         if roomid in location_shop_ids: | 
					
						
							| 
									
										
										
										
											2021-06-13 07:57:34 +02:00
										 |  |  |             misc_data = await snes_read(ctx, SHOP_ADDR, (len(Shops.shop_table) * 3) + 5) | 
					
						
							| 
									
										
										
										
											2020-12-15 02:34:22 -06:00
										 |  |  |             for cnt, b in enumerate(misc_data): | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |                 if int(b) and (Shops.SHOP_ID_START + cnt) not in ctx.locations_checked: | 
					
						
							|  |  |  |                     new_check(Shops.SHOP_ID_START + cnt) | 
					
						
							| 
									
										
										
										
											2020-12-15 02:34:22 -06:00
										 |  |  |     except Exception as e: | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |         snes_logger.info(f"Exception: {e}") | 
					
						
							| 
									
										
										
										
											2021-01-20 03:17:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |     for location_id, (loc_roomid, loc_mask) in location_table_uw_id.items(): | 
					
						
							| 
									
										
										
										
											2020-10-27 00:47:59 -07:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |             if location_id not in ctx.locations_checked and loc_roomid == roomid and ( | 
					
						
							| 
									
										
										
										
											2021-01-21 23:37:58 +01:00
										 |  |  |                     roomdata << 4) & loc_mask != 0: | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |                 new_check(location_id) | 
					
						
							| 
									
										
										
										
											2020-10-27 00:47:59 -07:00
										 |  |  |         except Exception as e: | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |             snes_logger.exception(f"Exception: {e}") | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     uw_begin = 0x129 | 
					
						
							| 
									
										
										
										
											2021-01-21 23:37:58 +01:00
										 |  |  |     ow_end = uw_end = 0 | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |     uw_unchecked = {} | 
					
						
							|  |  |  |     for location, (roomid, mask) in location_table_uw.items(): | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |         location_id = Regions.lookup_name_to_id[location] | 
					
						
							|  |  |  |         if location_id not in ctx.locations_checked: | 
					
						
							|  |  |  |             uw_unchecked[location_id] = (roomid, mask) | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |             uw_begin = min(uw_begin, roomid) | 
					
						
							|  |  |  |             uw_end = max(uw_end, roomid + 1) | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |     if uw_begin < uw_end: | 
					
						
							|  |  |  |         uw_data = await snes_read(ctx, SAVEDATA_START + (uw_begin * 2), (uw_end - uw_begin) * 2) | 
					
						
							|  |  |  |         if uw_data is not None: | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |             for location_id, (roomid, mask) in uw_unchecked.items(): | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                 offset = (roomid - uw_begin) * 2 | 
					
						
							|  |  |  |                 roomdata = uw_data[offset] | (uw_data[offset + 1] << 8) | 
					
						
							|  |  |  |                 if roomdata & mask != 0: | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |                     new_check(location_id) | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |     ow_begin = 0x82 | 
					
						
							|  |  |  |     ow_unchecked = {} | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |     for location_id, screenid in location_table_ow_id.items(): | 
					
						
							|  |  |  |         if location_id not in ctx.locations_checked: | 
					
						
							|  |  |  |             ow_unchecked[location_id] = screenid | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |             ow_begin = min(ow_begin, screenid) | 
					
						
							|  |  |  |             ow_end = max(ow_end, screenid + 1) | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |     if ow_begin < ow_end: | 
					
						
							|  |  |  |         ow_data = await snes_read(ctx, SAVEDATA_START + 0x280 + ow_begin, ow_end - ow_begin) | 
					
						
							|  |  |  |         if ow_data is not None: | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |             for location_id, screenid in ow_unchecked.items(): | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |                 if ow_data[screenid - ow_begin] & 0x40 != 0: | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |                     new_check(location_id) | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |     if not ctx.locations_checked.issuperset(location_table_npc_id): | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |         npc_data = await snes_read(ctx, SAVEDATA_START + 0x410, 2) | 
					
						
							|  |  |  |         if npc_data is not None: | 
					
						
							|  |  |  |             npc_value = npc_data[0] | (npc_data[1] << 8) | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |             for location_id, mask in location_table_npc_id.items(): | 
					
						
							|  |  |  |                 if npc_value & mask != 0 and location_id not in ctx.locations_checked: | 
					
						
							|  |  |  |                     new_check(location_id) | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |     if not ctx.locations_checked.issuperset(location_table_misc_id): | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |         misc_data = await snes_read(ctx, SAVEDATA_START + 0x3c6, 4) | 
					
						
							|  |  |  |         if misc_data is not None: | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |             for location_id, (offset, mask) in location_table_misc_id.items(): | 
					
						
							| 
									
										
										
										
											2021-01-21 23:37:58 +01:00
										 |  |  |                 assert (0x3c6 <= offset <= 0x3c9) | 
					
						
							| 
									
										
										
										
											2021-03-07 22:05:07 +01:00
										 |  |  |                 if misc_data[offset - 0x3c6] & mask != 0 and location_id not in ctx.locations_checked: | 
					
						
							|  |  |  |                     new_check(location_id) | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-21 23:37:58 +01:00
										 |  |  |     if new_locations: | 
					
						
							| 
									
										
										
										
											2021-02-21 23:46:05 +01:00
										 |  |  |         await ctx.send_msgs([{"cmd": 'LocationChecks', "locations": new_locations}]) | 
					
						
							| 
									
										
											  
											
												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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-21 23:37:58 +01:00
										 |  |  | async def game_watcher(ctx: Context): | 
					
						
							| 
									
										
										
										
											2020-04-24 20:07:28 -07:00
										 |  |  |     prev_game_timer = 0 | 
					
						
							|  |  |  |     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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-14 10:42:27 +01:00
										 |  |  |         if not ctx.rom: | 
					
						
							| 
									
										
										
										
											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-22 20:32:59 +01:00
										 |  |  |             game_name = await snes_read(ctx, SM_ROMNAME_START, 2) | 
					
						
							|  |  |  |             if game_name is None: | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2021-11-22 20:32:59 +01:00
										 |  |  |             elif game_name == b"SM": | 
					
						
							| 
									
										
										
										
											2021-11-12 14:36:34 +01:00
										 |  |  |                 ctx.game = GAME_SM | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2021-11-12 14:36:34 +01:00
										 |  |  |                 ctx.game = GAME_ALTTP | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 14:36:34 +01:00
										 |  |  |             rom = await snes_read(ctx, SM_ROMNAME_START if ctx.game == GAME_SM else ROMNAME_START, ROMNAME_SIZE) | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |             if rom is None or rom == bytes([0] * ROMNAME_SIZE): | 
					
						
							|  |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2020-01-14 10:42:27 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-10-19 08:26:31 +02:00
										 |  |  |             ctx.rom = rom | 
					
						
							| 
									
										
										
										
											2021-11-12 14:36:34 +01:00
										 |  |  |             death_link = await snes_read(ctx, DEATH_LINK_ACTIVE_ADDR if ctx.game == GAME_ALTTP else | 
					
						
							|  |  |  |                                          SM_DEATH_LINK_ACTIVE_ADDR, 1) | 
					
						
							| 
									
										
										
										
											2021-11-01 19:37:47 +01:00
										 |  |  |             if death_link: | 
					
						
							| 
									
										
										
										
											2021-12-02 00:11:42 -05:00
										 |  |  |                 ctx.death_link_allow_survive = bool(death_link[0] & 0b10) | 
					
						
							| 
									
										
										
										
											2021-11-24 01:38:58 -08:00
										 |  |  |                 await ctx.update_death_link(bool(death_link[0] & 0b1)) | 
					
						
							| 
									
										
										
										
											2020-06-02 07:38:23 -07:00
										 |  |  |             if not ctx.prev_rom or ctx.prev_rom != ctx.rom: | 
					
						
							|  |  |  |                 ctx.locations_checked = set() | 
					
						
							|  |  |  |                 ctx.locations_scouted = set() | 
					
						
							| 
									
										
										
										
											2020-07-14 04:48:56 +02:00
										 |  |  |             ctx.prev_rom = ctx.rom | 
					
						
							| 
									
										
										
										
											2020-06-02 07:38:23 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-01-14 10:42:27 +01:00
										 |  |  |             if ctx.awaiting_rom: | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |                 await ctx.server_auth(False) | 
					
						
							| 
									
										
										
										
											2020-01-14 10:42:27 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  |         if ctx.auth and ctx.auth != ctx.rom: | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |             snes_logger.warning("ROM change detected, please reconnect to the multiworld server") | 
					
						
							| 
									
										
											  
											
												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 ctx.disconnect() | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 14:36:34 +01:00
										 |  |  |         if ctx.game == GAME_ALTTP: | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             gamemode = await snes_read(ctx, WRAM_START + 0x10, 1) | 
					
						
							|  |  |  |             if "DeathLink" in ctx.tags and gamemode and ctx.last_death_link + 1 < time.time(): | 
					
						
							|  |  |  |                 currently_dead = gamemode[0] in DEATH_MODES | 
					
						
							| 
									
										
										
										
											2021-11-12 06:48:23 -08:00
										 |  |  |                 await ctx.handle_deathlink_state(currently_dead) | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |             gameend = await snes_read(ctx, SAVEDATA_START + 0x443, 1) | 
					
						
							|  |  |  |             game_timer = await snes_read(ctx, SAVEDATA_START + 0x42E, 4) | 
					
						
							|  |  |  |             if gamemode is None or gameend is None or game_timer is None or \ | 
					
						
							|  |  |  |                     (gamemode[0] not in INGAME_MODES and gamemode[0] not in ENDGAME_MODES): | 
					
						
							| 
									
										
										
										
											2020-04-24 20:07:28 -07:00
										 |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  | 
 | 
					
						
							|  |  |  |             delay = 7 if ctx.slow_mode else 2 | 
					
						
							|  |  |  |             if gameend[0]: | 
					
						
							|  |  |  |                 if not ctx.finished_game: | 
					
						
							|  |  |  |                     await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}]) | 
					
						
							|  |  |  |                     ctx.finished_game = True | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 if time.perf_counter() - perf_counter < delay: | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     perf_counter = time.perf_counter() | 
					
						
							| 
									
										
										
										
											2020-04-24 20:07:28 -07:00
										 |  |  |             else: | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |                 game_timer = game_timer[0] | (game_timer[1] << 8) | (game_timer[2] << 16) | (game_timer[3] << 24) | 
					
						
							|  |  |  |                 if abs(game_timer - prev_game_timer) < (delay * 60): | 
					
						
							|  |  |  |                     continue | 
					
						
							|  |  |  |                 else: | 
					
						
							|  |  |  |                     prev_game_timer = game_timer | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if gamemode in ENDGAME_MODES:  # triforce room and credits | 
					
						
							| 
									
										
										
										
											2020-04-24 20:07:28 -07:00
										 |  |  |                 continue | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             data = await snes_read(ctx, RECV_PROGRESS_ADDR, 8) | 
					
						
							|  |  |  |             if data is None: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             recv_index = data[0] | (data[1] << 8) | 
					
						
							|  |  |  |             recv_item = data[2] | 
					
						
							|  |  |  |             roomid = data[4] | (data[5] << 8) | 
					
						
							|  |  |  |             roomdata = data[6] | 
					
						
							|  |  |  |             scout_location = data[7] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if recv_index < len(ctx.items_received) and recv_item == 0: | 
					
						
							|  |  |  |                 item = ctx.items_received[recv_index] | 
					
						
							|  |  |  |                 recv_index += 1 | 
					
						
							|  |  |  |                 logging.info('Received %s from %s (%s) (%d/%d in list)' % ( | 
					
						
							|  |  |  |                     color(ctx.item_name_getter(item.item), 'red', 'bold'), color(ctx.player_names[item.player], 'yellow'), | 
					
						
							|  |  |  |                     ctx.location_name_getter(item.location), recv_index, len(ctx.items_received))) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 snes_buffered_write(ctx, RECV_PROGRESS_ADDR, | 
					
						
							|  |  |  |                                     bytes([recv_index & 0xFF, (recv_index >> 8) & 0xFF])) | 
					
						
							|  |  |  |                 snes_buffered_write(ctx, RECV_ITEM_ADDR, | 
					
						
							|  |  |  |                                     bytes([item.item])) | 
					
						
							|  |  |  |                 snes_buffered_write(ctx, RECV_ITEM_PLAYER_ADDR, | 
					
						
							|  |  |  |                                     bytes([min(ROM_PLAYER_LIMIT, item.player) if item.player != ctx.slot else 0])) | 
					
						
							|  |  |  |             if scout_location > 0 and scout_location in ctx.locations_info: | 
					
						
							|  |  |  |                 snes_buffered_write(ctx, SCOUTREPLY_LOCATION_ADDR, | 
					
						
							|  |  |  |                                     bytes([scout_location])) | 
					
						
							|  |  |  |                 snes_buffered_write(ctx, SCOUTREPLY_ITEM_ADDR, | 
					
						
							|  |  |  |                                     bytes([ctx.locations_info[scout_location][0]])) | 
					
						
							|  |  |  |                 snes_buffered_write(ctx, SCOUTREPLY_PLAYER_ADDR, | 
					
						
							|  |  |  |                                     bytes([min(ROM_PLAYER_LIMIT, ctx.locations_info[scout_location][1])])) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             await snes_flush_writes(ctx) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             if scout_location > 0 and scout_location not in ctx.locations_scouted: | 
					
						
							|  |  |  |                 ctx.locations_scouted.add(scout_location) | 
					
						
							|  |  |  |                 await ctx.send_msgs([{"cmd": "LocationScouts", "locations": [scout_location]}]) | 
					
						
							|  |  |  |             await track_locations(ctx, roomid, roomdata)         | 
					
						
							| 
									
										
										
										
											2021-11-12 14:36:34 +01:00
										 |  |  |         elif ctx.game == GAME_SM: | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             gamemode = await snes_read(ctx, WRAM_START + 0x0998, 1) | 
					
						
							|  |  |  |             if "DeathLink" in ctx.tags and gamemode and ctx.last_death_link + 1 < time.time(): | 
					
						
							|  |  |  |                 currently_dead = gamemode[0] in SM_DEATH_MODES | 
					
						
							| 
									
										
										
										
											2021-11-12 06:48:23 -08:00
										 |  |  |                 await ctx.handle_deathlink_state(currently_dead) | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             if gamemode is not None and gamemode[0] in SM_ENDGAME_MODES: | 
					
						
							|  |  |  |                 if not ctx.finished_game: | 
					
						
							|  |  |  |                     await ctx.send_msgs([{"cmd": "StatusUpdate", "status": ClientStatus.CLIENT_GOAL}]) | 
					
						
							|  |  |  |                     ctx.finished_game = True | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             data = await snes_read(ctx, SM_RECV_PROGRESS_ADDR + 0x680, 4) | 
					
						
							|  |  |  |             if data is None: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             recv_index = data[0] | (data[1] << 8) | 
					
						
							|  |  |  |             recv_item = data[2] | (data[3] << 8) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             while (recv_index < recv_item): | 
					
						
							|  |  |  |                 itemAdress = recv_index * 8 | 
					
						
							|  |  |  |                 message = await snes_read(ctx, SM_RECV_PROGRESS_ADDR + 0x700 + itemAdress, 8) | 
					
						
							| 
									
										
										
										
											2021-11-12 14:58:48 +01:00
										 |  |  |                 # worldId = message[0] | (message[1] << 8)  # unused | 
					
						
							|  |  |  |                 # itemId = message[2] | (message[3] << 8)  # unused | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |                 itemIndex = (message[4] | (message[5] << 8)) >> 3 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 recv_index += 1 | 
					
						
							|  |  |  |                 snes_buffered_write(ctx, SM_RECV_PROGRESS_ADDR + 0x680, bytes([recv_index & 0xFF, (recv_index >> 8) & 0xFF])) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 from worlds.sm.Locations import locations_start_id | 
					
						
							|  |  |  |                 location_id = locations_start_id + itemIndex | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |                 ctx.locations_checked.add(location_id) | 
					
						
							|  |  |  |                 location = ctx.location_name_getter(location_id) | 
					
						
							|  |  |  |                 snes_logger.info(f'New Check: {location} ({len(ctx.locations_checked)}/{len(ctx.missing_locations) + len(ctx.checked_locations)})') | 
					
						
							|  |  |  |                 await ctx.send_msgs([{"cmd": 'LocationChecks', "locations": [location_id]}]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |             data = await snes_read(ctx, SM_RECV_PROGRESS_ADDR + 0x600, 4) | 
					
						
							|  |  |  |             if data is None: | 
					
						
							|  |  |  |                 continue | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 14:58:48 +01:00
										 |  |  |             # recv_itemOutPtr = data[0] | (data[1] << 8) # unused | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             itemOutPtr = data[2] | (data[3] << 8) | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |             from worlds.sm.Items import items_start_id | 
					
						
							|  |  |  |             if itemOutPtr < len(ctx.items_received): | 
					
						
							|  |  |  |                 item = ctx.items_received[itemOutPtr] | 
					
						
							|  |  |  |                 itemId = item.item - items_start_id | 
					
						
							| 
									
										
										
										
											2020-01-18 11:28:08 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-13 09:40:20 -05:00
										 |  |  |                 playerID = item.player if item.player <= SM_ROM_PLAYER_LIMIT else 0 | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |                 snes_buffered_write(ctx, SM_RECV_PROGRESS_ADDR + itemOutPtr * 4, bytes([playerID & 0xFF, (playerID >> 8) & 0xFF, itemId & 0xFF, (itemId >> 8) & 0xFF])) | 
					
						
							|  |  |  |                 itemOutPtr += 1 | 
					
						
							|  |  |  |                 snes_buffered_write(ctx, SM_RECV_PROGRESS_ADDR + 0x602, bytes([itemOutPtr & 0xFF, (itemOutPtr >> 8) & 0xFF])) | 
					
						
							|  |  |  |                 logging.info('Received %s from %s (%s) (%d/%d in list)' % ( | 
					
						
							|  |  |  |                     color(ctx.item_name_getter(item.item), 'red', 'bold'), color(ctx.player_names[item.player], 'yellow'), | 
					
						
							|  |  |  |                     ctx.location_name_getter(item.location), itemOutPtr, len(ctx.items_received))) | 
					
						
							|  |  |  |             await snes_flush_writes(ctx) | 
					
						
							| 
									
										
										
										
											2020-03-10 00:38:29 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-12-10 09:29:59 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-03-10 00:38:29 +01:00
										 |  |  | async def run_game(romfile): | 
					
						
							| 
									
										
										
										
											2021-04-01 11:40:58 +02:00
										 |  |  |     auto_start = Utils.get_options()["lttp_options"].get("rom_start", True) | 
					
						
							| 
									
										
										
										
											2020-07-15 17:19:16 +02:00
										 |  |  |     if auto_start is True: | 
					
						
							|  |  |  |         import webbrowser | 
					
						
							|  |  |  |         webbrowser.open(romfile) | 
					
						
							|  |  |  |     elif os.path.isfile(auto_start): | 
					
						
							|  |  |  |         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
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | async def main(): | 
					
						
							| 
									
										
											  
											
												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') | 
					
						
							| 
									
										
										
										
											2021-07-07 03:45:27 +02:00
										 |  |  |     parser.add_argument('--snes', default='localhost:8080', 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..") | 
					
						
							| 
									
										
										
										
											2020-03-10 00:38:29 +01:00
										 |  |  |         meta, romfile = Patch.create_rom_file(args.diff_file) | 
					
						
							| 
									
										
										
										
											2021-12-01 01:01:41 +01:00
										 |  |  |         if "server" in meta: | 
					
						
							|  |  |  |             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 | 
					
						
							| 
									
										
										
										
											2021-12-01 01:27:51 +01:00
										 |  |  |             webbrowser.open("http://www.evermizer.com/apclient/" + | 
					
						
							|  |  |  |                             (f"#server={meta['server']}" if "server" in meta else "")) | 
					
						
							| 
									
										
										
										
											2021-11-14 21:14:22 +01:00
										 |  |  |             logging.info("Starting Evermizer Client in your Browser...") | 
					
						
							|  |  |  |             import time | 
					
						
							|  |  |  |             time.sleep(3) | 
					
						
							|  |  |  |             sys.exit() | 
					
						
							|  |  |  |         elif args.diff_file.endswith((".apbp", "apz3")): | 
					
						
							|  |  |  |             adjustedromfile, adjusted = Utils.get_adjuster_settings(romfile, gui_enabled) | 
					
						
							|  |  |  |             if adjusted: | 
					
						
							|  |  |  |                 try: | 
					
						
							|  |  |  |                     shutil.move(adjustedromfile, romfile) | 
					
						
							|  |  |  |                     adjustedromfile = romfile | 
					
						
							|  |  |  |                 except Exception as e: | 
					
						
							|  |  |  |                     logging.exception(e) | 
					
						
							|  |  |  |             asyncio.create_task(run_game(adjustedromfile if adjusted else romfile)) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             asyncio.create_task(run_game(romfile)) | 
					
						
							| 
									
										
										
										
											2020-01-18 12:21:57 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-23 20:04:51 +02:00
										 |  |  |     ctx = Context(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") | 
					
						
							| 
									
										
										
										
											2021-11-26 06:02:03 +01:00
										 |  |  |     input_task = None | 
					
						
							| 
									
										
										
										
											2021-08-20 22:31:17 +02:00
										 |  |  |     if gui_enabled: | 
					
						
							| 
									
										
										
										
											2021-11-12 08:00:11 -05:00
										 |  |  |         from kvui import SNIManager | 
					
						
							|  |  |  |         ctx.ui = SNIManager(ctx) | 
					
						
							| 
									
										
										
										
											2021-07-31 01:53:06 +02:00
										 |  |  |         ui_task = asyncio.create_task(ctx.ui.async_run(), name="UI") | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |     else: | 
					
						
							|  |  |  |         ui_task = None | 
					
						
							| 
									
										
										
										
											2021-11-26 06:02:03 +01:00
										 |  |  |     if sys.stdin: | 
					
						
							|  |  |  |         input_task = asyncio.create_task(console_loop(ctx), name="Input") | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-21 02:02:40 +01:00
										 |  |  |     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
										 |  |  |     if snes_connect_task: | 
					
						
							|  |  |  |         snes_connect_task.cancel() | 
					
						
							|  |  |  |     await watcher_task | 
					
						
							|  |  |  |     await ctx.shutdown() | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-31 00:03:48 +02:00
										 |  |  |     if ui_task: | 
					
						
							|  |  |  |         await ui_task | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if input_task: | 
					
						
							|  |  |  |         input_task.cancel() | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-01-21 23:37:58 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  | if __name__ == '__main__': | 
					
						
							| 
									
										
										
										
											2020-01-18 10:05:59 +01:00
										 |  |  |     colorama.init() | 
					
						
							| 
									
										
										
										
											2019-12-09 19:27:56 +01:00
										 |  |  |     loop = asyncio.get_event_loop() | 
					
						
							|  |  |  |     loop.run_until_complete(main()) | 
					
						
							|  |  |  |     loop.close() | 
					
						
							| 
									
										
										
										
											2020-01-18 10:05:59 +01:00
										 |  |  |     colorama.deinit() |