
io.gs2.core.net.Gs2Session Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of gs2-java-sdk Show documentation
Show all versions of gs2-java-sdk Show documentation
Game Server Services SDK for Java
The newest version!
package io.gs2.core.net;
import io.gs2.core.exception.Gs2Exception;
import io.gs2.core.exception.SessionNotOpenException;
import io.gs2.core.exception.UnknownException;
import io.gs2.core.model.AsyncAction;
import io.gs2.core.model.AsyncResult;
import io.gs2.core.model.BasicGs2Credential;
import io.gs2.core.model.Region;
import io.gs2.core.result.CloseResult;
import io.gs2.core.result.OpenResult;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.ReentrantLock;
public abstract class Gs2Session {
private enum State {
Idle,
Opening,
CancellingOpen,
Available,
CancellingTasks,
Closing,
}
;
private ReentrantLock m_Mutex = new ReentrantLock();
private State m_State;
private final BasicGs2Credential m_Gs2Credential;
private Region m_Region;
private String m_ProjectToken;
private List>> openCallbackList = new ArrayList<>();
private List>> closeCallbackList = new ArrayList<>();
private List gs2SessionTaskList = new ArrayList<>();
private AsyncAction> m_OnClose;
private Gs2SessionTaskId.Generator m_Gs2SessionIdTaskGenerator = new Gs2SessionTaskId.Generator();
private static void triggerOpenCallback(List>> openCallbackList, Gs2Exception result) {
for (AsyncAction> pOpenCallback : openCallbackList) {
pOpenCallback.callback(new AsyncResult<>(
new OpenResult(),
result
));
}
}
private static void triggerCloseCallback(List>> closeCallbackList, Gs2Exception result) {
for (AsyncAction> pCloseCallback : closeCallbackList) {
pCloseCallback.callback(new AsyncResult<>(
new CloseResult(),
result
));
}
}
private static void triggerCancelTasksCallback(List gs2SessionTaskList, Gs2Exception gs2Exception) {
for (Gs2SessionTask pGs2SessionTask : gs2SessionTaskList) {
pGs2SessionTask.triggerUserCallback(new Gs2ClientErrorResponse(gs2Exception)); // notifyComplete() は不要なのでユーザコールバックのみ呼ぶ
}
}
private void enterStateLock() {
m_Mutex.lock();
}
private void exitStateLock() {
m_Mutex.unlock();
}
;
private void changeStateToIdle() {
// 外部要因による切断がありうるので、どの状態からでも遷移しうる
assert (openCallbackList.isEmpty()); // すべてコールバックされ(るために取り出され)ているべき
assert (closeCallbackList.isEmpty()); // すべてコールバックされ(るために取り出され)ているべき
assert (gs2SessionTaskList.isEmpty()); // Available になる前に登録はできない
m_State = State.Idle;
exitStateLock();
}
private void changeStateToOpening() {
assert (m_State == State.Idle || m_State == State.Closing);
assert (!openCallbackList.isEmpty()); // open() タスクが登録されているときのみ遷移する
assert (closeCallbackList.isEmpty()); // すべてコールバックされ(るために取り出され)ているべき
assert (gs2SessionTaskList.isEmpty()); // Available になる前に登録はできない
m_State = State.Opening;
openImpl();
exitStateLock();
}
private void changeStateToCancellingOpen() {
assert (m_State == State.Opening);
assert (!openCallbackList.isEmpty()); // Opening は open() タスクが必ず存在する
assert (!closeCallbackList.isEmpty()); // 接続処理中の close() によってのみ遷移する
assert (gs2SessionTaskList.isEmpty()); // Available になる前に登録はできない
m_State = State.CancellingOpen;
cancelOpenImpl();
exitStateLock();
}
private void changeStateToAvailable(String projectToken) {
assert (m_State == State.Opening);
assert (openCallbackList.isEmpty()); // すべてコールバックされ(るために取り出され)ているべき
assert (closeCallbackList.isEmpty()); // close() が呼ばれている場合は Closing に遷移しなければならない
assert (gs2SessionTaskList.isEmpty()); // Available になる前に登録はできない
m_ProjectToken = projectToken;
m_State = State.Available;
exitStateLock();
}
private void changeStateToCancellingTasks() {
assert (m_State == State.Available);
assert (openCallbackList.isEmpty()); // Available のあいだの open() は即時返却される
// 外部要因による切断の場合に close() を呼ばなくても遷移することがある
assert (!gs2SessionTaskList.isEmpty()); // キャンセルしたいタスクがあるから遷移するのである
m_State = State.CancellingTasks;
exitStateLock();
}
private void changeStateToClosing() {
assert (m_State == State.Opening || m_State == State.CancellingOpen || m_State == State.Available || m_State == State.CancellingTasks);
// CancellingTasks のあいだには次の open() が積まれることがある
// 外部要因による切断の場合に close() を呼ばなくても遷移することがある
assert (gs2SessionTaskList.isEmpty()); // タスクがなくなったときに遷移する
m_ProjectToken = null;
m_State = State.Closing;
boolean isCloseInstant = closeImpl();
if (isCloseInstant) {
// Idle か Opening に遷移しているはずだけど、ロックから出てしまっているので検証はしない
} else {
exitStateLock();
}
}
private void keepCurrentState() {
exitStateLock();
}
// Gs2SessionTask から利用
public void execute(Gs2SessionTask gs2SessionTask) {
enterStateLock();
if (m_State == State.Available) {
gs2SessionTask.gs2SessionTaskId = m_Gs2SessionIdTaskGenerator.issue();
gs2SessionTask.prepareImpl();
gs2SessionTaskList.add(gs2SessionTask);
keepCurrentState();
gs2SessionTask.executeImpl();
} else {
keepCurrentState();
gs2SessionTask.callback(new Gs2ClientErrorResponse(new SessionNotOpenException("")));
}
}
void notifyComplete(Gs2SessionTask gs2SessionTask) {
enterStateLock();
gs2SessionTaskList.remove(gs2SessionTask);
if (m_State == State.CancellingTasks && gs2SessionTaskList.isEmpty()) {
changeStateToClosing();
} else {
keepCurrentState();
}
}
protected String getProjectToken() {
return m_ProjectToken;
}
public void openCallback(String pProjectToken, Gs2Exception exception) {
// 接続完了コールバック
enterStateLock();
if (exception == null) {
// ログイン処理がエラーなく応答された場合
if (pProjectToken != null) {
List>> openCallbackList = new ArrayList<>(this.openCallbackList);
this.openCallbackList.clear();
if (closeCallbackList.isEmpty()) {
changeStateToAvailable(pProjectToken);
} else {
changeStateToClosing();
}
triggerOpenCallback(openCallbackList, null);
} else {
// 応答からプロジェクトトークンが取得できなかった場合
// ただし、ここには来ないように派生クラスを実装しなければならない
List>> openCallbackList = new ArrayList<>(this.openCallbackList);
this.openCallbackList.clear();
List>> closeCallbackList = new ArrayList<>(this.closeCallbackList);
this.closeCallbackList.clear();
changeStateToIdle();
triggerOpenCallback(openCallbackList, new UnknownException(""));
triggerCloseCallback(closeCallbackList, new UnknownException(""));
}
} else {
// ログイン処理がエラーになった場合
List>> openCallbackList = new ArrayList<>(this.openCallbackList);
this.openCallbackList.clear();
List>> closeCallbackList = new ArrayList<>(this.closeCallbackList);
this.closeCallbackList.clear();
changeStateToIdle();
triggerOpenCallback(openCallbackList, exception);
triggerCloseCallback(closeCallbackList, exception);
}
}
public void closeCallback(Gs2Exception gs2Exception, boolean isCloseInstant) {
if (!isCloseInstant) {
enterStateLock();
}
AsyncAction> onClose = m_OnClose;
List gs2SessionTaskList = new ArrayList<>(this.gs2SessionTaskList);
this.gs2SessionTaskList.clear();
List>> closeCallbackList = new ArrayList<>(this.closeCallbackList);
this.closeCallbackList.clear();
if (openCallbackList.isEmpty()) {
changeStateToIdle();
} else {
changeStateToOpening();
}
triggerCancelTasksCallback(gs2SessionTaskList, gs2Exception);
if (onClose != null) {
onClose.callback(null);
}
triggerCloseCallback(closeCallbackList, null);
}
protected void cancelTasksCallback(Gs2Exception gs2Exception) {
enterStateLock();
List gs2SessionTaskList = new ArrayList<>(this.gs2SessionTaskList);
this.gs2SessionTaskList.clear();
keepCurrentState();
triggerCancelTasksCallback(gs2SessionTaskList, gs2Exception);
}
protected Gs2SessionTask findGs2SessionTask(final Gs2SessionTaskId gs2SessionTaskId) {
m_Mutex.lock();
for (Gs2SessionTask sessionTask : gs2SessionTaskList) {
if (sessionTask.gs2SessionTaskId == gs2SessionTaskId) {
return sessionTask;
}
}
return null;
}
public Gs2Session(final BasicGs2Credential gs2Credential) {
m_State = State.Idle;
m_Gs2Credential = gs2Credential;
m_Region = Region.AP_NORTHEAST_1;
}
public Gs2Session(final BasicGs2Credential gs2Credential, final Region region) {
m_State = State.Idle;
m_Gs2Credential = gs2Credential;
m_Region = region;
}
public Gs2Session(final BasicGs2Credential gs2Credential, final String region) {
m_State = State.Idle;
m_Gs2Credential = gs2Credential;
m_Region = Region.prettyValueOf(region);
}
public BasicGs2Credential getGs2Credential() {
return m_Gs2Credential;
}
public Region getRegion() {
return m_Region;
}
public void openAsync(AsyncAction> callback) {
enterStateLock();
switch (m_State) {
case Idle:
openCallbackList.add(callback);
changeStateToOpening();
break;
case Opening:
case CancellingOpen:
case CancellingTasks: // 切断処理が終わってから実行される
case Closing: // 切断処理が終わってから実行される
openCallbackList.add(callback);
keepCurrentState();
break;
case Available:
keepCurrentState();
callback.callback(null);
break;
}
}
public void open() {
AtomicReference> asyncResult = new AtomicReference<>();
openAsync(r -> {
asyncResult.set(r);
});
while(asyncResult.get() == null) {
try {
Thread.sleep(100);
} catch (InterruptedException ignored) {}
}
}
public void closeAsync(AsyncAction> callback) {
enterStateLock();
if (m_State == State.Idle) {
// 即コールバック
keepCurrentState();
callback.callback(null);
} else {
closeCallbackList.add(callback);
switch (m_State) {
case Opening:
changeStateToCancellingOpen();
break;
case Available:
if (gs2SessionTaskList.isEmpty()) {
changeStateToClosing();
} else {
changeStateToCancellingTasks();
}
break;
case Idle: // ここには来ない
case CancellingOpen:
case CancellingTasks:
case Closing:
keepCurrentState();
break;
}
}
}
public void close() {
closeAsync(r -> {});
}
public void setOnClose(AsyncAction> callback) {
m_Mutex.lock();
m_OnClose = callback;
}
// 以下の関数は m_Mutex のロック内から呼ばれます
abstract void openImpl();
abstract void cancelOpenImpl();
abstract boolean closeImpl(); // 中で closeCallback() を呼んだ場合は true を返すこと
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy