def __init__(self, serializer, database, authenticator, grids): # Aliases for convenience self.serializer = serializer self.database = database self.authenticator = authenticator self.grids = grids self.nat = NATClientService(self) # This is a reference to the grid object this protocol belongs to self.grid = None self.user = None
class GTGProtocol(basic.LineReceiver): """ Stateful communication with clients through a one-line-per-request serialization format. Any serialization can be used, default implementation is JSON. One of these is created for each client connection. """ def __init__(self, serializer, database, authenticator, grids): # Aliases for convenience self.serializer = serializer self.database = database self.authenticator = authenticator self.grids = grids self.nat = NATClientService(self) # This is a reference to the grid object this protocol belongs to self.grid = None self.user = None def lineReceived(self, line): try: #TODO: Perhaps in the future we should make (de)serialization operations asynchronous, # not sure if this is worth the effort/overhead or not. # The same can be said for all database lookups. request = self.serializer.deserialize(line) if PRINT_PACKETS: log.msg("IN : %s | %s" % (request.__class__.__name__, line)) if not self.user: if isinstance(request, LoginRequest): response, userAccount = self.authenticator.authenticateUser(request) response.externalhost = self.transport.getPeer().host self.writeResponse(response) if isinstance(response, LoginSuccess): log.msg("%s %s has logged in to grid %s." % (request.firstName, request.lastName, request.grid)) # Load this user's grid if we haven't already if not request.grid in self.grids: log.msg("Loading grid %s from database..." % request.grid) self.grids[request.grid] = Grid(request.grid, self.database.getGridUsers(request.grid), self.database.getGridRegions(request.grid)) self.grid = self.grids[request.grid] # Join the user to this grid if they are not a member #TODO: Implement "restricted" grids that have an access list #log.msg("SET USER 1") self.user = self.grid.users.get(userAccount.UUID) if not self.user: log.msg("Joining user to grid %s..." % self.grid.name) # Create a new user. If first user, give mod and host. #log.msg("SET USER 2") self.user = User(userAccount.UUID, userAccount.firstName, userAccount.lastName, True, False, (len(self.grid.users) < 1), (len(self.grid.users) < 1), False) self.grid.users[self.user.UUID] = self.user self.database.storeGridAssociation(self.user, request.grid) # broadcast this new user to connected clients self.grid.writeResponseToAll(self.user) # Duplicate instance checking #TODO: Kick old user off with a message instead of this hacky refusal if self.grid.protocols.get(self.user.UUID): self.transport.write('No multiple instances! >:O') self.transport.loseConnection() # send the client all the User objects in the grid for id in self.grid.users: self.writeResponse(self.grid.users[id]) # send the client all the Region objects in the grid for n in self.grid.regions: self.writeResponse(self.grid.regions[n]) # register this user's connection in our list self.grid.protocols[self.user.UUID] = self # mark this user online with a delta object delta = DeltaUser(self.user.UUID) delta.online = True self.grid.applyUserDelta(delta) elif isinstance(request, ResetPasswordRequest): response = self.authenticator.resetPassword(request) self.writeResponse(response) elif isinstance(request, CreateUserRequest): response = self.authenticator.createUser(request) self.writeResponse(response) else: # User is authenticated. if isinstance(request, DeltaUser): delta = None # moderators can change anything if self.user.moderator: delta = request # Users talking about themselves may change certain attributes elif request.UUID == self.user.UUID: delta = DeltaUser(request.UUID) # Online Status if hasattr(request, 'online'): delta.online = request.online # Surrendering GridHost if hasattr(request, 'gridHost') and self.user.gridHost: delta.gridHost = request.gridHost # gridHostActive if hasattr(request, 'gridHostActive') and self.user.gridHost: delta.gridHostActive = request.gridHostActive else: # This user has no permission return if delta: # Apply server-side delta self.grid.applyUserDelta(delta) # Replicate changes self.grid.writeResponseToAll(delta) # Save to database if necessary if hasattr(delta, 'gridHost') or hasattr(delta, 'moderator'): self.database.storeGridAssociation(delta, self.grid.name) elif isinstance(request, CreateRegionRequest): log.msg("Creating new region on grid %s: %s" % (request.gridName, request.regionName)) self.database.createRegion(request.gridName, request.regionName, request.location, request.uuid) region = Region(request.regionName, request.location, None, [self.user.UUID]) self.grid.regions[region.regionName] = region self.grid.writeResponseToAll(region) elif isinstance(request, NATCheckRequest): log.msg("[NAT] Received NATCheckRequest, checking client NAT") self.nat.run(request.ports, request.processports) except serialization.InvalidSerializedDataException: self.transport.write("Stop sending me bad data! >:|\r\n") self.transport.loseConnection() def connectionLost(self, reason): if self.user: if self.grid.protocols.get(self.user.UUID): del self.grid.protocols[self.user.UUID] delta = DeltaUser(self.user.UUID) delta.online = False delta.NATStatus = False delta.gridHostActive = False self.grid.applyUserDelta(delta) print("%s %s has disconnected." % (self.user.firstName, self.user.lastName)) def writeResponse(self, response): line = self.serializer.serialize(response) if PRINT_PACKETS: log.msg("OUT: %s | %s" % (response.__class__.__name__, line)) self.transport.write(line + "\r\n")