Problems with receiving chat messages

0 votes
Hello.
I'm using appWarp SDK for Flash. Sometimes i am not receiving chat messages when it is sent on another device. Example: i have device1 and device2 connected to same room. On device1 i use function that send 5 chat messages when player make some move. This player made 3 moves and send 15 messages. In onChatReceived listener on device1 i received all 15 messages from appWarp. But on device 2 i received only 1-6 and 10-15 messages so i lost 7, 8 and 9. It happens not always but maybe on evrery 30-40 iteration.

Does appWarp guarantees that all devices connected to same room will receive all messages that being sent from this devices? (i dont call initUDP function in appwarp initialization)
asked Apr 18, 2014 in AppWarp by makskamyshnikov (10 points)
edited Apr 18, 2014 by makskamyshnikov

1 Answer

0 votes
Yes, the default communication is over TCP. So that is guaranteed to be in-oreder and reliable.

If you can get a consistent way to reproduce this issue (either a specific payload string or a specific number of messages), please let us know and we can investigate it further.
answered Apr 19, 2014 by dhruvc (1,099 points)
sometimes i get this exception from flash sdk:
TypeError: Error #1009: Cannot access a property or method of a null object reference.
        at com.shephertz.appwarp.utility::WarpSocket/send()[F:\Suyash\Shephertz\flash\AppWarpAS3\AppWarpLib\src\com\shephertz\appwarp\utility\WarpSocket.as:111]
        at com.shephertz.appwarp::WarpClient/socketCallback()[F:\Suyash\Shephertz\flash\AppWarpAS3\AppWarpLib\src\com\shephertz\appwarp\WarpClient.as:288]
        at com.shephertz.appwarp.utility::WarpSocket/onTimer()[F:\Suyash\Shephertz\flash\AppWarpAS3\AppWarpLib\src\com\shephertz\appwarp\utility\WarpSocket.as:86]
        at flash.utils::Timer/_timerDispatch()
        at flash.utils::Timer/tick()

Maybe it is related to this issue somehow.
What version of SDK are you using??? Also can you provide us more details in which scenario the flash SDK fails.
I am using 1.5.2 version of SDK.  Last time it exception happened after this 2 listeners onUserChangeRoomProperties and onConnectDone. Unfortunately i can't give more details about this exception right now.
This is a very old version, please test out our newer SDK 1.6

https://github.com/shephertz/AppWarpAS3Library/tree/master/V_1.6
I updated appWarp SDK and i have same issue with onChatReceived when some message is not received.
I have few thoughts about this: is it possible that if two device is connected to the same wifi (with dynamic ip) could cause conflicts ? Or maybe on device where i don't receive message, there is micro-disconnect but onConnectDone is not working for some reason ?
Are you using connection resiliency?
This can be an issue if you are are having micro-disconnects. Because when a connection is lost, even if its recovered, you will lose the responses and notifications for that period. You should use connection resiliency feature. Also, when connection disconnects you will get onConnectDone after around 6 seconds. In older versions of SDK, we used to depend on system to tell us if the connection has been broke or not which was very unstable. But it has been corrected in new SDK.

So, please use newer version and connection resiliency. Then when a connection is broken you will get onConnectDone with ConnectionErrorRecoverable and all the other players will get onUserPaused event.

http://appwarp.shephertz.com/game-development-center/connection-resiliency/
Ok now i have new sdk and setRecoveryAllowance(60). Now when i miss message on device, sometimes i have onConnectDone with ConnectionErrorRecoverable on this device and onUserPaused on other devices, but not always. There is situations where i don't receive message on device and there is no onConnectionDone. There is no onUserPaused on other devices either.
Is it possible that when i have disconnect and recovered in less than 6 seconds, there will not be onConnectionDone. So if there was messages in this period i don't get them ?
I have 1 more question about recovering connection: If i have connection_error_recoverable in onConnectDone listener. If connection is not recovered, should i receive one more response in this listener (e.g. connection_error) ? Last time i got only connection_error_recoverable 2 times and there was nothing else. Also i don't understand why i got this error only on one device, when there was second device connected to same wifi and pc in same network. All three devices was connected to the same room.
I would like to add to makskamyshnikov's question, that we r working on the major game and we have a very tight deadline. This issue is crucial for the game, because it prevents the online mode from the proper functioning. We would really appreciate your help in solving the issue. Thank you!
Hi makskamyshnikov - we can investigate this further to check for issues in the SDK under specific conditions. For example for specific payloads or specific message frequency etc. Can you please share a minimum code snippet with us which we can use to reproduce the problem and investigate? It will help debug the cause faster that way.

In appWarp listener i have logs on events:

public class AppWarpListener extends EventDispatcher implements ConnectionRequestListener, RoomRequestListener, NotificationListener, ZoneRequestListener ,ChatRequestListener
    {
        
        ...
 
        public function onConnectDone(res:int):void
        {
trace("onConnectDone "  + res);
MainUI.log("onConnectDone " + res);
dispatchEvent(new AppWarpEvent(AppWarpEvent.CONNECTION_DONE,{res:res},true,false));
        }
        
        public function onDisConnectDone(res:int):void
        {
trace("onDisConnectDone "  + res);
MainUI.log("onDisConnectDone " + res);
dispatchEvent(new AppWarpEvent(AppWarpEvent.DISCONNECTION_DONE,{res:res},true,false));
        }
        
        public function onChatReceived(e:Chat):void
        {
MainUI.log("onChatReceived sender:" + e.sender + "; chat:" + e.chat);
dispatchEvent(new AppWarpEvent(AppWarpEvent.CHAT_RECEIVED,{chat:e},true,false));
        }
 
...
    }
 
There are 5 figures in game and when fastest player take correct one, new round begins.
When player take figure i need to synchronize it with other players. So i need to compare local time when round starts and decide which player is fastest. So when player make move he send chat message with his name, figure , local time. When other players receive this message if their local time is less than received local time, then they send chat message that they could not take this figure in given time. When everyone received message about specific figure, it is synchronized.

In the next class player moves via playerAction function. In the end of the round player send chant command that he is ready for the next round. When everyone is ready , start and reset function is called.

My problem is that i can't synchronize figures when i don't receive message. When i see in logs that on 1 device there is "sending command ..." and there is onChatReceived whith this command. But on ther device there is no onChatReceived sometimes.

 

public class Synchronizer extends EventDispatcher
{
private const ROUND_TIMER_DELAY:Number = 50;
private var roundTimer:Timer;
private var players:Vector.<PlayerData>;
private var figuresInteraction:Object;
private var figureRequests:Object;
private var localPlayers:Array; //players on device except host and ai players
private var timeoutId:uint;

public function Synchronizer(players:Vector.<PlayerData>)
{
this.players = players;
this.roundTimer = new Timer(ROUND_TIMER_DELAY);
this.figuresInteraction = new Object();
this.localPlayers = new Array();
this.figureRequests = new Object();
}

public function initialize():void
{
Multiplayer.appWarp.addEventListener(AppWarpEvent.CHAT_RECEIVED,onChatReceived);

for(var i:int=0;i!=players.length;++i)
{
if(players[i].name != Multiplayer.instance.player &&
(!players[i].isOnlinePlayer || players[i].aiPlayer != null))
{
localPlayers.push
(
{
name : players[i].name,
sentRequest : false
}
);
}
}

for(i=0;i!=FigureType.TYPES.length;++i)
{
figuresInteraction[FigureType.TYPES[i].name] = new Array();
figureRequests[FigureType.TYPES[i].name] = null;
}
}

public function updateFiguresInteractions():void
{
MainUI.log("Synchronizer.updateFiguresInteractions");
var playerNames:Array = new Array();
for(var i:int=0;i!=players.length;++i)
{
playerNames.push(players[i].name);
}

for(i=0;i!=FigureType.TYPES.length;++i)
{
var f:Array = figuresInteraction[FigureType.TYPES[i].name];
for(var j:int=0;j!=f.length;j++)
{
if(playerNames.indexOf(f[j].player) == -1)
{
f.splice(j,1);
break;
}
}
if(figuresInteraction[FigureType.TYPES[i].name].length == players.length)
{
synchronize(FigureType.TYPES[i].name);
figuresInteraction[FigureType.TYPES[i].name] = new Array();
}
}
}

public function playerAction(playerName:String,figure:String,swipeTime:Number):void
{
MainUI.log("Synchronizer.playerAction " + playerName + " " + figure + " " + swipeTime);
var timeToPick:Number = roundTimer.currentCount * ROUND_TIMER_DELAY - swipeTime;

sendFigureInteractionCommand(true,playerName,figure,timeToPick)
}

private function onChatReceived(e:AppWarpEvent):void
{
var mulCommand:Array = e.params.chat.chat.split(';');
if(mulCommand[0] == MultiplayerCommand.PLAYER_ACTION)
{
var isPicking:Boolean = (mulCommand[1]==1) ? true : false;
var player:String = mulCommand[2];
var figure:String = mulCommand[3];
var time:Number = Number(mulCommand[4]);

figuresInteraction[figure].push
(
{
player : player,
time : time,
pick : isPicking
}
);

MainUI.log(player + " isPicking("+isPicking+") " + figure + " in " + time + " ms.");

var i:int;
//check if it was command from any player on device
if(isPicking)
{
var isPlayerOnDivice:Boolean = false;
for(i=0;i!=localPlayers.length;++i)
{
if(localPlayers[i].name == player)
{
isPlayerOnDivice = true;
}
}
if(player == Multiplayer.instance.player)
{
isPlayerOnDivice = true;
}

if(!isPlayerOnDivice)
{
var allowedPickTime:Number = roundTimer.currentCount * ROUND_TIMER_DELAY + GameSettings.maxSwipeTime;

if(time <= allowedPickTime)
{
if(figureRequests[figure] == null)
{
sendFigureInteractionCommand(false,Multiplayer.instance.player,figure,time);
for(i=0;i!=localPlayers.length;++i)
{
sendFigureInteractionCommand(false,localPlayers[i],figure,time);
}
}
}
else
{
var timeToPickLeft:Number = time - allowedPickTime;
MainUI.log("Current player have " + timeToPickLeft + " ms. time to pick " + figure);
timeoutId = setTimeout
(
function ():void
{
if(figureRequests[figure] == null)
{
sendFigureInteractionCommand(false,Multiplayer.instance.player,figure,roundTimer.currentCount * ROUND_TIMER_DELAY + GameSettings.maxSwipeTime);
for(i=0;i!=localPlayers.length;++i)
{
sendFigureInteractionCommand(false,localPlayers[i],figure,roundTimer.currentCount * ROUND_TIMER_DELAY + GameSettings.maxSwipeTime);
}
}
}
,
timeToPickLeft
)
}
}
}
}

for(i=0;i!=FigureType.TYPES.length;++i)
{
if(figuresInteraction[FigureType.TYPES[i].name].length == players.length)
{
synchronize(FigureType.TYPES[i].name);
figuresInteraction[FigureType.TYPES[i].name] = new Array();
}
}
}


private function synchronize(figure:String):void
{
MainUI.log("Synchronizer.synchronize " + figure);
var f:Array = figuresInteraction[figure];
var fastestPlayerAction:Object = new Object();
for(var i:int=0;i!=f.length;++i)
{
if(f[i].pick)
{
if(fastestPlayerAction.name)
{
if(fastestPlayerAction.time > f[i].time)
{
fastestPlayerAction.figure = figure;
fastestPlayerAction.time = f[i].time;
fastestPlayerAction.name = f[i].player;
}
}
else
{
fastestPlayerAction.figure = figure;
fastestPlayerAction.time = f[i].time;
fastestPlayerAction.name = f[i].player;
}
}
}

if(fastestPlayerAction)
{
MainUI.log("fastest player " + fastestPlayerAction.name + " receives " + fastestPlayerAction.figure);
dispatchEvent(new MultiplayerEvent(MultiplayerEvent.PLAYER_ANSWERED,
{
name : fastestPlayerAction.name,
time : fastestPlayerAction.time,
figure : fastestPlayerAction.figure
}
));
}
}

public function start():void
{
MainUI.log("Synchronizer.start");
roundTimer.start();
}

public function destroy():void
{
MainUI.log("Synchronizer.destroy");
roundTimer.stop();
clearTimeout(timeoutId);
Multiplayer.appWarp.removeEventListener(AppWarpEvent.CHAT_RECEIVED,onChatReceived);
}

public function reset():void
{
MainUI.log("Synchronizer.reset");
clearTimeout(timeoutId);
for(var i:int=0;i!=FigureType.TYPES.length;++i)
{
figuresInteraction[FigureType.TYPES[i].name] = new Array();
figureRequests[FigureType.TYPES[i].name] = null;

}
roundTimer.reset();
}

private function sendFigureInteractionCommand(isPicking:Boolean,player:String,figure:String,mills:Number):void
{
MainUI.log("Synchronizer.sendFigureInteractionCommand");
var command:String;

if(isPicking)
{
if(localPlayers.length != 0)
{
for(var i:int=0;i!=localPlayers.length;++i)
{
if(player != localPlayers[i].name)
{
command = MultiplayerCommand.PLAYER_ACTION + ";" +
0 + ";" +
localPlayers[i].name + ";" +
figure + ";" +
mills;
MainUI.log("sending command " + command);
Multiplayer.appWarp.sendChatMessage(command);
}
}
if(Multiplayer.instance.player != player)
{
command = MultiplayerCommand.PLAYER_ACTION + ";" +
0 + ";" +
Multiplayer.instance.player + ";" +
figure + ";" +
mills;
MainUI.log("sending command " + command);
Multiplayer.appWarp.sendChatMessage(command);
}
}
}

figureRequests[figure] = player;
command = MultiplayerCommand.PLAYER_ACTION + ";" +
(isPicking ? 1 : 0) + ";" +
player + ";" +
figure + ";" +
mills;
MainUI.log("sending command " + command);

Multiplayer.appWarp.sendChatMessage(command);
}
}
It appears that you've implemented your own custom event layer on top of the client SDK. So its hard to identify where your issue might be and also we will not be able to reproduce the issue using your code.

We have locally tested a chat sample in which we sent up to 50 messages in a loop with each message 100 characters and there were no missed packets.

Here is the code https://github.com/SuyashMShepHertz/AppWarpFlashChatSample
We did implement our own custom layer. But we r pretty sure that the problem lies in the SDK. Look at this snippet:
   
public class AppWarpListener extends EventDispatcher implements ConnectionRequestListener, RoomRequestListener, NotificationListener, ZoneRequestListener ,ChatRequestListener
    {
        
        public function AppWarpListener()
        {
        }

.....

        public function onChatReceived(e:Chat):void
        {
            MainUI.log("onChatReceived sender:" + e.sender + "; chat:" + e.chat);
            dispatchEvent(new AppWarpEvent(AppWarpEvent.CHAT_RECEIVED,{chat:e},true,false));
        }


Thу onChatReceived function never works in the cases we described.
Otherwise we would see the log message.

We did also the same test you did and it worked fine. But in the real life situation it fails.

One time we encountered following situation. We had two devices (A,B) on the same wifi.
And device C far away,in another country. Device A created a game. Device B was connected to the appWarp, but didn't connect to the game - was actually looking on the list of rooms available.
Device C connected to the game,created by Device A.
Then, device A saw device C connected. But device C saw device A and device B in the game.

We have no idea what the problem here could be - device B was definitely doing nothing but just starring at the list of the rooms available for some time.

We have therefore a theory that all these problems might have to do smth with devices connected from the same wifi.
Hi supryin - can you perhaps share your project (github or zip file) so that we can run it locally and try to reproduce the problem? You can also send it to us offline over email - support@shephertz.com

It just seems strange that if the issue is within the SDK - then you should be able to reproduce it with the test code shared by suyash.

Also note that running on the same wifi shouldn't matter since while they might have the same public IP, the ports are different and the server will not mix them up.
Hi dhruvc,
thanks for the answer!
Unfortunately the project is quite big and sensitive. We cannot share it as a whole...
makskamyshnikov already shared the relevant parts.
>Also note that running on the same wifi shouldn't matter since while they might >have the same public IP, the ports are different and the server will not mix them >up.
Yes, sure. But how this situation with the devices A,B,C was possible?
May be u r using IP address somewhere to identify the devices?
I see.. can you edit the test code shared by Suyash and replace the chat messages with the exact payloads that are being generated in your application? Perhaps its something to do with the payload of the message.
ok, we rethought. we can give an access to our bitbucket to one person. Please provide me the mail address. And thanks a lot!
We rely on your discretion and on your understanding of the confidentiality of that matter.
Can you please send us a mail on support@shephertz.com with a reference to this thread? We can then discuss further over mail.
Download Widgets
Welcome to ShepHertz Product line forum, where you can ask questions and receive answers from the community. You can also reach out to us on support@shephertz.com
...