-
Notifications
You must be signed in to change notification settings - Fork 0
/
client.py
401 lines (330 loc) · 13 KB
/
client.py
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
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
from events import *
from model import *
from eventmanager import EventManager
from sjbeasts import PygameMasterController, CPUSpinnerController, PygameMasterView
from network import *
import network
from twisted.spread import pb
#------------------------------------------------------------------------------
class NetworkServerView(pb.Root):
"""We SEND events to the server through this object"""
STATE_PREPARING = 0
STATE_CONNECTING = 1
STATE_CONNECTED = 2
#----------------------------------------------------------------------
def __init__(self, evManager, sharedObjectRegistry, clientController):
self.evManager = evManager
self.evManager.RegisterListener( self )
self.state = NetworkServerView.STATE_PREPARING
self.server = None
self.sharedObjs = sharedObjectRegistry
self.clientController = clientController
#----------------------------------------------------------------------
def Connected(self, server):
print "CONNECTED"
self.server = server
self.state = NetworkServerView.STATE_CONNECTED
ev = ServerConnectEvent( server )
self.evManager.Post( ev )
#----------------------------------------------------------------------
def ConnectFailed(self, server):
self.state = NetworkServerView.STATE_PREPARING
print "CONNECTION FAILED"
#----------------------------------------------------------------------
def Notify(self, event):
ev = event
#if isinstance( event, FlipCardRequest):
#print "888888888888888888888888"
if isinstance( event, TickEvent ) \
and self.state == NetworkServerView.STATE_PREPARING:
self.state = NetworkServerView.STATE_CONNECTING
#remoteResponse = pb.getObjectAt("localhost", 8000, 30)
remoteResponse = pb.connect("localhost", 8000,
"user1", "asdf",
"myService", "perspective1",
timeout=30)
remoteResponse.addCallback(self.Connected )
#errback #self.ConnectFailed)
#see if it's Copyable. If not, see if there's a Copyable
#replacement for it. If not, just ignore it.
if not isinstance( event, pb.Copyable):
evName = event.__class__.__name__
if not hasattr( network, "Copyable" + evName ):
return
copyableClass = getattr( network, "Copyable" + evName )
if copyableClass not in clientToServerEvents:
return
ev = copyableClass( event, self.sharedObjs )
elif ev.__class__ not in clientToServerEvents:
print "CLIENT NOT SENDING: " +str(ev)
return
print "<== Sending to server:", ev.name
remoteCall = self.server.callRemote("ClientEvent",ev,
self.clientController)
#------------------------------------------------------------------------------
class NetworkServerController(pb.Referenceable):
"""We RECEIVE events from the server through this object"""
def __init__(self, evManager, twistedReactor):
self.evManager = evManager
self.evManager.RegisterListener( self )
self.reactor = twistedReactor
#we need to keep the events in order, so we track the active
#event, and keep an event Queue.
self.activeEventClass = None
self.eventQueue = []
#----------------------------------------------------------------------
def remote_ServerEvent(self, event):
#print "=================GOT AN EVENT FROM SERVER:", str(event)
self.eventQueue.append( event )
if self.activeEventClass is None:
self.DequeueNextEvent()
return 1
#----------------------------------------------------------------------
def DequeueNextEvent( self ):
if not self.eventQueue:
return
if self.activeEventClass is not None:
print "WEIRD. activeEventClass is not None"
event = self.eventQueue.pop(0)
evClass = None
import events
#does the events module have an event with the same name
if hasattr( events, event.name ):
evClass = getattr( events, event.name )
#does the events module have an event named the same,
#except without 'Copyable' at the front?
evName = event.__class__.__name__.replace('Copyable','',1)
print "watching for", evName
if hasattr(events, evName):
evClass = getattr( events, evName )
if evClass is None:
print "Couldn't get event class to watch for"
self.activeEventClass = evClass
self.evManager.Post( event )
#----------------------------------------------------------------------
def remote_Ping(self):
pong = 1
return pong
#----------------------------------------------------------------------
def Notify(self, event):
if isinstance( event, ServerConnectEvent ):
print "DO I GET IN HERE?"
#tell the server that we're listening to it and
#it can access this object
remoteResponse = event.server.callRemote(
"ClientConnect",
self )
if isinstance( event, TickEvent ):
#print "PUMPING NETWORK"
self.reactor.iterate()
if self.activeEventClass \
and isinstance( event, self.activeEventClass ):
self.activeEventClass = None
if self.eventQueue:
self.DequeueNextEvent()
#------------------------------------------------------------------------------
class PhonyEventManager(EventManager):
"""this object is responsible for coordinating most communication
between the Model, View, and Controller."""
#----------------------------------------------------------------------
def Notify( self, event ):
print "PHONY gets", event
pass
def Debug(self, ev):
#return
if isinstance( ev, GUIFocusThisWidgetEvent ):
return
if not isinstance( ev, GUIMouseMoveEvent ):
#print "self", self
print " Phony Message: " + ev.name
#------------------------------------------------------------------------------
class PhonyModel:
"""..."""
#----------------------------------------------------------------------
def __init__(self, evManager, sharedObjectRegistry):
self.sharedObjs = sharedObjectRegistry
self.game = None
self.server = None
self.phonyEvManager = PhonyEventManager()
self.realEvManager = evManager
print "REAL", self.realEvManager
print "FAKE", self.phonyEvManager
self.neededObjects = []
self.waitingObjectStack = []
self.realEvManager.RegisterListener( self )
#----------------------------------------------------------------------
def GameReturned(self, response):
if response[0] == 0:
print "GameReturned : game HASNT started"
#the game has not been started on the server.
#we'll be informed of the gameID when we receive the
#GameStartedEvent
return None
else:
gameID = response[0]
print "GameReturned : game started ", gameID
self.sharedObjs[gameID] = self.game
return self.ObjStateReturned( response, self.GameSyncCallback )
#----------------------------------------------------------------------
def ReplacePlaceholder( self, obj, objID ):
"""Call this when setCopyableState returns successfully"""
#if it was just a Placeholder object, get the real
#object and change the registry accordingly
if isinstance( obj, Placeholder ):
obj = obj.ChangeToFullObject()
if obj is None:
print "This should never happen"
raise Exception("Placeholder finish")
#import gc
#refs = gc.get_referrers( self.sharedObjs[objID] )
#if refs:
#print "about to replace placeholder",
#print self.sharedObjs[objID], ", but"
#print "these objects still refer to it"
#from pprint import pprint
#pprint( refs )
#self.sharedObjs[objID] = obj
#TODO: what about all the other objects that used to
# point to self.sharedObjs[objID]? shouldn't their
# references be replaced?
#----------------------------------------------------------------------
def ObjStateReturned(self, response, nextFn=None):
"""this is a callback that is called in response to
invoking GetObjectState on the server"""
"""This will recursively grab all objects needed by eventually
calling GetAllNeededObjects"""
objID = response[0]
objDict = response[1]
print "looking for ", response
if response[1] == 0:
print "GOT ZERO -- better error handler here"
print "Failed on key", objID
print "corresponds to obj:", self.sharedObjs[objID]
return None
obj = self.sharedObjs[objID]
if obj is None:
print objID
print self.sharedObjs
retval = obj.setCopyableState(objDict, self.sharedObjs)
if retval[0] == 1:
#we successfully set the state and no further objects
#are needed to complete the current object
if objID in self.neededObjects:
self.neededObjects.remove(objID)
#self.ReplacePlaceholder( obj, objID )
else:
#to complete the current object, we need to grab the
#state from some more objects on the server. The IDs
#for those needed objects were passed back in retval[1]
for neededObjID in retval[1]:
if neededObjID not in self.neededObjects:
self.neededObjects.append(neededObjID)
print "failed. still need ", self.neededObjects
#First check to see if this object is already in the
#waitingObjectStack, if not, append it.
#(this is a List Comprehension, might be unfamiliar to newbies)
if objID not in [item[1] for item in self.waitingObjectStack]:
self.waitingObjectStack.append(
(obj, objID, objDict, nextFn)
)
self.GetAllNeededObjects()
#----------------------------------------------------------------------
def GetAllNeededObjects(self):
if len(self.neededObjects) == 0:
#this is the recursion-ending condition. If there are
#no more objects needed to be grabbed from the server
#then we can try to setCopyableState on them again and
#we should now have all the needed objects, ensuring
#that setCopyableState succeeds
print "Got all objs. realize. Waiting Objs:"
print self.waitingObjectStack, "\n--***---"
while self.waitingObjectStack:
t = self.waitingObjectStack.pop()
obj = t[0]
objID = t[1]
objDict = t[2]
fn = t[3]
print "popped", obj.__class__.__name__
retval = obj.setCopyableState(objDict, self.sharedObjs)
if retval[0] == 0:
print "WEIRD!!!!!!!!!!!!!!!!!!",
print obj.__class__, "returned 0"
elif retval[0] == 1:
self.ReplacePlaceholder( obj, objID )
print "fn is ", fn
if fn:
fn( obj )
#from pprint import pprint
#pprint( self.sharedObjs )
return
#still in the recursion step. Try to get the object state for
#the objectID on the end of the stack. Note that the recursion
#is done via a deferred, which may be confusing
nextID = self.neededObjects[len(self.neededObjects)-1]
print "--"
print "grabbing from server: ", nextID
remoteResponse= self.server.callRemote("GetObjectState", nextID)
remoteResponse.addCallback(self.ObjStateReturned)
#----------------------------------------------------------------------
def Notify(self, event):
if isinstance( event, ServerConnectEvent ):
self.server = event.server
#when we connect to the server, we should get the
#entire game state. this also applies to RE-connecting
if not self.game:
self.game = Game( self.phonyEvManager )
remoteResponse = self.server.callRemote("GetGame")
remoteResponse.addCallback(self.GameReturned)
elif isinstance( event, CopyableGameStartedEvent ):
gameID = event.gameID
if not self.game:
self.game = Game( self.phonyEvManager )
self.sharedObjs[gameID] = self.game
ev = GameStartedEvent( self.game )
self.realEvManager.Post( ev )
elif hasattr( event, 'ReformatLocalToClient' ):
print "==> Got from server:", event.name
ev = event.ReformatLocalToClient( self.sharedObjs )
if ev:
self.realEvManager.Post( ev )
else:
event.ClientFetchNeededRemoteObjects( self )
#=== Here is some stuff to get it working for now
#=== TODO: make these work for real
if isinstance( event, PlayerExploreRequest ):
self.phonyEvManager.Post( event )
#----------------------------------------------------------------------
def ReformattingCallback( self, ev ):
self.realEvManager.Post( ev )
#----------------------------------------------------------------------
def GameSyncCallback(self, game):
print "sending out the GS EVENT------------------==========="
ev = GameSyncEvent( game )
self.realEvManager.Post( ev )
#------------------------------------------------------------------------------
def main():
"""..."""
evManager = EventManager()
sharedObjectRegistry = {}
#import random
#rng = random.Random()
#clientID = rng.randrange( 1, 10000 )
#playerName = str( rng.randrange(1,100) )
#player = Player( evManager )
from sjbeasts import AnimationTimerController
keybd = PygameMasterController( evManager )
spinner = CPUSpinnerController( evManager )
animationSpinner = AnimationTimerController( evManager )
pygameView = PygameMasterView( evManager )
phonyModel = PhonyModel( evManager, sharedObjectRegistry )
import gui
gui.playerRef = gui.GlobalPlayer( evManager )
print gui.playerRef
#from twisted.spread.jelly import globalSecurity
#globalSecurity.allowModules( network )
from twisted.internet import reactor
serverController = NetworkServerController( evManager, reactor )
serverView = NetworkServerView( evManager, sharedObjectRegistry, serverController )
spinner.Run()
if __name__ == "__main__":
main()