Цель
В настоящее время я создаю (для практики с Java) базовую многопользовательскую пошаговую игру для командной строки. В этой игре у каждого игрока есть 5 секунд, чтобы сделать свой ход. Когда он делает свой ход (или , когда заканчивается таймер ), другой игрок начинает свой ход и т. Д. И т. Д.
Сервер отправляет сообщение TimerEnded
каждый раз, когда заканчивается таймер.
Моя текущая цель - добиться безошибочного чтения ввода, чтобы мог прерваться , когда клиенту придет сообщение TimerEnded
.
Дизайн
Чтобы достичь этого, я создал синглтон под названием InputManager
. Этот класс обрабатывает все входные данные для чтения. Я создал метод с именем ask
, который принимает обратный вызов в качестве параметра. В этом методе я создаю новый поток, и внутри него я жду ввода с Scanner.hasNextInt
.
Этот класс также имеет метод closeInput
, который отправляет сообщение прерывания потоку, описанному выше.
Вот текущая реализация класса:
class InputManager{
private Thread thread;
private InputManager(){}
private static InputManager instance;
private static InputManager getInstance(){
if(instance == null){
instance = new InputManager();
}
return instance;
}
/**
* Ask user to type a number.
* @param onSelected When the user has made his choice, this callback will be executed
*/
public static void ask( Consumer<Integer> onSelected){
getInstance().thread = new Thread(() -> {
System.out.println("Type a number:");
Scanner sc = new Scanner(System.in);
int selection = -1;
while (selection == -1) {
if(Thread.currentThread().isInterrupted()){
return;
}
if(sc.hasNextInt()){
selection = sc.nextInt();
onSelected.accept(selection);
} else {
sc.next();
selection = -1;
}
}
});
getInstance().thread.start();
}
/**
* Reset input stream (?)
*/
public static void closeInput(){
try {
getInstance().thread.interrupt();
} catch(NullPointerException e){
// do nothing
}
}
}
Задача
Этот код крайне ненадежен. Я покажу вам, что я имею в виду через мгновение.
Я сделал игрушечный класс под названием Клиент и в main
я смоделировал доход TimerEnd
с помощью таймера.
class Client {
/**
* Ask user to type a number and send it to the server
*/
void makeRequest(){
InputManager.closeInput();
InputManager.ask((selected) -> {
System.out.println("Sent message: " + selected);
});
}
public static void main(String[] args) {
Client client = new Client();
client.makeRequest();
// Simulate Server messages
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println("Message received");
client.makeRequest();
}
}, 5000, 5000);
}
}
Вот как это работает в действии:
Type a number:
2
Sent message: 2
Message received
Type a number:
3
Sent message: 3
Message received
Type a number: // Here I don't type anything
Message received
Type a number:
Message received
Type a number:
Message received
Type a number: // Here I can send multiple messages on the same "turn"
1
Sent message: 1
2
Message received
Необразованное предположение
В настоящее время я предполагаю, что Scanner продолжает ожидать ввода, и поэтому оператор if(isInterrupted)
не выполняется, пока не будет введен ввод. Если так, как я могу избежать этого поведения?
Я понимаю, что этот вопрос чрезвычайно (и, возможно, излишне) длинный, и, поскольку вы его прочитали, позвольте мне поблагодарить вас за то, что вы нашли время.
Минимальный, полный и проверяемый код
package com.company;
import java.util.*;
import java.util.function.Consumer;
class InputManager{
private Thread thread;
private InputManager(){}
private static InputManager instance;
private static InputManager getInstance(){
if(instance == null){
instance = new InputManager();
}
return instance;
}
/**
* Ask user to type a number.
* @param onSelected When the user has made his choice, this callback will be executed
*/
public static void ask( Consumer<Integer> onSelected){
getInstance().thread = new Thread(() -> {
System.out.println("Type a number:");
Scanner sc = new Scanner(System.in);
int selection = -1;
while (selection == -1) {
if(Thread.currentThread().isInterrupted()){
return;
}
if(sc.hasNextInt()){
selection = sc.nextInt();
onSelected.accept(selection);
} else {
sc.next();
selection = -1;
}
}
});
getInstance().thread.start();
}
/**
* Reset input stream (?)
*/
public static void closeInput(){
try {
getInstance().thread.interrupt();
} catch(NullPointerException e){
// do nothing
}
}
}
class Client {
/**
* Ask user to type a number and send it to the server
*/
void makeRequest(){
InputManager.closeInput();
InputManager.ask((selected) -> {
System.out.println("Sent message: " + selected);
});
}
public static void main(String[] args) {
Client client = new Client();
client.makeRequest();
Timer timer = new Timer();
timer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
System.out.println("Message received: thread interrupted");
client.makeRequest();
}
}, 5000, 5000);
}
}