|
|
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.