I am working on a multiplayer domino game where a device acts as a host and makes the decisions for the rest of the players. So far, it works great when everyone has a good connection, but once you introduce some latency, all things go to heck. One problem I'm having is that I need to move some sprites around on command. When everyone has played, the host device will send a message out that tells the remote players who won the hand. After that message is sent, the winner can make the next move.
I move the dominoes to the right using:
void HelloWorld::onChatReceived(AppWarp::chat chatevent)
{
if (chatprefix.compare("_determinewinner_")==0)
{
//chatstring in this case is the playerID who won the hand
MultiPlayerdetermineTrickWinner(atoi(chatstring.c_str()));
}
}
void HelloWorld::MultiPlayerdetermineTrickWinner(int winningplayer)
{
...
for (int i = 0; i <marray_table->count(); ++i)
{
Domino *marray_table_tile = (Domino *)(marray_table->objectAtIndex(i));
CCMoveTo *translate = CCMoveTo::create(0.1f,ccp(pos_x,pos_y));
CCRotateBy *rotleft=CCRotateBy::create(0.1f, 90*int_rotate);
marray_table_tile->runAction(CCSequence::create(translate,rotleft,NULL));
}
...
}
When the players selects a tile, I call
CCMoveTo *translate = CCMoveTo::create(0.1f,ccp(pos_x,pos_y));
selectedtile->runAction(CCSequence::create(translate,NULL));
marray_table->addObject(selectedtile);
The problem I have is, that there are times when the latency is so bad, that the runaction never completes and the tiles don't actually make it to their final position. For instance, if the host player won the hand and makes their move before the other players received the "determinewinner" message. So short of having all the players tell the host when it can move on, how do I schedule a function to occur only after all actions have been completed?
Related
I am trying to include TBMP into my Mahjong game and this issue has been stuck with me for a few months now, and I am no closer to resolving it.
I have four players in a game and the game progressing counterclockwise. So imagine you have four players at a table:
Player 3
---------------------------
Player 4 | | Player 2
---------------------------
Player 1
Now Player 1 takes a turn, the data is pushed via JSON into the game data, and Player 2 gets notified that it is his turn. Player 3 and 4 also get a notification that a match update has occurred, but when they query the game data, it returns the stale game information, unless it becomes their turn, then they get the accurate and current game data.
I would like all players to be updated after every turn, rather than getting all the updates in one go when it becomes their turn.
I register an update listener as follows:
mTurnBasedMultiplayerClient.registerTurnBasedMatchUpdateCallback(mMatchUpdateCallback);
I have a function that processes the update as follows:
private TurnBasedMatchUpdateCallback mMatchUpdateCallback = new TurnBasedMatchUpdateCallback() {
#Override
public void onTurnBasedMatchReceived(#NonNull TurnBasedMatch turnBasedMatch) {
int turnStatus = turnBasedMatch.getTurnStatus();
// OK, it's active. Check on turn status.
switch (turnStatus) {
case TurnBasedMatch.MATCH_TURN_STATUS_MY_TURN:
MainActivity.mTurnData =
MahjongTurn.unpersist(turnBasedMatch.getData());
setLocalGameValues();
state = GameState.Playing;
return;
case TurnBasedMatch.MATCH_TURN_STATUS_THEIR_TURN:
MainActivity.mTurnData =
MahjongTurn.unpersist(turnBasedMatch.getData());
setLocalGameValues();
state = GameState.MultiWait;
return;
}
}
#Override
public void onTurnBasedMatchRemoved(#NonNull String matchId) {
game.showToast("A match was removed.");
state = GameState.MultiWait;
return;
}
};
Anyway, the listener is registered correctly and I can see that my listener gets called, but the call to turnBasedMatch.getData() only returns the correct game data when it is also the player's turn.
So Player 4 gets the updates from Player 1, Player 2 and Player 3 only when it becomes his turn. He gets notified that there is an update to the match, but has no way of knowing what the current game data looks like.
Is this the way it is meant to work? Am I doing something wrong?
One workaround for this is to store all the turns inside the message data. This way, I can treat the game data like a Message Queue and replay everything up to the current turn. This is not ideal, but it works.
I am trying to make a four player turn based game.
Now in case their are less than 4 human player I want to start X human player and 4-X AI player. Is it possible with the google turn based multiplayer https://developers.google.com/games/services/android/turnbasedMultiplayer
Also in case a human player leaves I want to replace it with the AI player instead of cancelling the game. Does it support this ?
I am implementing this and have it working 90%. When it is a new turn in my game, I check if the next player is a BOT or HUMAN player. This is prearranged using a seating algorithm, so that I space out the HUMANs neatly. i.e. opposite when there are two human players. When the next player is a BOT, I take a turn, but do not advance the Particpant. That way it will persist and send an update without passing the turn to the other player. When the next player is a HUMAN, I do a take Turn and switch to the next player. The code looks something like this....
if (Players[playerTurn%4]==BOT) {
Games.TurnBasedMultiplayer.takeTurn(MainActivity.mHelper.getApiClient(), MainActivity.mMatch.getMatchId(),
MainActivity.mTurnData.persist(), myParticipantId).setResultCallback(
new ResultCallback() {
#Override
public void onResult(TurnBasedMultiplayer.UpdateMatchResult result) {
processResult(result);
}
});
state = GameState.Playing;
} else {
Games.TurnBasedMultiplayer.takeTurn(MainActivity.mHelper.getApiClient(), MainActivity.mMatch.getMatchId(),
MainActivity.mTurnData.persist(), nextParticipantId).setResultCallback(
new ResultCallback<TurnBasedMultiplayer.UpdateMatchResult>() {
public void onResult(TurnBasedMultiplayer.UpdateMatchResult result) {
processResult(result);
}
});
state = GameState.multiplayerWait;
}
I am not sure how to swap inactive players with another player. I don't think this is possible unless you run your own server or use something like Parse.
Using the Cricket Audio Sound Engine ( ios & android) how would I set up a machine gun type sound effect. I need to be able to play many instances of a sound per second. The sound effects need to layer on top of each other.
My solution is to create a new CkSound instance and forget about it. I don't see a easy to destroy the sound, with out a complex sound tracking method. Will this cause memory problems as I am creating thousands of CkSounds over the course of a play session? I really don't want to have to keep track of individual sounds for garbage collection.
// Example sound effect call
void SoundManager::playEffect(const char* name){
// I make a sound , play it , and forget about it
sound = CkSound::newBankSound(g_bank, name);
sound->play();
}
I don’t recommend you create instances and don’t destroy them, as this is a memory leak, so your app will use more and more memory as time goes on.
You could try something like this…
to initialize:
const int k_maxSounds = 5; // maximum number of sound instances to be playing at once
CkSound* g_sounds[k_maxSounds];
for (int i = 0; i < k_maxSounds; ++i)
{
g_sounds[i] = CkSound::newBankSound(g_bank, name);
}
to play another sound instance, find the first available instance and play it:
for (int i = 0; i < k_maxSounds; ++i)
{
if (!g_sounds[i]->isPlaying())
{
g_sounds[i]->play();
break;
}
}
-steve -Cricket Audio Creator answered via email
Apparently no exception is thrown so that I can recognize an error while buffering streaming audio content. For example I've disconnected my router and the app will continue to try to buffer the whole time. When I reconnect then it completes buffering and continues even after being disconnected for over a minute!
So the problem is I can't let my user sit there for that long without considering that a problem. What is the proper method to detect a buffering problem with the Android media player?
I'm thinking about using a Timer for a timeout. I'll start probably with 15 seconds (using a proxy I tested a 5kbps connection, which would be a worst case, was able to start playing in 6-10 seconds, so I think 15 seconds would be a reasonable timeout period). Does this sound like a good plan? If so should I create a new Timer with each buffer attempt or should I keep the same Timer throughout the lifetime of the playback service?
So basically I'm asking two questions:
1) What's the proper way to detect if a buffer is having a problem? Is there a listener I'm overlooking? I've tried MediaPlayer.OnErrorListener of course that doesn't fire in my tests. My conclusion is I have to have a timeout to detect a buffering error.
2) If I'm correct on number one, what is the proper way to use a Timer? Create one with each buffer attempt or reuse the same one? EDIT Also should I restart the (or cancel and create a new) Timer onBufferUpdate? With the onBufferUpdate listener I should know that some data is coming back so should maybe reset the timer with that.
From your question, I understand that the primary objective is to detect a situation if your player is stalled due to buffering and take some actions thereof. To handle this situation, I feel that the following 2 listeners may be helpful to identify the same.
MediaPlayer.onBufferingUpdate would provide the timely progress of the buffering. So, if there are 2 callbacks with same percent value, this could be an indication of potential buffering.
There is another listener MediaPlayer.onInfoListener which has some specific events which could be of interest to you. On this listener, if the what is MEDIA_INFO_BUFFERING_START, this would indicate that the player is pausing the playback for buffering i.e. trigger for your logic. Similarly MEDIA_INFO_BUFFERING_END indicates the restart of the playback after filling the buffers.
You Should see this article. The mediaplayer has a ErrorListener to get any error.
http://developer.android.com/reference/android/media/MediaPlayer.OnErrorListener.html
int count=40;//for 40 seconds to wait for buffering after it will finish the activity
//boolean timeoutflag=false;
timeout = new Handler(Looper.getMainLooper()) {
#Override
public void handleMessage(Message msg) {
System.out.println("value of count="+msg.getData().getLong("count"));
if (msg.getData().getBoolean("valid")) {
if (msg.getData().getLong("count") == 0 && !timeoutflag)
{
if (pDialog != null && pDialog.isShowing())
{
try
{
pDialog.dismiss();
}catch(Exception e)
{
e.printStackTrace();
}
}
Toast.makeText(getApplicationContext(),
"Unable To Load This Video", Toast.LENGTH_LONG).show();
finish();
} else {
}
}
}
};
timeout.postDelayed(null, 0);
new Thread(new Runnable() {
#Override
public void run() {
while (count > 0) {
try {
Thread.sleep(1020);
} catch (Exception e) {
}
Message msg = new Message();
Bundle b = new Bundle();
b.putBoolean("valid", true);
b.putLong("count", --count);
msg.setData(b);
timeout.sendMessage(msg);
}
}
}).start();
// set timeoutflag=true; in setOnPreparedListener of video view
For buffering during preparation, you have to set your own timer which calls player.reset() after some interval. This puts the player back into init state.
For buffering after preparation (during play) you have to monitor getPosition(). If it falls behind some maximum, call reset(). This allows you to set an experience threshold for your playback. Handles not only failed connection, but also choppy connection.
Best solution is to not use MediaPlayer. Use a public VLC derivative instead. MP has too many internalized private design limitations requiring horrible workarounds (eg. CANT add codecs). RTFM gives you false hope in this case.
Unless you are doing a very straight laced android app, don't depend on any android api. Some opensource substitutes are better supported, and for good reason.
(really bandeely olly jolly satisfying editorial rant deleted)
I am making a Poker game in Android, I have made some TextViews and ImageViews to show on the cards of player and computer and the Community cards..
Now the problem is that when a player takes its turn after that it calls a method of call_computer and all the implementation of the computer executes but there is no delay between the player and computer turn.
So a player takes its turn and just after that a card is displayed. What I want is after the player turn, the computer should wait for a while and then it would display a text:
"Computer Selects Check/Fold/Raise"
and then according to the action selected the card should be displayed....
I have called a call_computer function just after the player's turn and action performed.. and I put that method in a new thread and put a sleep of 5 sec, but still no success...
You could use a call to postDelayed(Runnable, int):
long DELAY_IN_MSEC = 1000; // 1s
postDelayed(new Runnable() {
#Override
public void run() {
// call_computer();
}
}, DELAY_IN_MSEC);