Continuing from this thread on implementing 2-players mode for a tetris game.
When one player reaches a game over state, I want to send a message to other player to inform them and restart the game. This communication will be facilitated through a TCP stream, with data being transferred from one thread to the main thread using mpsc::channel
:
player1 <--- TCP stream ---> [`receive_message` thread <--- mpsc::channel --> main thread] player 2
Let's take a look at my Game
struct:
struct Game {
stream: Option<TcpStream>,
receiver: Option<Receiver<MessageType>>,
}
impl Game {
fn new(
conn: Connection,
stream: Option<TcpStream>,
receiver: Option<Receiver<MessageType>>,
) -> Self {
The receive_message
thread:
let (sender, receiver): (Sender<MessageType>, Receiver<MessageType>) = channel();
let mut game = Game::new(conn, Some(stream), Some(receiver));
thread::spawn(move || {
thread_barrier_clone.wait();
receive_message(&mut stream_clone, sender);
});
fn receive_message(stream: &mut TcpStream, sender: Sender<MessageType>) {
let mut buffer = [0u8; 256];
loop {
match stream.read(&mut buffer) {
Ok(n) if n > 0 => {
let msg = String::from_utf8_lossy(&buffer[0..n]);
println!("received: {}", msg);
if msg.starts_with(PREFIX_CLEARED_ROWS) {
if let Ok(rows) = msg.trim_start_matches(PREFIX_CLEARED_ROWS).parse() {
println!("sending cleared rows: {}", rows);
sender.send(MessageType::ClearedRows(rows)).unwrap();
}
} else if msg.starts_with(PREFIX_NOTIFICATION) {
let msg = msg.trim_start_matches(PREFIX_NOTIFICATION).to_string();
sender.send(MessageType::Notification(msg)).unwrap();
}
}
Ok(_) | Err(_) => {
break;
}
}
}
}
On the receiver side (main thread):
fn handle_event(&mut self, stdout: &mut std::io::Stdout) -> Result<()> {
let mut drop_timer = Instant::now();
let mut soft_drop_timer = Instant::now();
loop {
if self.paused {
self.handle_pause_event(stdout)?;
} else {
if let Some(receiver) = self.receiver.take() {
self.process_received_messages(stdout, &receiver)?;
}
fn process_received_messages(
&mut self,
stdout: &mut io::Stdout,
receiver: &Receiver<MessageType>,
) -> Result<()> {
let receiver_clone = receiver.clone();
for message in receiver.try_iter() {
match message {
MessageType::Notification(msg) => match msg.as_str() {
GAME_OVER_MESSAGE => {
loop {
if poll(Duration::from_millis(10))? {
let event = read()?;
match event {
Event::Key(KeyEvent {
code,
modifiers: _,
kind: _,
state: _,
}) => match code {
KeyCode::Enter | KeyCode::Char('c') => {
self.render(stdout);
self.paused = false;
break;
}
KeyCode::Char('r') => {
reset_game(self.stream.take(), Some(*receiver_clone))?;
}
The reset_game
function:
fn reset_game(stream: Option<TcpStream>, receiver: Option<Receiver<MessageType>>) -> Result<()> {
let conn = open()?;
let mut game = Game::new(conn, stream, receiver);
let mut stdout = std::io::stdout();
game.render(&mut stdout);
match game.handle_event(&mut stdout) {
Ok(_) => {}
Err(err) => eprintln!("Error: {}", err),
}
Ok(())
}
However, there is an error:
error[E0507]: cannot move out of `*receiver_clone` which is behind a shared reference
--> src/main.rs:583:81
|
583 | ... reset_game(self.stream.take(), Some(*receiver_clone))?;
| ^^^^^^^^^^^^^^^ move occurs because `*receiver_clone` has type `std::sync::mpsc::Receiver<MessageType>`, which does not implement the `Copy` trait
If I try to change the signature to:
fn process_received_messages(
&mut self,
stdout: &mut io::Stdout,
receiver: Receiver<MessageType>,
) -> Result<()> {
then the error will be:
error[E0505]: cannot move out of `receiver` because it is borrowed
--> src/main.rs:582:81
|
526 | receiver: Receiver<MessageType>,
| -------- binding `receiver` declared here
527 | ) -> Result<()> {
528 | for message in receiver.try_iter() {
| ------------------- borrow of `receiver` occurs here
...
582 | reset_game(self.stream.take(), Some(receiver))?;
| ^^^^^^^^ move out of `receiver` occurs here
How can I fix this?
You can view my code here.
Thank you.