-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathlobby_server.py
165 lines (136 loc) · 6.04 KB
/
lobby_server.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
#!./virt_env/bin/python
from game_server import Game, \
PlayerSocketProtocol, \
PlayerSocketFactory
from twisted.internet import reactor
from twisted.web.server import Site
from twisted.web.static import File
from autobahn.wamp1.protocol import WampServerFactory, \
WampServerProtocol
from autobahn.twisted.websocket import WebSocketServerProtocol, \
WebSocketServerFactory, \
listenWS
import re
"""
For deployment we'll be using the server's addresses, but if we're just
doing testing it's more convenient to run everything on your own machine.
Comment and uncomment the following lines as needed.
"""
# domain = "tetramor.ph"
domain = "localhost"
print("Using domain: " + domain)
lobbychannel = "http://%s/lobby" % domain
gamechannel = "http://%s/gamechat/" % domain
playeruri = "ws://%s:9002" % domain
wampuri = "ws://%s:9001" % domain
wsuri = "ws://%s:9000" % domain
MAX_NAMELEN = 21
class PubSubProtocol(WampServerProtocol):
"""
A pub/sub server that performs two functions: allows for chat between
clients, and publishes updates to lobby data.
"""
def onSessionOpen(self):
self.registerForPubSub(lobbychannel)
self.registerForPubSub(gamechannel, True)
class LobbyDataProtocol(WebSocketServerProtocol):
"""
A websocket for individual clients to push lobby data to the server.
"""
def onConnect(self, request):
self.sendMessage("Server socket established")
# Push an update for the new user.
# This is inefficient so fix it if it becomes a problem.
self.factory.pushUpdate()
WebSocketServerProtocol.onConnect(self, request)
def onMessage(self, payload, isBinary):
"""
Parse and respond to commands from the client.
The payload is a JS list which is formatted here as a comma-delimited string.
The first field is a command and the rest is the argument.
Change this as needed but make sure the frontend knows!
"""
if not isBinary:
print("Got message:", payload, "from", self.peer)
part = payload.partition(',')
if part[0] == 'setname':
response = self.factory.register(self, part[2])
self.sendMessage(response)
elif part[0] == 'requpdate':
self.factory.pushUpdate()
else:
print("Malformed command:", payload, "from", self.peer)
def connectionLost(self, reason):
WebSocketServerProtocol.connectionLost(self, reason)
self.factory.deregister(self)
class LobbyDataFactory(WebSocketServerFactory):
def __init__(self, url, gameList, wampdispatch, debug = False, debugCodePaths = False):
WebSocketServerFactory.__init__(self, url, debug = debug, \
debugCodePaths = debugCodePaths)
self.usernames = {None : 'Server'} # A map of users to their usernames
self.rooms = gameList # List of active rooms
self.wampdispatch = wampdispatch # Dispatch method for our pub/sub
def register(self, user, username):
"""
Try to register a username to a user.
Returns:
'good': if the requested username is allowed
'taken': if the username is already being used by another user
'collision': if the user already has a username - this shouldn't happen
"""
if not user in self.usernames:
if not username in self.usernames.values():
if re.match('^[a-zA-Z0-9_]+$', username) and len(username) <= MAX_NAMELEN:
print("Registering user", user.peer, "as", username)
self.usernames[user] = username
self.wampdispatch(lobbychannel, {'type':'chat', 'user':'Server', 'msg':(username + ' has joined.')})
return 'good'
else:
return 'bad'
else:
return 'taken'
else:
return 'collision'
def deregister(self, user):
""" Just takes the username out of the registry when the user leaves """
if user in self.usernames:
print("Deregistering user", self.usernames[user])
del self.usernames[user]
def pushUpdate(self):
"""
Dispatch an update to the lobby channel
This update contains a complete list of active game rooms
"""
print("Pushing update to server!")
self.cleanEmptyRooms()
roomData = [ (room.roomName, room.getPlayerCount()) for room in self.rooms.values() ]
self.wampdispatch(lobbychannel, {'type': 'update', 'data' : roomData})
def cleanEmptyRooms(self):
""" Remove empty games from the room list """
for key in self.rooms.keys():
if self.rooms[key].getPlayerCount() <= 0:
self.rooms.pop(key, None)
if __name__ == '__main__':
import sys
from twisted.python import log
log.startLogging(sys.stdout)
gameList = {}
# Initialize pub/sub factory
psfactory = WampServerFactory(wampuri, debugWamp = True)
psfactory.protocol = PubSubProtocol
listenWS(psfactory)
# Initialize client socket factory
clientfactory = LobbyDataFactory(wsuri, gameList, psfactory.dispatch, debug = False)
clientfactory.protocol = LobbyDataProtocol
# Initialize player socket factory
playerfactory = PlayerSocketFactory(playeruri, gameList, clientfactory.pushUpdate, psfactory.dispatch, gamechannel, debug = False)
playerfactory.protocol = PlayerSocketProtocol
# Initialize web server factory on port 8080
# We're not using this yet but maybe we will in the future?
# webfactory = Site(File('.'))
# reactor.listenTCP(8080, webfactory)
# Start the reactor!
# Because I'm a professional, I'm not going to quote Total Recall here...
reactor.listenTCP(9000, clientfactory)
reactor.listenTCP(9002, playerfactory)
reactor.run()