-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAwesomeNetworker.mm
352 lines (252 loc) · 11.2 KB
/
AwesomeNetworker.mm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
//
// AwesomeNetworker.mm
// awesomesauce
//
// Created by Keegan Poppen on 3/16/11.
// Copyright 2011 Lord Keeganus Industries. All rights reserved.
//
#import "AwesomeNetworker.h"
#import "awesomesauceAppDelegate.h"
#import "MatrixHandler.h"
#import <math.h>
#define NUM_SYNCHRO_OFFSETS 25
@implementation TimeSync
@synthesize offsets;
@synthesize networker;
-(id)init {
self = [super init];
if (self) {
//startTime = [NSDate timeIntervalSinceReferenceDate];
//NSLog(@"initting timesync.... setting start time to %f", startTime);
offsets = [[NSMutableArray alloc] init];
num_packets_handled = 0;
}
return self;
}
-(void)receiveData:(NSDictionary*)data fromTime:(NSTimeInterval)updateTime {
MatrixHandler *matrixHandler = [(awesomesauceAppDelegate*)[[UIApplication sharedApplication] delegate] getMatrixHandler];
++num_packets_handled;
NSNumber *time_received = [data objectForKey:@"time_received"];
NSNumber *age_offset = [data objectForKey:@"age_offset"];
//check if we were the sender, or the recipient
if (age_offset != nil) {
globalOffset = [age_offset doubleValue];
NSLog(@"global offset set to: %f", globalOffset);
matrixHandler->addOffset(globalOffset);
NSLog(@"set global time to %f thanks to being told as such", matrixHandler->time_elapsed);
if(globalOffset < 0.001) {
NSLog(@"gonna send all m'data");
NSDictionary *matrixData = matrixHandler->encode();
[networker sendData:matrixData withEventName:@"load_data"];
}
NSLog(@"syncing is done, bro");
[[NSNotificationCenter defaultCenter] postNotificationName:@"synchronizing_done" object:nil userInfo:nil];
} else if (time_received == nil) {
//NSLog(@"gonna forward that packet right the fuck back, yo");
NSMutableDictionary *toSend = [[[NSMutableDictionary dictionaryWithDictionary:data] retain] autorelease];
[toSend setObject:[NSNumber numberWithDouble:matrixHandler->time_elapsed] forKey:@"time_received"];
[self.networker sendData:toSend withEventName:@"time_sync" overrideTime:YES];
if(num_packets_handled == NUM_SYNCHRO_OFFSETS) {
NSLog(@"done forwarding packets, man. fuck that.");
[[NSNotificationCenter defaultCenter] postNotificationName:@"loading_data" object:nil userInfo:nil];
}
} else {
NSTimeInterval rec_time = [time_received doubleValue];
NSTimeInterval sent_time = [[data objectForKey:@"time_sent"] doubleValue];
NSTimeInterval offset = rec_time - ((sent_time + matrixHandler->time_elapsed) / 2.);
[offsets addObject:[NSNumber numberWithDouble:offset]];
totalOffset += offset;
if([offsets count] != NUM_SYNCHRO_OFFSETS) {
//if we haven't gotten enough data, send some more
[networker sendData:nil withEventName:@"time_sync"];
} else {
NSTimeInterval mean = totalOffset / (double)NUM_SYNCHRO_OFFSETS;
NSTimeInterval sd_accum = 0;
for (unsigned i = 0; i < NUM_SYNCHRO_OFFSETS; ++i) {
sd_accum += pow([[offsets objectAtIndex:i] doubleValue] - mean, 2.);
}
sd_accum /= pow((double)NUM_SYNCHRO_OFFSETS, .5);
sd_accum = pow(sd_accum, .5);
NSLog(@"mean: %f", mean);
NSLog(@"standard deviation of mean offset: %f", sd_accum);
NSLog(@"offsets: %@", offsets);
//if(mean > 0) then they are older than us
if(mean > 0) {
NSLog(@"they're older, so I'm updating my age and sending 0");
globalOffset = mean;
[networker sendData:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithDouble:0.0], @"age_offset", nil] withEventName:@"time_sync"];
NSLog(@"setting phasers to load");
[[NSNotificationCenter defaultCenter] postNotificationName:@"loading_data" object:nil userInfo:nil];
} else {
NSLog(@"I'm older, so they're gonna have to grow up");
globalOffset = 0;
[networker sendData:[NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithDouble:-mean], @"age_offset", nil] withEventName:@"time_sync"];
NSLog(@"I'm gonna go ahead and send them my data too");
NSDictionary *matrixData = matrixHandler->encode();
NSLog(@"sending data to help out a friend in need");
[networker sendData:matrixData withEventName:@"load_data"];
NSLog(@"syncing is done, dawg");
[[NSNotificationCenter defaultCenter] postNotificationName:@"synchronizing_done" object:nil userInfo:nil];
}
matrixHandler->addOffset(globalOffset);
NSLog(@"set global time to %f thanks to my own volition", matrixHandler->time_elapsed);
}
}
}
@end
@implementation LoadDataSync
@synthesize networker;
-(void)receiveData:(NSDictionary*)data fromTime:(NSTimeInterval)updateTime {
//NSLog(@"received some matrix data: %@", data);
MatrixHandler *matrixHandler = [(awesomesauceAppDelegate*)[[UIApplication sharedApplication] delegate] getMatrixHandler];
matrixHandler->decode(data);
NSLog(@"the synkkkro is duun");
[[NSNotificationCenter defaultCenter] postNotificationName:@"synchronizing_done" object:nil userInfo:nil];
}
@end
@implementation AwesomeNetworker
@synthesize handlerMap;
@synthesize session;
@synthesize timeSync, loadDataSync;
@synthesize networkSyncer;
- (id)initWithMatrixHandler:(MatrixHandler*)handler {
self = [super init];
if (self) {
handlerMap = [[NSMutableDictionary alloc] init];
//set self to handle incoming data from gamekit
[session setDataReceiveHandler:self withContext:NULL];
//set up time_sync handler
timeSync = [[TimeSync alloc] init];
[self registerEventHandler:@"time_sync" withSyncee:timeSync];
//set up load_data handler
loadDataSync = [[LoadDataSync alloc] init];
[self registerEventHandler:@"load_data" withSyncee:loadDataSync];
session = [[GKSession alloc] initWithSessionID:@"awesomesauce" displayName:nil sessionMode:GKSessionModePeer];
[session setDelegate:self];
[session setDataReceiveHandler:self withContext:nil];
[session setAvailable:YES];
/*
NOTE: ONE HACK THAT MIGHT FIX THE BLUETOOTH BUG IS TO SET AVAILABLE TO FASE AND THEN BACK TO TRUE AFTER A RANDOM TIME INTERVAL
*/
networkSyncer = [[AwesomeNetworkSyncer alloc] initWithNetworker:self andMatrixHandler:handler];
NSLog(@"starting server in peer mode w/ peerID %@", session.peerID);
}
return self;
}
//registers an ASDataSyncee to handle all events with name eventName
-(void)registerEventHandler:(NSString*)eventName withSyncee:(id<ASDataSyncee>)syncee {
NSLog(@"registering handler for key: %@", eventName);
[handlerMap setObject:syncee forKey:eventName];
syncee.networker = self;
}
-(id<ASDataSyncee>)handlerForEvent:(NSString*)eventName {
id<ASDataSyncee> syncee = [handlerMap objectForKey:eventName];
if (syncee == nil) NSLog(@"no handler for event w/ name: %@", eventName);
return syncee;
}
-(NSTimeInterval)sendData:(NSDictionary*)data withEventName:(NSString*)eventName {
return [self sendData:data withEventName:eventName overrideTime:NO];
}
//sends data and returns the time that the data was sent
-(NSTimeInterval)sendData:(NSDictionary*)data withEventName:(NSString*)eventName overrideTime:(BOOL)shouldOverride{
//id<ASDataSyncee> syncee = [self handlerForEvent:eventName];
NSMutableDictionary *dict = [NSMutableDictionary dictionaryWithDictionary:data];
[dict setObject:eventName forKey:@"event_name"];
//NSTimeInterval curAge = [NSDate timeIntervalSinceReferenceDate] - timeSync.startTime;
MatrixHandler *matrixHandler = [(awesomesauceAppDelegate*)[[UIApplication sharedApplication] delegate] getMatrixHandler];
float time_elapsed = matrixHandler->time_elapsed;
if (!shouldOverride) {
[dict setObject:[NSNumber numberWithDouble:time_elapsed] forKey:@"time_sent"];
}
//NSLog(@"sending some data that looks like this: %@", [dict description]);
NSData *toSend = [[[NSKeyedArchiver archivedDataWithRootObject:dict] retain] autorelease];
NSLog(@"data sent: %@", [toSend description]);
NSError *err;
if (![session sendDataToAllPeers:toSend withDataMode:GKSendDataReliable error:&err]) {
NSLog(@"DATA SEND ERROR: %@", err);
}
return time_elapsed;
}
/*
* gk functions
*/
- (BOOL) comparePeerID:(NSString*)otherID {
//return [otherID compare:session.peerID] == NSOrderedAscending;
NSString *otherPeer = [session displayNameForPeer:otherID];
NSComparisonResult comp = [otherPeer caseInsensitiveCompare:@"Chewbacca"];
return comp != NSOrderedSame;
}
- (void)session:(GKSession *)session peer:(NSString *)peerID didChangeState:(GKPeerConnectionState)state {
NSLog(@"peer state change for peer %@ with display name %@", peerID, [session displayNameForPeer:peerID]);
switch (state) {
case GKPeerStateConnected:
NSLog(@"connected to peer %@", [session displayNameForPeer:peerID]);
if ([self comparePeerID:peerID]) {
NSLog(@"sending timing packets");
//send the (first of the) timing packets. most of the work is done by default, which is why this looks a bit silly
[self sendData:nil withEventName:@"time_sync"];
[[NSNotificationCenter defaultCenter] postNotificationName:@"synchronizing_clocks" object:nil userInfo:nil];
} else {
NSLog(@"waiting for timing packets");
}
break;
case GKPeerStateConnecting:
NSLog(@"connecting");
break;
case GKPeerStateAvailable:
NSLog(@"availability found");
if ([self comparePeerID:peerID]) {
NSLog(@"available... gonna try and connect");
[session connectToPeer:peerID withTimeout:30.];
} else {
NSLog(@"gonna wait for peer to connect to me");
}
//let the dope-ass progress bar show up and rock your world
[[NSNotificationCenter defaultCenter] postNotificationName:@"connection_start" object:nil
userInfo:[NSDictionary dictionaryWithObjectsAndKeys:[session displayNameForPeer:peerID],@"username",nil]];
break;
case GKPeerStateUnavailable:
NSLog(@"unavailable");
break;
case GKPeerStateDisconnected:
NSLog(@"disconnected");
break;
}
}
- (void)session:(GKSession *)session didReceiveConnectionRequestFromPeer:(NSString *)peerID {
NSLog(@"cnxn request from peer %@, dawg. gonna accept it.", [session displayNameForPeer:peerID]);
NSError *err;
if (![session acceptConnectionFromPeer:peerID error:&err]) {
NSLog(@"ERROR: %@", [err localizedDescription]);
}
}
- (void)session:(GKSession *)session connectionWithPeerFailed:(NSString *)peerID withError:(NSError *)error {
NSLog(@"CONNECTION FAILED with error: %@", error);
}
- (void)session:(GKSession *)session didFailWithError:(NSError *)error {
NSLog(@"SESSION FAILED with error: %@", error);
}
- (void) receiveData:(NSData *)data fromPeer:(NSString *)peer inSession: (GKSession *)session context:(void *)context {
//unserialize data from peer
NSMutableDictionary *dict = [[[NSKeyedUnarchiver unarchiveObjectWithData:data] retain] autorelease];
//NSLog(@"got some data, yo. it's: %@", [dict description]);
NSString *eventName = [dict objectForKey:@"event_name"];
if (eventName == nil) {
NSLog(@"GOT MESSAGE I DON'T UNDERSTAND!!!!");
NSLog(@"%@", [dict description]);
return;
}
NSNumber *time_obj = (NSNumber*)[dict objectForKey:@"time_sent"];
if (time_obj == nil) {
NSLog(@"time not attached... problem?");
}
id<ASDataSyncee> syncee = [self handlerForEvent:eventName];
if(syncee == nil) {
NSLog(@"event: %@ has no handler!", eventName);
return;
}
NSTimeInterval send_time = [time_obj doubleValue];
//let syncee take care of the rest
[syncee receiveData:dict fromTime:send_time];
}
@end