In order to read a new display name of a peer I need to kill and renew the GKSession. Setting it to nil and initiate it anew does not work. In the code below, the NSLog in the for-loop to show the available peers is not called (there's no error message):
-(IBAction) btnRefresh:(id) sender {
self.currentSession = nil;
self.currentSession = [[GKSession alloc] initWithSessionID:#"anything" displayName:name sessionMode:GKSessionModePeer];
self.currentSession.delegate = self;
self.currentSession.available = YES;
self.currentSession.disconnectTimeout = 0;
[self.currentSession setDataReceiveHandler:self withContext:nil];
peerListAvailable = [[NSMutableArray alloc] initWithArray:[currentSession peersWithConnectionState:GKPeerStateAvailable]];
for (NSString *peer in peerListAvailable) {
NSLog(#"found available peer; checking name and ID... %#, %#",[currentSession displayNameForPeer:peer], peer);
}
What is wrong with setting the currentSession to nil and initiate it anew?
Maybe you know of another way to renew a GKSession?
Thanks very much in advance.
The following methods illustrate GKSession setup and teardown:
- (void)setupSession
{
gkSession = [[GKSession alloc] initWithSessionID:nil displayName:nil sessionMode:GKSessionModePeer];
gkSession.delegate = self;
gkSession.disconnectTimeout = 5;
gkSession.available = YES;
}
- (void)teardownSession
{
gkSession.available = NO;
[gkSession disconnectFromAllPeers];
}
If you're interested in delving deeper, take a look at GKSessionP2P, a demo app that illustrates the ad-hoc networking features of GKSession. The app both advertises itself on the local network and automatically connects to available peers, establishing a peer-to-peer network.
Related
I'm working on a local network based game. By this I mean that you can play it on different iphone on local network wifi or bluetooth. I did the implementation with NSStream with a tcp connection to transfer data. I have one server who listens the network :
service = [[NSNetService alloc] initWithDomain:#"local."// 4
type:CreateNSString(serviceType)
name:CreateNSString(serviceName)
port:80];
if(service)
{
service.includesPeerToPeer = YES;
[service setDelegate:delegatePublisherObject];// 5
[service publishWithOptions:NSNetServiceListenForConnections];
And clients who search for game sessions around :
serviceBrowser = [[NSNetServiceBrowser alloc] init];
serviceBrowser.includesPeerToPeer = YES;
[serviceBrowser setDelegate:delegateBrowserObject];
[serviceBrowser searchForServicesOfType: CreateNSString(service) inDomain:#"local"];
But i have an issue with the data transfer between them. What i did from now, is :
i queued the data i want to send. If i can write it directly in the output stream i do
dataWriteQueue insertObject:data atIndex:0];
if (flag_canSendDirectly)
[self _sendData];
...
- (void)_sendData {
NSData *data = [dataWriteQueue lastObject];
flag_canSendDirectly = NO;
if (data == nil)
{
flag_canSendDirectly = YES;
return;
}
bufferToSend = (uint8_t *)[data bytes];
NSInteger bytesWritten;
bytesWritten = [_outputStream write:bufferToSend maxLength:dataBufferLimit - currentDataOffset];
if the callback NSStreamEventHasSpaceAvailable is called i send the last pieces of data queued.
case NSStreamEventHasSpaceAvailable: {
if (stream == _outputStream)
{
//read a new chunk of data
[self _sendData];
}
} break;
I have stopped the browsing of devices when i transfer data between peers.
The transfer is done well. But my issue here is that the transfer is pretty fast in bluetooth. But slower in wifi. I don't understand this, it doesn't seems logical. Do you have any clues about this issue ? The browsing is shut down when the peers are connected.
Update: hideCreditCardButton has no role in this issue, it was just a coincident that when I tried it w/o credit card it worked for me but thats not the case anymore.I does work but only 20-30% of the time.
I am using PayPal iOS SDK 1.4.6. if I use paymentViewController.hideCreditCardButton = YES; it works fine but if I set this to paymentViewController.hideCreditCardButton = NO; I get the server error (ref. to image).
here's my code:
- (void)paypalPayment {
// Create a PayPalPayment
float paypalPrice =[youPay floatValue];
NSString* currencyCode = appDelegate.countryCurrency;
if ([currencyCode isEqual: #"INR"]) {
float new = [[[ExchangeRate sharedManager]getExchangeRate:#"INR" toCurrency:#"USD"]
floatValue];
paypalPrice = paypalPrice*new;
}
PayPalPayment *payment = [[PayPalPayment alloc] init];
payment.amount = [[NSDecimalNumber alloc] initWithString:
[NSString stringWithFormat:#"%.2f",paypalPrice]];
payment.currencyCode = #"USD";
payment.shortDescription = #"Total Order";
[PayPalPaymentViewController setEnvironment:PayPalEnvironmentProduction];
// Provide a payerId that uniquely identifies a user within the scope of your system,
// such as an email address or user ID.
NSString *aPayerId = #"abc#msronline.in";
PayPalPaymentViewController *paymentViewController;
paymentViewController = [[PayPalPaymentViewController alloc]
initWithClientId:kPayPalClientId
receiverEmail:kPayPalReceiverEmail
payerId:aPayerId
payment:payment
delegate:self];
paymentViewController.languageOrLocale = #"en";
paymentViewController.hideCreditCardButton = NO;
[self presentViewController:paymentViewController animated:YES completion:nil];
}
Dave from PayPal here.
That's indeed weird. You're getting this as soon as you present the viewcontroller? Is it still happening today?
From your screenshot, it looks like the SDK is still trying to get an initial connection with the PayPal server. At this early stage, the setting of hideCreditCardButton shouldn't yet affect anything.
You are using the Client ID that's designated for Production (as opposed to the Client ID designated for Sandbox), right?
Do you see anything helpful in the console log?
I have same issue and solved by change kPayPalReceiverEmail.
If you use same email as kPayPalReceiverEmail to login in paypal to send payment than this error comes.
I just change kPayPalReceiverEmail email to another one and I get successful response from paypal server.
It may help you
Alright, this involves a lot of network coding from this part of a multiplayer tutorial.
Basically, I'm trying to implement a multiplayer game using GameKit as per the tutorial linked above. I put in all of the necessary network coding and more or less understand it, however I've hit a snag somewhere along the line of method calls. Basically, the setup that I have is that one device acts as the host and the rest act as the clients. I have two separate UIViewcontrollers for the host and clients respectively where the connection is established.
Now the thing is, the connection gets established, but it's only the host that recognizes the connection, not the client. The problem is here:
- (void)sendPacketToAllClients:(Packet *)packet
{
[_players enumerateKeysAndObjectsUsingBlock:^(id key, Player *obj, BOOL *stop)
{
obj.receivedResponse = [_session.peerID isEqualToString:obj.peerID];
}];
GKSendDataMode dataMode = GKSendDataReliable;
NSData *data = [packet data];
NSError *error;
if (![_session sendDataToAllPeers:data withDataMode:dataMode error:&error])
{
NSLog(#"Error sending data to clients: %#", error);
}
}
This is implemented in GameMultiplayer, where the actual game will be implemented. What this method is supposed to be doing is sending data packets to each of the clients saying that the host received the connection request and is able to connect with them. After [_session sendDataToAllPeers:data withDataMode:dataMode error:&error] is called (the method in the if statement), this method is supposed to be triggered:
- (void)receiveData:(NSData *)data fromPeer:(NSString *)peerID inSession:(GKSession *)session context:(void *)context
{
#ifdef DEBUG
NSLog(#"Game: receive data from peer: %#, data: %#, length: %d", peerID, data, [data length]);
#endif
Packet *packet = [Packet packetWithData:data];
if (packet == nil)
{
NSLog(#"Invalid packet: %#", data);
return;
}
Player *player = [self playerWithPeerID:peerID];
if (player != nil)
{
player.receivedResponse = YES; // this is the new bit
}
if (self.isServer)
[self serverReceivedPacket:packet fromPlayer:player];
else
[self clientReceivedPacket:packet];
}
This method is in the next part of the tutorial I linked above (which is here) and is supposed to receive the packets that the host sends to all clients and implement the next methods in this networking chain. However, the method never gets called. No debug breakpoints are triggered and I get nothing in the console.
I understand if I need to provide more source material, but there is a lot of network coding already implemented, so I want to keep it down to what people need to see. Also, [_session setDataReceiveHandler:self withContext:nil] and _session.delegate = self are written in another method that is called in GameMultiplayer, so that's not the problem. Does anyone know what I need to fix?
EDIT: As requested, here's where GKSession is initialized:
#property (nonatomic, strong, readonly) GKSession *session; //This is done in the header file
#synthesize session = _session; //This is done in the main file
- (void)startAcceptingConnectionsForSessionID:(NSString *)sessionID
{
if (_serverState == ServerStateIdle)
{
_serverState = ServerStateAcceptingConnections;
_connectedClients = [NSMutableArray arrayWithCapacity:self.maxClients];
_session = [[GKSession alloc] initWithSessionID:sessionID displayName:nil sessionMode:GKSessionModeServer];
_session.delegate = self;
_session.available = YES;
}
}
The session is initialized in MatchmakingServer, which is used in the host view controller. The session is then passed on to the main view controller of the app, which then initializes GameMultiplayer and sends the GKSession to it. Here's where the host view controller sends it to the main view controller:
- (IBAction)startAction:(id)sender
{
if (_matchmakingServer != nil && [_matchmakingServer connectedClientCount] > 0)
{
NSString *name = [self.nameTextField.text stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
if ([name length] == 0)
name = _matchmakingServer.session.displayName;
[_matchmakingServer stopAcceptingConnections];
[self.delegate hostViewController:self startGameWithSession:_matchmakingServer.session playerName:name clients:_matchmakingServer.connectedClients];
}
}
and then the main view controller handles that method call here:
- (void)hostViewController:(MatchmakerHost *)controller startGameWithSession:(GKSession *)session playerName:(NSString *)name clients:(NSArray *)clients
{
[self dismissViewControllerAnimated:NO completion:^
{
[self startGameWithBlock:^(GameMultiplayer *aGame)
{
[aGame startServerGameWithSession:session playerName:name clients:clients];
}];
}];
}
and finally, this is where that method call is implemented in GameMultiplayer:
- (void)startServerGameWithSession:(GKSession *)session playerName:(NSString *)name clients:(NSArray *)clients
{
_clients = clients;
const char* className = class_getName([[_clients objectAtIndex:0] class]);
NSLog(#"yourObject is a: %s", className);
self.isServer = YES;
_session = session;
_session.available = NO;
_session.delegate = self;
[_session setDataReceiveHandler:self withContext:nil];
_state = GameStateWaitingForSignIn;
[self.delegate gameWaitingForClientsReady:self];
// Create the Player object for the server.
Player *player = [[Player alloc] init];
player.name = name;
player.peerID = _session.peerID;
player.position = PlayerPositionBottom;
[_players setObject:player forKey:player.peerID];
// Add a Player object for each client.
int index = 0;
for (NSString *peerID in clients)
{
Player *player = [[Player alloc] init];
player.peerID = peerID;
[_players setObject:player forKey:player.peerID];
if (index == 0)
player.position = ([clients count] == 1) ? PlayerPositionTop : PlayerPositionLeft;
else if (index == 1)
player.position = PlayerPositionTop;
else
player.position = PlayerPositionRight;
index++;
}
NSLog(#"Players:");
Packet *packet = [Packet packetWithType:PacketTypeSignInRequest];
[self sendPacketToAllClients:packet];
// for (int i = 0; i < [_players count]; i++) {
// NSLog([NSString stringWithFormat:#"%#", [clients objectAtIndex:i]]);
// }
}
I think you are calling send to fast. When server realize about connection it will send confirmation to client to really establish connection - so client knows about it succeed.
If you are sending packets before that happens - it will be lost.
Just do this:
[self performSelector:#selector(sendPacketToAllClients) withObject:nil afterDelay:1.0];
instead of:
[self sendPacketToAllClients];
I had the same problem that connection is established in different moment with small delay on client. The best is to send first packet from client that he is ready to receive packets from server - and than proceed normally from there.
Also try debugging:
- (void)session:(GKSession *)session peer:(NSString *)peerID didChangeState:(GKPeerConnectionState)state
On both devices (server and client).
I have also had my troubles with GKSession. I was interested to learn (on this site) today that GKSession is being deprecated in favor of using the Multipeer Connectivity Framework. With luck, Wenderlich et al. will do a tutorial using the new technology. :)
The system has some similarities to GKSession, so is not too hard to wrap your head around.
Apple's doc link.
I have created a FacebookManager singleton that gets called on a background thread when my app launches. Everything is working just fine with the facebook manager the singleton, the app etc. However, when the app first launches, it is quite a few seconds before it is useful because the facebook manager has not finished doing its thing yet. So what I want to do, is use NSKeyedArchiver to save the facebookManager and all its dictionaries so that upon launch, the app has a navigable interface while the facebook data is being updated in the background. Make sense?
All within the FacebookManager.m, first, when the manager is done updating the friends dictionaries, etc, I call the method that saves the data:
- (BOOL)saveFacebookData
{
// returns success or failure
NSString *path = [self archivePath]; // just a helper method
return [NSKeyedArchiver archiveRootObject:self toFile:path];
}
Then in init, I am trying this, which doesn't seem to work. :
-(id)init
{
self = [super init];
NSString *path = [self archivePath];
self = [NSKeyedUnarchiver unarchiveObjectWithFile:path];
// If the manager hadn't been saved previously, create a new new one
if (!self) {
if (_idsByNameDict == nil) {
_idsByNameDict = [[NSMutableDictionary alloc] init];
}
if (_photosByNameDict == nil) {
_photosByNameDict = [[NSMutableDictionary alloc] init];
}
if (_installedByNameDict == nil) {
_installedByNameDict = [[NSMutableDictionary alloc] init];
}
if (_allFriendsArray == nil) {
_allFriendsArray = [[NSArray alloc] init];
}
basicPermissions = NO;
extendedPermissions = NO;
// Create synchronous dispatch queue for all facebook activity
if (_facebookUpdateQueue == nil) {
_facebookUpdateQueue = dispatch_queue_create("com.facebookUpdateQueue", NULL);
}
}
I think my general strategy is sound but I am tripping over how to actually grab the archived version of the manager during init! Any advice?
Your class needs to implement <NSCoding> and both of its methods encodeWithCoder: to archive all of your property values and initWithCoder: to in archive them. Make sure to call super in the implementations. Generally, the class using the archived class would know about the archiving but you could hide that knowledge in init by using initForReadingWithData: to create your NSKeyedUnarchiver and then calling [self initWithCoder:...];.
I am working with Blackraccoon FTP client to do FTP operations,working with ARC.but i am getting leaks in instruments.
but there were no leaks in sample application here is my code
BRRequestCreateDirectory *createEventDir = [BRRequestCreateDirectory initWithDelegate:nil];
//NSString *EventCode = [[NSUserDefaults standardUserDefaults] stringForKey:kEventCodeKey];
createEventDir.path = #"/12341234";
createEventDir.hostname = #"media.example.com/httpdocs/events/";
createEventDir.username = #"badboy";
createEventDir.password = #"hai!";
createEventDir.tag = 103;
[createEventDir start];
createEventDir = nil;
sample code from FTP clent Blackraccoon FTP client
leaks showing in instruments like,but i am using ARC
can any one help me to solve this prob..
I ported and heavily modified BlackRaccoon. It is designed to use delegates. In other words, delegates are required.
BRRequestCreateDirectory *createEventDir = [BRRequestCreateDirectory initWithDelegate:nil];
//NSString *EventCode = [[NSUserDefaults standardUserDefaults] stringForKey:kEventCodeKey];
createEventDir.path = #"/12341234";
createEventDir.hostname = #"media.example.com/httpdocs/events/";
createEventDir.username = #"badboy";
createEventDir.password = #"hai!";
createEventDir.tag = 103;
[createEventDir start];
createEventDir = nil;
Is incorrect. It starts a lot of things going and then deletes the object - the action is undefined.
Instead you need something as indicated in the code that I provided (that doesn't leak).
First, the class that uses the ftp needs to have BRRequestDelegate to indicate the delegate protocol.
- (IBAction) createDirectory:(id)sender
{
//----- createEventDir must be a variable in your class...
createEventDir = [BRRequestCreateDirectory initWithDelegate: self];
createEventDir.path = #"/12341234;
createEventDir.hostname = #"media.example.com/httpdocs/events/";
createEventDir.username = #"badboy";
createEventDir.password = #"hai!";
[createEventDir start];
//----- createEventDir MUST NOT BE DELETED OR SET TO NIL HERE
}
Then you have to have the two delegates (at a minimum):
-(void) requestCompleted: (BRRequest *) request
{
//----- handle Create Directory
if (request == createEventDir)
{
NSLog(#"%# completed!", request);
//----- everything is done, NOW you can set it to nil
createEventDir = nil;
}
}
-(void) requestFailed: (BRRequest *) request
{
if (request == createEventDir)
{
NSLog(#"%#", request.error.message);
//----- everything is done, NOW you can set it to nil
createEventDir = nil;
}
}
If you go back and look at my test code you'll see how things work. If you are still having issues, post in the issues on http://github.com/lloydsargent/BlackRaccoon
Hopefully this will get you past your problem.