我试图编写游戏服务器核心,我可以很容易地扩展到各种游戏中使用。从简单的在线Blackjack会话,到MMORPG。长话短说,我想出了一个解决方案:
首先,我们为服务器和客户端都提供了公共类。这些都是简单的数据结构,可能需要用Builders包装它们,但是对于这个演示,我想我可以跳过这个。
public abstract class DataPacket implements Serializable {
}
final public class LoginRequest extends DataPacket implements Serializable {
private final String accountNumber;
private final String password;
public LoginRequest(String accountNumber, String password) {
this.accountNumber = accountNumber;
this.password = password;
}
public String getAccountNumber() {
return accountNumber;
}
public String getPassword() {
return password;
}
}
final public class LoginResponse extends DataPacket implements Serializable {
private final String message;
public LoginResponse(String message) {
this.message = message;
}
public String getMessage() {
return message;
}
}现在让我们转到核心ServerController,它是一种Server,具有所有的公共方法。Config只是一个Property加载器类,将来可能会改为*.ini文件而不是*.property,所以我在这里使用DI。
public class ServerController {
private Config config;
private Dispatcher dispatcher;
@Autowired
public ServerController(Config config) {
this.config = config;
dispatcher = new Dispatcher(config.getPort(), config.getMaxHosts());
}
public void start() {
dispatcher.start();
}
public void stop() {
dispatcher.stopAccepting();
}
}然后是Dispatcher,它侦听传入的连接并为每个连接创建新的ServerWorker。
class Dispatcher extends Thread {
private ServerSocket serverSocket;
private List<ServerWorker> serverWorkers;
private Status status;
private final int maxHosts;
Dispatcher(int port, int maxHosts) {
serverWorkers = new ArrayList<>();
status = Status.DOWN;
this.maxHosts = maxHosts;
try {
this.serverSocket = new ServerSocket(port);
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
status = Status.UP_ACCEPTING;
while(status == Status.UP_ACCEPTING) {
acceptIncomingConnections();
}
}
void stopAccepting() {
status = Status.UP_NOT_ACCEPTING;
this.interrupt();
}
void disconnectClient(ServerWorker worker) {
serverWorkers.remove(worker);
worker.interrupt();
}
private void acceptIncomingConnections() {
try {
Socket socket = serverSocket.accept();
ServerWorker worker = new ServerWorker(socket, this);
worker.start();
serverWorkers.add(worker);
} catch (IOException e) {
e.printStackTrace();
}
}
}这将导致我们进入ServerWorker,它正在侦听请求并将其重定向到进一步的处理。
class ServerWorker extends Thread {
private ObjectInputStream inputStream;
private ObjectOutputStream outputStream;
private Dispatcher dispatcher;
ServerWorker(Socket socket, Dispatcher dispatcher){
this.dispatcher = dispatcher;
try {
inputStream = new ObjectInputStream(socket.getInputStream());
outputStream = new ObjectOutputStream(socket.getOutputStream());
} catch (IOException e) {
e.printStackTrace();
}
}
@Override
public void run() {
try {
listen();
} catch (IOException | NullPointerException e) {
e.printStackTrace();
dispatcher.disconnectClient(this); //Once Client disconnects, this Exception is thrown
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
private void listen() throws IOException, ClassNotFoundException {
DataPacket dataPacket;
while((dataPacket = (DataPacket)inputStream.readObject()) != null ) {
process(dataPacket);
}
}
private void process(DataPacket dataPacket) throws IOException {
Optional<Event> event = EventFactory.getEvent(dataPacket);
event.ifPresent(e -> {
e.process();
send(e.getProcessedData());
});
}
private void send(DataPacket dataPacket) throws IOException {
outputStream.flush();
outputStream.writeObject(dataPacket);
}
}这就留给我们最后的部分了,Events。不确定是否可以避免在这里使用instanceof,因为它将传入的数据转换为事件,这是两个不同的对象。
public interface Event {
void process();
DataPacket getProcessedData();
}
public class EventFactory {
public static Optional<Event> getEvent(DataPacket dataPacket){
if(dataPacket instanceof LoginRequest) {
return Optional.of(new LoginEvent((LoginRequest) dataPacket));
}
return Optional.empty();
}
}
public class LoginEvent implements Event {
private LoginRequest request;
private LoginResponse response;
LoginEvent(LoginRequest loginRequest) {
request = loginRequest;
}
@Override
public void process() {
if(isValid(request.getAccountNumber(), request.getPassword())) {
response = new LoginResponse("You successfully logged in!");
} else {
response = new LoginResponse("Something went wrong...");
}
}
@Override
public DataPacket getProcessedData() {
return response;
}
private boolean isValid(String accountNumber, String password) {
return true;
}
}发布于 2018-09-24 22:11:36
我试图编写游戏服务器核心,我可以很容易地扩展到各种游戏中使用。从简单的在线Blackjack会话,到MMORPG。
我很肯定,这不会像你想的那么容易。几个玩家每秒交换一次数据,和每秒交换数据15/30/60次,这是一个巨大的差异。“大量”玩家。
您可以使DataPacket成为扩展Serializable的接口。
包含String作为响应。这意味着,首先,它没有国际化,如果这是一个问题,第二,客户端必须解析一个字符串来验证,如果登录尝试是成功的。布尔值有什么问题?
acceptIncomingConnections()期间继续运行并再次失败。status必须声明为volatile。this作为参数传递。这是一个双向依赖,必须避免。Optional?我对此的解释是,发送服务器所不知道的数据是可能的,如果发生这种情况,什么都不会发生。我不认为这是你想要的。这里,您在网络层和事件层之间有一个双向依赖关系。从长远来看,这将是可怕的维持。我建议,首先让系统在没有网络层的情况下运行。更多地考虑API,您想要从上面的一个层调用它。
希望这能帮上忙
慢慢来
https://codereview.stackexchange.com/questions/204300
复制相似问题