1
from twisted.internet import protocol, reactor, error
2
from twisted.protocols.basic import NetstringReceiver
3
from pyscrabble.command import helper
4
from pyscrabble.game.player import Player, User,PlayerInfo
5
from pyscrabble.game.game import ScrabbleGame, ScrabbleGameInfo
6
from pyscrabble.lookup import *
7
from pyscrabble.game import rank
8
from pyscrabble import audit
9
from pyscrabble import constants
10
from pyscrabble import db
11
from pyscrabble import exceptions
12
from pyscrabble import manager
13
from pyscrabble import serialize
14
from pyscrabble import util
25
from sets import Set as set
27
logger = logging.getLogger("pyscrabble.net.server")
32
class ScrabbleServerFactory(protocol.ServerFactory, object):
34
ScrabbleServerFactory controls all the Games on the server and the Server Protocols for each
35
client that is connected
42
resources = manager.ResourceManager()
47
self.maxUsersLoggedIn = 0
48
self.startDate = util.Time(seconds=time.time(), dispDate=True)
49
self.rankings = rank.Rankings( resources["config"][constants.RANK_CONFIG] )
51
dir = resources["resources"][constants.DICT_DIR].path
52
for lang in os.listdir( dir ):
53
if not lang.islower(): continue # Avoids CVS directories
54
self.dicts[lang] = set()
55
for file in os.listdir( os.path.join(dir, lang) ):
56
if not file.islower(): continue # Avoids CVS directories
57
path = os.path.join(dir, lang, file)
59
f = codecs.open(path, encoding='utf-8', mode='rb')
60
lines = f.read().split()
65
l.append( line.upper() )
68
self.dicts[lang] = self.dicts[lang].union(x)
70
for game in self.db.games.values():
71
self.gameList[ game.getGameId() ] = game
74
def removeClient(self, client):
81
if self.clients.has_key(client):
82
del self.clients[client]
88
for username in self.db.users.keys():
89
self.db.users[username].setRank( 0 )
90
self.db.users[username].rankName = self.rankings.getMinRank().name
92
def auditUser(self, username, action, sync=True):
94
Add an audit action for a user
101
u = util.getUnicode(username)
102
self.db.users[u].addAuditAction( action )
108
def getServerBulletins(self):
110
Return list of ServerBulletins
112
if not self.db.messages.has_key(constants.SERVER_MESSAGE_KEY):
115
return self.db.messages[constants.SERVER_MESSAGE_KEY]
118
def addServerBulletin(self, message):
122
@param message: Bulletin message
125
if not self.db.messages.has_key(constants.SERVER_MESSAGE_KEY):
128
l = self.db.messages[constants.SERVER_MESSAGE_KEY]
130
b = self.createServerInfoMessage(message)
131
l.append( util.ServerBulletin(data=b, id=util.getRandomId(), seconds=time.time()) )
133
self.db.messages[constants.SERVER_MESSAGE_KEY] = l
136
for c in self.clients:
137
c.postChatMessage( (b, True) )
139
def deleteServerBulletin(self, id):
141
Delete Server bulletin
143
@param id: Bulletin ID
145
l = self.db.messages[constants.SERVER_MESSAGE_KEY]
147
l = [ m for m in l if m.id != key ]
148
self.db.messages[constants.SERVER_MESSAGE_KEY] = l
151
def isLoggedIn(self, player):
153
Check if a user was logged in
155
@param player: Player
158
for client,_player in self.clients.iteritems():
159
if player.getUsername() == _player.getUsername():
164
def getLoggedInPlayers(self):
166
Retrieve listing of players logged in
168
@return: Formatted string of players logged in
171
for player in self.clients.values():
172
str = str + player.getUsername() + ' '
177
def bootUser(self, username):
179
Boot a user from the server
181
@param username: Username
183
p = Player( username )
184
c = self.getPlayerClient(p)
187
for game in self.gameList.itervalues():
188
if game.hasPlayer(p):
189
self.leaveGame(game.getGameId(), c, penalize=True, booted=True)
190
c.gameBoot(game.getGameId())
191
if game.hasSpectator( player ):
192
cmd = helper.GameCommand(constants.GAME_LEAVE, game.getGameId(), '')
193
self.spectatorLeaveGame(cmd, client)
198
def removeUser(self, username):
200
Remove a user from the server.
202
If the user is logged in, send a booted command
204
@param username: Username
206
self.bootUser(username)
208
del self.db.users[util.getUnicode(username)]
212
def updateUser(self, user):
219
self.db.users[ user.getUsername() ] = user
221
def doChangePassword(self, username, newPassword):
223
Change a users password
229
user = self.db.users[util.getUnicode(username)]
230
user.setPassword(newPassword)
237
@return: Dictionary of users
239
x = self.db.users.values()[:]
240
x.sort( lambda x,y : cmp(x.lastLogin, y.lastLogin) )
243
def getUser(self, username):
247
@param username: Username
248
@return: User obect or not
250
u = util.getUnicode(username)
251
if self.db.users.has_key(u):
252
return self.db.users[ u ]
256
def stopFactory(self):
258
Callback when the factory is stopping. Close the users file
262
def buildProtocol(self, addr):
264
Build a Server Protocol instance for each connection
266
@param addr: Incoming address
277
Check to see if the server has users
279
@return: True if there are users defined on the server
281
return len(self.db.users) != 0
284
def isUserAdmin(self, username):
286
Check to see if a user is administrator
288
@param username: Username
289
@return: True if the user is an Administrator
291
u = util.getUnicode(username)
292
if not self.db.users.has_key(u):
295
user = self.db.users[u]
296
return user.isAdmin()
298
def addNewUser(self, username, password, isAdmin):
304
@param isAdmin: True if the user is an administrator
307
if username.upper() in map(upper, self.db.users.keys()):
308
return False, USER_ALREADY_EXISTS
310
if username in constants.RESERVED_NAMES:
311
return False, USERNAME_NOT_ALLOWED
313
if (len(username) > constants.MAX_NAME_LENGTH):
314
return False, USERNAME_MUST_BE_LESS_THAN
316
# Ensure that the first user created is an Admin
317
if not self.hasUsers():
320
user = User( username, password, isAdmin )
321
user.rankName = self.rankings.getMinRank().name
322
self.db.users[ user.getUsername() ] = user
328
def getGameListing(self):
330
Return a Listing of ScrabbleGameInfo objects about each Game on the server
334
return [ScrabbleGameInfo(game) for game in self.gameList.values()]
336
def createGame(self, gameId, client, options):
340
@param gameId: Game ID
341
@param client: Client
342
@param options: Options
345
if len(gameId) > constants.MAX_NAME_LENGTH:
347
client.showError( ServerMessage( [GAME_NAME_MUST_BE_LESS_THAN, str(constants.MAX_NAME_LENGTH), CHARACTERS] ) )
350
if not self.gameList.has_key(gameId):
351
game = ScrabbleGame( gameId, options )
352
game.creator = self.clients[client].getUsername()
353
self.gameList[ gameId ] = game
354
self.refreshGameList()
356
client.showError( ServerMessage([GAME_ALREADY_EXISTS]) )
359
def deleteGame(self, gameId):
361
Boot all the players and spectators from the game and then call removeGame
363
@param gameId: Game ID
366
if (not self.gameList.has_key(gameId)):
369
game = self.gameList[ gameId ]
372
self.sendGameInfoMessage(gameId, [SERVER_DELETE_GAME], client=None, level=constants.GAME_LEVEL)
374
for player in game.getPlayers():
375
c = self.getPlayerClient(player)
377
c.gameLeave(gameId, True)
379
for s in game.getSpectators():
380
c = self.getPlayerClient(s)
382
c.gameLeave(gameId, True)
384
if not game.isPaused():
385
if len(game.getPlayers()) == 0:
386
self.removeGame(gameId)
388
if len(game.getPending()) == len(game.getPlayers()):
389
self.removeGame(gameId)
391
self.refreshGameList()
394
def removeGame(self, gameId):
396
Remove a game from the system and database
400
if self.gameList.has_key(gameId):
401
del self.gameList[ gameId ]
402
if self.db.games.has_key(gameId):
403
del self.db.games[ gameId ]
406
# Get a players client
407
def getPlayerClient(self, player):
409
Get the client protocol belonging to C{player}
411
@param player: Player
414
for client,_player in self.clients.iteritems():
415
if player.getUsername() == _player.getUsername():
418
# Authenticate a user
419
def authenticate(self, username, password):
426
u = util.getUnicode(username)
427
if self.db.users.has_key(u):
428
user = self.db.users[u]
429
if user.getPassword() == password:
434
def sendGameStats(self, gameId):
436
Send game statistics to each person in the game
438
@param gameId: Game ID
440
game = self.gameList[ gameId ]
442
for p in game.getPlayers():
443
c = self.getPlayerClient(p)
445
c.sendGameStats(gameId, game.getStats())
447
for s in game.getSpectators():
448
c = self.getPlayerClient(s)
450
c.sendGameStats(gameId, game.getStats())
452
def sendSpectatorList(self, gameId):
454
Send list of spectators
456
@param gameId: Game ID
458
game = self.gameList[ gameId ]
460
l = game.getSpectators()
462
for p in game.getPlayers():
463
c = self.getPlayerClient(p)
465
c.gameSendSpectators(gameId, l)
467
for s in game.getSpectators():
468
c = self.getPlayerClient(s)
470
c.gameSendSpectators(gameId, l)
474
def sendGameInfoMessage(self, gameId, message, client=None, level=constants.GAME_LEVEL):
476
Send an information message to the players of a Game
478
@param gameId: Game ID
479
@param message: Message
482
message = self.createServerChatMessage("GAME", message)
484
message = self.createChatMessage(client.getUsername(), message)
486
game = self.gameList[ gameId ]
487
for p in game.getPlayers():
488
c = self.getPlayerClient(p)
490
c.gameInfo(game.getGameId(), [(level, message)])
492
for s in game.getSpectators():
493
c = self.getPlayerClient(s)
495
c.gameInfo(game.getGameId(), [(level, message)])
497
game.appendLogMessage( (level, message) )
499
# Change a users password
500
def changePassword(self, command, client):
504
@param command: LoginCommand
505
@param client: ScrabbleServer Protocol
508
if command.getUsername() != None and len(command.getUsername()) != 0:
509
user = self.db.users[ command.getUsername() ]
511
player = self.clients[client]
512
user = self.db.users[ player.getUsername() ]
514
if (user.getPassword() == command.getData()): # Data will have the old password
515
user.setPassword( command.getPassword() )
516
self.db.users[ user.getUsername() ] = user
519
client.showError( ServerMessage([INVALID_OLD_PASSWORD]) )
521
def loginUser(self, player, client):
523
Log a user into the system. Join the main chat
529
self.joinChat( player, client )
530
self.clients[client] = player
532
self.db.users[player.getUsername()].setLastLogin( time.time() )
535
if len(self.clients) > self.maxUsersLoggedIn:
536
self.maxUsersLoggedIn = len(self.clients)
538
def handlePrivateMessageCommand(self, command, client):
540
Send a private message
547
if not self.db.users.has_key(command.getRecipient()):
548
client.showError( ServerMessage([command.getRecipient(), DOES_NOT_EXIST]) )
551
p = Player( command.getRecipient() )
552
c = self.getPlayerClient(p)
554
sender = self.clients[client].getUsername()
555
recipient = command.getRecipient()
556
data = command.getData()
559
if not self.db.messages.has_key(recipient):
562
l = self.db.messages[recipient]
563
self.db.messages[recipient] = []
565
num = util.getRandomId()
566
msg = util.PrivateMessage(sender, data, num, time=util.Time(seconds=time.time(), dispDate=True))
569
self.db.messages[recipient] = l
572
client.sendPrivateMessage(recipient, self.createChatMessage(sender, data))
573
client.showInfo( ServerMessage([MESSAGE_SENT, ': %s' % recipient]) )
577
msg = self.createChatMessage(sender, data)
578
c.sendPrivateMessage(sender, msg)
579
client.sendPrivateMessage(recipient, msg)
582
def handleGameCommand(self, command, client):
584
Handle a game command
586
@param command: GameCommand
587
@param client: ScrabbleServer Protocol
591
if (command.getCommand() == constants.GAME_GET_LETTERS):
592
letters = self.game.getLetters( int(command.getData()) )
593
client.sendLetters( letters )
595
if (command.getCommand() == constants.GAME_LIST):
596
client.sendGameList( self.getGameListing() )
598
if (command.getCommand() == constants.GAME_JOIN):
599
self.joinGame(command, client)
601
if (command.getCommand() == constants.GAME_START):
602
self.startGame( command.getGameId(), client )
604
if (command.getCommand() == constants.GAME_LEAVE):
605
if not self.gameList.has_key(command.getGameId()):
608
game = self.gameList[ command.getGameId() ]
609
if game.hasPlayer(self.clients[client]):
610
self.leaveGame( command.getGameId(), client )
611
if game.hasSpectator(self.clients[client]):
612
self.spectatorLeaveGame(command, client)
614
if (command.getCommand() == constants.GAME_SEND_MOVE):
615
onboard, moves = command.getData()
616
self.gameSendMove( command.getGameId(), onboard, moves, client )
618
if (command.getCommand() == constants.GAME_CREATE):
619
self.createGame( command.getGameId(), client, command.getData() )
620
if (command.getCommand() == constants.GAME_PASS):
621
self.gamePassMove( command.getGameId(), client )
623
if (command.getCommand() == constants.GAME_PAUSE):
624
self.saveGame(command, client)
626
if (command.getCommand() == constants.GAME_UNPAUSE):
627
game = self.gameList[ command.getGameId() ]
629
if self.clients[client].getUsername() != game.creator:
630
client.gameError(game.getGameId(), ServerMessage([NOT_CREATOR]))
633
# We can only unpause if all former players are present
634
if (len(game.getPending()) > 0):
635
client.gameError(game.getGameId(), ServerMessage([REQUIRED_NOT_MET]))
639
for player in game.getPlayers():
640
c = self.getPlayerClient(player)
641
c.unPauseGame( game.getGameId() )
642
self.refreshGameList()
643
self.sendGameInfoMessage(command.getGameId(), [GAME_RESUMED], None, level=constants.GAME_LEVEL)
644
self.doGameTurn(game.getGameId(), wasUnpaused=True)
646
del self.db.games[ game.getGameId() ]
649
if (command.getCommand() == constants.GAME_TRADE_LETTERS):
650
self.tradeLetters(command, client)
652
if (command.getCommand() == constants.GAME_CHAT_MESSAGE):
653
_level = constants.GAME_LEVEL
654
game = self.gameList[ command.getGameId() ]
655
if ( game.hasPlayer(self.clients[client]) ):
656
_level = constants.PLAYER_LEVEL
657
elif ( game.hasSpectator(self.clients[client]) ):
658
_level = constants.SPECTATOR_LEVEL
660
if _level == constants.SPECTATOR_LEVEL:
661
if not game.isSpectatorChatEnabled():
662
command.setCommand( constants.GAME_ERROR )
663
command.setData( ServerMessage([SPECTATOR_CHAT_DISABLED]) )
664
client.writeCommand(command)
667
self.sendGameInfoMessage(command.getGameId(), command.getData(), self.clients[client], level=_level)
669
if (command.getCommand() == constants.GAME_SPECTATOR_JOIN):
670
game = self.gameList[ command.getGameId() ]
671
if not game.isSpectatorsAllowed():
672
command.setCommand( constants.GAME_JOIN_DENIED )
673
command.setData( ServerMessage([SPECTATORS_BANNED]) )
674
client.denyJoinGame(command)
676
self.spectatorJoinGame(command, client)
678
if (command.getCommand() == constants.GAME_SPECTATOR_CHAT_SET):
679
game = self.gameList[ command.getGameId() ]
680
game.setSpectatorChatEnabled(command.getData())
682
if game.isSpectatorChatEnabled():
683
self.sendGameInfoMessage(command.getGameId(), [self.clients[client].getUsername(), ENABLE_SPEC_CHAT], client=None, level=constants.GAME_LEVEL)
685
self.sendGameInfoMessage(command.getGameId(), [self.clients[client].getUsername(), DISABLE_SPEC_CHAT], client=None, level=constants.GAME_LEVEL)
687
for p in game.getPlayers():
688
c = self.getPlayerClient(p)
689
c.writeCommand(command)
691
if (command.getCommand() == constants.GAME_SPECTATOR_SET):
692
game = self.gameList[ command.getGameId() ]
693
game.setSpectatorsAllowed(command.getData())
695
if game.isSpectatorsAllowed():
696
self.sendGameInfoMessage(command.getGameId(), [self.clients[client].getUsername(), ENABLE_SPEC], client=None, level=constants.GAME_LEVEL)
698
self.sendGameInfoMessage(command.getGameId(), [self.clients[client].getUsername(), DISABLE_SPEC], client=None, level=constants.GAME_LEVEL)
700
for p in game.getPlayers():
701
c = self.getPlayerClient(p)
702
c.writeCommand(command)
704
if not game.isSpectatorsAllowed():
705
for s in game.getSpectators():
706
c = self.getPlayerClient(s)
707
self.spectatorLeaveGame(command, c)
708
c.gameBoot(command.getGameId())
710
if (command.getCommand() == constants.GAME_TIME_EXPIRE):
711
self.gameTimeExpired( command.getGameId(), client )
713
if (command.getCommand() == constants.GAME_MOVE_TIME_EXPIRE):
714
self.moveTimeExpired(command.getGameId(), client )
717
def handleChatCommand(self, command, client):
719
Handle a chat command
721
@param command: ChatCommand
722
@param client: ScrabbleServer Protocol
725
if (command.getCommand() == constants.CHAT_JOIN):
726
self.joinChat( self.clients[client], client )
728
if (command.getCommand() == constants.CHAT_LEAVE):
729
if self.clients.has_key(client):
730
player = self.clients[client]
732
# Remove player from game and notify other players
733
for game in self.gameList.values():
734
if game.hasPlayer( player ):
735
self.leaveGame( game.getGameId(), client, command.getData() )
737
if game.hasSpectator( player ):
738
cmd = helper.GameCommand(constants.GAME_LEAVE, game.getGameId(), '')
739
self.spectatorLeaveGame(cmd, client)
741
self.removeClient(client)
742
self.leaveChat( player )
746
if (command.getCommand() == constants.CHAT_USERS):
747
self.sendUserList(client)
749
if (command.getCommand() == constants.CHAT_MESSAGE):
750
self.postChatMessage( self.clients[client], command.getData() )
752
if (command.getCommand() == constants.USER_INFO):
753
if not self.db.users.has_key(command.getUsername()):
754
client.showError( ServerMessage([command.getUsername(), DOES_NOT_EXIST]) )
756
u = self.db.users[command.getUsername()].clone()
757
u.status = self.getUserStatus(command.getUsername())
758
client.sendUserInfo(u)
760
if (command.getCommand() == constants.SERVER_STATS):
761
client.sendServerStats(self.getStats())
763
if (command.getCommand() == constants.CHECK_MESSAGES):
764
# Print server bulletins first
765
if self.db.messages.has_key(constants.SERVER_MESSAGE_KEY):
766
for message in self.db.messages[constants.SERVER_MESSAGE_KEY]:
767
client.postChatMessage( (message.data, True) )
769
key = self.clients[client].getUsername()
770
if self.db.messages.has_key(key):
771
if len(self.db.messages[key]) > 0:
773
for m in self.db.messages[key]:
778
client.postChatMessage( (self.createServerInfoMessage(MESSAGES_AVAILABLE), True) )
780
client.postChatMessage( (self.createServerInfoMessage(OLD_MESSAGES_AVAILABLE), True) )
783
if (command.getCommand() == constants.GET_MESSAGES):
784
key = self.clients[client].getUsername()
785
if self.db.messages.has_key(key):
786
for m in self.db.messages[key]:
788
client.sendOfflineMessages( self.db.messages[key] )
790
client.sendOfflineMessages( [] )
793
if (command.getCommand() == constants.DELETE_MESSAGE):
794
key = self.clients[client].getUsername()
795
l = self.db.messages[key]
796
data = int(command.getData())
797
l = [ m for m in l if m.id != data ]
799
self.db.messages[key] = l
801
del self.db.messages[key]
804
# Alert other users that a user has joined
805
def joinChat(self, player, client):
813
for c in self.clients.keys():
815
c.joinChat( player.getUsername() )
817
for c in self.clients.keys():
818
c.postChatMessage( (self.createLoginMessage(player.getUsername()), True) )
820
self.auditUser( player.getUsername(), audit.LogonAction(player.getUsername()) )
822
# Log a user of the system
823
def leaveChat(self, player):
830
for c in self.clients.keys():
831
c.postChatMessage( (self.createLogoutMessage(player.getUsername()),True) )
833
for c in self.clients.keys():
836
self.auditUser( player.getUsername(), audit.LogoffAction(player.getUsername()) )
838
# Post a chat message
839
def postChatMessage(self, player, msg):
841
User posts a chat message
847
for c in self.clients.keys():
848
c.postChatMessage( (self.createChatMessage(player.getUsername(), msg), False) )
850
# Send the list of users to the client
851
def sendUserList(self, client):
853
Send the client a list of all users on the server
858
client.sendUserList( [ self.clients[c] for c in self.clients.keys()] )
860
# Create formatted chat message
861
def createServerChatMessage(self, username, msg_keys):
863
Helper function to create a server chat message
870
x.append( "<%s>" % (username) )
875
return ServerMessage(x, util.Time(seconds=time.time(), dispDate=False))
877
def createServerInfoMessage(self, msg):
879
Helper function to create a serverchat message
884
return ServerMessage([msg,"\n"], util.Time(seconds=time.time(), dispDate=True))
886
# Create formatted chat message
887
def createChatMessage(self, username, msg):
889
Helper function to create a chat message
894
return ServerMessage(["<%s> %s\n" % (username, util.getUnicode(msg))],util.Time(seconds=time.time(), dispDate=False))
896
# Create logout message
897
def createLogoutMessage(self, username):
899
Helper function to create a logout message
905
return ServerMessage(["<%s>" % (username), LOGGED_OUT, "\n"], util.Time(seconds=time.time(), dispDate=False))
907
# Create logout message
908
def createLoginMessage(self, username):
910
Helper function to create a login message
914
return ServerMessage(["<%s>" % (username), LOGGED_IN, "\n"], util.Time(seconds=time.time(), dispDate=False))
916
def refreshGameList(self):
918
Send all clients the current game list
921
for c in self.clients.keys():
922
c.sendGameList( [ScrabbleGameInfo(game) for game in self.gameList.values()] )
925
def joinGame(self, command, client):
929
@param command: GameCommand
930
@param client: ScrabbleServer protocol
932
game = self.gameList[ command.getGameId() ]
933
if (game.isStarted()):
934
command.setData( ServerMessage([CANNOT_JOIN_STARTED]) )
935
command.setCommand( constants.GAME_JOIN_DENIED )
936
client.denyJoinGame(command)
939
if (game.getNumberOfPlayers() == constants.MAX_PLAYERS):
940
command.setData( ServerMessage([GAME_FULL]) )
941
command.setCommand( constants.GAME_JOIN_DENIED )
942
client.denyJoinGame(command)
945
p = self.clients[client].clone()
947
if (game.isPaused() and not game.hasPlayer(p)):
948
command.setData( ServerMessage([CANNOT_JOIN_STARTED]) )
949
command.setCommand( constants.GAME_JOIN_DENIED )
950
client.denyJoinGame(command)
953
if not game.hasPlayer( p ):
956
game.removePending( p )
958
command.setCommand( constants.GAME_JOIN_OK )
959
client.acceptJoinGame( command, game.options )
962
if game.options.has_key(OPTION_TIMED_GAME):
963
time = int(game.options[OPTION_TIMED_GAME])
964
elif game.options.has_key(OPTION_MOVE_TIME):
965
time = int(game.options[OPTION_MOVE_TIME])
968
p.setInitialTime( time )
970
players = game.getPlayers()
971
pending = game.getPending()
973
self.sendGameScores(game.getGameId())
975
client.sendMoves( game.getGameId(), game.getMoves() )
976
client.sendGameStats( game.getGameId(), game.getStats() )
977
client.gameInfo( game.getGameId(), game.getLog() )
978
client.gameSendSpectators( game.getGameId(), game.getSpectators() )
979
client.sendGameOptions( game.getGameId(), game.getOptions() )
981
client.setSpectatorChatEnabled(game.getGameId(), game.isSpectatorChatEnabled())
982
client.setSpectatorsAllowed(game.getGameId(), game.isSpectatorsAllowed())
984
if (game.isPaused()):
985
client.pauseGame( game.getGameId() )
987
if (game.isPaused() and game.isInProgress()):
989
player = game.getPlayer( self.clients[client] )
991
letters = game.getLetters( player.getNumberOfLettersNeeded() )
992
if (len(letters) > 0):
993
player.addLetters(letters)
994
client.sendLetters(game.getGameId(), player.getLetters())
996
# If there is only one person (the new person), make sure that person has control of the board
997
#if (len(players) == 1):
998
# self.doGameTurn( game.getGameId() )
1000
self.refreshGameList()
1003
def startGame(self, gameId, client):
1007
@param gameId: Game ID
1008
@param client: ScrabbleServer Protocol
1011
game = self.gameList[ gameId ]
1013
if self.clients[client].getUsername() != game.creator:
1014
client.gameError(gameId, ServerMessage([NOT_CREATOR]))
1017
if (game.isStarted()):
1018
client.gameError(gameId, ServerMessage([GAME_ALREADY_STARTED]))
1024
if game.options.has_key(OPTION_TIMED_GAME):
1025
time = int(game.options[OPTION_TIMED_GAME])
1026
elif game.options.has_key(OPTION_MOVE_TIME):
1027
time = int(game.options[OPTION_MOVE_TIME])
1029
for player in game.getPlayers():
1030
c = self.getPlayerClient(player)
1031
letters = game.getLetters( player.getNumberOfLettersNeeded() )
1032
player.addLetters(letters)
1033
if time is not None:
1034
player.setInitialTime( time )
1035
c.sendLetters( game.getGameId(), letters )
1037
self.sendGameInfoMessage(gameId, [gameId, STARTED], client=None, level=constants.GAME_LEVEL)
1038
self.sendGameScores(game.getGameId())
1040
self.doGameTurn( gameId )
1041
self.refreshGameList()
1043
# Turn gameplayer over to the next player and notify other players of whose turn it is
1044
def doGameTurn(self, gameId, wasUnpaused=False ):
1046
Turn control of the board to the next player in the game
1048
@param gameId: Game ID
1051
game = self.gameList[ gameId ]
1053
player = game.getNextPlayer()
1058
player.stamp = datetime.datetime.now()
1059
client = self.getPlayerClient(player)
1061
if game.timer is not None and game.timer.active():
1065
if game.options.has_key(OPTION_MOVE_TIME):
1067
time = datetime.timedelta(seconds=60 * int(game.options[OPTION_MOVE_TIME]))
1069
if game.options.has_key(OPTION_MOVE_TIME):
1070
game.timer = reactor.callLater(time.seconds, self.moveTimeExpired, gameId, client)
1071
elif game.options.has_key(OPTION_TIMED_GAME):
1072
if game.options.has_key(OPTION_TIMED_LIMIT):
1073
t = 60 * int(game.options[OPTION_TIMED_LIMIT])
1076
x = datetime.timedelta(seconds=t + time.seconds)
1077
if game.options.has_key(OPTION_TIMED_LIMIT):
1080
x = datetime.timedelta(seconds=t - x.seconds )
1081
game.timer = reactor.callLater(x.seconds, self.gameTimeExpired, gameId, client)
1083
client.gameTurnCurrent(gameId, time)
1085
for _player in game.getPlayers():
1086
_client = self.getPlayerClient(_player)
1087
if (_player != player):
1088
_client.gameTurnOther( gameId, PlayerInfo(player.getUsername(), player.getScore(), len(player.getLetters()), player.time ))
1090
for s in game.getSpectators():
1091
c = self.getPlayerClient(s)
1092
c.gameTurnOther( gameId, PlayerInfo(player.getUsername(), player.getScore(), len(player.getLetters()), player.time ))
1094
self.sendGameInfoMessage(gameId, [player.getUsername(),TURN], client=None, level=constants.GAME_LEVEL)
1095
self.sendGameStats(gameId)
1096
if game.options[OPTION_SHOW_COUNT]:
1097
self.sendLetterDistribution(gameId)
1099
def sendLetterDistribution(self, gameId):
1101
Send the letter distribution
1103
@param gameId: Game ID
1105
game = self.gameList[ gameId ]
1107
for p in game.getPlayers():
1108
c = self.getPlayerClient(p)
1109
c.sendLetterDistribution( gameId, game.getDistribution() )
1111
for s in game.getSpectators():
1112
c = self.getPlayerClient(s)
1113
c.sendLetterDistribution( gameId, game.getDistribution() )
1116
def moveTimeExpired(self, gameId, client):
1118
Move time for a player has expired
1123
game = self.gameList[ gameId ]
1124
player = game.getPlayer( self.clients[client] )
1125
player.time = datetime.timedelta(seconds=60 * int(game.options[OPTION_MOVE_TIME]))
1127
self.sendGameScores(gameId)
1128
self.sendGameInfoMessage(gameId, [player.getUsername(),MOVE_OUT_OF_TIME], client=None, level=constants.GAME_LEVEL)
1129
self.doGameTurn(gameId)
1131
def gameTimeExpired(self, gameId, client):
1135
@param gameId: Game ID
1136
@param client: Player client who has run out of time
1138
game = self.gameList[ gameId ]
1139
player = game.getPlayer( self.clients[client] )
1141
self.sendGameInfoMessage(gameId, [player.getUsername(),OUT_OF_TIME], client=None, level=constants.GAME_LEVEL)
1142
player.time = datetime.timedelta(seconds=0)
1144
self.gameOver(game, player)
1147
def leaveGame(self, gameId, client, penalize=True, booted=False):
1149
Player leaves a game
1151
@param gameId: Game ID
1152
@param client: ScrabbleServer Protocol
1153
@param penalize: Flag to penalize the player a loss. Not penalized if the connection was not closed cleanly
1156
game = self.gameList[ gameId ]
1157
player = game.getPlayer( self.clients[client] )
1159
if (game.isPaused() and not booted):
1160
game.addPending( player )
1163
if not game.isComplete() and len(game.getPlayers()) > 1:
1164
if game.isStarted():
1165
if game.options[OPTION_RANKED] and penalize:
1166
self.db.users[ player.getUsername() ].addLoss(None)
1168
game.playerLeave(player)
1170
self.sendGameScores(gameId)
1172
self.sendGameInfoMessage(gameId, [player.getUsername(),LEFT_GAME], client=None, level=constants.GAME_LEVEL)
1174
# If there are no more players left, remove the game
1175
if len(game.getPlayers()) == 0 and len(game.getSpectators()) == 0 and not game.isPaused():
1176
#for s in game.getSpectators():
1177
# c = self.getPlayerClient( s )
1178
# c.gameLeave( game.getGameId() )
1179
if game.timer is not None and game.timer.active():
1181
del self.gameList[ gameId ]
1182
elif not game.isComplete() and not game.isPaused():
1183
if game.isCurrentPlayer( player ):
1184
self.doGameTurn( gameId )
1186
self.refreshGameList()
1189
def checkServerStats(self, player, moves):
1191
Check if this move is a new server stat
1193
@param player: Player
1200
score = score + move.getScore()
1201
newdisp = '%s (%s) by %s' % (move.getWord(), str(move.getScore()), player.getUsername())
1202
if self.db.stats.has_key(STAT_HIGHEST_SCORING_WORD):
1203
data,disp = self.db.stats[STAT_HIGHEST_SCORING_WORD]
1204
if (move.getScore() > data.getScore()):
1205
self.db.stats[STAT_HIGHEST_SCORING_WORD] = move,newdisp
1207
self.db.stats[STAT_HIGHEST_SCORING_WORD] = move,newdisp
1209
newdisp = '%s (%s) by %s' % (move.getWord(), str(move.length()), player.getUsername())
1210
if self.db.stats.has_key(STAT_LONGEST_WORD):
1211
data,disp = self.db.stats[STAT_LONGEST_WORD]
1212
if (move.length() > data.length()):
1213
self.db.stats[STAT_LONGEST_WORD] = move,newdisp
1215
self.db.stats[STAT_LONGEST_WORD] = move,newdisp
1216
allDisp = allDisp + '%s (%s) ' % (move.getWord(), str(move.getScore()))
1218
allDisp = allDisp + 'by %s' % player.getUsername()
1220
d = '%s, ' % str(score) + d
1221
if self.db.stats.has_key(STAT_HIGHEST_SCORING_MOVE):
1222
data, disp = self.db.stats[STAT_HIGHEST_SCORING_MOVE]
1224
self.db.stats[STAT_HIGHEST_SCORING_MOVE] = score, d
1226
self.db.stats[STAT_HIGHEST_SCORING_MOVE] = score, d
1229
d = '%s, ' % str(len(moves)) + d
1230
if self.db.stats.has_key(STAT_MOST_WORDS_IN_MOVE):
1231
data, disp = self.db.stats[STAT_MOST_WORDS_IN_MOVE]
1232
if len(moves) > data:
1233
self.db.stats[STAT_MOST_WORDS_IN_MOVE] = len(moves), d
1235
self.db.stats[STAT_MOST_WORDS_IN_MOVE] = len(moves), d
1240
# Player send move to game
1241
def gameSendMove(self, gameId, onboard, moves, client):
1243
User sends moves to the game
1245
@param gameId: Game ID
1246
@param onboard: Move containing letters put on the board
1247
@param moves: List of Moves formed
1248
@param client: ScrabbleServer Protocol
1251
game = self.gameList[ gameId ]
1252
player = game.getPlayer( self.clients[client] )
1254
if not player == game.getCurrentPlayer():
1257
if (game.isPaused()):
1258
client.gameError(gameId, ServerMessage([MOVE_GAME_PAUSED]) )
1260
if (not game.isInProgress()):
1261
client.gameError(gameId, ServerMessage([NOT_IN_PROGRESS]) )
1265
# Validate word in dictionary and not on the board alread
1268
word = util.getUnicode( move.getWord() )
1269
if word not in self.dicts[ game.options[OPTION_RULES] ]:
1270
client.gameError( gameId, ServerMessage([word, NOT_IN_DICT]) )
1272
words.append( word )
1276
client.acceptMove(gameId)
1277
score = self.getMovesScore(game, moves)
1278
letters = self.getLettersFromMove(onboard)
1279
player.removeLetters( letters )
1280
player.addScore( score )
1282
self.removeModifiers(game, moves)
1284
game.addMoves(moves, player)
1286
game.resetPassCount()
1288
if len(game.getPlayers()) > 1:
1289
self.checkServerStats(player, moves)
1291
# If the player used all 7 of his/her letters, give them an extra 50
1292
if onboard.length() == 7:
1293
player.addScore( constants.BINGO_BONUS_SCORE )
1294
self.sendGameInfoMessage(gameId, [player.getUsername(), MADE_A_BINGO, '(%s)' % str(constants.BINGO_BONUS_SCORE)], client=None, level=constants.GAME_LEVEL)
1296
for p in game.getPlayers():
1297
c = self.getPlayerClient(p)
1298
c.sendMoves( gameId, moves )
1300
for s in game.getSpectators():
1301
c = self.getPlayerClient(s)
1302
c.sendMoves( gameId, moves )
1305
self.sendGameInfoMessage(gameId, [player.getUsername(), HAS_ADDED, ' %s (%d)' % (move.getWord(), move.getScore())], client=None, level=constants.GAME_LEVEL)
1307
# If the player used all his/her letters and there are no more letters in the bag, the game is over
1308
if (len(player.getLetters()) == 0 and game.isBagEmpty()):
1310
# Subtract everyones letter points
1311
# Give points to the person who went out
1312
players = game.getPlayers()
1315
continue # Skip winner
1317
letters = p.getLetters()
1320
for letter in letters:
1321
p.addScore( letter.getScore() * -1 )
1322
player.addScore( letter.getScore() )
1323
lmsg = lmsg + '%s(%d) ' % (letter.getLetter(), letter.getScore() * -1)
1324
wmsg = wmsg + '%s(%d) ' % (letter.getLetter(), letter.getScore())
1326
self.sendGameInfoMessage(gameId, [p.getUsername(), LOSES, lmsg], client=None, level=constants.GAME_LEVEL)
1327
self.sendGameInfoMessage(gameId, [player.getUsername(), GAINS, wmsg, FROM, p.getUsername()], client=None, level=constants.GAME_LEVEL)
1329
self.sendGameScores(game.getGameId())
1334
letters = game.getLetters( player.getNumberOfLettersNeeded() )
1335
if (len(letters) > 0):
1336
player.addLetters(letters)
1337
client.sendLetters(gameId, player.getLetters())
1340
if game.options.has_key(OPTION_TIMED_GAME):
1341
player.time = player.time - (datetime.datetime.now() - player.stamp)
1342
player.time = datetime.timedelta(days=player.time.days, seconds=player.time.seconds+1 ) # +1 account for error
1343
if game.timer is not None and game.timer.active():
1346
self.sendGameScores(game.getGameId())
1348
if game.isBagEmpty() or game.getCountLetters() < 7:
1349
for p in game.getPlayers():
1350
c = self.getPlayerClient(p)
1351
c.gameBagEmpty(gameId)
1352
for s in game.getSpectators():
1353
c = self.getPlayerClient(s)
1354
c.gameBagEmpty(gameId)
1358
self.doGameTurn(gameId)
1360
# Get the letters from the moves
1361
def getLettersFromMove(self, move):
1363
Get the letters in a move
1366
@return: List of letters in C{move}
1371
for letter, x, y in move.getTiles():
1372
letters.append( letter.clone() )
1376
# Get the score of the list of moves
1377
def getMovesScore(self, game, moves):
1379
Get the total score for a list of Moves
1381
@param game: ScrabbleGame
1382
@param moves: List of Moves
1383
@return: Total score for the list of Moves
1391
modifier = constants.TILE_NORMAL
1394
for letter,x,y in move.getTiles():
1395
m = util.getTileModifier(x,y)
1396
if m in constants.LETTER_MODIFIERS and not game.hasUsedModifier((x,y)):
1397
score = score + (m * letter.getScore())
1399
score = score + letter.getScore()
1401
if (m >= modifier and not game.hasUsedModifier((x,y))):
1403
if m in constants.WORD_MODIFIERS:
1408
if modifier in constants.WORD_MODIFIERS and not game.hasUsedModifier((m_x,m_y)):
1410
if util.isCenter(m_x, m_y):
1411
if game.options[OPTION_CENTER_TILE]:
1412
score = score * (modifier/2)
1414
score = score * ((modifier/2) ** apply)
1416
move.setScore( score )
1417
total = total + score
1421
def removeModifiers(self, game, moves):
1423
Mark off modifiers that are used in this move
1426
@param moves: List of moves
1429
for letter,x,y in move.getTiles():
1430
m = util.getTileModifier(x,y)
1431
if m in constants.LETTER_MODIFIERS and not game.hasUsedModifier((x,y)):
1432
game.addUsedModifier( (x,y) )
1433
if m in constants.WORD_MODIFIERS and not game.hasUsedModifier((x,y)):
1434
game.addUsedModifier( (x,y) )
1438
# Player passes a move. If all players pass, the game is over
1439
def gamePassMove(self, gameId, client):
1441
Player passes his/her turn
1443
@param gameId: Game ID
1444
@param client: ScrabbleServer Protocol
1447
game = self.gameList[ gameId ]
1448
player = game.getPlayer( self.clients[client] )
1450
if not player == game.getCurrentPlayer():
1453
if (not game.isInProgress()):
1454
client.gameError(gameId, ServerMessage([NOT_IN_PROGRESS]) )
1457
if (game.isPaused()):
1458
client.gameError(gameId, ServerMessage([PASS_PAUSED]) )
1462
self.sendGameInfoMessage(gameId, [player.getUsername(), HAS_PASSED], client=None, level=constants.GAME_LEVEL)
1464
if game.options.has_key(OPTION_TIMED_GAME):
1465
player.time = player.time - (datetime.datetime.now() - player.stamp)
1466
player.time = datetime.timedelta(days=player.time.days, seconds=player.time.seconds)
1467
if game.timer is not None and game.timer.active():
1472
self.sendGameScores(gameId)
1474
self.doGameTurn(gameId)
1475
except exceptions.GameOverException:
1476
# If everyone has passed, assume that everyone still has letters
1477
# Subtract everyones letter points
1478
players = game.getPlayers()
1479
for player in players:
1480
letters = player.getLetters()
1482
for letter in letters:
1483
player.addScore( letter.getScore() * -1 )
1484
msg = msg + '%s(%d) ' % (letter.getLetter(), letter.getScore() * -1)
1486
self.sendGameInfoMessage(gameId, [player.getUsername(), LOSES, msg], client=None, level=constants.GAME_LEVEL)
1488
self.sendGameScores(game.getGameId())
1491
def sendGameScores(self, gameId):
1495
game = self.gameList[ gameId ]
1496
players = game.getPlayers()
1499
c = self.getPlayerClient(p)
1501
c.sendGameUserList( game.getGameId(), self.getGamePlayerInfo(gameId, removePending=True) )
1503
for s in game.getSpectators():
1504
c = self.getPlayerClient(s)
1506
c.sendGameUserList( game.getGameId(), self.getGamePlayerInfo(gameId, removePending=False) )
1508
def getGamePlayerInfo(self, gameId, removePending=False):
1510
Return list of PlayerInfo objects for each Player in a game
1512
@param gameId: Game ID
1513
@param removePending: True to remove pending players from list
1515
game = self.gameList[ gameId ]
1516
players = game.getPlayers()
1519
if (game.isPaused()):
1521
pending = game.getPending()
1523
if p not in pending:
1527
return [ PlayerInfo(p.getUsername(), p.getScore(), len(p.getLetters()), util.TimeDeltaWrapper(p.time)) for p in players ]
1531
def gameOver(self, game, exclude=None):
1533
Declare game over for C{game}
1536
@param exclude: Player to exclude
1539
if game.timer is not None and game.timer.active():
1542
if game.options.has_key(OPTION_TIMED_GAME):
1543
for p in game.getPlayers():
1547
mins = math.ceil( (t.seconds / 60.0) )
1548
score = int((mins * constants.OVERTIME_PENALTY) * -1)
1550
msg = '%d points' % score
1551
self.sendGameInfoMessage(game.getGameId(), [p.getUsername(), LOSES, msg], client=None, level=constants.GAME_LEVEL)
1553
winners = game.getWinners(exclude)
1555
if len(winners) > 0:
1556
newdisp = '%s by ' % (winners[0].getScore())
1558
newdisp = newdisp + w.getUsername() + ' '
1560
if len(game.getPlayers()) > 1:
1561
if self.db.stats.has_key(STAT_HIGHEST_TOTAL_SCORE):
1562
data,disp = self.db.stats[STAT_HIGHEST_TOTAL_SCORE]
1563
if (winners[0].getScore() > data):
1564
self.db.stats[STAT_HIGHEST_TOTAL_SCORE] = winners[0].getScore(),newdisp
1566
self.db.stats[STAT_HIGHEST_TOTAL_SCORE] = winners[0].getScore(),newdisp
1568
winners = game.getWinners(exclude)
1570
# If there is less than one player in the game, don't count the score
1571
count = len(game.getPlayers()) > 1
1573
if len(winners) > 0:
1574
if len(winners) == 1:
1576
self.sendGameInfoMessage(game.getGameId(), ['%s (%d)' % (winner.username, int(winner.score)), HAS_WON], client=None, level=constants.GAME_LEVEL)
1578
if game.options[OPTION_RANKED]:
1579
self.db.users[ winner.getUsername() ].addWin( game.getPlayers() )
1580
self.setRankForPlayer( winner.getUsername() )
1581
self.auditUser( winner.getUsername(), audit.GameWinAction(winner, game.name, game.getPlayers()), False )
1584
for winner in winners:
1585
msg += '%s (%d)' % (winner.username, int(winner.score))
1588
if game.options[OPTION_RANKED]:
1589
self.db.users[ winner.getUsername() ].addTie( winners )
1590
self.auditUser( winner.getUsername(), audit.GameTieAction(winner, game.name, winners), False )
1592
self.sendGameInfoMessage(game.getGameId(), [msg, HAVE_TIED], client=None, level=constants.GAME_LEVEL)
1594
for p in game.getPlayers():
1595
if p not in winners:
1597
if game.options[OPTION_RANKED]:
1598
self.db.users[ p.getUsername() ].addLoss(winners)
1599
self.auditUser( p.getUsername(), audit.GameLossAction(winners, game.name, p), False )
1600
c = self.getPlayerClient( p )
1601
c.gameOver( game.getGameId() )
1603
for s in game.getSpectators():
1604
c = self.getPlayerClient( s )
1605
c.gameLeave( game.getGameId() )
1607
self.sendGameScores(game.getGameId())
1609
self.gameList[ game.getGameId() ].setComplete()
1610
self.refreshGameList()
1614
# Player trades in current set of letters for new letters
1615
def tradeLetters(self, command, client):
1617
Player trades letters
1619
@param command: GameCommand
1620
@param client: ScrabbleServer Protocol
1623
game = self.gameList[ command.getGameId() ]
1624
player = game.getPlayer( self.clients[client] )
1626
if not player == game.getCurrentPlayer():
1629
if (not game.isInProgress()):
1630
client.gameError(gameId, ServerMessage([NOT_IN_PROGRESS]) )
1633
l = command.getData()
1635
player.removeLetters( l )
1636
letters = game.getLetters( player.getNumberOfLettersNeeded() )
1637
player.addLetters( letters )
1638
game.returnLetters( l )
1639
client.sendLetters( game.getGameId(), player.getLetters() )
1641
game.resetPassCount()
1643
if game.options.has_key(OPTION_TIMED_GAME):
1644
player.time = player.time - (datetime.datetime.now() - player.stamp)
1645
player.time = datetime.timedelta(days=player.time.days, seconds=player.time.seconds)
1646
if game.timer is not None and game.timer.active():
1649
self.sendGameScores(command.getGameId())
1651
self.sendGameInfoMessage(game.getGameId(), [player.getUsername(),HAS_TRADED, '%s' % str(num), util.ternary(num == 1, LETTER, LETTERS)], client=None, level=constants.GAME_LEVEL)
1653
self.doGameTurn(game.getGameId())
1656
def spectatorJoinGame(self, command, client):
1658
Spectator joins the game
1663
game = self.gameList[ command.getGameId() ]
1665
command.setCommand( constants.GAME_SPECTATE_JOIN_OK )
1666
client.acceptJoinGame( command, game.options )
1668
player = self.clients[client].clone()
1669
game.addSpectator( player )
1671
client.sendGameUserList( game.getGameId(), self.getGamePlayerInfo(game.getGameId()) )
1672
client.sendMoves( game.getGameId(), game.getMoves() )
1673
client.gameInfo( game.getGameId(), game.getLog() )
1674
client.sendGameStats( game.getGameId(), game.getStats() )
1675
client.sendGameOptions( game.getGameId(), game.getOptions() )
1677
if game.options.has_key(OPTION_TIMED_GAME) or game.options.has_key(OPTION_MOVE_TIME):
1678
if not game.isPaused():
1679
p = game.getCurrentPlayer()
1681
time = p.time - (datetime.datetime.now() - p.stamp)
1682
time = datetime.timedelta(days=p.time.days, seconds=time.seconds+1 ) # +1 account for error
1683
client.gameTurnOther( game.getGameId(), PlayerInfo(p.getUsername(), p.getScore(), len(p.getLetters()), time ))
1685
self.sendGameInfoMessage(game.getGameId(), [player.getUsername(), IS_SPECTATING], client=None, level=constants.GAME_LEVEL)
1686
self.sendSpectatorList( game.getGameId() )
1688
def spectatorLeaveGame(self, command, client):
1690
Spectator leaves the game
1695
game = self.gameList[ command.getGameId() ]
1696
game.spectatorLeave(self.clients[client])
1698
self.sendGameInfoMessage(game.getGameId(), [self.clients[client].getUsername(), NO_LONGER_SPECTATING], client=None, level=constants.GAME_LEVEL)
1699
self.sendSpectatorList( game.getGameId() )
1700
if len(game.getPlayers()) == 0 and len(game.getSpectators()) == 0 and not game.isPaused():
1701
if game.timer is not None and game.timer.active():
1703
del self.gameList[ game.getGameId() ]
1704
self.refreshGameList()
1708
Retrieve list of game stats and the list of users
1710
@return: List of tuples (stat_name, stat_value), users
1714
s.append( (ServerMessage([NUMBER_USERS]), str(len(self.db.users))) )
1715
s.append( (ServerMessage([MOST_USERS]), str(self.maxUsersLoggedIn)) )
1716
s.append( (ServerMessage([UPTIME]), self.startDate) )
1717
s.append( (ServerMessage([SERVER_VERSION]), constants.VERSION) )
1719
for key,value in self.db.stats.iteritems():
1721
s.append ( (ServerMessage([key]), disp) )
1724
for user in self.db.users.values():
1725
users.append( (user.getUsername(), int(user.getNumericStat(constants.STAT_WINS)), int(user.getNumericStat(constants.STAT_LOSSES)), int(user.getNumericStat(constants.STAT_TIES)), user.rankName) )
1728
return s, users, self.rankings.getRankInfo()
1730
def setRankForPlayer(self, username):
1732
Set rank for a player
1734
@param username: Username
1736
u = util.getUnicode(username)
1737
r = self.db.users[u].getNumericStat(constants.STAT_RANK)
1738
rank = self.rankings.getRankByWins(r)
1739
self.db.users[u].rankName = rank.name
1741
def getUserStatus(self, username):
1746
@return: ServerMessage detailing users activity on the system
1749
for client,player in self.clients.iteritems():
1750
if player.getUsername() == username:
1754
if p is not None and c is not None:
1758
for game in self.gameList.itervalues():
1759
if game.hasPlayer(p):
1760
playing.append( game.getGameId() )
1762
for game in self.gameList.itervalues():
1763
if game.hasSpectator(p):
1764
watching.append( game.getGameId() )
1766
if len(playing) > 0:
1767
message.append(PLAYING)
1769
for game in playing:
1772
message.append(game)
1775
if len(watching) > 0:
1776
if len(message) > 0:
1777
message.append( '-' )
1779
message.append(WATCHING)
1781
for game in watching:
1784
message.append(game)
1787
if len(message) == 0:
1788
message = [ ONLINE ]
1790
return ServerMessage(message)
1792
return ServerMessage([OFFLINE])
1795
def saveGame(self, command, client):
1802
game = self.gameList[ command.getGameId() ]
1804
if self.clients[client].getUsername() != game.creator:
1805
client.gameError(game.getGameId(), ServerMessage([NOT_CREATOR]))
1808
if game.options.has_key(OPTION_TIMED_GAME) or game.options.has_key(OPTION_MOVE_TIME):
1809
player = game.getCurrentPlayer()
1810
player.time = player.time - (datetime.datetime.now() - player.stamp)
1811
player.time = datetime.timedelta(days=player.time.days, seconds=player.time.seconds+1 ) # +1 account for error
1814
for player in game.getPlayers():
1815
c = self.getPlayerClient(player)
1816
c.pauseGame( game.getGameId() )
1817
self.refreshGameList()
1819
self.db.games[ game.getGameId() ] = game
1821
self.sendGameInfoMessage(command.getGameId(), [GAME_SAVED], None, level=constants.GAME_LEVEL)
1823
for player in game.getPlayers():
1824
c = self.getPlayerClient(player)
1825
c.gameLeave( game.getGameId() )
1840
class ScrabbleServer(NetstringReceiver):
1844
This class is responsible for shuttling data to and from a client.
1846
There will be one instance of this class per client connected to the ServerFactory
1855
self.command = helper.CommandCreator()
1856
self.username = None
1858
def stringReceived(self, data):
1860
Callback when data is received from the client
1862
Parse the data into a Command and handle it.
1864
@param data: Text data representing a command
1865
@see: L{pyscrabble.command.helper.Command}
1867
logger.debug('Incoming: %s %s %s' % (repr(self.username), self.addr.host, data))
1869
command = serialize.loads( data )
1871
if ( isinstance(command, helper.LoginCommand) ):
1872
self.handleLoginCommand( command )
1875
if ( isinstance(command, helper.ChatCommand) ):
1876
self.factory.handleChatCommand( command, self )
1879
if ( isinstance(command, helper.GameCommand) ):
1880
self.factory.handleGameCommand( command, self )
1883
if ( isinstance(command, helper.PrivateMessageCommand) ):
1884
self.factory.handlePrivateMessageCommand(command, self)
1887
def handleLoginCommand(self, command):
1889
Handle a login command
1891
Callback to the Factory to authenticate the user
1893
@param command: LoginCommand
1894
@see: L{pyscrabble.command.helper.LoginCommand}
1898
if (command.getCommand() == constants.LOGIN_INIT):
1901
version = command.getData()
1902
if (version is '' or version < constants.REQUIRED_VERSION):
1903
command.setCommand( constants.LOGIN_DENIED )
1904
command.setData( ServerMessage([REQ_VERSION, constants.REQUIRED_VERSION]) )
1907
player = Player( command.getUsername() )
1909
if (self.factory.authenticate(command.getUsername(), command.getPassword())):
1910
if (self.factory.isLoggedIn(player)):
1911
c = self.factory.getPlayerClient(player)
1913
self.factory.removeClient(c)
1914
c.transport.loseConnection()
1916
self.username = command.getUsername()
1917
command.setCommand( constants.LOGIN_OK )
1918
self.factory.loginUser( player, self )
1920
command.setData( ServerMessage([INVALID_USERNAME_PASSWORD]) )
1921
command.setCommand( constants.LOGIN_DENIED )
1923
self.writeCommand( command )
1925
if (command.getCommand() == constants.NEW_USER):
1926
self.factory.createNewUser(command, self)
1929
if (command.getCommand() == constants.CHANGE_PASSWORD):
1930
self.factory.changePassword(command, self)
1933
def joinChat(self, username):
1935
User joins the chatroom
1937
@param username: Username
1940
command = self.command.createJoinChatCommand(username)
1941
self.writeCommand( command )
1943
def sendUserList(self, users):
1945
Send List of Players on the server
1947
@param users: List of Players
1948
@see: L{pyscrabble.game.player.Player}
1951
command = self.command.createGetChatUsersCommand( users )
1952
self.writeCommand( command )
1954
def postChatMessage(self, message):
1958
@param message: Message text
1961
command = self.command.createPostChatMessageCommand(message)
1962
self.writeCommand( command )
1964
def sendLetters(self, gameId, letters):
1968
@param gameId: Game ID
1969
@param letters: List of Letters
1970
@see: L{pyscrabble.game.pieces.Letter}
1973
command = self.command.createGetLettersCommand( gameId, letters )
1974
self.writeCommand( command )
1976
def sendLetterDistribution(self, gameId, distribution):
1978
Send letter distribution
1980
@param gameId: GameID
1981
@param distribution: dict(Letter,count)
1983
command = self.command.createGameDistributionCommand( gameId, distribution )
1984
self.writeCommand( command )
1987
def sendGameList(self, gameList):
1989
Send List of Games on the server
1991
@param gameList: List of Games on the server
1992
@see: L{pyscrabble.game.game.ScrabbleGameInfo}
1995
command = self.command.createGetGameListCommand( gameList )
1996
self.writeCommand( command )
1998
def sendGameUserList(self, gameId, users):
2000
Send List of Players in a game
2002
@param gameId: Game ID
2003
@param users: List of Players
2004
@see: L{pyscrabble.game.player.Player}
2007
command = self.command.createGameUserListCommand( gameId, users )
2008
self.writeCommand( command )
2010
def denyJoinGame(self, command):
2012
Deny the users join game request
2014
@param command: GameCommand
2015
@see: L{pyscrabble.command.helper.GameCommand}
2018
self.writeCommand( command )
2020
def acceptJoinGame(self, command, options):
2022
Accept the users join game request
2024
@param command: GameCommand
2025
@param options: Game options dict
2026
@see: L{pyscrabble.command.helper.GameCommand}
2028
command.setData( options )
2029
self.writeCommand( command )
2031
def gameTurnOther(self, gameId, player):
2033
Notify the user that it is C{player}'s turn
2035
@param gameId: Game ID
2036
@param player: Player who has control of the board
2039
command = self.command.createGameTurnOtherCommand(gameId, player)
2040
self.writeCommand( command )
2042
def gameTurnCurrent(self, gameId, time):
2044
Notify the user that he/she has control of the board
2046
@param gameId: Game ID
2047
@param time: Time left
2048
@see: L{pyscrabble.command.helper.GameCommand}
2051
command = self.command.createGameTurnCurrentCommand(gameId, time)
2052
self.writeCommand( command )
2054
def sendMoves(self, gameId, moves):
2056
Notify the user that C{moves} have been posted to a Game
2058
@param gameId: Game ID
2059
@param moves: List of Moves
2060
@see: L{pyscrabble.game.pieces.Move}
2063
command = self.command.createGameSendMoveCommand(gameId, moves)
2064
self.writeCommand( command )
2066
def acceptMove(self, gameId):
2068
Notify the user that their submitted moves have been accepted
2070
@param gameId: Game ID
2073
command = self.command.createGameAcceptMoveCommand(gameId)
2074
self.writeCommand( command )
2076
def gameError(self, gameId, msg):
2078
Notify the user of a Game Error
2080
@param gameId: Game ID
2081
@param msg: Error message
2082
@see: L{pyscrabble.command.helper.GameCommand}
2085
command = self.command.createGameErrorCommand(gameId, msg)
2086
self.writeCommand( command )
2088
def showError(self, msg):
2090
Show a General Error message
2092
@param msg: Error message
2095
command = self.command.createErrorCommand(msg)
2096
self.writeCommand( command )
2098
def showInfo(self, msg):
2100
Show a General Info message
2102
@param msg: Info message
2105
command = self.command.createInfoCommand(msg)
2106
self.writeCommand( command )
2108
def gameLeave(self, gameId, disableChat = False):
2110
Notify the user that they are leaving the game
2112
@param gameId: Game ID
2113
@see: L{pyscrabble.command.helper.GameCommand}
2116
command = self.command.createGameLeaveCommand(gameId, disableChat)
2117
self.writeCommand( command )
2119
def gameBoot(self, gameId):
2121
Boot a user from the game
2123
@param gameId: Game ID
2124
@see: L{pyscrabble.command.helper.GameCommand}
2127
command = self.command.createGameBootCommand(gameId)
2128
self.writeCommand( command )
2130
def gameOver(self, gameId):
2132
Notify the user the game is over
2134
@param gameId: Game ID
2135
@see: L{pyscrabble.command.helper.GameCommand}
2138
command = self.command.createGameOverCommand(gameId)
2139
self.writeCommand( command )
2141
def gameInfo(self, gameId, tup):
2143
Send a Game Info message
2145
@param gameId: Game ID
2146
@param tup: A Tuple containing (boolean, message). If boolean is true, it is a Server info Message. Else it is a Player info message.
2147
@see: L{pyscrabble.net.server.ServerFactory.sendGameInfoMessage}
2150
command = self.command.createGameInfoCommand(gameId, tup)
2151
self.writeCommand( command )
2153
def pauseGame(self, gameId):
2155
Notify the user that the Game is paused
2157
@param gameId: Game ID
2158
@see: L{pyscrabble.command.helper.GameCommand}
2161
command = self.command.createGamePauseCommand(gameId)
2162
self.writeCommand( command )
2164
def unPauseGame(self, gameId):
2166
Notify the user that the Game is unpaused
2168
@param gameId: Game ID
2171
command = self.command.createGameUnpauseCommand(gameId)
2172
self.writeCommand( command )
2174
def connectionLost(self, reason):
2176
This users client has been disconnected.
2178
Remove them from the server
2180
@param reason: Failure
2181
@see: L{twisted.python.failure.Failure}
2183
command = self.command.createLeaveChatCommand()
2184
command.setData( isinstance(reason.value, error.ConnectionDone) )
2185
self.factory.handleChatCommand(command, self)
2189
Log the user out of the game
2191
@see: L{pyscrabble.command.helper.LoginCommand}
2194
command = self.command.createGoodbyeCommand()
2195
self.writeCommand( command )
2199
Remove the user from the game
2201
@see: L{pyscrabble.command.helper.LoginCommand}
2204
command = self.command.createBootedCommand()
2205
self.writeCommand( command )
2207
def sendPrivateMessage(self, sender, data):
2209
Send a private message to this user
2211
@param sender: Username of the sender
2212
@param data: Message text
2213
@see: L{pyscrabble.command.helper.PrivateMessageCommand}
2216
command = self.command.createPrivateMessageCommand(sender, '', data)
2217
self.writeCommand( command )
2219
def setSpectatorChatEnabled(self, gameId, flag):
2221
Set Spectator Chat Enabeld
2223
@param gameId: Game ID
2224
@param flag: True to enable Spectator Chatting
2226
command = self.command.createGameSpectatorChatCommand(gameId, flag)
2227
self.writeCommand( command )
2229
def setSpectatorsAllowed(self, gameId, flag):
2231
Set Spectators allowed
2233
@param gameId: Game ID
2234
@param flag: True to allow Spectators
2236
command = self.command.createGameSpectatorSetCommand(gameId, flag)
2237
self.writeCommand( command )
2239
def sendGameStats(self, gameId, stats):
2243
@param gameId: Game ID
2246
command = self.command.createGameStatsCommand(gameId, stats)
2247
self.writeCommand( command )
2249
def sendGameOptions(self, gameId, options):
2253
@param gameId: Game ID
2254
@param options: Pptions
2256
command = self.command.createGameSendOptionsCommand(gameId, options)
2257
self.writeCommand( command )
2259
def gameBagEmpty(self, gameId):
2261
Notify the client that the game bag is empty
2263
@param gameId: Game ID
2265
command = self.command.createGameBagEmptyCommand(gameId)
2266
self.writeCommand( command )
2268
def gameSendSpectators(self, gameId, list):
2270
Send the list of spectators in a game
2272
@param gameId: Game ID
2275
command = self.command.createGameSendSpectatorsCommand(gameId, list)
2276
self.writeCommand( command )
2278
def sendUserInfo(self, user):
2282
@param user: Username
2284
command = self.command.createUserInfoCommand(user.getUsername(), user)
2285
self.writeCommand( command )
2287
def sendServerStats(self, stats):
2293
command = self.command.createServerStatsCommand(stats)
2294
self.writeCommand( command )
2296
def sendOfflineMessages(self, messages):
2298
Send offline messages
2300
@param messages: List of PrivateMessages
2302
command = self.command.createGetMessagesCommand(messages)
2303
self.writeCommand( command )
2306
def writeCommand(self, command):
2308
Write the command data to the client
2312
dumped = serialize.dumps(command)
2313
x = zlib.compress( dumped )
2314
logger.debug('Outgoing: %s %s %s' % (self.username, self.addr.host,str(command)))
2315
self.sendString( x )
2319
String representation of this Protocol.
2323
return "%s (%s) " % (self.username, self.addr.host)