Impl conditionally update front-end board
When the front-end polls the back-end for the game-state, the back-end includes a "date_updated" String in the JSON. If the String is the same as in the front-end, then no updates are needed, but if they are not the same, then the front-end will update the board. Because the front-end polls the back-end's board state approximately every second, this should make the front-end more efficient.
This commit is contained in:
parent
b4eaba09c5
commit
b2ea79a7f7
5 changed files with 99 additions and 38 deletions
|
@ -31,8 +31,14 @@ pub type GetIDSenderType = (Option<u32>, Option<bool>);
|
||||||
/// third bool is if cyan player
|
/// third bool is if cyan player
|
||||||
pub type CheckPairingType = (bool, bool, bool);
|
pub type CheckPairingType = (bool, bool, bool);
|
||||||
|
|
||||||
/// second String is board string, third String is received emote type
|
/// second String is board string, third String is date updated, fourth value
|
||||||
pub type BoardStateType = (DBGameState, Option<String>, Option<EmoteEnum>);
|
/// is EmoteEnum
|
||||||
|
pub type BoardStateType = (
|
||||||
|
DBGameState,
|
||||||
|
Option<String>,
|
||||||
|
Option<String>,
|
||||||
|
Option<EmoteEnum>,
|
||||||
|
);
|
||||||
|
|
||||||
pub type PlaceResultType = Result<(DBPlaceStatus, Option<String>), DBPlaceError>;
|
pub type PlaceResultType = Result<(DBPlaceStatus, Option<String>), DBPlaceError>;
|
||||||
|
|
||||||
|
@ -250,7 +256,7 @@ impl DBHandler {
|
||||||
// don't stop server on send fail, may have timed out and
|
// don't stop server on send fail, may have timed out and
|
||||||
// dropped the receiver
|
// dropped the receiver
|
||||||
response_sender
|
response_sender
|
||||||
.send((DBGameState::UnknownID, None, None))
|
.send((DBGameState::UnknownID, None, None, None))
|
||||||
.ok();
|
.ok();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -677,66 +683,91 @@ impl DBHandler {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO maybe handle "opponent_disconnected" case
|
// TODO maybe handle "opponent_disconnected" case
|
||||||
let row_result: Result<(String, i64, Option<u32>, Option<u32>), RusqliteError> = conn.query_row(
|
let row_result: Result<(String, i64, Option<u32>, Option<u32>, String), RusqliteError> = conn.query_row(
|
||||||
"SELECT games.board, games.status, games.cyan_player, games.magenta_player FROM games JOIN players WHERE players.id = ? AND games.id = players.game_id;",
|
"SELECT games.board, games.status, games.cyan_player, games.magenta_player, games.turn_time_start FROM games JOIN players WHERE players.id = ? AND games.id = players.game_id;",
|
||||||
[player_id],
|
[player_id],
|
||||||
|row| {
|
|row| {
|
||||||
let board_result = row.get(0);
|
let board_result = row.get(0);
|
||||||
let status_result = row.get(1);
|
let status_result = row.get(1);
|
||||||
let cyan_player = row.get(2);
|
let cyan_player = row.get(2);
|
||||||
let magenta_player = row.get(3);
|
let magenta_player = row.get(3);
|
||||||
if board_result.is_ok() && status_result.is_ok() && cyan_player.is_ok() && magenta_player.is_ok() {
|
let updated_time = row.get(4);
|
||||||
if let (Ok(board), Ok(status), Ok(cyan_id), Ok(magenta_id)) = (board_result, status_result, cyan_player, magenta_player) {
|
if board_result.is_ok() && status_result.is_ok() && cyan_player.is_ok() && magenta_player.is_ok() && updated_time.is_ok() {
|
||||||
Ok((board, status, cyan_id, magenta_id))
|
if let (Ok(board), Ok(status), Ok(cyan_id), Ok(magenta_id), Ok(updated_time)) = (board_result, status_result, cyan_player, magenta_player, updated_time) {
|
||||||
|
Ok((board, status, cyan_id, magenta_id, updated_time))
|
||||||
} else {
|
} else {
|
||||||
unreachable!("Both row items should be Ok");
|
unreachable!("All row items should be Ok");
|
||||||
}
|
}
|
||||||
} else if board_result.is_err() {
|
} else if board_result.is_err() {
|
||||||
board_result
|
board_result
|
||||||
.map(|_| (String::from("this value should never be returned"), 0, None, None))
|
.map(|_| (String::from("this value should never be returned"), 0, None, None, String::new()))
|
||||||
} else if status_result.is_err() {
|
} else if status_result.is_err() {
|
||||||
status_result
|
status_result
|
||||||
.map(|_| (String::from("this value should never be returned"), 0, None, None))
|
.map(|_| (String::from("this value should never be returned"), 0, None, None, String::new()))
|
||||||
} else if cyan_player.is_err() {
|
} else if cyan_player.is_err() {
|
||||||
cyan_player
|
cyan_player
|
||||||
.map(|_| (String::from("this value should never be returned"), 0, None, None))
|
.map(|_| (String::from("this value should never be returned"), 0, None, None, String::new()))
|
||||||
} else {
|
} else if magenta_player.is_err() {
|
||||||
magenta_player
|
magenta_player
|
||||||
.map(|_| (String::from("this value should never be returned"), 0, None, None))
|
.map(|_| (String::from("this value should never be returned"), 0, None, None, String::new()))
|
||||||
|
} else {
|
||||||
|
updated_time
|
||||||
|
.map(|_| (String::from("this value should never be returned"), 0, None, None, String::new()))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
if let Ok((board, status, cyan_opt, magenta_opt)) = row_result {
|
if let Ok((board, status, cyan_opt, magenta_opt, updated_time)) = row_result {
|
||||||
if board.len() != (ROWS * COLS) as usize {
|
if board.len() != (ROWS * COLS) as usize {
|
||||||
// board is invalid size
|
// board is invalid size
|
||||||
Ok((DBGameState::InternalError, None, received_emote))
|
Ok((
|
||||||
|
DBGameState::InternalError,
|
||||||
|
None,
|
||||||
|
Some(updated_time),
|
||||||
|
received_emote,
|
||||||
|
))
|
||||||
} else if cyan_opt.is_none() || magenta_opt.is_none() {
|
} else if cyan_opt.is_none() || magenta_opt.is_none() {
|
||||||
// One player disconnected
|
// One player disconnected
|
||||||
self.disconnect_player(Some(conn), player_id).ok();
|
self.disconnect_player(Some(conn), player_id).ok();
|
||||||
// Remove the game(s) with disconnected players
|
// Remove the game(s) with disconnected players
|
||||||
if self.clear_empty_games(Some(conn)).is_err() {
|
if self.clear_empty_games(Some(conn)).is_err() {
|
||||||
Ok((DBGameState::InternalError, None, received_emote))
|
Ok((
|
||||||
|
DBGameState::InternalError,
|
||||||
|
None,
|
||||||
|
Some(updated_time),
|
||||||
|
received_emote,
|
||||||
|
))
|
||||||
} else if status == 2 || status == 3 {
|
} else if status == 2 || status == 3 {
|
||||||
Ok((DBGameState::from(status), Some(board), received_emote))
|
Ok((
|
||||||
|
DBGameState::from(status),
|
||||||
|
Some(board),
|
||||||
|
Some(updated_time),
|
||||||
|
received_emote,
|
||||||
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok((
|
Ok((
|
||||||
DBGameState::OpponentDisconnected,
|
DBGameState::OpponentDisconnected,
|
||||||
Some(board),
|
Some(board),
|
||||||
|
Some(updated_time),
|
||||||
received_emote,
|
received_emote,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Game in progress, or other state depending on "status"
|
// Game in progress, or other state depending on "status"
|
||||||
Ok((DBGameState::from(status), Some(board), received_emote))
|
Ok((
|
||||||
|
DBGameState::from(status),
|
||||||
|
Some(board),
|
||||||
|
Some(updated_time),
|
||||||
|
received_emote,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
} else if let Err(RusqliteError::QueryReturnedNoRows) = row_result {
|
} else if let Err(RusqliteError::QueryReturnedNoRows) = row_result {
|
||||||
// No rows is either player doesn't exist or not paired
|
// No rows is either player doesn't exist or not paired
|
||||||
let (exists, is_paired, _is_cyan) =
|
let (exists, is_paired, _is_cyan) =
|
||||||
self.check_if_player_is_paired(Some(conn), player_id)?;
|
self.check_if_player_is_paired(Some(conn), player_id)?;
|
||||||
if !exists {
|
if !exists {
|
||||||
Ok((DBGameState::UnknownID, None, received_emote))
|
Ok((DBGameState::UnknownID, None, None, received_emote))
|
||||||
} else if !is_paired {
|
} else if !is_paired {
|
||||||
Ok((DBGameState::NotPaired, None, received_emote))
|
Ok((DBGameState::NotPaired, None, None, received_emote))
|
||||||
} else {
|
} else {
|
||||||
unreachable!("either exists or is_paired must be false");
|
unreachable!("either exists or is_paired must be false");
|
||||||
}
|
}
|
||||||
|
|
|
@ -266,19 +266,25 @@ fn handle_game_state(root: Value, tx: SyncSender<DBHandlerRequest>) -> Result<St
|
||||||
return Err("{\"type\":\"game_state\", \"status\":\"internal_error\"}".into());
|
return Err("{\"type\":\"game_state\", \"status\":\"internal_error\"}".into());
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Ok((db_game_state, board_string_opt, received_emote_opt)) =
|
if let Ok((db_game_state, board_string_opt, updated_time_opt, received_emote_opt)) =
|
||||||
resp_rx.recv_timeout(DB_REQUEST_TIMEOUT)
|
resp_rx.recv_timeout(DB_REQUEST_TIMEOUT)
|
||||||
{
|
{
|
||||||
if let Some(board_string) = board_string_opt {
|
if let Some(board_string) = board_string_opt {
|
||||||
|
let updated_time = if let Some(time_string) = updated_time_opt {
|
||||||
|
time_string
|
||||||
|
} else {
|
||||||
|
return Err("{\"type\":\"game_state\", \"status\":\"internal_error\"}".into());
|
||||||
|
};
|
||||||
|
|
||||||
if let Some(emote) = received_emote_opt {
|
if let Some(emote) = received_emote_opt {
|
||||||
Ok(format!(
|
Ok(format!(
|
||||||
"{{\"type\":\"game_state\", \"status\":\"{}\", \"board\":\"{}\", \"peer_emote\": \"{}\"}}",
|
"{{\"type\":\"game_state\", \"status\":\"{}\", \"board\":\"{}\", \"peer_emote\": \"{}\", \"updated_time\": \"{}\"}}",
|
||||||
db_game_state, board_string, emote
|
db_game_state, board_string, emote, updated_time
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
Ok(format!(
|
Ok(format!(
|
||||||
"{{\"type\":\"game_state\", \"status\":\"{}\", \"board\":\"{}\"}}",
|
"{{\"type\":\"game_state\", \"status\":\"{}\", \"board\":\"{}\", \"updated_time\": \"{}\"}}",
|
||||||
db_game_state, board_string
|
db_game_state, board_string, updated_time
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -558,6 +558,7 @@ pub struct GameStateResponse {
|
||||||
pub status: String,
|
pub status: String,
|
||||||
pub board: Option<String>,
|
pub board: Option<String>,
|
||||||
pub peer_emote: Option<String>,
|
pub peer_emote: Option<String>,
|
||||||
|
pub updated_time: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Serialize, Deserialize)]
|
#[derive(Debug, Serialize, Deserialize)]
|
||||||
|
|
|
@ -392,6 +392,7 @@ pub struct Wrapper {
|
||||||
place_request: Option<u8>,
|
place_request: Option<u8>,
|
||||||
do_backend_tick: bool,
|
do_backend_tick: bool,
|
||||||
cleanup_id_callback: Rc<RefCell<Option<Function>>>,
|
cleanup_id_callback: Rc<RefCell<Option<Function>>>,
|
||||||
|
board_updated_time: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Wrapper {
|
impl Wrapper {
|
||||||
|
@ -555,11 +556,12 @@ impl Wrapper {
|
||||||
_ => NetworkedGameState::InternalError,
|
_ => NetworkedGameState::InternalError,
|
||||||
};
|
};
|
||||||
|
|
||||||
WrapperMsg::BackendResponse(BREnum::GotStatus(
|
WrapperMsg::BackendResponse(BREnum::GotStatus {
|
||||||
networked_game_state,
|
networked_game_state,
|
||||||
response.board,
|
board_string: response.board,
|
||||||
response.peer_emote,
|
received_emote: response.peer_emote,
|
||||||
))
|
updated_time: response.updated_time,
|
||||||
|
})
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -725,8 +727,14 @@ pub enum BREnum {
|
||||||
Error(String),
|
Error(String),
|
||||||
GotID(u32, Option<Turn>),
|
GotID(u32, Option<Turn>),
|
||||||
GotPairing(Option<Turn>),
|
GotPairing(Option<Turn>),
|
||||||
/// Second opt string is board_str, third opt string is received emote
|
/// Second opt string is board_str, third opt string is received emote,
|
||||||
GotStatus(NetworkedGameState, Option<String>, Option<String>),
|
/// fourth opt string is updated_time
|
||||||
|
GotStatus {
|
||||||
|
networked_game_state: NetworkedGameState,
|
||||||
|
board_string: Option<String>,
|
||||||
|
received_emote: Option<String>,
|
||||||
|
updated_time: Option<String>,
|
||||||
|
},
|
||||||
GotPlaced(PlacedEnum, String),
|
GotPlaced(PlacedEnum, String),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -762,6 +770,7 @@ impl Component for Wrapper {
|
||||||
place_request: None,
|
place_request: None,
|
||||||
do_backend_tick: true,
|
do_backend_tick: true,
|
||||||
cleanup_id_callback: Rc::new(RefCell::new(None)),
|
cleanup_id_callback: Rc::new(RefCell::new(None)),
|
||||||
|
board_updated_time: None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1526,13 +1535,18 @@ impl Component for Wrapper {
|
||||||
.ok();
|
.ok();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BREnum::GotStatus(networked_game_state, board_opt, emote_opt) => {
|
BREnum::GotStatus {
|
||||||
|
networked_game_state,
|
||||||
|
board_string,
|
||||||
|
received_emote,
|
||||||
|
updated_time,
|
||||||
|
} => {
|
||||||
let current_side = shared
|
let current_side = shared
|
||||||
.game_state
|
.game_state
|
||||||
.borrow()
|
.borrow()
|
||||||
.get_networked_current_side()
|
.get_networked_current_side()
|
||||||
.expect("Should be Networked mode");
|
.expect("Should be Networked mode");
|
||||||
if let Some(emote_string) = emote_opt {
|
if let Some(emote_string) = received_emote {
|
||||||
if let Ok(emote_enum) = EmoteEnum::try_from(emote_string.as_str()) {
|
if let Ok(emote_enum) = EmoteEnum::try_from(emote_string.as_str()) {
|
||||||
append_to_info_text(
|
append_to_info_text(
|
||||||
&document,
|
&document,
|
||||||
|
@ -1561,8 +1575,14 @@ impl Component for Wrapper {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(board_string) = board_opt {
|
// only update board string if updated_time is different
|
||||||
self.update_board_from_string(&shared, &document, board_string);
|
if self.board_updated_time != updated_time {
|
||||||
|
if let Some(updated_time) = updated_time {
|
||||||
|
self.board_updated_time.replace(updated_time);
|
||||||
|
if let Some(board_string) = board_string {
|
||||||
|
self.update_board_from_string(&shared, &document, board_string);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut current_game_state: GameState = shared.game_state.borrow().clone();
|
let mut current_game_state: GameState = shared.game_state.borrow().clone();
|
||||||
|
|
|
@ -149,7 +149,7 @@ then the back-end will respond with "too\_many\_players".
|
||||||
// "opponent_disconnected", "internal_error"
|
// "opponent_disconnected", "internal_error"
|
||||||
|
|
||||||
// "board" may not be in the response if "unknown_id" is the status
|
// "board" may not be in the response if "unknown_id" is the status
|
||||||
"board": "abcdefg..." // 56-char long string with mapping:
|
"board": "abcdefg...",// 56-char long string with mapping:
|
||||||
// a - empty
|
// a - empty
|
||||||
// b - cyan
|
// b - cyan
|
||||||
// c - magenta
|
// c - magenta
|
||||||
|
@ -160,7 +160,10 @@ then the back-end will respond with "too\_many\_players".
|
||||||
// h - cyan winning and placed piece
|
// h - cyan winning and placed piece
|
||||||
// i - magenta winning and placed piece
|
// i - magenta winning and placed piece
|
||||||
// optional "peer_emote" entry is message from opponent
|
// optional "peer_emote" entry is message from opponent
|
||||||
"peer_emote": "smile" // or "frown", or "neutral", or "think"
|
"peer_emote": "smile",// or "frown", or "neutral", or "think"
|
||||||
|
|
||||||
|
// should always be available when "board" is available
|
||||||
|
"updated_time": "2022-04-30 12:00:00"
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue