首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >游戏服务器应用

游戏服务器应用
EN

Code Review用户
提问于 2018-09-24 19:35:57
回答 1查看 89关注 0票数 0

我试图编写游戏服务器核心,我可以很容易地扩展到各种游戏中使用。从简单的在线Blackjack会话,到MMORPG。长话短说,我想出了一个解决方案:

首先,我们为服务器和客户端都提供了公共类。这些都是简单的数据结构,可能需要用Builders包装它们,但是对于这个演示,我想我可以跳过这个。

代码语言:javascript
复制
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。

代码语言:javascript
复制
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

代码语言:javascript
复制
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,它正在侦听请求并将其重定向到进一步的处理。

代码语言:javascript
复制
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,因为它将传入的数据转换为事件,这是两个不同的对象。

代码语言:javascript
复制
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;
    }
}
EN

回答 1

Code Review用户

发布于 2018-09-24 22:11:36

我试图编写游戏服务器核心,我可以很容易地扩展到各种游戏中使用。从简单的在线Blackjack会话,到MMORPG。

我很肯定,这不会像你想的那么容易。几个玩家每秒交换一次数据,和每秒交换数据15/30/60次,这是一个巨大的差异。“大量”玩家。

类DataPacket

您可以使DataPacket成为扩展Serializable的接口。

类LoginResponse

包含String作为响应。这意味着,首先,它没有国际化,如果这是一个问题,第二,客户端必须解析一个字符串来验证,如果登录尝试是成功的。布尔值有什么问题?

类调度器

  • 什么调度员?
  • 通常情况下,更倾向于组合而不是继承。
  • maxHosts从未被使用过,因此死代码。
  • e.printStackTrace:不,我们不这么做。在构造函数中,它特别糟糕,因为程序在acceptIncomingConnections()期间继续运行并再次失败。
  • 字段status必须声明为volatile
  • Thread.interrupt():这是你必须确切知道自己在做什么的事情之一。
  • 创建ServerWorker的一个新实例,将this作为参数传递。这是一个双向依赖,必须避免。

类ServerWorker

  • run()捕获IOE和NPE并执行相同的代码。我不认为你的计划应该达到这一点,所以NPE是可能的。
  • 听():听什么?而且它不完全听,准确地说,它更像是'readAndProcessDataPacket()‘。
  • process():为什么要使用Optional?我对此的解释是,发送服务器所不知道的数据是可能的,如果发生这种情况,什么都不会发生。我不认为这是你想要的。
  • send():您必须在写入对象之后进行刷新,以保证数据已经刷新,而不是以前。

接口事件

这里,您在网络层和事件层之间有一个双向依赖关系。从长远来看,这将是可怕的维持。我建议,首先让系统在没有网络层的情况下运行。更多地考虑API,您想要从上面的一个层调用它。

希望这能帮上忙

慢慢来

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/204300

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档