|
|
1.1 ! root 1: ! 2: /* ! 3: * VoteTally.m Written by Joe Freeman 5/27/92 ! 4: * ! 5: * Note: ! 6: * You can talk back to a remote object, if it gives you ! 7: * its id. But, you cannot, while responding to a message that came ! 8: * remote, send a message to a remote server in the same process. That ! 9: * be blocked out by the conversation that you are responding to. ! 10: * ! 11: * You can talk to the NXConnection object in the client, that ! 12: * was created when connecting to the server, if the client has ! 13: * done a runFromAppkit using that connection object ! 14: * ! 15: * Also, ! 16: * clientList = nil if we are not the server ! 17: * ! 18: */ ! 19: #import "VoteTally.h" ! 20: ! 21: @implementation VoteTally ! 22: ! 23: #define numAttendees ([clientList count] ) ! 24: - appDidInit:sender ! 25: { ! 26: ! 27: /* You should really give it the name of the machine you want it to ! 28: * connect to. The "*" will cause the server to look at every machine ! 29: * on the subnet. That could take a LONG time. ! 30: */ ! 31: remoteHub = [NXConnection connectToName:"VoteTally" onHost:"*"]; ! 32: if (remoteHub){ ! 33: [clearAllMenu setEnabled:NO]; ! 34: ! 35: /* if someone is already the hub, then we will be the client ! 36: * we find the NXConnection object that is handling the Proxy ! 37: * that represents the vote server. We then register this object ! 38: * to handle errors for that server ! 39: * ! 40: * the client understands more than this protocol ! 41: * but these are used most so lets tell it about the them. ! 42: */ ! 43: [[scrollers window] setTitle: "Voting Client"]; ! 44: ourServer = [remoteHub connectionForProxy]; ! 45: [remoteHub setProtocolForProxy:@protocol(VoteTallyServerMethods)]; ! 46: ! 47: } else { ! 48: /* The fact that no one is registered, means that we are going to ! 49: * be the server. So, we register our connection ! 50: * ! 51: * The server understands the client protocol also ! 52: * but these are the most common messages ! 53: */ ! 54: clientList = [[List alloc] init]; ! 55: [[scrollers window] setTitle: "Voting Server"]; ! 56: ourServer = [NXConnection registerRoot: self withName:"VoteTally"]; ! 57: ! 58: /* add ourselves as a psuedo client */ ! 59: remoteHub = self; ! 60: } ! 61: ! 62: /* ! 63: * we take ourServer, which represets the NXConnection objec that ! 64: * will handle all our proxys (?) and make ourselves the handler for ! 65: * errors for that object. Then we run from appkit. ! 66: * ! 67: * The case for the voting server makes sense. We need to run from ! 68: * appkit so that we can server all the clients. ! 69: * ! 70: * The case for the clients is not as clear. We register the ! 71: * NXConnection that is behind our connection to the vote server ! 72: * so that the server, which will have our ID will be able to talk ! 73: * back without the client having initiated the connection. This is ! 74: * NOT OBVIOUS from the PR1 docs ! 75: */ ! 76: [ourServer registerForInvalidationNotification:self]; ! 77: [ourServer runFromAppKit]; ! 78: [remoteHub addClient:self]; ! 79: ! 80: return self; ! 81: } ! 82: ! 83: - appWillTerminate:sender ! 84: { ! 85: [remoteHub removeClient:self]; ! 86: return self; ! 87: } ! 88: ! 89: - free ! 90: { ! 91: [super free]; ! 92: [ourServer free]; ! 93: return self; ! 94: } ! 95: ! 96: /* ============================================================ ! 97: * who is part of this conference anyway? ! 98: * ! 99: * All three of these messages may cause traffic to ALL the ! 100: * clients which means the NXConnection objects on those ! 101: * need to be free. So, we declare these oneway to release ! 102: * ! 103: * ============================================================ */ ! 104: ! 105: - addClient:sender ! 106: { ! 107: id connectionToClient; ! 108: ! 109: if (sender != self){ ! 110: /* do some error trapping and optimizaitons */ ! 111: ! 112: /* figure out who is behind this new client and get their errors */ ! 113: connectionToClient = [sender connectionForProxy]; ! 114: [connectionToClient registerForInvalidationNotification:self]; ! 115: ! 116: /* make sure this new object is smart about the client messages */ ! 117: [sender setProtocolForProxy:@protocol(VoteTallyClientMethods)]; ! 118: } ! 119: ! 120: /* do some bookeeping */ ! 121: [clientList addObjectIfAbsent:sender]; ! 122: [self updateStats]; ! 123: return self; ! 124: } ! 125: ! 126: - removeClient:anObject ! 127: { ! 128: [clientList removeObject:anObject]; ! 129: if ( anObject != self ){ ! 130: /* the server should rebuild the stats */ ! 131: [self updateStats]; ! 132: } ! 133: return self; ! 134: } ! 135: ! 136: /* an updateStats:message means two different things, depending on server ! 137: * Server rebuilds the stats and then tells all clients to get stats ! 138: * Client gets all the stats from the server ! 139: */ ! 140: #define NUMSLIDERS 6 ! 141: ! 142: - (void) updateStats ! 143: { ! 144: int last = numAttendees; ! 145: int i,s; ! 146: float sliders[NUMSLIDERS]; ! 147: float tmp; ! 148: int iTmp; ! 149: ! 150: /// fprintf(stderr,"%d updateStats with remote hub = %d\n", self, remoteHub); ! 151: ! 152: [remoteHub getNumAttend:&i]; ! 153: [numVoting setIntValue: i]; ! 154: ! 155: if (remoteHub != self){ ! 156: for ( s = 0 ; s < NUMSLIDERS; s++){ ! 157: [remoteHub getMean:&tmp at:s]; ! 158: [[means findCellWithTag:s] setFloatValue:tmp]; ! 159: } ! 160: ! 161: } else { ! 162: /* build new stats */ ! 163: for ( s = 0 ; s < NUMSLIDERS; s++){ ! 164: sliders[s] = 0.0; ! 165: for ( i = 0 ; i < last; i++){ ! 166: [[clientList objectAt:i] getValue:&iTmp at:s]; ! 167: sliders[s]+= iTmp; ! 168: } ! 169: sliders[s] = sliders[s]/numAttendees; ! 170: [[means findCellWithTag:s] setFloatValue:sliders[s]]; ! 171: } ! 172: ! 173: /// fprintf(stderr,"%d stats rebuilt \n", self); ! 174: /* tell all the clients to update their displays from us */ ! 175: for ( i = 0 ; i < last ; i++){ ! 176: if ([clientList objectAt:i] != self) ! 177: [[clientList objectAt:i] updateStats]; ! 178: } ! 179: } ! 180: } ! 181: ! 182: ! 183: /* ============================================================ ! 184: * ! 185: * ============================================================ */ ! 186: ! 187: /* ! 188: * only the hub is allowed to restore everyone's score ! 189: */ ! 190: - resetAll:sender ! 191: { ! 192: [clientList makeObjectsPerform:@selector(resetLocal:) with:self]; ! 193: [self updateStats]; ! 194: return self; ! 195: } ! 196: ! 197: /* reset is the same as moving all sliders. So, we should notify hub ! 198: * if the hub sent us this message, then it already knows ! 199: */ ! 200: - resetLocal:sender ! 201: { ! 202: int i,numRows,numCols; ! 203: [scrollers getNumRows:&numRows numCols:&numCols]; ! 204: for ( i = 0 ; i < numCols; i++){ ! 205: [[scrollers findCellWithTag:i] setIntValue:50]; ! 206: } ! 207: if (sender != remoteHub) ! 208: [remoteHub updateStats]; ! 209: return self; ! 210: } ! 211: ! 212: /* a slider has moved... have the hub query everyone for new stats */ ! 213: - takeVoteFrom:sender ! 214: { ! 215: [remoteHub updateStats]; ! 216: return self; ! 217: } ! 218: ! 219: /* ============================================================ ! 220: * ! 221: * ============================================================ */ ! 222: ! 223: - getNumAttend:(int *)anInt ! 224: { ! 225: *anInt = numAttendees; ! 226: return self; ! 227: } ! 228: ! 229: - getValue:(int *)aVal at:(int)index ! 230: { ! 231: *aVal = [[scrollers findCellWithTag:index] intValue]; ! 232: return self; ! 233: } ! 234: ! 235: - getMean:(float *)aFloat at:(int)index ! 236: { ! 237: *aFloat = [[means findCellWithTag:index] floatValue]; ! 238: return self; ! 239: } ! 240: ! 241: /* ============================================================ ! 242: * ! 243: * part of the registerForInvalidationNotification stuff ! 244: * ! 245: * ! 246: * ============================================================ */ ! 247: ! 248: - senderIsInvalid:sender ! 249: { ! 250: if (self != remoteHub){ ! 251: /* server is dead, so lets kill ourselves */ ! 252: remoteHub = nil; ! 253: [NXApp terminate:self]; ! 254: } else { ! 255: /* the server ran into a dead client */ ! 256: [self removeClient:sender]; ! 257: } ! 258: return self; ! 259: } ! 260: ! 261: @end
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.