GameHammerを使用するための基本モジュールです。基本設定とConnectionオブジェクトの管理を担当します。Testerオブジェクトを作成するには、まずBuilderを作成し、テストに必要なオプションを設定した後、build()
を呼び出してください。
Tester tester = Tester.newBuilder()
.addProtoBufClass(0, RPSGame.getDescriptor())
.build();
オプション設定を外部から読み込むこともできます。vmoption config.file=PATH
でパスを指定するか、resourceフォルダの下にGameHammerConfg.jsonファイルを作成しておくと、TesterConfigLoaderで自動的に読み込みます。次のような方法で読み込んだオプション設定が適用されたTesterを作成できます。
Tester tester = Tester.newBuilderWithConfig()
.addProtoBufClass(0, RPSGame.getDescriptor())
.build();
ゲームサーバーとの接続、認証などの機能を処理し、ユーザーの管理を担当します。次のようにTesterを利用して作成します。
Connection connection = tester.createConnection(uuid);
作成されたConnectionオブジェクトはTesterで管理し、uuidで区別されます。作成されたオブジェクトのuuidが入力された場合は、そのオブジェクトを返します。
Connectionは次のような機能を提供します。
GameAnvilサーバーに接続します。
Future<ResultConnect> future = connection.connect(new RemoteInfo("127.0.0.1", 11200));
ResultConnect resultConnect = futre.get(); // blocked
if(resultConnect.isSuccess()){
// connect success
}
connect()
から返されたFutureのget()
を呼び出すと、接続が成功するか失敗するまで待機した後、ResultConnectオブジェクトを返します。返されたResultConnectオブジェクトで結果を確認できます。connect()
の2つ目の引数でコールバックを渡して結果を受け取ることもできます。connect()
以外の他のAPIもFutureをリターンして結果を待つか、コールバックを渡して結果を受け取ることができます。
GameAnvilサーバーに認証をリクエストします。認証に成功するとGameAnvilコネクタの他の機能を使用できます。
Future<ResultAuthentication> future = connection.authentication(accountId, password, deviceId, payload);
ResultAuthentication resultAuthentication = future.get(); // blocked
if(resultAuthentication.isSuccess){
// authentication success
}
指定したサービスで使用可能なチャンネルリストをリクエストします。
Future<ResultChannelList> future = connection.getChannelList(serviceName);
ResultChannelList resultChannelList = future.get(); // blocked
if(resultChannelList.isSuccess){
// authentication success
}
指定したチャンネルの情報をリクエストします。
Future<ResultChannelList> future = connection.getChannelList(serviceName, String channelId);
ResultChannelList resultChannelList = future.get(); // blocked
if(resultChannelList.isSuccess){
// authentication success
}
サーバーにメッセージを送り、レスポンスを待ちます。
Future<PacketResult> future = connection.request(message);
PacketResult packetResult = future.get(); // blocked
if(packetResult.isSuccess()){
// request success
}
サーバーにメッセージを送ります。
connection.send(message);
接続を切ります。接続終了時、作成したユーザーを全てログアウトするには、引数にtrueを入力します。
connection.close(true);
Admin強制終了通知を受け取るまで待機します。Adminで強制終了した場合に伝達されます。
Future<ResultAdminKickoutNoti> future = connection.waitForAdminKickoutNoti()
ResultAdminKickoutNoti resultAdminKickoutNoti = future.get(WAIT_TIME_OUT, TimeUnit.MILLISECOND); // blocked
// resultAdminKickoutNoti
強制終了通知を受け取るまで待機します。次のような場合に強制終了通知を受け取ります。1.サーバーでBaseUser.closeConnection()
を呼び出す。2.Authenticationが失敗する。3.同じアカウントで重複ログインを行う。4.UserTrasnfer中に例外が発生する。
Future<ResultForceCloseNoti> future = connection.waitForForceCloseNoti();
ResultForceCloseNoti resultForceCloseNoti = future.get(WAIT_TIME_OUT, TimeUnit.MILLISECOND); // blocked
// resultForceCloseNoti
ネットワーク接続切断通知を受け取るまで待機します。次のような場合に通知を受け取ります。1.サーバーでBaseConnection.close()
を呼び出す。2.ソケットエラーが発生する。3.Connection.close()
、Tester.Close()
を呼び出す。
Future<ResultDisconnect> future = connection.waitForDisconnect();
ResultDisconnect resultDisconnect = future.get(WAIT_TIME_OUT, TimeUnit.MILLISECOND); // blocked
// resultDisconnect
ログインをはじめ、ルーム作成、入場、マッチングなど、ゲームに必要な主な機能を担当します。次のようにUserを作成できます。
User user = connection.getUserAgent(serviceName, subId);
if(user == null){
user = connection.createUser(serviceName, subId);
}
Connection.getUserAgent()
でServiceNameとSubIdにマッチするUserがいるかを確認し、いない場合、Connection.createUser()
で新しいUserを作成します。
Userは次のような機能を提供します。
指定したユーザータイプで指定したチャンネルにログインします。ユーザータイプとチャンネルはサーバーで設定した文字列を使用します。
Future<ResultLogin> future = user.login(resultLogin -> {
if(resultLogin.isSuccess()){
// login success
}
}, userType, channelId, payload);
ResultLogin resultLogin = future.get(); // blocked
if(resultLogin.isSuccess()){
// login success
}
ログインしたチャンネルからログアウトします。
Future<ResultLogout> future = user.logout(resultLogout -> {
if(resultLogout.isSuccess()){
// logout success
}
}, userType, channelId, payload);
ResultLogout resultLogout = future.get(); // blocked
if(resultLogout.isSuccess()){
// logout success
}
強制ログアウト通知を受け取るまで待機します。サーバーでBaseUser.kickout()
を呼び出す場合に伝達されます。
Future<ResultForceLogoutNoti> future = connection.waitForForceLogoutNoti();
ResultForceLogoutNoti resultForceLogoutNoti = future.get(WAIT_TIME_OUT, TimeUnit.MILLISECOND); // blocked
// resultForceLogoutNoti
指定したルームタイプで指定した名前のルームを作成し、そのルームに入場します。ルームタイプはサーバーで設定した文字列を使用します。
Future<ResultCreateRoom> future = user.createRoom(resultCreateRoom -> {
if(resultCreateRoom.isSuccess()){
// createRoom success
}
}, roomType, roomName, payload);
ResultCreateRoom resultCreateRoom = future.get(); // blocked
if(resultCreateRoom.isSuccess()){
// createRoom success
}
指定したIDのルームに入場します。指定したIDのルームがない場合は失敗します。
Future<ResultJoinRoom> future = user.joinRoom(resultJoinRoom -> {
if(resultJoinRoom.isSuccess()){
// joinRoom success
}
}, roomType, roomId, payload);
ResultJoinRoom resultJoinRoom = future.get(); // blocked
if(resultJoinRoom.isSuccess()){
// joinRoom success
}
指定した名前のルームに入場します。指定した名前のルームがない場合は、ルームを作成し、そのルームに入場します。パーティマッチング用のルームの場合はusePartyをtrueにします。
Future<ResultNamedRoom> future = user.namedRoom(resultNamedRoom -> {
if(resultNamedRoom.isSuccess()){
// namedRoom success
}
}, roomType, roomName, useParty, payload);
ResultNamedRoom resultNamedRoom = future.get(); // blocked
if(resultNamedRoom.isSuccess()){
// namedRoom success
}
現在のルームから退出します。
Future<ResultLeaveRoom> future = user.leaveRoom(resultLeaveRoom -> {
if(resultLeaveRoom.isSuccess()){
// leaveRoom success
}
}, payload);
ResultLeaveRoom resultLeaveRoom = future.get(); // blocked
if(resultLeaveRoom.isSuccess()){
// leaveRoom success
}
ルーム強制退出通知を受け取るまで待機します。サーバーでBaseUser.kickoutRoom()を呼び出す場合に伝達されます。
Future<ResultForceLeaveRoomNoti> future = connection.waitForForceLeaveRoomNoti();
ResultForceLeaveRoomNoti resultForceLeaveRoomNoti = future.get(WAIT_TIME_OUT, TimeUnit.MILLISECOND); // blocked
// resultForceLeaveRoomNoti
ユーザーマッチメイキングをリクエストします。すでにルームに入場している場合など、サーバーの条件によってリクエストが失敗することがあります。WaitForMatchUserDoneNotiを介してマッチ成功通知を、WaitForMatchUserTimeoutNotiを介してマッチタイムアウト通知を受け取れます。
Future<ResultMatchUserStart> future = user.matchUserStart(resultMatchUserStart -> {
if(resultMatchUserStart.isSuccess()){
// matchUserStart success
}
}, roomType, payload);
ResultMatchUserStart resultMatchUserStart = future.get(); // blocked
if(resultMatchUserStart.isSuccess()){
// matchUserStart success
}
ユーザーマッチメイキングリクエストをキャンセルします。マッチリクエスト中ではない場合や、すでにマッチングが成功していたり、タイムアウトが発生した場合は失敗することがあります。
Future<ResultMatchUserCancel> future = user.matchUserCancel(resultMatchUserCancel -> {
if(resultMatchUserCancel.isSuccess()){
// matchUserCancel success
}
}, roomType);
ResultMatchUserCancel resultMatchUserCancel = future.get(); // blocked
if(resultMatchUserCancel.isSuccess()){
// matchUserCancel success
}
ユーザーマッチメイキングまたはパーティマッチメイキング完了通知を受け取るまで待機します。
Future<ResultMatchUserDone> future = connection.waitForMatchUserDoneNoti();
ResultMatchUserDone resultMatchUserDone = future.get(WAIT_TIME_OUT, TimeUnit.MILLISECOND); // blocked
// resultMatchUserDone
ユーザーマッチメイキングまたはパーティマッチメイキングのタイムアウト通知を受け取るまで待機します。
Future<ResultMatchUserTimeout> future = connection.waitForMatchUserTimeoutNoti();
ResultMatchUserTimeout resultMatchUserTimeout = future.get(WAIT_TIME_OUT, TimeUnit.MILLISECOND); // blocked
// resultMatchUserTimeout
パーティマッチメイキングをリクエストします。パーティマッチメイキング用のルームに入場した状態でリクエストできます。WaitForMatchUserDoneNotiを介してマッチ成功通知を、WaitForMatchUserTimeoutNotiを介してマッチタイムアウト通知を受け取れます。
Future<ResultMatchPartyStart> future = user.matchPartyStart(resultMatchPartyStart -> {
if(resultMatchPartyStart.isSuccess()){
// matchPartyStart success
}
}, roomType, payload);
ResultMatchPartyStart resultMatchPartyStart = future.get(); // blocked
if(resultMatchPartyStart.isSuccess()){
// matchPartyStart success
}
パーティマッチメイキング開始通知を受け取るまで待機します。パーティマッチメイキング用のルームで他の人がパーティマッチメイキングを始めた場合に伝達されます。
Future<ResultMatchPartyStart> future = connection.waitForMatchPartyStartNoti();
ResultMatchPartyStart resultMatchPartyStart = future.get(WAIT_TIME_OUT, TimeUnit.MILLISECOND); // blocked
// resultMatchPartyStart
パーティマッチメイキングリクエストをキャンセルします。パーティマッチメイキング中ではない場合や、すでにパーティマッチメイキングに成功していたり、タイムアウトが発生した場合は、失敗することがあります。
Future<ResultMatchPartyCancel> future = user.matchPartyCancel(resultMatchPartyCancel -> {
if(resultMatchPartyCancel.isSuccess()){
// matchPartyCancel success
}
}, roomType);
ResultMatchPartyCancel resultMatchPartyCancel = future.get(); // blocked
if(resultMatchPartyCancel.isSuccess()){
// matchPartyCancel success
}
パーティマッチメイキングキャンセル通知を受け取るまで待機します。パーティマッチメイキング中に他の人がパーティマッチメイキングをキャンセルした場合に伝達されます。
Future<ResultMatchPartyCancel> future = connection.waitForMatchPartyCancelNoti();
ResultMatchPartyCancel resultMatchPartyCancel = future.get(WAIT_TIME_OUT, TimeUnit.MILLISECOND); // blocked
// resultMatchPartyCancel
ルームマッチメイキングをリクエストします。ルームがない場合、任意のルームを作成し、そのルームに入場することもできます。
Future<ResultMatchRoom> future = user.matchRoom(resultMatchRoom -> {
if(resultMatchRoom.isSuccess()){
// matchRoom success
}
}, roomType);
ResultMatchRoom resultMatchRoom = future.get(); // blocked
if(resultMatchRoom.isSuccess()){
// matchRoom success
}
指定したチャンネルに移動します。
Future<ResultMoveChannel> future = user.moveChannel(resultMoveChannel -> {
if(resultMoveChannel.isSuccess()){
// moveChannel success
}
}, roomType);
ResultMoveChannel resultMoveChannel = future.get(); // blocked
if(resultMoveChannel.isSuccess()){
// moveChannel success
}
チャンネル移動通知を受け取るまで待機します。ルーム入場、マッチメイキングなどの理由でチャンネルを移動する場合に伝達されます。
Future<ResultMoveChannelNoti> future = connection.waitForMoveChannelNoti();
ResultMoveChannelNoti resultMoveChannelNoti = future.get(WAIT_TIME_OUT, TimeUnit.MILLISECOND); // blocked
// resultMoveChannelNoti
サーバーにメッセージを送り、レスポンスを待ちます。
Future<PacketResult> future = user.request(packetResult -> {
if(packetResult.isSuccess()){
// request success
}
}, message);
PacketResult packetResult = future.get(); // blocked
if(packetResult.isSuccess()){
// request success
}
サーバーにメッセージを送ります。
user.send(message);
告知通知を受け取るまで待機します。Adminから告知を送ったり、REST APIを利用して告知を送る場合に伝達されます。
Future<ResultNotice> future = connection.waitForMoveChannelNoti();
ResultNotice resultNotice = future.get(WAIT_TIME_OUT, TimeUnit.MILLISECOND); // blocked
// resultNotice
JUnitを利用してテストコードを作成する時、次のようにBeforeClass、AfterClass、Afterコードを作成します。
public class TestWithGameHammer {
static Tester tester;
@BeforeClass
public static void beforeClass() {
tester = Tester.newBuilder()
.addProtoBufClass(0, RPSGame.getDescriptor())
.build();
}
@Before
public void before() {
}
@After
public void after() {
tester.closeAllConnections();
}
@AfterClass
public static void afterClass() {
tester.close();
tester = null;
}
@Test
public void connectTest() {
ResultConnect resultConnect = connect(connection);
assertEquals(ResultCodeConnect.CONNECT_SUCCESS, resultConnect.getResultCode());
}
@Test
public void someTest() {
// some test code
}
このように作成すると、先行テストで使用したユーザーが残って、次のテストに影響を及ぼすことを防ぐことができます。
GameHammerを利用したテストコードは、大きく2つのタイプに分けられます。1つ目はRequest/Response形式のテストコードです。クライアントからRequestを送る場合、サーバーからは必ずResponseを送る必要があります。レスポンスを送らない場合、timeoutが発生します。GameAnvilコネクタが提供するAPIの大部分がこのようなRequest/Response方式で、GameHammerでもこのようなRequest/Response方式のテストをサポートします。
例えば、Connectionのconnectのテストコードは次のように作成できます。
@Test
public void Connect() {
Connection connection = tester.createConnection(uuid);
Future<ResultConnect> future = connection.connect(new RemoteInfo("127.0.0.1", 11200));
ResultConnect resultConnect = futre.get(); // blocked
Assert.assertTrue("Connection fail", resultConnect.isSuccess());
// more test code
}
connection.connect()
を呼び出し、サーバーに接続します。このとき返されたFuture
のget()
を呼び出すと、connection.connect()
が完了するまで待機し、完了するとその結果としてResultConnect
をリターンします。ここでリターンされたResultConnect
を利用して成否を判断できます。
また他の例として、ゲームで使用するメッセージのRequest/Response方式テストコードは次のように作成できます。
@Test
public void RequestTest() {
Connection connection = tester.createConnection(uuid);
// assume aleady connected.
Messages.Request request = Messages.Request.newBuilder().build();
Future<PacketResult> future = connection.request(request);
PacketResult packetResult = future.get(); // blocked
Assert.assertTrue("Response fail", packetResult.isSuccess());
Messages.Response response = Messages.Response.parseFrom(packetResult.getStream());
// more test code
}
まずメッセージを作成し、これをconnection.request()
の引数に入れ、サーバーに送信します。このとき返されたFuture
のget()
を呼び出すとサーバーでレスポンスするか、タイムアウトが発生するまで待機し、レスポンスを受け取るか、タイムアウトが発生すると、PacketResult
を返します。ここで返されたPacketResult
を利用して成否を判断できます。成功するとPacketResult.getStream()
を利用してメッセージを解析して、内容を確認できます。
Connnection、UserのAPIのうち、FutureをリターンするAPIは全てこのような方式でテストできます。
クライアントはSendを送り、レスポンスを待機しません。そしてサーバーでもクライアントの動作に関係なくSendを送れます。テストは次のように作成できます。
@Test
public void RequestTest() {
Connection connection = tester.createConnection(uuid);
// assume aleady connected.
Messages.SendToServer sendToServer = Messages.SendToServer.newBuilder().build();
connection.send(sendToServer);
Future<PacketResult> future = connection.waitFor(Messages.SendToClient.getDescription());
PacketResult packetResult = future.get(3000, TimeUnit.MILLISECONDS); // blocked
Assert.assertTrue("receive fail", packetResult.isSuccess());
Messages.SendToClient sendToClient = Messages.SendToClient.parseFrom(packetResult.getStream());
// more test code
}