Update WebUI to display server, check, and hint info. CURRENT HINT POINTS DO NOT WORK YET
This commit is contained in:
parent
aa7fe2aa9d
commit
e11f33b589
|
@ -48,9 +48,15 @@ class Context():
|
||||||
self.snes_address = snes_address
|
self.snes_address = snes_address
|
||||||
self.server_address = server_address
|
self.server_address = server_address
|
||||||
|
|
||||||
|
# WebUI Stuff
|
||||||
self.ui_node = WebUI.WebUiClient()
|
self.ui_node = WebUI.WebUiClient()
|
||||||
self.custom_address = None
|
self.custom_address = None
|
||||||
self.webui_socket_port: typing.Optional[int] = port
|
self.webui_socket_port: typing.Optional[int] = port
|
||||||
|
self.hint_cost = 0
|
||||||
|
self.check_points = 0
|
||||||
|
self.forfeit_mode = ''
|
||||||
|
self.remaining_mode = ''
|
||||||
|
# End WebUI Stuff
|
||||||
|
|
||||||
self.exit_event = asyncio.Event()
|
self.exit_event = asyncio.Event()
|
||||||
self.watcher_event = asyncio.Event()
|
self.watcher_event = asyncio.Event()
|
||||||
|
@ -761,7 +767,13 @@ async def process_server_cmd(ctx: Context, cmd, args):
|
||||||
if "forfeit_mode" in args: # could also be version > 2.2.1, but going with implicit content here
|
if "forfeit_mode" in args: # could also be version > 2.2.1, but going with implicit content here
|
||||||
logging.info("Forfeit setting: "+args["forfeit_mode"])
|
logging.info("Forfeit setting: "+args["forfeit_mode"])
|
||||||
logging.info("Remaining setting: "+args["remaining_mode"])
|
logging.info("Remaining setting: "+args["remaining_mode"])
|
||||||
logging.info(f"A !hint costs {args['hint_cost']} points and you get {args['location_check_points']} for each location checked.")
|
logging.info(f"A !hint costs {args['hint_cost']} points and you get {args['location_check_points']}"
|
||||||
|
f" for each location checked.")
|
||||||
|
ctx.hint_cost = int(args['hint_cost'])
|
||||||
|
ctx.check_points = int(args['location_check_points'])
|
||||||
|
ctx.forfeit_mode = args['forfeit_mode']
|
||||||
|
ctx.remaining_mode = args['remaining_mode']
|
||||||
|
ctx.ui_node.send_game_info(ctx)
|
||||||
if len(args['players']) < 1:
|
if len(args['players']) < 1:
|
||||||
ctx.ui_node.log_info('No player connected')
|
ctx.ui_node.log_info('No player connected')
|
||||||
else:
|
else:
|
||||||
|
@ -1045,6 +1057,7 @@ async def track_locations(ctx : Context, roomid, roomdata):
|
||||||
def new_check(location):
|
def new_check(location):
|
||||||
ctx.locations_checked.add(location)
|
ctx.locations_checked.add(location)
|
||||||
ctx.ui_node.log_info("New check: %s (%d/216)" % (location, len(ctx.locations_checked)))
|
ctx.ui_node.log_info("New check: %s (%d/216)" % (location, len(ctx.locations_checked)))
|
||||||
|
ctx.ui_node.send_location_check(ctx, location)
|
||||||
new_locations.append(Regions.location_table[location][0])
|
new_locations.append(Regions.location_table[location][0])
|
||||||
|
|
||||||
for location, (loc_roomid, loc_mask) in location_table_uw.items():
|
for location, (loc_roomid, loc_mask) in location_table_uw.items():
|
||||||
|
@ -1223,6 +1236,10 @@ async def websocket_server(websocket: websockets.WebSocketServerProtocol, path,
|
||||||
ctx.ui_node.send_connection_status(ctx)
|
ctx.ui_node.send_connection_status(ctx)
|
||||||
elif data['content'] == 'devices':
|
elif data['content'] == 'devices':
|
||||||
await get_snes_devices(ctx)
|
await get_snes_devices(ctx)
|
||||||
|
elif data['content'] == 'gameInfo':
|
||||||
|
ctx.ui_node.send_game_info(ctx)
|
||||||
|
elif data['content'] == 'checkData':
|
||||||
|
ctx.ui_node.send_location_check(ctx, 'Waiting for check...')
|
||||||
|
|
||||||
elif data['type'] == 'webConfig':
|
elif data['type'] == 'webConfig':
|
||||||
if 'serverAddress' in data['content']:
|
if 'serverAddress' in data['content']:
|
||||||
|
|
31
WebUI.py
31
WebUI.py
|
@ -57,6 +57,7 @@ class WebUiClient(Node):
|
||||||
'serverAddress': server_address,
|
'serverAddress': server_address,
|
||||||
'server': 1 if ctx.server is not None and not ctx.server.socket.closed else 0,
|
'server': 1 if ctx.server is not None and not ctx.server.socket.closed else 0,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
def send_device_list(self, devices):
|
def send_device_list(self, devices):
|
||||||
self.broadcast_all(self.build_message('availableDevices', {
|
self.broadcast_all(self.build_message('availableDevices', {
|
||||||
'devices': devices,
|
'devices': devices,
|
||||||
|
@ -105,11 +106,20 @@ class WebUiClient(Node):
|
||||||
'entranceLocation': entrance_location,
|
'entranceLocation': entrance_location,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
def send_game_state(self, ctx: Context):
|
def send_game_info(self, ctx: Context):
|
||||||
self.broadcast_all(self.build_message('gameState', {
|
self.broadcast_all(self.build_message('gameInfo', {
|
||||||
'hintCost': 0,
|
'serverVersion': Utils.__version__,
|
||||||
'checkPoints': 0,
|
'hintCost': ctx.hint_cost,
|
||||||
'playerPoints': 0,
|
'checkPoints': ctx.check_points,
|
||||||
|
'forfeitMode': ctx.forfeit_mode,
|
||||||
|
'remainingMode': ctx.remaining_mode,
|
||||||
|
}))
|
||||||
|
|
||||||
|
def send_location_check(self, ctx: Context, last_check: str):
|
||||||
|
self.broadcast_all(self.build_message('locationCheck', {
|
||||||
|
'totalChecks': len(ctx.locations_checked),
|
||||||
|
'hintPoints': 0,
|
||||||
|
'lastCheck': last_check,
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
@ -117,22 +127,27 @@ class WaitingForUiException(Exception):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
webthread = None
|
web_thread = None
|
||||||
PORT = 5050
|
PORT = 5050
|
||||||
|
|
||||||
|
|
||||||
class RequestHandler(http.server.SimpleHTTPRequestHandler):
|
class RequestHandler(http.server.SimpleHTTPRequestHandler):
|
||||||
def log_request(self, code='-', size='-'):
|
def log_request(self, code='-', size='-'):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def log_message(self, format, *args):
|
def log_message(self, format, *args):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def log_date_time_string(self):
|
def log_date_time_string(self):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
Handler = partial(RequestHandler,
|
Handler = partial(RequestHandler,
|
||||||
directory=Utils.local_path(os.path.join("data", "web", "public")))
|
directory=Utils.local_path(os.path.join("data", "web", "public")))
|
||||||
|
|
||||||
|
|
||||||
def start_server(socket_port: int, on_start=lambda: None):
|
def start_server(socket_port: int, on_start=lambda: None):
|
||||||
global webthread
|
global web_thread
|
||||||
try:
|
try:
|
||||||
server = socketserver.TCPServer(("", PORT), Handler)
|
server = socketserver.TCPServer(("", PORT), Handler)
|
||||||
except OSError:
|
except OSError:
|
||||||
|
@ -152,4 +167,4 @@ def start_server(socket_port: int, on_start=lambda: None):
|
||||||
else:
|
else:
|
||||||
print("serving at port", PORT)
|
print("serving at port", PORT)
|
||||||
on_start()
|
on_start()
|
||||||
webthread = threading.Thread(target=server.serve_forever).start()
|
web_thread = threading.Thread(target=server.serve_forever).start()
|
||||||
|
|
File diff suppressed because one or more lines are too long
|
@ -46,6 +46,7 @@ class MonitorControls extends Component {
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
|
// If there is only one SNES device available, connect to it automatically
|
||||||
if (
|
if (
|
||||||
prevProps.availableDevices.length !== this.props.availableDevices.length &&
|
prevProps.availableDevices.length !== this.props.availableDevices.length &&
|
||||||
this.props.availableDevices.length === 1
|
this.props.availableDevices.length === 1
|
||||||
|
@ -57,6 +58,17 @@ class MonitorControls extends Component {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If we have moved from a disconnected state (default) into a connected state, request the game information
|
||||||
|
if (
|
||||||
|
(
|
||||||
|
(prevProps.snesConnected !== this.props.snesConnected) || // SNES status changed
|
||||||
|
(prevProps.serverConnected !== this.props.serverConnected) // OR server status changed
|
||||||
|
) && ((this.props.serverConnected) && (this.props.snesConnected)) // AND both are connected
|
||||||
|
) {
|
||||||
|
this.props.webSocket.send(WebSocketUtils.formatSocketData('webStatus', 'gameInfo'));
|
||||||
|
this.props.webSocket.send(WebSocketUtils.formatSocketData('webStatus', 'checkData'));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
increaseTextSize = () => {
|
increaseTextSize = () => {
|
||||||
|
|
|
@ -2,6 +2,17 @@ import React, { Component } from 'react';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import '../../../styles/WidgetArea/containers/WidgetArea.scss';
|
import '../../../styles/WidgetArea/containers/WidgetArea.scss';
|
||||||
|
|
||||||
|
const mapReduxStateToProps = (reduxState) => ({
|
||||||
|
serverVersion: reduxState.gameState.serverVersion,
|
||||||
|
forfeitMode: reduxState.gameState.forfeitMode,
|
||||||
|
remainingMode: reduxState.gameState.remainingMode,
|
||||||
|
hintCost: reduxState.gameState.hintCost,
|
||||||
|
checkPoints: reduxState.gameState.checkPoints,
|
||||||
|
hintPoints: reduxState.gameState.hintPoints,
|
||||||
|
totalChecks: reduxState.gameState.totalChecks,
|
||||||
|
lastCheck: reduxState.gameState.lastCheck,
|
||||||
|
});
|
||||||
|
|
||||||
class WidgetArea extends Component {
|
class WidgetArea extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
super(props);
|
super(props);
|
||||||
|
@ -30,10 +41,67 @@ class WidgetArea extends Component {
|
||||||
{
|
{
|
||||||
this.state.collapsed ? null : (
|
this.state.collapsed ? null : (
|
||||||
<div id="widget-area-contents">
|
<div id="widget-area-contents">
|
||||||
|
<div id="game-info">
|
||||||
|
<div id="game-info-title">
|
||||||
|
Game Info:
|
||||||
|
<button className="collapse-button" onClick={ this.toggleCollapse }>↪</button>
|
||||||
|
</div>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>Server Version:</th>
|
||||||
|
<td>{this.props.serverVersion}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Forfeit Mode:</th>
|
||||||
|
<td>{this.props.forfeitMode}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Remaining Mode:</th>
|
||||||
|
<td>{this.props.remainingMode}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div id="check-data">
|
||||||
|
<div id="check-data-title">Checks:</div>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>Total Checks:</th>
|
||||||
|
<td>{this.props.totalChecks}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Last Check:</th>
|
||||||
|
<td>{this.props.lastCheck}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
<div id="hint-data">
|
||||||
|
<div id="hint-data-title">
|
||||||
|
Hint Data:
|
||||||
|
</div>
|
||||||
|
<table>
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>Hint Cost:</th>
|
||||||
|
<td>{this.props.hintCost}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Check Points:</th>
|
||||||
|
<td>{this.props.checkPoints}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Current Points:</th>
|
||||||
|
<td>{this.props.hintPoints}</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
<div id="notes">
|
<div id="notes">
|
||||||
<div id="notes-title">
|
<div id="notes-title">
|
||||||
<div>Notes:</div>
|
<div>Notes:</div>
|
||||||
<button className="collapse-button" onClick={ this.toggleCollapse }>↪</button>
|
|
||||||
</div>
|
</div>
|
||||||
<textarea defaultValue={ localStorage.getItem('notes') } onKeyUp={ this.saveNotes } />
|
<textarea defaultValue={ localStorage.getItem('notes') } onKeyUp={ this.saveNotes } />
|
||||||
</div>
|
</div>
|
||||||
|
@ -46,4 +114,4 @@ class WidgetArea extends Component {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default connect()(WidgetArea);
|
export default connect(mapReduxStateToProps)(WidgetArea);
|
||||||
|
|
|
@ -1,17 +1,20 @@
|
||||||
import _assign from 'lodash-es/assign';
|
import _assign from 'lodash-es/assign';
|
||||||
|
|
||||||
const initialState = {
|
const initialState = {
|
||||||
|
serverVersion: null,
|
||||||
|
forfeitMode: null,
|
||||||
|
remainingMode: null,
|
||||||
connections: {
|
connections: {
|
||||||
snesDevice: '',
|
snesDevice: '',
|
||||||
snesConnected: false,
|
snesConnected: false,
|
||||||
serverAddress: null,
|
serverAddress: null,
|
||||||
serverConnected: false,
|
serverConnected: false,
|
||||||
},
|
},
|
||||||
hints: {
|
totalChecks: 0,
|
||||||
hintCost: null,
|
lastCheck: null,
|
||||||
checkPoints: null,
|
hintCost: null,
|
||||||
playerPoints: 0,
|
checkPoints: null,
|
||||||
},
|
hintPoints: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
const gameStateReducer = (state = initialState, action) => {
|
const gameStateReducer = (state = initialState, action) => {
|
||||||
|
|
|
@ -60,6 +60,22 @@ class WebSocketUtils {
|
||||||
parseInt(data.content.iAmFinder, 10) === 1, parseInt(data.content.iAmRecipient, 10) === 1,
|
parseInt(data.content.iAmFinder, 10) === 1, parseInt(data.content.iAmRecipient, 10) === 1,
|
||||||
data.content.entranceLocation));
|
data.content.entranceLocation));
|
||||||
|
|
||||||
|
case 'gameInfo':
|
||||||
|
return updateGameState({
|
||||||
|
serverVersion: data.content.serverVersion,
|
||||||
|
forfeitMode: data.content.forfeitMode,
|
||||||
|
remainingMode: data.content.remainingMode,
|
||||||
|
hintCost: parseInt(data.content.hintCost, 10),
|
||||||
|
checkPoints: parseInt(data.content.checkPoints, 10),
|
||||||
|
});
|
||||||
|
|
||||||
|
case 'locationCheck':
|
||||||
|
return updateGameState({
|
||||||
|
totalChecks: parseInt(data.content.totalChecks, 10),
|
||||||
|
lastCheck: data.content.lastCheck,
|
||||||
|
hintPoints: parseInt(data.content.hintPoints, 10),
|
||||||
|
});
|
||||||
|
|
||||||
// The client prints several types of messages to the console
|
// The client prints several types of messages to the console
|
||||||
case 'critical':
|
case 'critical':
|
||||||
case 'error':
|
case 'error':
|
||||||
|
|
|
@ -21,15 +21,36 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
||||||
#notes{
|
table{
|
||||||
display: flex;
|
th{
|
||||||
flex-direction: column;
|
text-align: left;
|
||||||
|
}
|
||||||
|
td{
|
||||||
|
padding-left: 1em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#notes-title{
|
#game-info{
|
||||||
|
margin-bottom: 1em;
|
||||||
|
|
||||||
|
#game-info-title{
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#check-data{
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#hint-data{
|
||||||
|
margin-bottom: 1em;
|
||||||
|
}
|
||||||
|
|
||||||
|
#notes{
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
textarea{
|
textarea{
|
||||||
height: 10em;
|
height: 10em;
|
||||||
|
|
Loading…
Reference in New Issue