home *** CD-ROM | disk | FTP | other *** search
-
- /*
- * VoteTally.m Written by Joe Freeman 5/27/92
- *
- * Note:
- * You can talk back to a remote object, if it gives you
- * its id. But, you cannot, while responding to a message that came
- * remote, send a message to a remote server in the same process. That
- * be blocked out by the conversation that you are responding to.
- *
- * You can talk to the NXConnection object in the client, that
- * was created when connecting to the server, if the client has
- * done a runFromAppkit using that connection object
- *
- * Also,
- * clientList = nil if we are not the server
- *
- */
- #import "VoteTally.h"
-
- @implementation VoteTally
-
- #define numAttendees ([clientList count] )
- - appDidInit:sender
- {
-
- /* You should really give it the name of the machine you want it to
- * connect to. The "*" will cause the server to look at every machine
- * on the subnet. That could take a LONG time.
- */
- remoteHub = [NXConnection connectToName:"VoteTally" onHost:"*"];
- if (remoteHub){
- [clearAllMenu setEnabled:NO];
-
- /* if someone is already the hub, then we will be the client
- * we find the NXConnection object that is handling the Proxy
- * that represents the vote server. We then register this object
- * to handle errors for that server
- *
- * the client understands more than this protocol
- * but these are used most so lets tell it about the them.
- */
- [[scrollers window] setTitle: "Voting Client"];
- ourServer = [remoteHub connectionForProxy];
- [remoteHub setProtocolForProxy:@protocol(VoteTallyServerMethods)];
-
- } else {
- /* The fact that no one is registered, means that we are going to
- * be the server. So, we register our connection
- *
- * The server understands the client protocol also
- * but these are the most common messages
- */
- clientList = [[List alloc] init];
- [[scrollers window] setTitle: "Voting Server"];
- ourServer = [NXConnection registerRoot: self withName:"VoteTally"];
-
- /* add ourselves as a psuedo client */
- remoteHub = self;
- }
-
- /*
- * we take ourServer, which represets the NXConnection objec that
- * will handle all our proxys (?) and make ourselves the handler for
- * errors for that object. Then we run from appkit.
- *
- * The case for the voting server makes sense. We need to run from
- * appkit so that we can server all the clients.
- *
- * The case for the clients is not as clear. We register the
- * NXConnection that is behind our connection to the vote server
- * so that the server, which will have our ID will be able to talk
- * back without the client having initiated the connection. This is
- * NOT OBVIOUS from the PR1 docs
- */
- [ourServer registerForInvalidationNotification:self];
- [ourServer runFromAppKit];
- [remoteHub addClient:self];
-
- return self;
- }
-
- - appWillTerminate:sender
- {
- [remoteHub removeClient:self];
- return self;
- }
-
- - free
- {
- [super free];
- [ourServer free];
- return self;
- }
-
- /* ============================================================
- * who is part of this conference anyway?
- *
- * All three of these messages may cause traffic to ALL the
- * clients which means the NXConnection objects on those
- * need to be free. So, we declare these oneway to release
- *
- * ============================================================ */
-
- - addClient:sender
- {
- id connectionToClient;
-
- if (sender != self){
- /* do some error trapping and optimizaitons */
-
- /* figure out who is behind this new client and get their errors */
- connectionToClient = [sender connectionForProxy];
- [connectionToClient registerForInvalidationNotification:self];
-
- /* make sure this new object is smart about the client messages */
- [sender setProtocolForProxy:@protocol(VoteTallyClientMethods)];
- }
-
- /* do some bookeeping */
- [clientList addObjectIfAbsent:sender];
- [self updateStats];
- return self;
- }
-
- - removeClient:anObject
- {
- [clientList removeObject:anObject];
- if ( anObject != self ){
- /* the server should rebuild the stats */
- [self updateStats];
- }
- return self;
- }
-
- /* an updateStats:message means two different things, depending on server
- * Server rebuilds the stats and then tells all clients to get stats
- * Client gets all the stats from the server
- */
- #define NUMSLIDERS 6
-
- - (void) updateStats
- {
- int last = numAttendees;
- int i,s;
- float sliders[NUMSLIDERS];
- float tmp;
- int iTmp;
-
- /// fprintf(stderr,"%d updateStats with remote hub = %d\n", self, remoteHub);
-
- [remoteHub getNumAttend:&i];
- [numVoting setIntValue: i];
-
- if (remoteHub != self){
- for ( s = 0 ; s < NUMSLIDERS; s++){
- [remoteHub getMean:&tmp at:s];
- [[means findCellWithTag:s] setFloatValue:tmp];
- }
-
- } else {
- /* build new stats */
- for ( s = 0 ; s < NUMSLIDERS; s++){
- sliders[s] = 0.0;
- for ( i = 0 ; i < last; i++){
- [[clientList objectAt:i] getValue:&iTmp at:s];
- sliders[s]+= iTmp;
- }
- sliders[s] = sliders[s]/numAttendees;
- [[means findCellWithTag:s] setFloatValue:sliders[s]];
- }
-
- /// fprintf(stderr,"%d stats rebuilt \n", self);
- /* tell all the clients to update their displays from us */
- for ( i = 0 ; i < last ; i++){
- if ([clientList objectAt:i] != self)
- [[clientList objectAt:i] updateStats];
- }
- }
- }
-
-
- /* ============================================================
- *
- * ============================================================ */
-
- /*
- * only the hub is allowed to restore everyone's score
- */
- - resetAll:sender
- {
- [clientList makeObjectsPerform:@selector(resetLocal:) with:self];
- [self updateStats];
- return self;
- }
-
- /* reset is the same as moving all sliders. So, we should notify hub
- * if the hub sent us this message, then it already knows
- */
- - resetLocal:sender
- {
- int i,numRows,numCols;
- [scrollers getNumRows:&numRows numCols:&numCols];
- for ( i = 0 ; i < numCols; i++){
- [[scrollers findCellWithTag:i] setIntValue:50];
- }
- if (sender != remoteHub)
- [remoteHub updateStats];
- return self;
- }
-
- /* a slider has moved... have the hub query everyone for new stats */
- - takeVoteFrom:sender
- {
- [remoteHub updateStats];
- return self;
- }
-
- /* ============================================================
- *
- * ============================================================ */
-
- - getNumAttend:(int *)anInt
- {
- *anInt = numAttendees;
- return self;
- }
-
- - getValue:(int *)aVal at:(int)index
- {
- *aVal = [[scrollers findCellWithTag:index] intValue];
- return self;
- }
-
- - getMean:(float *)aFloat at:(int)index
- {
- *aFloat = [[means findCellWithTag:index] floatValue];
- return self;
- }
-
- /* ============================================================
- *
- * part of the registerForInvalidationNotification stuff
- *
- *
- * ============================================================ */
-
- - senderIsInvalid:sender
- {
- if (self != remoteHub){
- /* server is dead, so lets kill ourselves */
- remoteHub = nil;
- [NXApp terminate:self];
- } else {
- /* the server ran into a dead client */
- [self removeClient:sender];
- }
- return self;
- }
-
- @end
-