Marauroa Chat Tutorial/Server

出自gamedev
跳至導覽 跳至搜尋


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 | 使用 | 教程 | 伺服器}}