network: reworked client-server events (related to triggers dialog problem from #11173) (#11189)

* added bad connection mode to test client works on slow network, use -Dxmage.badconnection;
* added bad connection protection in events processing due event type;
* split events to different types (can be ignored, must be synced, etc);
* removed some unused server events.
This commit is contained in:
Oleg Agafonov 2023-09-21 18:40:52 +04:00 committed by GitHub
parent fa8e93a29d
commit 342979a55a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 299 additions and 202 deletions

View file

@ -2,6 +2,7 @@ package mage.interfaces.callback;
import mage.remote.traffic.ZippedObject;
import mage.utils.CompressUtil;
import mage.utils.ThreadUtils;
import java.io.Serializable;
import java.util.UUID;
@ -11,11 +12,23 @@ import java.util.UUID;
*/
public class ClientCallback implements Serializable {
// for debug only: simulate bad connection on client side, use launcher's client param like -Dxmage.badconnection
private static final String SIMULATE_BAD_CONNECTION_PROP = "xmage.badconnection";
public static final boolean SIMULATE_BAD_CONNECTION;
static {
SIMULATE_BAD_CONNECTION = System.getProperty(SIMULATE_BAD_CONNECTION_PROP) != null;
}
private UUID objectId;
private Object data;
private ClientCallbackMethod method;
private int messageId;
public ClientCallback(ClientCallbackMethod method, UUID objectId) {
this(method, objectId, null);
}
public ClientCallback(ClientCallbackMethod method, UUID objectId, Object data) {
this(method, objectId, data, true);
}
@ -26,8 +39,10 @@ public class ClientCallback implements Serializable {
this.setData(data, useCompress);
}
public ClientCallback(ClientCallbackMethod method, UUID objectId) {
this(method, objectId, null);
private void simulateBadConnection() {
if (SIMULATE_BAD_CONNECTION) {
ThreadUtils.sleep(100);
}
}
public void clear() {
@ -55,12 +70,14 @@ public class ClientCallback implements Serializable {
this.data = data;
} else {
this.data = CompressUtil.compress(data);
simulateBadConnection();
}
}
public void decompressData() {
if (this.data instanceof ZippedObject) {
this.data = CompressUtil.decompress(this.data);
simulateBadConnection();
}
}

View file

@ -1,68 +1,86 @@
package mage.interfaces.callback;
/**
* Created by IGOUDT on 4-4-2017.
* Server's commands to process on client side. Commands can come in un-synced state due bad/slow network
* <p>
* Can be:
* - critical events (messages, game events, choose dialogs, etc)
* - non-critical events (game updates, messages)
*/
public enum ClientCallbackMethod {
CHATMESSAGE("chatMessage"),
TOURNAMENT_INIT("tournamentInit"),
TOURNAMENT_UPDATE("tournamentUpdate"),
TOURNAMENT_OVER("tournamentOver"),
JOINED_TABLE("joinedTable"),
START_DRAFT("startDraft"),
START_TOURNAMENT("startTournament"),
SIDEBOARD("sideboard"),
VIEW_LIMITED_DECK("viewLimitedDeck"),
VIEW_SIDEBOARD("viewSideboard"),
CONSTRUCT("construct"),
SHOW_USERMESSAGE("showUserMessage"),
WATCHGAME("watchGame"),
REPLAY_GAME("replayGame"),
START_GAME("startGame"),
SHOW_TOURNAMENT("showTournament"),
SHOW_GAME_END_DIALOG("showGameEndDialog"),
SERVER_MESSAGE("serverMessage"),
GAME_INIT("gameInit"),
GAME_OVER("gameOver"),
GAME_INFORM("gameInform"),
GAME_INFORM_PERSONAL("gameInformPersonal"),
GAME_ERROR("gameError"),
GAME_UPDATE("gameUpdate"),
GAME_REDRAW_GUI("gameRedrawGUI", true),
DRAFT_OVER("draftOver"),
REPLAY_DONE("replayDone"),
USER_REQUEST_DIALOG("userRequestDialog"),
REPLAY_UPDATE("replayUpdate"),
REPLAY_INIT("replayInit"),
END_GAME_INFO("endGameInfo"),
GAME_TARGET("gameTarget"),
GAME_CHOOSE_ABILITY("gameChooseAbility"),
GAME_CHOOSE_PILE("gameChoosePile"),
GAME_CHOOSE_CHOICE("gameChooseChoice"),
GAME_ASK("gameAsk"),
GAME_SELECT("gameSelect"),
GAME_PLAY_MANA("gamePlayMana"),
GAME_PLAY_XMANA("gamePlayXMana"),
GAME_GET_AMOUNT("gameSelectAmount"),
GAME_GET_MULTI_AMOUNT("gameSelectMultiAmount"),
DRAFT_INIT("draftInit"),
DRAFT_PICK("draftPick"),
DRAFT_UPDATE("draftUpdate");
// TODO: rename events due place/action like GAME_STARTED, GAME_ASK_DIALOG, GAME_TARGET_DIALOG
String code;
boolean isClientSideMessage;
// messages
CHATMESSAGE(ClientCallbackType.MESSAGE, "chatMessage"),
SHOW_USERMESSAGE(ClientCallbackType.MESSAGE, "showUserMessage"),
SERVER_MESSAGE(ClientCallbackType.MESSAGE, "serverMessage"),
ClientCallbackMethod(String code) {
this(code, false);
}
// table
JOINED_TABLE(ClientCallbackType.TABLE_CHANGE, "joinedTable"),
ClientCallbackMethod(String code, boolean isClientSideMessage) {
// tournament
START_TOURNAMENT(ClientCallbackType.TABLE_CHANGE, "startTournament"),
TOURNAMENT_INIT(ClientCallbackType.TABLE_CHANGE, "tournamentInit"), // TODO: unused on client
TOURNAMENT_UPDATE(ClientCallbackType.UPDATE, "tournamentUpdate"), // TODO: unused on client
TOURNAMENT_OVER(ClientCallbackType.TABLE_CHANGE, "tournamentOver"), // TODO: unused on client
// draft/sideboard
START_DRAFT(ClientCallbackType.TABLE_CHANGE, "startDraft"),
SIDEBOARD(ClientCallbackType.TABLE_CHANGE, "sideboard"),
CONSTRUCT(ClientCallbackType.TABLE_CHANGE, "construct"),
DRAFT_OVER(ClientCallbackType.TABLE_CHANGE, "draftOver"),
DRAFT_INIT(ClientCallbackType.TABLE_CHANGE, "draftInit"),
DRAFT_PICK(ClientCallbackType.TABLE_CHANGE, "draftPick"),
DRAFT_UPDATE(ClientCallbackType.UPDATE, "draftUpdate"),
// watch
SHOW_TOURNAMENT(ClientCallbackType.TABLE_CHANGE, "showTournament"),
WATCHGAME(ClientCallbackType.TABLE_CHANGE, "watchGame"),
// in-game actions
VIEW_LIMITED_DECK(ClientCallbackType.MESSAGE, "viewLimitedDeck"),
VIEW_SIDEBOARD(ClientCallbackType.MESSAGE, "viewSideboard"),
// other
USER_REQUEST_DIALOG(ClientCallbackType.DIALOG, "userRequestDialog"),
GAME_REDRAW_GUI(ClientCallbackType.CLIENT_SIDE_EVENT, "gameRedrawGUI"),
// game
START_GAME(ClientCallbackType.TABLE_CHANGE, "startGame"),
GAME_INIT(ClientCallbackType.TABLE_CHANGE, "gameInit"),
GAME_INFORM(ClientCallbackType.MESSAGE, "gameInform"),
GAME_INFORM_PERSONAL(ClientCallbackType.MESSAGE, "gameInformPersonal"),
GAME_ERROR(ClientCallbackType.MESSAGE, "gameError"),
GAME_UPDATE(ClientCallbackType.UPDATE, "gameUpdate"),
GAME_TARGET(ClientCallbackType.DIALOG, "gameTarget"),
GAME_CHOOSE_ABILITY(ClientCallbackType.DIALOG, "gameChooseAbility"),
GAME_CHOOSE_PILE(ClientCallbackType.DIALOG, "gameChoosePile"),
GAME_CHOOSE_CHOICE(ClientCallbackType.DIALOG, "gameChooseChoice"),
GAME_ASK(ClientCallbackType.DIALOG, "gameAsk"),
GAME_SELECT(ClientCallbackType.DIALOG, "gameSelect"),
GAME_PLAY_MANA(ClientCallbackType.DIALOG, "gamePlayMana"),
GAME_PLAY_XMANA(ClientCallbackType.DIALOG, "gamePlayXMana"),
GAME_GET_AMOUNT(ClientCallbackType.DIALOG, "gameSelectAmount"),
GAME_GET_MULTI_AMOUNT(ClientCallbackType.DIALOG, "gameSelectMultiAmount"),
GAME_OVER(ClientCallbackType.TABLE_CHANGE, "gameOver"),
END_GAME_INFO(ClientCallbackType.TABLE_CHANGE, "endGameInfo"),
// replay (unsupported)
REPLAY_GAME(ClientCallbackType.TABLE_CHANGE, "replayGame"),
REPLAY_INIT(ClientCallbackType.TABLE_CHANGE, "replayInit"),
REPLAY_UPDATE(ClientCallbackType.UPDATE, "replayUpdate"),
REPLAY_DONE(ClientCallbackType.TABLE_CHANGE, "replayDone");
final ClientCallbackType type;
final String code;
ClientCallbackMethod(ClientCallbackType type, String code) {
this.type = type;
this.code = code;
this.isClientSideMessage = isClientSideMessage;
}
public boolean isClientSideMessage() {
return this.isClientSideMessage;
public ClientCallbackType getType() {
return this.type;
}
}

View file

@ -0,0 +1,35 @@
package mage.interfaces.callback;
/**
* Server event type for processing on the client
*
* @author JayDi85
*/
public enum ClientCallbackType {
TABLE_CHANGE,
UPDATE(true, true),
MESSAGE(true, false),
DIALOG,
CLIENT_SIDE_EVENT(true, true);
final boolean canProcessInAnyOrder;
final boolean mustIgnoreOnOutdated;
ClientCallbackType() {
this(false, false);
}
ClientCallbackType(boolean canProcessInAnyOrder, boolean mustIgnoreOnOutdated) {
this.canProcessInAnyOrder = canProcessInAnyOrder;
this.mustIgnoreOnOutdated = mustIgnoreOnOutdated;
}
public boolean canProcessInAnyOrder() {
return this.canProcessInAnyOrder;
}
public boolean mustIgnoreOnOutdated() {
return this.mustIgnoreOnOutdated;
}
}