Turn-based Multiplayer Game cannot be completed in Google Play Game Services - android

This used to work perfectly, but currently Game Services appear unable to handle the request issued by TurnBasedMultiplayer.finishMatch() and my players cannot complete their games. Nothing unusual occurs during game play (taking turns), but completion results in response code 400.
What might be going on and what can I do about it?
Logcat:
W/GLSUser ( 887): GoogleAccountDataService.getToken()
I/qtaguid ( 1173): Failed write_ctrl(u 180) res=-1 errno=22
I/qtaguid ( 1173): Untagging socket 180 failed errno=-22
W/NetworkManagementSocketTagger( 1173): untagSocket(180) failed with errno -22
E/Volley ( 1188): [87] tm.a: Unexpected response code 400 for https://www.googleapis.com/games/v1/turnbasedmatches/ChEKCQjrgfqCvgsQAhACGAAgARDruaLm9un3vyg/finish?language=de_DE
E/dwr ( 1188): Failed to finish match: null
W/dwr ( 1188): {"code":400,"errors":[{"message":"Invalid results. results","domain":"global","reason":"InvalidMatchResults"}]}
D/UPDATE_MATCH_RESULT(30627): Status{statusCode=unknown status code: 6504, resolution=null}
Code:
ParticipantResult opponentResult = null;
ParticipantResult creatorResult = null;
if (mMatchData.opponentWonCounter > mMatchData.creatorWonCounter) {
opponentResult = new ParticipantResult(getParticipantId(),
ParticipantResult.MATCH_RESULT_WIN, 1);
creatorResult = new ParticipantResult(
mMatchData.creatorParticipantId,
ParticipantResult.MATCH_RESULT_LOSS, 2);
} else if (mMatchData.opponentWonCounter < mMatchData.creatorWonCounter) {
opponentResult = new ParticipantResult(getParticipantId(),
ParticipantResult.MATCH_RESULT_LOSS, 2);
creatorResult = new ParticipantResult(
mMatchData.creatorParticipantId,
ParticipantResult.MATCH_RESULT_WIN, 1);
} else {
opponentResult = new ParticipantResult(getParticipantId(),
ParticipantResult.MATCH_RESULT_TIE, 1);
creatorResult = new ParticipantResult(
mMatchData.creatorParticipantId,
ParticipantResult.MATCH_RESULT_TIE, 1);
}
Games.TurnBasedMultiplayer
.finishMatch(getApiClient(), mMatch.getMatchId(), data,
creatorResult, opponentResult)
.setResultCallback(
new ResultCallback<TurnBasedMultiplayer.UpdateMatchResult>() {
#Override
public void onResult(
TurnBasedMultiplayer.UpdateMatchResult result) {
Log.d("UPDATE_MATCH_RESULT", result
.getStatus().toString());
dismissProgress();
completeMatch();
}
});

EDIT:
I confirm that this works as intended. Also, the player taking the last turn can finish the match and post the final results.
I introduced a bug myself, so sorry for the confusion. (I have a two player setup. The 'creator' wasn't able to finish the match whereas the 'opponent' was. This was caused by participant IDs getting mixed up and code blindness preventing me from identifying the problem.)
Old answer:
Further investigation revealed that these methods had stopped working:
finishMatch(GoogleApiClient apiClient, String matchId, byte[] matchData, ParticipantResult... results)
finishMatch(GoogleApiClient apiClient, String matchId, byte[] matchData, List<ParticipantResult> results)
Whereas this one was still working flawlessly:
finishMatch(GoogleApiClient apiClient, String matchId)
I filed a bug report here: https://code.google.com/p/play-games-platform/issues/list .
EDIT: Acknowledged by Google: http://goo.gl/Ubqy9n
So here's the nasty workaround:
1) Take an extra turn to update the match.
2) Finish the match.
Games.TurnBasedMultiplayer
.takeTurn(getApiClient(), mMatch.getMatchId(), data,
nextParticipantId)
.setResultCallback(
new ResultCallback<TurnBasedMultiplayer.UpdateMatchResult>() {
#Override
public void onResult(UpdateMatchResult result) {
mMatch = result.getMatch();
Games.TurnBasedMultiplayer
.finishMatch(getApiClient(),
mMatch.getMatchId())
.setResultCallback(
new ResultCallback<TurnBasedMultiplayer.UpdateMatchResult>() {
#Override
public void onResult(
TurnBasedMultiplayer.UpdateMatchResult result) {
Log.d("UPDATE_MATCH_RESULT",
result.getStatus()
.toString());
dismissProgress();
completeMatch();
}
});
}
});

Related

Android send log lines as an email periodically

I want to send the log lines to email in every 10 minutes.
To do that, I have used a Timer and inside of timer I send the logs via email.
However I loose some log lines between 2 emails.
For example my first email contains no lines which is normal according to my algorithm.
My second email contains log lines between 15.37 and 15.38 seconds.
My third email contains logs in between 15.44 and 15.48 time intervals.
My fourth email contains logs in between 15.55 and 15.58 time intervals.
As you can see I loose some of my logs but I could not find a way to avoid that.
Following is my code in my service class:
#Override
public void onCreate() {
super.onCreate();
mTimer = new Timer();
mTimer.scheduleAtFixedRate(new TimerTask() {
#Override
public void run() {
sendLogFile();
}
}, 0, 1000 * 60 * 10 );
}
Inside of sendSupport method the second parameter is sent as a content of the log lines where logs is a static string variable.
private void sendLogFile() {
mInteractor.sendSupport("LOG FILE", "MSG"+logs, "SUBJECT"+ System.currentTimeMillis(), "",
result -> {
Timber.log(Log.DEBUG, "sendSupport Thread.currentThread().getName() " + Thread.currentThread().getName());
if (result.isSuccess) {
Timber.d("is sent");
writeLogFile();
} else {
Timber.d("is NOT sent");
}
}
);
}
private void writeLogFile()
{
try {
StringBuilder logBuilder = new StringBuilder();
process = Runtime.getRuntime().exec( "logcat -d");
BufferedReader bufferedReader = new BufferedReader(
new InputStreamReader(process.getInputStream()));
String line;
while ((line = bufferedReader.readLine()) != null) {
logBuilder.append(line + "\n");
}
logs = logBuilder.toString();
} catch (IOException e) {
e.printStackTrace();
}
}
As a result I could not figure out how am I going to be able to get all logs in periodically in my email.
Thanks.
While the #Knossos answer is pointing the reason why some logs are missing, it doesn't suggest how to use that knowledge to reliably get the logs from users' phones, when you don't have an access to their devices to run some adb commands.
Here is what I suggest to do instead:
As you do already use Timber, just add some more power to it. Namely, add an additional LoggingTree that will save the logs into a file, instead of just posting them to Logcat. You can have many Timber Trees working simultaneously, so you can have both Logcat and File logs if needed.
Use the same timer to send an email message when and where needed. But, instead of using access to logcat -d, simply use the file where Timber have already written the logs. Don't forget to flush the stream before sending the email. In order not to send the same logs again and again, configure the FileTree in a way that it creates a new file every time when a previous one is sent (seta a new file name via a method call, for example).
Profit :)
To log into two different systems (Trees), you need to simply add one more to Timber:
Timber.plant(new Timber.DebugTree());
Timber.plant(new FileLoggingTree());
And here is an example of FileLoggingTree(source):
public class FileLoggingTree extends Timber.DebugTree {
private static Logger mLogger = LoggerFactory.getLogger(FileLoggingTree.class);
private static final String LOG_PREFIX = "my-log";
public FileLoggingTree(Context context) {
final String logDirectory = context.getFilesDir() + "/logs";
configureLogger(logDirectory);
}
private void configureLogger(String logDirectory) {
// reset the default context (which may already have been initialized)
// since we want to reconfigure it
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
loggerContext.reset();
RollingFileAppender<ILoggingEvent> rollingFileAppender = new RollingFileAppender<>();
rollingFileAppender.setContext(loggerContext);
rollingFileAppender.setAppend(true);
rollingFileAppender.setFile(logDirectory + "/" + LOG_PREFIX + "-latest.html");
SizeAndTimeBasedFNATP<ILoggingEvent> fileNamingPolicy = new SizeAndTimeBasedFNATP<>();
fileNamingPolicy.setContext(loggerContext);
fileNamingPolicy.setMaxFileSize("1MB");
TimeBasedRollingPolicy<ILoggingEvent> rollingPolicy = new TimeBasedRollingPolicy<>();
rollingPolicy.setContext(loggerContext);
rollingPolicy.setFileNamePattern(logDirectory + "/" + LOG_PREFIX + ".%d{yyyy-MM-dd}.%i.html");
rollingPolicy.setMaxHistory(5);
rollingPolicy.setTimeBasedFileNamingAndTriggeringPolicy(fileNamingPolicy);
rollingPolicy.setParent(rollingFileAppender); // parent and context required!
rollingPolicy.start();
HTMLLayout htmlLayout = new HTMLLayout();
htmlLayout.setContext(loggerContext);
htmlLayout.setPattern("%d{HH:mm:ss.SSS}%level%thread%msg");
htmlLayout.start();
LayoutWrappingEncoder<ILoggingEvent> encoder = new LayoutWrappingEncoder<>();
encoder.setContext(loggerContext);
encoder.setLayout(htmlLayout);
encoder.start();
// Alternative text encoder - very clean pattern, takes up less space
// PatternLayoutEncoder encoder = new PatternLayoutEncoder();
// encoder.setContext(loggerContext);
// encoder.setCharset(Charset.forName("UTF-8"));
// encoder.setPattern("%date %level [%thread] %msg%n");
// encoder.start();
rollingFileAppender.setRollingPolicy(rollingPolicy);
rollingFileAppender.setEncoder(encoder);
rollingFileAppender.start();
// add the newly created appenders to the root logger;
// qualify Logger to disambiguate from org.slf4j.Logger
ch.qos.logback.classic.Logger root = (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
root.setLevel(Level.DEBUG);
root.addAppender(rollingFileAppender);
// print any status messages (warnings, etc) encountered in logback config
StatusPrinter.print(loggerContext);
}
#Override
protected void log(int priority, String tag, String message, Throwable t) {
if (priority == Log.VERBOSE) {
return;
}
String logMessage = tag + ": " + message;
switch (priority) {
case Log.DEBUG:
mLogger.debug(logMessage);
break;
case Log.INFO:
mLogger.info(logMessage);
break;
case Log.WARN:
mLogger.warn(logMessage);
break;
case Log.ERROR:
mLogger.error(logMessage);
break;
}
}
}
The problem is that logcat -d is only delivering you the latest X bytes of data from the stream. You aren't guaranteed to get everything between 10 minute intervals.
In the best case, you get what you want. In the worst cases, you miss log data or log sections overlap (you get some from the previous dump too).
You can see this here: adb logcat -d | dd
...
03-27 11:36:27.474 791 22420 E ResolverController: No valid NAT64 prefix (147, <unspecified>/0)
03-27 11:36:27.612 3466 3521 I PlayCommon: [657] alsu.c(187): Successfully uploaded logs.
453+111 records in
499+1 records out
255863 bytes (256 kB, 250 KiB) copied, 0,136016 s, 1,9 MB/s
As you can see, it is clearly a 256 kB chunk that is pulled through logcat -d.
On the plus side, you can change that! If you look at adb logcat --help you can see options.
For example, if you use adb logcat -d -t '100000' | dd (all logs in the last 100000 seconds. I now have the following:
...
03-27 11:45:41.687 791 1106 I netd : bandwidthSetGlobalAlert(2097152) <0.90ms>
03-27 11:45:42.098 21897 23376 V FA : Inactivity, disconnecting from the service
2237+1558 records in
2879+1 records out
1474408 bytes (1,5 MB, 1,4 MiB) copied, 1,20785 s, 1,2 MB/s
1.5 MB of logs. You should be able to get all logs with this.
You log the timestamp of each logcat pull, then use that each time to determine the seconds since the last pull.
I hope that helps!
You cannot send an email from the device without user interaction or implementing the email function yourself that allows to do this without user interaction.
Most apps have some api endpoint to send the logs to.

DJI Mobile SDK 3.0

When getting the mission manager, either by DJIMissionManager.getInstance() or djiAircraftInstance.getMissionManager(), the mission manager instance is never connected, ie missionManagerInstance.isConnected() always returns false, and proceeding without the isConnected check causes a crash. Am I missing a step in setting up or retreiving the mission manager?
A minute ago I asked the same question on their forums here.
Any help would be appreciated. I have been over their examples a thousand times but it seems all of the examples are using an older version of the sdk.
EDIT: More information that you could figure out but I'll add in for the heck of it.
Mission manager instance is not null because calling isConnected() returns false, and the drone is connected as well.
I just test the isMissionReadyToExecute and MissionManager.isConnected, i found that no matter when I call them, they always return true. So I consider that it should be the bugs inside the SDK.
And I found a walk around solution for this problem.
Initialize the mission.
// Step 1: create mission
DJIWaypointMission waypointMission = new DJIWaypointMission();
waypointMission.maxFlightSpeed = 14;
waypointMission.autoFlightSpeed = 4;
List<DJIWaypoint> waypointsList = new LinkedList<>();
// Step 2: create waypoints and prepare coordinates
DJIWaypoint northPoint = new DJIWaypoint(mHomeLatitude + 10 * Utils.ONE_METER_OFFSET, mHomeLongitude, 10f);
DJIWaypoint eastPoint = new DJIWaypoint(mHomeLatitude, mHomeLongitude + 10 * Utils.calcLongitudeOffset(mHomeLatitude), 20f);
DJIWaypoint southPoint = new DJIWaypoint(mHomeLatitude - 10 * Utils.ONE_METER_OFFSET, mHomeLongitude, 30f);
DJIWaypoint westPoint = new DJIWaypoint(mHomeLatitude, mHomeLongitude - 10 * Utils.calcLongitudeOffset(mHomeLatitude), 40f);
//Step 3: add actions
northPoint.addAction(new DJIWaypoint.DJIWaypointAction(DJIWaypoint.DJIWaypointActionType.GimbalPitch, -60));
northPoint.addAction(new DJIWaypoint.DJIWaypointAction(DJIWaypoint.DJIWaypointActionType.StartTakePhoto, 0));
eastPoint.addAction(new DJIWaypoint.DJIWaypointAction(DJIWaypoint.DJIWaypointActionType.StartTakePhoto, 0));
southPoint.addAction(new DJIWaypoint.DJIWaypointAction(DJIWaypoint.DJIWaypointActionType.RotateAircraft, 60));
southPoint.addAction(new DJIWaypoint.DJIWaypointAction(DJIWaypoint.DJIWaypointActionType.StartRecord, 0));
westPoint.addAction(new DJIWaypoint.DJIWaypointAction(DJIWaypoint.DJIWaypointActionType.StopRecord, 0));
//Step 4: add waypoints into the mission
waypointsList.add(northPoint);
waypointsList.add(eastPoint);
waypointsList.add(southPoint);
waypointsList.add(westPoint);
waypointMission.addWaypoints(waypointsList);
mDJIMission = waypointMission;
prepare the mission.
mMissionManager.prepareMission(mDJIMission, new DJIMission.DJIMissionProgressHandler() {
#Override
public void onProgress(DJIMission.DJIProgressType type, float progress) {
setProgressBar((int)(progress * 100f));
}
}, new DJICompletionCallback() {
#Override
public void onResult(DJIError error) {
if (error == null) {
Utils.setResultToToast(mContext, "Success!");
} else {
Utils.setResultToToast(mContext, "Prepare: " + error.getDescription());
}
}
});

OpenIAB dont work Unity3D

i include OpenIAB plugin in my Android application in unity3D, but when i test my app on device, purchase window isnt called. Where problem??
void Start() {
OpenIAB.mapSku (SKU, OpenIAB_Android.STORE_GOOGLE, "no_ads");
var options = new OnePF.Options();
options.checkInventory = false;
options.verifyMode = OptionsVerifyMode.VERIFY_EVERYTHING;
options.storeKeys.Add(OpenIAB_Android.STORE_GOOGLE, "My key");
OpenIAB.init(options);
}
void OnGUI(){
if (GUI.Button (new Rect (100, 100, 100, 100), "Push me")) {
OpenIAB.purchaseProduct(SKU);
GUI.TextArea(new Rect(200,200,150,150),"Its work!!!");
}
}
private void OnPurchaseSucceded(Purchase purchase)
{
if (purchase.Sku == SKU)
GUI.TextArea(new Rect(150,150,150,150),"Good job!");
}
GUI functions only work inside OnGUI() call. And GUI.TextArea(new Rect(200,200,150,150),"Its work!!!"); just probably blinks too fast.

Turn-based Multiplayer Game cannot finish in Google Play Game Services

I'm using a turn based multiplayer game.
Player 1 starts the game, Player 2 ends. But I always get errors.
This is the code for Player 2:
String playerId = Games.Players.getCurrentPlayerId(getApiClient());
String myOponentId = mMatch.getParticipantId(playerId);
opponentResult = new ParticipantResult(myOponentId,
ParticipantResult.MATCH_RESULT_WIN, 1);
creatorResult = new ParticipantResult(playerId,
ParticipantResult.MATCH_RESULT_LOSS, 2);
Games.TurnBasedMultiplayer.finishMatch(getApiClient(), mMatch.getMatchId(),mMatch.getData(), creatorResult,opponentResult )
.setResultCallback(new ResultCallback<TurnBasedMultiplayer.UpdateMatchResult>() {
#Override
public void onResult(TurnBasedMultiplayer.UpdateMatchResult result) {
processResult(result);
}
});
Log:
E/Volley﹕ [2816] a.a: Unexpected response code 400 for https://www.googleapis.com/games/v1/turnbasedmatches/ChEKCQixqozpwBoQAhACGAAgARDEt9LOpreWivoB/finish?language=en_GB
E/cc﹕ Failed to finish match: null
W/cc﹕ {"code":400,"errors":[{"message":"Invalid participantId with value 108607338309210360902.","domain":"global","reason":"invalid"}]}
Using finishMatch like in the example SkeletonActivity.java is working, but does not produce a winner:
Games.TurnBasedMultiplayer.finishMatch(getApiClient(), mMatch.getMatchId()
ok i messed up the nubers. have to set participant ids whichs is something like p_1 and not a number
myOponentParticipantId

Set winner after turn based game Google API Android

I'm creating a turn based game on the Android platform using the API provided by Google.
At the end of the latest turn (in my case the third one), I'd like to set the winner but I have not found any API that permits this. Naturally I have all the data at the end of the game to set it.
I've not read something in the documentation - does this feature exist or should I handle it myself?
String playerId = Games.Players.getCurrentPlayerId(getApiClient());
String myOponentId = mMatch.getParticipantId(playerId);
opponentResult = new ParticipantResult(myOponentId,
ParticipantResult.MATCH_RESULT_WIN, 1);
creatorResult = new ParticipantResult(playerId,
ParticipantResult.MATCH_RESULT_LOSS, 2);
Games.TurnBasedMultiplayer.finishMatch(getApiClient(), mMatch.getMatchId(),mMatch.getData(), creatorResult,opponentResult )
.setResultCallback(new ResultCallback<TurnBasedMultiplayer.UpdateMatchResult>() {
#Override
public void onResult(TurnBasedMultiplayer.UpdateMatchResult result) {
processResult(result);
}
});

Categories

Resources