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