Marauroa Chat Tutorial/Server

来自gamedev
imported>朱志凌2020年4月27日 (一) 21:00的版本
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳转到导航 跳转到搜索


Marauroa Tutorial


编码[编辑]

为了创建基于Marauroa的游戏服务器,您必须至少提供marauroa.server.game.rp.IRPRuleProcessor接口的实现以及用于区域管理的类,即marauroa.server.game.rp.RPWorld的子类。 您可以使用marauroa.server.game.rp.RPWorld本身,但它不会为您提供任何RPZone,您几乎肯定需要至少一个RPZone。

我们从RPWorld的以下实现开始

import marauroa.server.game.rp.RPWorld;
import marauroa.server.game.rp.MarauroaRPZone;

public class World extends RPWorld {
  private static World instance;

  public static World get() {
    if (instance == null) {
      instance = new World();
    }
    return instance;
  }
  
  @Override
  public void onInit() {
    super.onInit();
    MarauroaRPZone zone = new MarauroaRPZone("lobby");
    addRPZone(zone);
  }
}

不要忘记实现静态的get方法,该方法由Marauroa框架用来检索您的类的实例。

创建世界时将调用onInit方法。 我们唯一要做的就是创建区域(因为我们有一个聊天应用程序,我们称之为“大厅”)。 区域是Marauroa的一般概念。 它代表一个游戏区。 停留在特定区域中的所有客户端仅会收到有关该区域中数据更改的通知。 聊天室就是一个很好的例子,就像您在聊天室中一样,您看不到其他聊天室中的对话,即您仅在当前房间中收到有关新事物的通知。

实现IRPRuleProcessor将需要更多代码,因为我们必须实现接口的所有方法。 我们将从以下存根开始

import java.sql.SQLException;

import marauroa.common.crypto.Hash;
import marauroa.common.game.AccountResult;
import marauroa.common.game.CharacterResult;
import marauroa.common.game.IRPZone;
import marauroa.common.game.RPAction;
import marauroa.common.game.RPObject;
import marauroa.common.game.Result;
import marauroa.server.game.db.DAORegister;
import marauroa.server.game.db.AccountDAO;
import marauroa.server.game.db.CharacterDAO;
import marauroa.server.db.TransactionPool;
import marauroa.server.db.DBTransaction;
import marauroa.server.game.rp.IRPRuleProcessor;
import marauroa.server.game.rp.RPServerManager;


public class Rule implements IRPRuleProcessor {
  private static Rule instance;

  private World world = World.get();

  private RPServerManager manager;

  public static IRPRuleProcessor get() {
    if (instance == null) {
      instance = new Rule();
    }
    return instance;
  }

  @Override
  public void setContext(RPServerManager rpman) {
    manager = rpman;
  }

  @Override
  public boolean checkGameVersion(String game, String version) {
    return game.equals("Chat");
  }

  @Override
  public synchronized void onTimeout(RPObject character) {
    onExit(character);
  }

  @Override
  public synchronized boolean onExit(RPObject character) {
    world.remove(character.getID());
    return true;
  }

  @Override
  public synchronized boolean onInit(RPObject character) {
    IRPZone zone = world.getRPZone(new IRPZone.ID("lobby"));
    zone.add(character);
    return true;
  }

  @Override
  public synchronized void beginTurn() {
  }

  @Override
  public boolean onActionAdd(RPObject caster, RPAction action, List<RPAction> actionList) {
    return true;
  }

  @Override
  public synchronized void endTurn() {
  }

  @Override
  public void execute(RPObject caster, RPAction action) {
    if (action.get("type").equals("chat")) {
      RPObject chatEntry = new RPObject();
      chatEntry.put("text", action.get("text"));
      chatEntry.put("from", caster.get("nick"));
      chatEntry.put("turn", manager.getTurn());
      IRPZone zone = world.getRPZone(new IRPZone.ID(caster.getID().getZoneID()));
      zone.assignRPObjectID(chatEntry);
      zone.add(chatEntry);
    }
  }

  @Override
  public AccountResult createAccount(String username, String password, String email) {
    TransactionPool transactionPool = TransactionPool.get();
    DBTransaction trans = transactionPool.beginWork();
    AccountDAO accountDAO = DAORegister.get().get(AccountDAO.class);
    try {
      if (accountDAO.hasPlayer(trans, username)) {
        return new AccountResult(Result.FAILED_PLAYER_EXISTS, username);
      }
      accountDAO.addPlayer(trans, username, Hash.hash(password), email);
      transactionPool.commit(trans);
      return new AccountResult(Result.OK_CREATED, username);
    } catch (SQLException e1) {
      transactionPool.rollback(trans);

      return new AccountResult(Result.FAILED_EXCEPTION, username);
    }
  }

  @Override
  public CharacterResult createCharacter(String username, String characterName, RPObject template) {
    TransactionPool transactionPool = TransactionPool.get();
    DBTransaction trans = transactionPool.beginWork();
    CharacterDAO characterDAO = DAORegister.get().get(CharacterDAO.class);
    try {
      if (characterDAO.hasCharacter(trans, username, characterName)) {
        return new CharacterResult(Result.FAILED_CHARACTER_EXISTS, characterName, template);
      }
      IRPZone zone = world.getRPZone(new IRPZone.ID("lobby"));
      RPObject character = new RPObject(template);
      character.put("nick", characterName);
      zone.assignRPObjectID(character);
      characterDAO.addCharacter(trans, username, characterName, character);
      transactionPool.commit(trans);
      return new CharacterResult(Result.OK_CREATED, characterName, character);
    } catch (Exception e1) {
      transactionPool.rollback(trans);

      return new CharacterResult(Result.FAILED_EXCEPTION, characterName, template);
    }
  }
}

我们再次实现静态的get()方法,该方法由Marauroa用于检索RuleProcessor实例。

大多数功能都是简单的存根,可以将其替换为实际起作用的代码。

您可以使用checkGameVersion()拒绝版本过旧的客户端。 在我们的情况下,我们只要求游戏名称为“聊天”即可。

每当玩家成功登录游戏时,就会调用onInit()函数。 他的角色是从数据库中加载的。 我们会立即将玩家添加到大厅区域,因为连接到聊天室的每个人都首先进入大厅。

最重要的函数是execute(),每次服务器从一个客户端接收到操作时都会调用该函数。 在这种情况下,我们正在等待“聊天”动作。 一旦我们收到一个消息,就会创建一个代表一个聊天消息的新对象。 我们还设置了消息的其他一些属性:发送此消息的人的昵称,发送消息时的回合码(turn code)。

创建帐户和角色的功能应确定是否创建一个新帐户/角色。 在我们的例子中,我们总是这样做(当然不能重复)。 此操作的结果将立即写入数据库。 请注意,客户端可以为头像对象(与角色关联的RPObject)提供模板。 由您决定如何在构建实际的头像对象时使用它。 我们采用客户端提供的内容,添加一个“ nick”属性(与角色名称相同),并将得到的属性用作化身对象。

部署[编辑]

现在,我们有两个文件World.java和Rule.java,其中包含上述类。

在 Windows 上, 你可以用下面的命令编译

javac -cp marauroa.jar;log4j.jar;. *.java

在Linux 和 MacOSX 上, 只需要把 ";" 换成 ":".

javac -cp marauroa.jar:log4j.jar:. *.java

此命令假定您在同一目录中具有文件marauroa.jar和log4j.jar。 您将得到World.class和Rule.class输出文件。

为了运行服务器,您还需要server.ini文件:

database_adapter=marauroa.server.db.adapter.H2DatabaseAdapter
jdbc_url=jdbc:h2:~/clientserverchat/database/h2db;AUTO_RECONNECT=TRUE;DB_CLOSE_ON_EXIT=FALSE
jdbc_class=org.h2.Driver

# TCP port
tcp_port=5555

# World and RP configuration. Don't edit.
world=World
ruleprocessor=Rule

turn_length=300

请运行以下命令生成将用于加密登录信息的密钥对。只需在server.ini的末尾复制输出即可。

java -cp marauroa.jar;log4j.jar marauroa.tools.GenerateKeys


现在,您已经准备好启动和运行服务器。要启动服务器,我通常使用以下命令

java -cp marauroa.jar;h2.jar;log4j.jar;. marauroa.server.marauroad -c server.ini

在Linux和MacOSX下, 把 ";" 替换成 ":".

java -cp marauroa.jar:h2.jar:log4j.jar:. marauroa.server.marauroad -c server.ini

当然,请确保所有jar都在当前目录中。 Marauroa框架将解析server.ini文件,查找并加载类World和Rule。

下一步[编辑]

在本教程的下一部分中,我们将编写 客户端 它将连接到我们刚写的服务器上. {{#breadcrumbs: Marauroa | 使用 | 教程 | 服务器}}