实时多人游戏 Google 当应用程序进入后台时,Play 游戏服务对等点断开连接
Real-time Multiplayer Google Play Games Services peer gets disconnected when app goes in background
我正在使用 'Google Play Games Services' 构建实时多人游戏。我的问题是,当对等点连接到一个房间并玩游戏时,其中一个将他们的应用程序置于后台,该对等点与房间断开连接,而其他对等点收到该对等点离开房间的回调,这是他们防止的一种方法这不会发生。
出于测试目的,片段不会在其任何生命周期方法中调用离开该房间。
我添加了代码片段以提供更好的理解。
@Override
public void onStop() {
super.onStop();
// if (mRoomId!=null && !mRoomId.isEmpty()) {
// Games.RealTimeMultiplayer.leave(((MainActivity) getActivity()).getGoogleApiClient(), roomUpdateListener, mRoomId);
// }
// getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// mPlaying = false;
}
@Override
public void onActivityResult(int request, int response, Intent data) {
if (request == RC_SELECT_PLAYERS) {
if (response != Activity.RESULT_OK) {
// user canceled
return;
}
// get the invitee list
Bundle extras = data.getExtras();
final ArrayList<String> invitees =
data.getStringArrayListExtra(Games.EXTRA_PLAYER_IDS);
// get auto-match criteria
Bundle autoMatchCriteria = null;
int minAutoMatchPlayers =
data.getIntExtra(Multiplayer.EXTRA_MIN_AUTOMATCH_PLAYERS, 0);
int maxAutoMatchPlayers =
data.getIntExtra(Multiplayer.EXTRA_MAX_AUTOMATCH_PLAYERS, 0);
if (minAutoMatchPlayers > 0) {
autoMatchCriteria = RoomConfig.createAutoMatchCriteria(
minAutoMatchPlayers, maxAutoMatchPlayers, 0);
} else {
autoMatchCriteria = null;
}
// create the room and specify a variant if appropriate
RoomConfig.Builder roomConfigBuilder = makeBasicRoomConfigBuilder();
roomConfigBuilder.addPlayersToInvite(invitees);
if (autoMatchCriteria != null) {
roomConfigBuilder.setAutoMatchCriteria(autoMatchCriteria);
}
RoomConfig roomConfig = roomConfigBuilder.build();
Games.RealTimeMultiplayer.create(((MainActivity) getActivity()).getGoogleApiClient(), roomConfig);
// prevent screen from sleeping during handshake
getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
if (request == RC_WAITING_ROOM) {
if (mWaitingRoomFinishedFromCode) return;
if (response == Activity.RESULT_OK) {
// (start game)
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if (getContext()!=null) {
Player[] players = getParticipantPlayers();
if (players!=null && players.length>0) {
startGame(players);
}
}
}
}, 1000);
}
else if (response == Activity.RESULT_CANCELED) {
// Waiting room was dismissed with the back button. The meaning of this
// action is up to the game. You may choose to leave the room and cancel the
// match, or do something else like minimize the waiting room and
// continue to connect in the background.
// in this example, we take the simple approach and just leave the room:
Games.RealTimeMultiplayer.leave(((MainActivity) getActivity()).getGoogleApiClient(), roomUpdateListener, mRoomId);
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
else if (response == GamesActivityResultCodes.RESULT_LEFT_ROOM) {
// player wants to leave the room.
Games.RealTimeMultiplayer.leave(((MainActivity) getActivity()).getGoogleApiClient(), roomUpdateListener, mRoomId);
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}
}
private RoomUpdateListener roomUpdateListener = new RoomUpdateListener() {
@Override
public void onJoinedRoom(int statusCode, Room room) {
if (getContext()!=null) {
roomCreatorId = room.getCreatorId();
mRoomId = room.getRoomId();
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
// setCurrentPlayerParticipantId();
if (statusCode != GamesStatusCodes.STATUS_OK) {
// let screen go to sleep
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// show error message, return to main screen.
Toast.makeText(getContext(), "Error while joining room.", Toast.LENGTH_SHORT).show();
showRoomUi();
}
// get waiting room intent
Intent i = Games.RealTimeMultiplayer.getWaitingRoomIntent(((MainActivity) getActivity()).getGoogleApiClient(), room, MIN_PLAYERS);
startActivityForResult(i, RC_WAITING_ROOM);
}
}
@Override
public void onRoomCreated(int statusCode, Room room) {
if (getContext()!=null) {
roomCreatorId = room.getCreatorId();
mRoomId = room.getRoomId();
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
// setCurrentPlayerParticipantId();
if (statusCode != GamesStatusCodes.STATUS_OK) {
// let screen go to sleep
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// show error message, return to main screen.
Toast.makeText(getContext(), "Error creating room.", Toast.LENGTH_SHORT).show();
showRoomUi();
}
// get waiting room intent
Intent i = Games.RealTimeMultiplayer.getWaitingRoomIntent(((MainActivity) getActivity()).getGoogleApiClient(), room, MIN_PLAYERS);
startActivityForResult(i, RC_WAITING_ROOM);
}
}
@Override
public void onLeftRoom(int i, String s) {
if (getContext()!=null) {
// remove the flag that keeps the screen on
mRoomId = null;
}
}
@Override
public void onRoomConnected(int statusCode, Room room) {
if (getContext()!=null) {
roomCreatorId = room.getCreatorId();
mRoomId = room.getRoomId();
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
// setCurrentPlayerParticipantId();
if (statusCode != GamesStatusCodes.STATUS_OK) {
// let screen go to sleep
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// show error message, return to main screen.
Toast.makeText(getContext(), "Error connecting to room.", Toast.LENGTH_SHORT).show();
showRoomUi();
}
}
}
};
private RoomStatusUpdateListener roomStatusUpdateListener = new RoomStatusUpdateListener() {
@Override
public void onRoomConnecting(Room room) {
if (getContext()!=null) {
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
}
}
@Override
public void onRoomAutoMatching(Room room) {
if (getContext()!=null) {
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
}
}
@Override
public void onPeerInvitedToRoom(Room room, List<String> list) {
if (getContext()!=null) {
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
}
}
@Override
public void onPeerJoined(Room room, List<String> list) {
if (getContext()!=null) {
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
}
}
@Override
public void onConnectedToRoom(Room room) {
if (getContext()!=null) {
mMyId = room.getParticipantId(Games.Players.getCurrentPlayerId(((MainActivity) getActivity()).getGoogleApiClient()));
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
}
}
@Override
public void onDisconnectedFromRoom(Room room) {
if (getContext()!=null) {
// leave the room
Games.RealTimeMultiplayer.leave(((MainActivity) getActivity()).getGoogleApiClient(), roomUpdateListener, room.getRoomId());
// clear the flag that keeps the screen on
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// show error message and return to main screen
Toast.makeText(getContext(), "Network error.", Toast.LENGTH_SHORT).show();
showRoomUi();
}
}
@Override
public void onP2PConnected(String s) {
}
@Override
public void onP2PDisconnected(String s) {
}
@Override
public void onPeersConnected(Room room, List<String> peers) {
if (getContext()!=null) {
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
if (mPlaying) {
// add new player to an ongoing game
} else if (shouldStartGame(room)) {
// start game!
}
}
}
@Override
public void onPeersDisconnected(Room room, List<String> peers) {
if (getContext()!=null) {
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
if (mPlaying) {
// do game-specific handling of this -- remove player's avatar
// from the screen, etc. If not enough players are left for
// the game to go on, end the game and leave the room.
}
else if (shouldCancelGame(room)) {
// cancel the game
Games.RealTimeMultiplayer.leave(((MainActivity) getActivity()).getGoogleApiClient(), roomUpdateListener, room.getRoomId());
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
showRoomUi();
}
}
}
@Override
public void onPeerLeft(Room room, List<String> peers) {
if (getContext()!=null) {
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
// peer left -- see if game should be canceled
if (!mPlaying && shouldCancelGame(room)) {
Games.RealTimeMultiplayer.leave(((MainActivity) getActivity()).getGoogleApiClient(), roomUpdateListener, room.getRoomId());
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
showRoomUi();
}
}
}
@Override
public void onPeerDeclined(Room room, List<String> peers) {
if (getContext()!=null) {
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
// peer declined invitation -- see if game should be canceled
if (!mPlaying && shouldCancelGame(room)) {
Games.RealTimeMultiplayer.leave(((MainActivity) getActivity()).getGoogleApiClient(), roomUpdateListener, room.getRoomId());
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
showRoomUi();
}
}
}
};
private RealTimeMessageReceivedListener realTimeMessageReceivedListener = new RealTimeMessageReceivedListener() {
@Override
public void onRealTimeMessageReceived(RealTimeMessage realTimeMessage) {
if (getContext()!=null) {
if (realTimeMessage.isReliable()) {
handleReliableMessages(realTimeMessage.getMessageData());
}
}
}
};
感谢任何帮助。谢谢。
AFAIK,这就是 API 的设计方式和预期的工作方式。
如本 documentation 所述,只要您的游戏进入后台,您就应该离开活动房间。
如需更多见解(来自 Bruno Oliveira),您可能希望查看这些相关的 SO 帖子:
- When a peer disconnected from a room in google play services
- Google Play Game Services - How to Not Leave Room onPause
不过,您也可以选择尝试这个 suggested solution。尝试编辑 BaseGameActivity.onStop()
并删除 gamehelper.onStop()
。有了这个,gamesclient
只会在 onDestroy
停止。这可能是个不错的选择,但我还没有真正尝试过。
我正在使用 'Google Play Games Services' 构建实时多人游戏。我的问题是,当对等点连接到一个房间并玩游戏时,其中一个将他们的应用程序置于后台,该对等点与房间断开连接,而其他对等点收到该对等点离开房间的回调,这是他们防止的一种方法这不会发生。
出于测试目的,片段不会在其任何生命周期方法中调用离开该房间。
我添加了代码片段以提供更好的理解。
@Override
public void onStop() {
super.onStop();
// if (mRoomId!=null && !mRoomId.isEmpty()) {
// Games.RealTimeMultiplayer.leave(((MainActivity) getActivity()).getGoogleApiClient(), roomUpdateListener, mRoomId);
// }
// getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// mPlaying = false;
}
@Override
public void onActivityResult(int request, int response, Intent data) {
if (request == RC_SELECT_PLAYERS) {
if (response != Activity.RESULT_OK) {
// user canceled
return;
}
// get the invitee list
Bundle extras = data.getExtras();
final ArrayList<String> invitees =
data.getStringArrayListExtra(Games.EXTRA_PLAYER_IDS);
// get auto-match criteria
Bundle autoMatchCriteria = null;
int minAutoMatchPlayers =
data.getIntExtra(Multiplayer.EXTRA_MIN_AUTOMATCH_PLAYERS, 0);
int maxAutoMatchPlayers =
data.getIntExtra(Multiplayer.EXTRA_MAX_AUTOMATCH_PLAYERS, 0);
if (minAutoMatchPlayers > 0) {
autoMatchCriteria = RoomConfig.createAutoMatchCriteria(
minAutoMatchPlayers, maxAutoMatchPlayers, 0);
} else {
autoMatchCriteria = null;
}
// create the room and specify a variant if appropriate
RoomConfig.Builder roomConfigBuilder = makeBasicRoomConfigBuilder();
roomConfigBuilder.addPlayersToInvite(invitees);
if (autoMatchCriteria != null) {
roomConfigBuilder.setAutoMatchCriteria(autoMatchCriteria);
}
RoomConfig roomConfig = roomConfigBuilder.build();
Games.RealTimeMultiplayer.create(((MainActivity) getActivity()).getGoogleApiClient(), roomConfig);
// prevent screen from sleeping during handshake
getActivity().getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
if (request == RC_WAITING_ROOM) {
if (mWaitingRoomFinishedFromCode) return;
if (response == Activity.RESULT_OK) {
// (start game)
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
if (getContext()!=null) {
Player[] players = getParticipantPlayers();
if (players!=null && players.length>0) {
startGame(players);
}
}
}
}, 1000);
}
else if (response == Activity.RESULT_CANCELED) {
// Waiting room was dismissed with the back button. The meaning of this
// action is up to the game. You may choose to leave the room and cancel the
// match, or do something else like minimize the waiting room and
// continue to connect in the background.
// in this example, we take the simple approach and just leave the room:
Games.RealTimeMultiplayer.leave(((MainActivity) getActivity()).getGoogleApiClient(), roomUpdateListener, mRoomId);
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
else if (response == GamesActivityResultCodes.RESULT_LEFT_ROOM) {
// player wants to leave the room.
Games.RealTimeMultiplayer.leave(((MainActivity) getActivity()).getGoogleApiClient(), roomUpdateListener, mRoomId);
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
}
}
}
private RoomUpdateListener roomUpdateListener = new RoomUpdateListener() {
@Override
public void onJoinedRoom(int statusCode, Room room) {
if (getContext()!=null) {
roomCreatorId = room.getCreatorId();
mRoomId = room.getRoomId();
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
// setCurrentPlayerParticipantId();
if (statusCode != GamesStatusCodes.STATUS_OK) {
// let screen go to sleep
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// show error message, return to main screen.
Toast.makeText(getContext(), "Error while joining room.", Toast.LENGTH_SHORT).show();
showRoomUi();
}
// get waiting room intent
Intent i = Games.RealTimeMultiplayer.getWaitingRoomIntent(((MainActivity) getActivity()).getGoogleApiClient(), room, MIN_PLAYERS);
startActivityForResult(i, RC_WAITING_ROOM);
}
}
@Override
public void onRoomCreated(int statusCode, Room room) {
if (getContext()!=null) {
roomCreatorId = room.getCreatorId();
mRoomId = room.getRoomId();
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
// setCurrentPlayerParticipantId();
if (statusCode != GamesStatusCodes.STATUS_OK) {
// let screen go to sleep
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// show error message, return to main screen.
Toast.makeText(getContext(), "Error creating room.", Toast.LENGTH_SHORT).show();
showRoomUi();
}
// get waiting room intent
Intent i = Games.RealTimeMultiplayer.getWaitingRoomIntent(((MainActivity) getActivity()).getGoogleApiClient(), room, MIN_PLAYERS);
startActivityForResult(i, RC_WAITING_ROOM);
}
}
@Override
public void onLeftRoom(int i, String s) {
if (getContext()!=null) {
// remove the flag that keeps the screen on
mRoomId = null;
}
}
@Override
public void onRoomConnected(int statusCode, Room room) {
if (getContext()!=null) {
roomCreatorId = room.getCreatorId();
mRoomId = room.getRoomId();
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
// setCurrentPlayerParticipantId();
if (statusCode != GamesStatusCodes.STATUS_OK) {
// let screen go to sleep
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// show error message, return to main screen.
Toast.makeText(getContext(), "Error connecting to room.", Toast.LENGTH_SHORT).show();
showRoomUi();
}
}
}
};
private RoomStatusUpdateListener roomStatusUpdateListener = new RoomStatusUpdateListener() {
@Override
public void onRoomConnecting(Room room) {
if (getContext()!=null) {
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
}
}
@Override
public void onRoomAutoMatching(Room room) {
if (getContext()!=null) {
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
}
}
@Override
public void onPeerInvitedToRoom(Room room, List<String> list) {
if (getContext()!=null) {
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
}
}
@Override
public void onPeerJoined(Room room, List<String> list) {
if (getContext()!=null) {
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
}
}
@Override
public void onConnectedToRoom(Room room) {
if (getContext()!=null) {
mMyId = room.getParticipantId(Games.Players.getCurrentPlayerId(((MainActivity) getActivity()).getGoogleApiClient()));
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
}
}
@Override
public void onDisconnectedFromRoom(Room room) {
if (getContext()!=null) {
// leave the room
Games.RealTimeMultiplayer.leave(((MainActivity) getActivity()).getGoogleApiClient(), roomUpdateListener, room.getRoomId());
// clear the flag that keeps the screen on
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
// show error message and return to main screen
Toast.makeText(getContext(), "Network error.", Toast.LENGTH_SHORT).show();
showRoomUi();
}
}
@Override
public void onP2PConnected(String s) {
}
@Override
public void onP2PDisconnected(String s) {
}
@Override
public void onPeersConnected(Room room, List<String> peers) {
if (getContext()!=null) {
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
if (mPlaying) {
// add new player to an ongoing game
} else if (shouldStartGame(room)) {
// start game!
}
}
}
@Override
public void onPeersDisconnected(Room room, List<String> peers) {
if (getContext()!=null) {
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
if (mPlaying) {
// do game-specific handling of this -- remove player's avatar
// from the screen, etc. If not enough players are left for
// the game to go on, end the game and leave the room.
}
else if (shouldCancelGame(room)) {
// cancel the game
Games.RealTimeMultiplayer.leave(((MainActivity) getActivity()).getGoogleApiClient(), roomUpdateListener, room.getRoomId());
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
showRoomUi();
}
}
}
@Override
public void onPeerLeft(Room room, List<String> peers) {
if (getContext()!=null) {
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
// peer left -- see if game should be canceled
if (!mPlaying && shouldCancelGame(room)) {
Games.RealTimeMultiplayer.leave(((MainActivity) getActivity()).getGoogleApiClient(), roomUpdateListener, room.getRoomId());
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
showRoomUi();
}
}
}
@Override
public void onPeerDeclined(Room room, List<String> peers) {
if (getContext()!=null) {
mParticipants.clear();
mParticipants.addAll(room.getParticipants());
// peer declined invitation -- see if game should be canceled
if (!mPlaying && shouldCancelGame(room)) {
Games.RealTimeMultiplayer.leave(((MainActivity) getActivity()).getGoogleApiClient(), roomUpdateListener, room.getRoomId());
getActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
showRoomUi();
}
}
}
};
private RealTimeMessageReceivedListener realTimeMessageReceivedListener = new RealTimeMessageReceivedListener() {
@Override
public void onRealTimeMessageReceived(RealTimeMessage realTimeMessage) {
if (getContext()!=null) {
if (realTimeMessage.isReliable()) {
handleReliableMessages(realTimeMessage.getMessageData());
}
}
}
};
感谢任何帮助。谢谢。
AFAIK,这就是 API 的设计方式和预期的工作方式。
如本 documentation 所述,只要您的游戏进入后台,您就应该离开活动房间。
如需更多见解(来自 Bruno Oliveira),您可能希望查看这些相关的 SO 帖子:
- When a peer disconnected from a room in google play services
- Google Play Game Services - How to Not Leave Room onPause
不过,您也可以选择尝试这个 suggested solution。尝试编辑 BaseGameActivity.onStop()
并删除 gamehelper.onStop()
。有了这个,gamesclient
只会在 onDestroy
停止。这可能是个不错的选择,但我还没有真正尝试过。