PageRenderTime 56ms CodeModel.GetById 20ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/fiveserver/py/fiveserver/protocol/pes6.py

https://bitbucket.org/juce/fiveserver
Python | 1086 lines | 1028 code | 30 blank | 28 comment | 48 complexity | aeddd3f33df0371231fc5b59a8e34146 MD5 | raw file
Possible License(s): BSD-3-Clause
  1. """
  2. Protocol implementations for PES6
  3. """
  4. from twisted.internet import reactor, defer
  5. from twisted.application import service
  6. from twisted.web import client
  7. from Crypto.Cipher import Blowfish
  8. from datetime import datetime, timedelta
  9. from hashlib import md5
  10. import binascii
  11. import struct
  12. import time
  13. import re
  14. import zlib
  15. from fiveserver.model import packet, user, lobby, util
  16. from fiveserver.model.util import PacketFormatter
  17. from fiveserver import log, stream, errors
  18. from fiveserver.protocol import PacketDispatcher, isSameGame
  19. from fiveserver.protocol import pes5
  20. CHAT_HISTORY_DELAY = 3 # seconds
  21. ERRORS = [
  22. '\xff\xff\xfd\xb6', # owner cancelled
  23. '\xff\xff\xfd\xbb', # only 4 players can participate
  24. '\xff\xff\xfe\x00', # deadline passed
  25. ]
  26. def getHomePlayerNames(match):
  27. home_players = [match.teamSelection.home_captain]
  28. home_players.extend(match.teamSelection.home_more_players)
  29. return ','.join([x.name for x in home_players])
  30. def getAwayPlayerNames(match):
  31. away_players = [match.teamSelection.away_captain]
  32. away_players.extend(match.teamSelection.away_more_players)
  33. return ','.join([x.name for x in away_players])
  34. class NewsProtocol(pes5.NewsProtocol):
  35. """
  36. News-service for PES6
  37. """
  38. GREETING = {
  39. 'title': 'SYSTEM: Fiveserver v%s',
  40. 'text': ('Welcome to Fiveserver -\r\n'
  41. 'independent community server\r\n'
  42. 'supporting PES6/WE2007 games.\r\n'
  43. 'Have a good time, play some nice\r\n'
  44. 'football and try to score goals.\r\n'
  45. '\r\n'
  46. 'Credits:\r\n'
  47. 'Protocol analysis: reddwarf, juce\r\n'
  48. 'Server programming: juce, reddwarf')
  49. }
  50. SERVER_NAME = 'Fiveserver'
  51. NEW_FEATURES = {
  52. '0.4.1': (
  53. 'NEW features in 0.4.1:',
  54. '* introducing PES6 support!\r\n')
  55. }
  56. def register(self):
  57. pes5.NewsProtocol.register(self)
  58. self.addHandler(0x2200, self.getWebServerList_2200)
  59. def getServerList_2005(self, pkt):
  60. myport = self.transport.getHost().port
  61. gameName = None
  62. for name,port in self.factory.serverConfig.GamePorts.items():
  63. if port == myport:
  64. gameName = name
  65. break
  66. serverIP = self.factory.configuration.serverIP_wan
  67. servers = [
  68. (-1,2,'LOGIN',serverIP,
  69. self.factory.serverConfig.NetworkServer['loginService'][gameName],
  70. 0,2),
  71. (-1,3,self.SERVER_NAME.encode('utf-8'),serverIP,
  72. self.factory.serverConfig.NetworkServer['mainService'],
  73. max(0, self.factory.getNumUsersOnline()-1),3),
  74. (-1,8,'NETWORK_MENU',serverIP,
  75. self.factory.serverConfig.NetworkServer['networkMenuService'],
  76. 0,8),
  77. ]
  78. data = ''.join(['%s%s%s%s%s%s%s' % (
  79. struct.pack('!i',a),
  80. struct.pack('!i',b),
  81. '%s%s' % (name,'\0'*(32-len(name[:32]))),
  82. '%s%s' % (ip,'\0'*(15-len(ip))),
  83. struct.pack('!H',port),
  84. struct.pack('!H',c),
  85. struct.pack('!H',d)) for a,b,name,ip,port,c,d in servers])
  86. self.sendZeros(0x2002,4)
  87. self.sendData(0x2003,data)
  88. self.sendZeros(0x2004,4)
  89. def getWebServerList_2200(self, pkt):
  90. self.sendZeros(0x2201,4)
  91. #self.sendData(0x2202,data) #TODO
  92. self.sendZeros(0x2203,4)
  93. class RosterHandler:
  94. """
  95. Provide means of extracting roster hash
  96. from the client auth packet data.
  97. """
  98. def getRosterHash(self, pkt_data):
  99. return pkt_data[58:74]
  100. class LoginService(RosterHandler, pes5.LoginService):
  101. """
  102. Login-service for PES6
  103. """
  104. @defer.inlineCallbacks
  105. def getProfiles_3010(self, pkt):
  106. if self.factory.serverConfig.ShowStats:
  107. results = yield defer.DeferredList([
  108. self.factory.matchData.getGames(
  109. profile.id) for profile in self._user.profiles])
  110. profiles = self._user.profiles
  111. else:
  112. # hide all stats
  113. results = yield defer.succeed([(True, 0)
  114. for profile in self._user.profiles])
  115. profiles = [self.makePristineProfile(profile)
  116. for profile in self._user.profiles]
  117. data = '\0'*4 + ''.join([
  118. '%(index)s%(id)s%(name)s%(playTime)s'
  119. '%(division)s%(points)s%(rating)s%(games)s' % {
  120. 'index':struct.pack('!B', i),
  121. 'id':struct.pack('!i', profile.id),
  122. 'name':util.padWithZeros(profile.name, 48),
  123. 'division':struct.pack('!B',
  124. self.factory.ratingMath.getDivision(profile.points)),
  125. 'playTime':struct.pack('!i', profile.playTime.seconds),
  126. 'points':struct.pack('!i', profile.points),
  127. 'games':struct.pack('!H', games),
  128. 'rating':struct.pack('!H',profile.rating),
  129. }
  130. for (_, games), (i, profile) in zip(
  131. results, enumerate(profiles))])
  132. self.sendData(0x3012, data)
  133. defer.returnValue(None)
  134. def getMatchResults_3070(self, pkt):
  135. self.sendZeros(0x3071,4)
  136. self.sendZeros(0x3073,4)
  137. def do_3120(self, pkt):
  138. self.sendZeros(0x3121,4)
  139. self.sendZeros(0x3123,0)
  140. class LoginServicePES6(LoginService):
  141. """
  142. Specific implementation of login service for PES6
  143. """
  144. def __init__(self):
  145. LoginService.__init__(self)
  146. self.gameName = 'pes6'
  147. class LoginServiceWE2007(LoginService):
  148. """
  149. Specific implementation of login service for WE2007
  150. """
  151. def __init__(self):
  152. LoginService.__init__(self)
  153. self.gameName = 'we2007'
  154. class NetworkMenuService(RosterHandler, pes5.NetworkMenuService):
  155. """
  156. PES6 implementation.
  157. The service that communicates with the player, when
  158. he/she is in the "NETWORK MENU" mode.
  159. """
  160. class MainService(RosterHandler, pes5.MainService):
  161. """
  162. PES6 implementation
  163. The main game server, which keeps track of matches, goals
  164. and other important statistics.
  165. """
  166. @defer.inlineCallbacks
  167. def connectionLost(self, reason):
  168. pes5.LoginService.connectionLost(self, reason)
  169. if self._user.state:
  170. room = self._user.state.room
  171. if room:
  172. room.cancelParticipation(self._user)
  173. yield self.exitingRoom(room, self._user)
  174. # update participation of remaining players in room
  175. data = self.formatRoomParticipationStatus(room)
  176. for player in room.players:
  177. player.sendData(0x4365, data)
  178. self.exitingLobby(self._user)
  179. def formatPlayerInfo(self, usr, roomId, stats=None):
  180. if stats is None:
  181. stats = user.Stats(usr.profile.id, 0,0,0,0,0,0,0)
  182. return ('%(id)s%(name)s%(groupid)s%(groupname)s'
  183. '%(groupmemberstatus)s%(division)s%(roomid)s'
  184. '%(points)s%(rating)s%(matches)s%(wins)s'
  185. '%(losses)s%(draws)s%(pad1)s' % {
  186. 'id': struct.pack('!i',usr.profile.id),
  187. 'name': util.padWithZeros(usr.profile.name,48),
  188. 'groupid': struct.pack('!i',0),
  189. 'groupname': '\0'*48,
  190. 'groupmemberstatus': struct.pack('!B',0),
  191. 'division': struct.pack('!B',
  192. self.factory.ratingMath.getDivision(usr.profile.points)),
  193. 'roomid': struct.pack('!i',roomId),
  194. 'points': struct.pack('!i',usr.profile.points),
  195. 'rating': struct.pack('!H',0),
  196. 'matches': struct.pack('!H',
  197. stats.wins + stats.losses + stats.draws),
  198. 'wins': struct.pack('!H',stats.wins),
  199. 'losses': struct.pack('!H',stats.losses),
  200. 'draws': struct.pack('!H',stats.draws),
  201. 'pad1': '\0'*3,
  202. })
  203. def formatProfileInfo(self, profile, stats):
  204. if not self.factory.serverConfig.ShowStats:
  205. profile = self.makePristineProfile(profile)
  206. return ('%(id)s%(name)s%(groupid)s%(groupname)s'
  207. '%(groupmemberstatus)s%(division)s'
  208. '%(points)s%(rating)s%(matches)s'
  209. '%(wins)s%(losses)s%(draws)s%(win-strk)s'
  210. '%(win-best)s%(disconnects)s'
  211. '%(goals-scored)s%(goals-allowed)s'
  212. '%(comment)s%(rank)s'
  213. '%(competition-gold-medals)s%(competition-silver-medals)s'
  214. '%(unknown1)s'
  215. '%(winnerscup-gold-medals)s%(winnerscup-silver-medals)s'
  216. '%(unknown2)s%(unknown3)s'
  217. '%(language)s%(recent-used-teams)s' % {
  218. 'id': struct.pack('!i',profile.id),
  219. 'name': util.padWithZeros(profile.name,48),
  220. 'groupid': struct.pack('!i',0),
  221. 'groupname': util.padWithZeros('Playmakers',48),
  222. 'groupmemberstatus': struct.pack('!B',1),
  223. 'division': struct.pack('!B',
  224. self.factory.ratingMath.getDivision(profile.points)),
  225. 'points': struct.pack('!i',profile.points),
  226. 'rating': struct.pack('!H',profile.rating),
  227. 'matches': struct.pack('!H',
  228. stats.wins + stats.losses + stats.draws),
  229. 'wins': struct.pack('!H',stats.wins),
  230. 'losses': struct.pack('!H',stats.losses),
  231. 'draws': struct.pack('!H',stats.draws),
  232. 'win-strk': struct.pack('!H', stats.streak_current),
  233. 'win-best': struct.pack('!H', stats.streak_best),
  234. 'disconnects': struct.pack(
  235. '!H', profile.disconnects),
  236. 'goals-scored': struct.pack('!i', stats.goals_scored),
  237. 'goals-allowed': struct.pack('!i', stats.goals_allowed),
  238. 'comment': util.padWithZeros((
  239. profile.comment or 'Fiveserver rules!'), 256),
  240. 'rank': struct.pack('!i',profile.rank),
  241. 'competition-gold-medals': struct.pack('!H', 0),
  242. 'competition-silver-medals': struct.pack('!H', 0),
  243. 'unknown1': struct.pack('!H', 0),
  244. 'winnerscup-gold-medals': struct.pack('!H', 0),
  245. 'winnerscup-silver-medals': struct.pack('!H', 0),
  246. 'unknown2': struct.pack('!H', 0),
  247. 'unknown3': struct.pack('!B', 0),
  248. 'language': struct.pack('!B', 0),
  249. 'recent-used-teams': ''.join([struct.pack('!H', team)
  250. for team in stats.teams]) + '\xff\xff'*(5-len(stats.teams))
  251. })
  252. def formatHomeOrAway(self, room, usr):
  253. if room.teamSelection:
  254. return room.teamSelection.getHomeOrAway(usr)
  255. return 0xff
  256. def formatTeamsAndGoals(self, room):
  257. homeTeam, awayTeam = 0xffff, 0xffff
  258. if room.teamSelection:
  259. homeTeam = (room.teamSelection.home_team_id
  260. if room.teamSelection.home_team_id != None else 0xffff)
  261. awayTeam = (room.teamSelection.away_team_id
  262. if room.teamSelection.away_team_id != None else 0xffff)
  263. (homeGoals1st, homeGoals2nd, homeGoalsEt1,
  264. homeGoalsEt2, homeGoalsPen) = 0, 0, 0, 0, 0
  265. (awayGoals1st, awayGoals2nd, awayGoalsEt1,
  266. awayGoalsEt2, awayGoalsPen) = 0, 0, 0, 0, 0
  267. if room.match:
  268. homeGoals1st = room.match.score_home_1st
  269. homeGoals2nd = room.match.score_home_2nd
  270. homeGoalsEt1 = room.match.score_home_et1
  271. homeGoalsEt2 = room.match.score_home_et2
  272. homeGoalsPen = room.match.score_home_pen
  273. awayGoals1st = room.match.score_away_1st
  274. awayGoals2nd = room.match.score_away_2nd
  275. awayGoalsEt1 = room.match.score_away_et1
  276. awayGoalsEt2 = room.match.score_away_et2
  277. awayGoalsPen = room.match.score_away_pen
  278. return '%s%s%s%s%s%s%s%s%s%s%s%s' % (
  279. struct.pack('!H', homeTeam),
  280. struct.pack('!B', homeGoals1st), # 1st
  281. struct.pack('!B', homeGoals2nd), # 2nd
  282. struct.pack('!B', homeGoalsEt1), # et1
  283. struct.pack('!B', homeGoalsEt2), # et2
  284. struct.pack('!B', homeGoalsPen), # pen
  285. struct.pack('!H', awayTeam),
  286. struct.pack('!B', awayGoals1st), # 1st
  287. struct.pack('!B', awayGoals2nd), # 2nd
  288. struct.pack('!B', awayGoalsEt1), # et1
  289. struct.pack('!B', awayGoalsEt2), # et2
  290. struct.pack('!B', awayGoalsPen)) # pen
  291. def formatRoomInfo(self, room):
  292. n = len(room.players)
  293. if room.match:
  294. match_state = room.match.state
  295. match_clock = room.match.clock
  296. else:
  297. match_state, match_clock = 0, 0
  298. return '%s%s%s%s%s%s%s%s%s%s%s' % (
  299. struct.pack('!i',room.id),
  300. struct.pack('!B',room.phase),
  301. struct.pack('!B',match_state),
  302. util.padWithZeros(room.name,64),
  303. struct.pack('!B',match_clock),
  304. ''.join(['%s%s%s%s%s%s%s' % (
  305. struct.pack('!i',usr.profile.id),
  306. struct.pack('!B',room.isOwner(usr)),
  307. # matchstarter or 1st host?
  308. struct.pack('!B',room.isMatchStarter(usr)),
  309. struct.pack('!B',self.formatHomeOrAway(room, usr)), # team
  310. struct.pack('!B',usr.state.spectator), # spectator
  311. struct.pack('!B',room.getPlayerPosition(usr)), # pos in room
  312. struct.pack('!B',room.getPlayerParticipate(usr))) # participate
  313. for usr in room.players]),
  314. '\0\0\0\0\0\0\xff\0\0\xff'*(4-n), # empty players
  315. self.formatTeamsAndGoals(room),
  316. '\0', #padding
  317. struct.pack('!B', int(room.usePassword)), # room locked
  318. '\0\x02\0\0') # competition flag, match chat setting, 2 unknowns
  319. def formatRoomParticipationStatus(self, room):
  320. """
  321. Used to format the 0x4365 payload
  322. """
  323. n = len(room.players)
  324. data = '%s%s' % (
  325. ''.join(['%s%s%s' % (
  326. struct.pack('!i',usr.profile.id),
  327. struct.pack('!B',room.getPlayerPosition(usr)),
  328. struct.pack('!B',room.getPlayerParticipate(usr)))
  329. for usr in room.players]),
  330. '\0\0\0\0\0\xff'*(4-n))
  331. return data
  332. def becomeSpectator_4366(self, pkt):
  333. self._user.state.spectator = 1
  334. self.sendZeros(0x4367, 4)
  335. def do_4351(self, pkt):
  336. """
  337. Contains connection information of playing players
  338. Received from hosting player
  339. Send to possible spectators
  340. """
  341. data = '%s' % (pkt.data)
  342. room = self._user.state.room
  343. if room:
  344. spectatingPlayers = (player for player in room.players
  345. if player not in room.participatingPlayers)
  346. for player in spectatingPlayers:
  347. player.sendData(0x4351, data)
  348. self.sendZeros(0x4352, 4)
  349. def backToMatchMenu_4383(self, pkt):
  350. """
  351. Contains old,added,new points & rating
  352. For players and groups
  353. """
  354. room = self._user.state.room
  355. n = len(room.participatingPlayers)
  356. data = '\0\0\0\0%s%s%s' % (
  357. ''.join(['%s%s%s%s%s%s%s%s%s' % (
  358. struct.pack('!i',usr.profile.id),
  359. struct.pack('!H',0), # added points
  360. struct.pack('!i',0), # new points
  361. struct.pack('!H',0), # ?
  362. struct.pack('!H',0), # ?
  363. struct.pack('!H',0), # ?
  364. struct.pack('!H',0), # ?
  365. struct.pack('!H',0), # new rating
  366. struct.pack('!H',0)) # old rating
  367. for usr in room.participatingPlayers]),
  368. '\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0'*(4-n),
  369. ''.join('%s%s%s%s%s%s' % (
  370. struct.pack('!i',0), # group1 id
  371. struct.pack('!H',0), # group1 added points
  372. struct.pack('!i',0), # group1 new points
  373. struct.pack('!i',0), # group2 id
  374. struct.pack('!H',0), # group2 added points
  375. struct.pack('!i',0)))) # group2 new points
  376. self.sendData(0x4384, data)
  377. def quickGameSearch_6020(self, pkt):
  378. self.sendZeros(0x6021,0)
  379. def getStunInfo_4345(self, pkt):
  380. self.sendZeros(0x4346, 0)
  381. roomId = struct.unpack('!i',pkt.data[0:4])[0]
  382. thisLobby = self.factory.getLobbies()[self._user.state.lobbyId]
  383. room = thisLobby.getRoomById(roomId)
  384. if room is not None:
  385. # send stun info of players in room to requester
  386. for usr in room.players:
  387. data = ('%(pad1)s%(ip1)s%(port1)s'
  388. '%(ip2)s%(port2)s%(id)s'
  389. '%(someField)s%(participate)s') % {
  390. 'pad1': '\0'*32,
  391. 'ip1': util.padWithZeros(usr.state.ip1, 16),
  392. 'port1': struct.pack('!H', usr.state.udpPort1),
  393. 'ip2': util.padWithZeros(usr.state.ip2, 16),
  394. 'port2': struct.pack('!H', usr.state.udpPort2),
  395. 'id': struct.pack('!i', usr.profile.id),
  396. 'someField': struct.pack('!H', 0),
  397. 'participate': struct.pack('!B',
  398. room.getPlayerParticipate(usr)),
  399. }
  400. self.sendData(0x4347, data)
  401. self.do_4330(room)
  402. self.sendZeros(0x4348, 0)
  403. def chat_4400(self, pkt):
  404. thisLobby = self.factory.getLobbies()[self._user.state.lobbyId]
  405. chatType = pkt.data[0:2]
  406. message = util.stripZeros(pkt.data[10:])
  407. data = '%s%s%s%s%s' % (
  408. chatType,
  409. pkt.data[2:6],
  410. struct.pack('!i',self._user.profile.id),
  411. util.padWithZeros(self._user.profile.name,48),
  412. #util.padWithZeros(message, 128))
  413. message[:126]+'\0\0')
  414. if chatType=='\x00\x01':
  415. # add to lobby chat history
  416. thisLobby.addToChatHistory(
  417. lobby.ChatMessage(self._user.profile, message))
  418. # lobby chat
  419. for usr in thisLobby.players.itervalues():
  420. usr.sendData(0x4402, data)
  421. elif chatType=='\x01\x08':
  422. # room chat
  423. room = self._user.state.room
  424. if room:
  425. for usr in room.players:
  426. usr.sendData(0x4402, data)
  427. elif chatType=='\x00\x02':
  428. # private message
  429. profileId = struct.unpack('!i',pkt.data[6:10])[0]
  430. usr = thisLobby.getPlayerByProfileId(profileId)
  431. if usr:
  432. # add to lobby chat history
  433. thisLobby.addToChatHistory(
  434. lobby.ChatMessage(
  435. self._user.profile, message, usr.profile,
  436. pkt.data[2:6]))
  437. usr.sendData(0x4402, data)
  438. if usr != self._user:
  439. self._user.sendData(0x4402, data) # echo to self
  440. else:
  441. log.msg(
  442. 'WARN: user with profile id = '
  443. '%d not found.' % profileId)
  444. elif chatType=='\x01\x05':
  445. # match chat
  446. room = self._user.state.room
  447. if room:
  448. for usr in room.players:
  449. usr.sendData(0x4402, data)
  450. elif chatType=='\x01\x07':
  451. # stadium chat
  452. room = self._user.state.room
  453. if room:
  454. for usr in room.players:
  455. usr.sendData(0x4402, data)
  456. def sendChatHistory(self, aLobby, who):
  457. if aLobby is None or who is None:
  458. return
  459. for chatMessage in list(aLobby.chatHistory):
  460. chatType = '\0\1'
  461. if chatMessage.toProfile is not None:
  462. if who.profile.id not in [
  463. chatMessage.fromProfile.id, chatMessage.toProfile.id]:
  464. continue
  465. special = chatMessage.special
  466. else:
  467. special = '\0\0\0\0'
  468. data = '%s%s%s%s%s' % (
  469. chatType,
  470. special,
  471. struct.pack('!i', chatMessage.fromProfile.id),
  472. util.padWithZeros(chatMessage.fromProfile.name,48),
  473. chatMessage.text[:126]+'\0\0')
  474. who.sendData(0x4402, data)
  475. def broadcastSystemChat(self, aLobby, text):
  476. chatMessage = lobby.ChatMessage(lobby.SYSTEM_PROFILE, text)
  477. for usr in aLobby.players.itervalues():
  478. data = '%s%s%s%s%s' % (
  479. '\0\1',
  480. '\0\0\0\0',
  481. struct.pack('!i', chatMessage.fromProfile.id),
  482. util.padWithZeros(chatMessage.fromProfile.name,48),
  483. chatMessage.text[:126]+'\0\0')
  484. usr.sendData(0x4402, data)
  485. aLobby.addToChatHistory(chatMessage)
  486. def broadcastRoomChat(self, room, text):
  487. chatMessage = lobby.ChatMessage(lobby.SYSTEM_PROFILE, text)
  488. for usr in room.players:
  489. data = '%s%s%s%s%s' % (
  490. '\x01\x08',
  491. '\0\0\0\0',
  492. struct.pack('!i', chatMessage.fromProfile.id),
  493. util.padWithZeros(chatMessage.fromProfile.name,48),
  494. chatMessage.text[:126]+'\0\0')
  495. usr.sendData(0x4402, data)
  496. def sendRoomUpdate(self, room):
  497. thisLobby = self.factory.getLobbies()[self._user.state.lobbyId]
  498. data = self.formatRoomInfo(room)
  499. for usr in thisLobby.players.itervalues():
  500. usr.sendData(0x4306,data)
  501. @defer.inlineCallbacks
  502. def sendPlayerUpdate(self, roomId):
  503. thisLobby = self.factory.getLobbies()[self._user.state.lobbyId]
  504. stats = yield self.getStats(self._user.profile.id)
  505. data = self.formatPlayerInfo(self._user, roomId, stats)
  506. for usr in thisLobby.players.itervalues():
  507. usr.sendData(0x4222,data)
  508. @defer.inlineCallbacks
  509. def getUserList_4210(self, pkt):
  510. self.sendZeros(0x4211,4)
  511. thisLobby = self.factory.getLobbies()[self._user.state.lobbyId]
  512. for usr in thisLobby.players.itervalues():
  513. if usr.state.inRoom == 1:
  514. roomId = usr.state.room.id
  515. else:
  516. roomId = 0
  517. stats = yield self.getStats(usr.profile.id)
  518. data = self.formatPlayerInfo(usr, roomId, stats)
  519. self.sendData(0x4212,data)
  520. self.sendZeros(0x4213,4)
  521. yield defer.succeed(None)
  522. def createRoom_4310(self, pkt):
  523. thisLobby = self.factory.getLobbies()[self._user.state.lobbyId]
  524. roomName = util.stripZeros(pkt.data[0:64])
  525. try:
  526. existing = thisLobby.getRoom(roomName)
  527. self.sendData(0x4311,'\xff\xff\xff\x10')
  528. except KeyError:
  529. room = lobby.Room(thisLobby)
  530. room.name = roomName
  531. room.usePassword = struct.unpack('!B',pkt.data[64:65])[0] == 1
  532. if room.usePassword:
  533. room.password = util.stripZeros(pkt.data[65:80])
  534. # put room creator into the room
  535. room.enter(self._user)
  536. # add room to the lobby
  537. thisLobby.addRoom(room)
  538. log.msg('Room created: %s' % str(room))
  539. # notify all users in the lobby about the new room
  540. self.sendRoomUpdate(room)
  541. # notify all users in the lobby that player is now in a room
  542. self.sendPlayerUpdate(room.id)
  543. self.sendZeros(0x4311,4)
  544. def getRoomList_4300(self, pkt):
  545. self.sendZeros(0x4301,4)
  546. thisLobby = self.factory.getLobbies()[self._user.state.lobbyId]
  547. for room in thisLobby.rooms.itervalues():
  548. data = self.formatRoomInfo(room)
  549. self.sendData(0x4302, data)
  550. self.sendZeros(0x4303,4)
  551. def setOwner_4349(self, pkt):
  552. newOwnerProfileId = struct.unpack('!i',pkt.data[0:4])[0]
  553. thisLobby = self.factory.getLobbies()[self._user.state.lobbyId]
  554. room = self._user.state.room
  555. if room:
  556. usr = thisLobby.getPlayerByProfileId(newOwnerProfileId)
  557. if not usr:
  558. log.msg('WARN: player %s cannot become owner: not in the room.')
  559. else:
  560. room.setOwner(usr)
  561. self.sendRoomUpdate(room)
  562. self.sendZeros(0x434a,4)
  563. def setRoomName_434d(self, pkt):
  564. newName = util.stripZeros(pkt.data[0:63])
  565. thisLobby = self.factory.getLobbies()[self._user.state.lobbyId]
  566. room = self._user.state.room
  567. data = '\0\0\0\0'
  568. if room:
  569. if newName != room.name:
  570. # prevent renaming to existing rooms
  571. if thisLobby.isRoom(newName):
  572. data = '\xff\xff\xff\xff'
  573. else:
  574. thisLobby.renameRoom(room, newName)
  575. room.usePassword = struct.unpack('!B',pkt.data[64:65])[0] == 1
  576. if room.usePassword:
  577. room.password = util.stripZeros(pkt.data[65:80])
  578. self.sendRoomUpdate(room)
  579. self.sendData(0x434e,data)
  580. def do_4330(self, room):
  581. """
  582. Notify people INSIDE room of
  583. ip,ports and participation status
  584. """
  585. for otherUsr in room.players:
  586. if otherUsr == self._user:
  587. continue
  588. data = ('%(pad1)s%(ip1)s%(port1)s'
  589. '%(ip2)s%(port2)s%(id)s'
  590. '%(someField)s%(participate)s') % {
  591. 'pad1': '\0'*36,
  592. 'ip1': util.padWithZeros(self._user.state.ip1, 16),
  593. 'port1': struct.pack('!H', self._user.state.udpPort1),
  594. 'ip2': util.padWithZeros(self._user.state.ip2, 16),
  595. 'port2': struct.pack('!H', self._user.state.udpPort2),
  596. 'id': struct.pack('!i', self._user.profile.id),
  597. 'someField': struct.pack('!H', 0),
  598. 'participate': struct.pack('!B',
  599. room.getPlayerParticipate(self._user)),
  600. }
  601. otherUsr.sendData(0x4330, data)
  602. def joinRoom_4320(self, pkt):
  603. roomId = struct.unpack('!i',pkt.data[0:4])[0]
  604. thisLobby = self.factory.getLobbies()[self._user.state.lobbyId]
  605. room = thisLobby.getRoomById(roomId)
  606. if room is None:
  607. log.msg('ERROR: Room (id=%d) does not exist.' % roomId)
  608. self.sendData(0x4321,'\0\0\0\1')
  609. else:
  610. if room.usePassword:
  611. enteredPassword = util.stripZeros(pkt.data[4:19])
  612. if enteredPassword != room.password:
  613. log.msg(
  614. 'ERROR: Room (id=%d) password does not match.' % roomId)
  615. self.sendData(0x4321,'\xff\xff\xfd\xda')
  616. else:
  617. room.enter(self._user)
  618. else:
  619. room.enter(self._user)
  620. self.sendRoomUpdate(room)
  621. self.sendPlayerUpdate(room.id)
  622. data = '\0\0\0\0'
  623. if room.matchSettings:
  624. data += room.matchSettings.match_time
  625. self.sendData(0x4321, data)
  626. # give players in room stun of joiner
  627. # special 4330 packet
  628. self.do_4330(room)
  629. # give joiner stun of players in room
  630. self.sendZeros(0x4346, 0)
  631. for otherUsr in room.players:
  632. if otherUsr == self._user:
  633. continue
  634. data = ('%(pad1)s%(ip1)s%(port1)s'
  635. '%(ip2)s%(port2)s%(id)s'
  636. '%(someField)s%(participate)s') % {
  637. 'pad1': '\0'*32,
  638. 'ip1': util.padWithZeros(otherUsr.state.ip1, 16),
  639. 'port1': struct.pack('!H', otherUsr.state.udpPort1),
  640. 'ip2': util.padWithZeros(otherUsr.state.ip2, 16),
  641. 'port2': struct.pack('!H', otherUsr.state.udpPort2),
  642. 'id': struct.pack('!i', otherUsr.profile.id),
  643. 'someField': struct.pack('!H', 0),
  644. 'participate': struct.pack('!B',
  645. room.getPlayerParticipate(otherUsr)),
  646. }
  647. self.sendData(0x4347, data)
  648. self.sendZeros(0x4348, 0)
  649. def exitingLobby(self, usr):
  650. usrLobby = self.factory.getLobbies()[usr.state.lobbyId]
  651. usrLobby.exit(usr)
  652. # user now considered OFFLINE
  653. self.factory.userOffline(usr)
  654. # notify every remaining occupant in the lobby
  655. for otherUsr in usrLobby.players.itervalues():
  656. otherUsr.sendData(0x4221,struct.pack('!i', usr.profile.id))
  657. def exitingRoom(self, room, usr):
  658. usrLobby = self.factory.getLobbies()[usr.state.lobbyId]
  659. room = usr.state.room
  660. room.exit(usr)
  661. self.sendRoomUpdate(room)
  662. self.sendPlayerUpdate(room.id)
  663. self.sendZeros(0x432b,4)
  664. # destroy the room, if none left in it
  665. if room.isEmpty():
  666. # notify users in lobby that the room is gone
  667. data = struct.pack('!i',room.id)
  668. for otherUsr in usrLobby.players.itervalues():
  669. otherUsr.sendData(0x4305,data)
  670. usrLobby.deleteRoom(room)
  671. def exitRoom_432a(self, pkt):
  672. if self._user.state.inRoom == 0:
  673. log.msg('WARN: user not in a room.')
  674. self.sendZeros(0x432b,4)
  675. else:
  676. return self.exitingRoom(
  677. self._user.state.room, self._user)
  678. def toggleParticipate_4363(self, pkt):
  679. participate = (struct.unpack('!B', pkt.data[0])[0] == 1)
  680. room = self._user.state.room
  681. packetPayload = '\0\0\0\0' # success
  682. if room:
  683. if participate:
  684. # check roster-hash match with host
  685. rosterHashMismatch = False
  686. if room.participatingPlayers:
  687. gameHost = room.participatingPlayers[0]
  688. rosterHashMismatch = (
  689. room.lobby.checkRosterHash and not self.checkHashes(
  690. gameHost, self._user))
  691. if rosterHashMismatch:
  692. packetPayload = '\0\0\0\1'
  693. text = (
  694. 'Roster mismatch: %s vs %s. '
  695. 'Player %s cannot participate.' % (
  696. gameHost.profile.name,
  697. self._user.profile.name,
  698. self._user.profile.name))
  699. log.msg(text)
  700. self.broadcastRoomChat(room, text.encode('utf-8'))
  701. elif room.isForcedCancelledParticipation(self._user):
  702. packetPayload = '\xff\xff\xfd\xb6' # still cancelled
  703. else:
  704. room.participate(self._user)
  705. else:
  706. room.cancelParticipation(self._user)
  707. # share participation status with players in room
  708. data = self.formatRoomParticipationStatus(room)
  709. for player in room.players:
  710. player.sendData(0x4365, data)
  711. data = '%s%s%s' % (
  712. packetPayload,
  713. struct.pack('!B', participate),
  714. struct.pack('!B', room.getPlayerParticipate(self._user)))
  715. self.sendData(0x4364, data)
  716. def forcedCancelParticipation_4380(self, pkt):
  717. profileId = struct.unpack('!i',pkt.data[0:4])[0]
  718. thisLobby = self.factory.getLobbies()[self._user.state.lobbyId]
  719. room = self._user.state.room
  720. if room:
  721. usr = thisLobby.getPlayerByProfileId(profileId)
  722. room.cancelParticipation(usr)
  723. usr.state.timeCancelledParticipation = datetime.now()
  724. data = self.formatRoomParticipationStatus(room)
  725. for player in room.players:
  726. player.sendData(0x4365, data)
  727. self.sendZeros(0x4381,4)
  728. def startMatch_4360(self, pkt):
  729. thisLobby = self.factory.getLobbies()[self._user.state.lobbyId]
  730. room = self._user.state.room
  731. if room:
  732. data = '%s%s' % (
  733. '\x02',
  734. ''.join(['%s' % (
  735. struct.pack('!i',usr.profile.id))
  736. for usr in room.participatingPlayers]))
  737. data = util.padWithZeros(data, 37)
  738. for player in room.players:
  739. player.sendData(0x4362, data)
  740. # Tell everyone of new phase of room
  741. room.phase = lobby.RoomState.ROOM_MATCH_SIDE_SELECT
  742. room.setMatchStarter(self._user)
  743. room.readyCount = 0
  744. self.sendRoomUpdate(room)
  745. self.sendZeros(0x4361, 4)
  746. def updateRoomPhase(self, room):
  747. if room.readyCount == len(room.participatingPlayers):
  748. room.phase += 1
  749. data = struct.pack('B', room.phase)
  750. for usr in room.players:
  751. usr.sendData(0x4344, data)
  752. # reset count
  753. room.readyCount = 0
  754. # Tell everyone of new phase of room
  755. self.sendRoomUpdate(room)
  756. def toggleReady_436f(self, pkt):
  757. payload = struct.unpack('!B', pkt.data[0])[0]
  758. room = self._user.state.room
  759. if room:
  760. # phase 2-6: match not really started
  761. if room.isAtPregameSettings(room):
  762. if payload == 1:
  763. room.readyCount += 1
  764. elif payload == 0:
  765. room.readyCount -= 1
  766. # phase 7-8: match finished
  767. elif room.phase > lobby.RoomState.ROOM_MATCH_FORMATION_SELECT:
  768. # exit match
  769. if payload == 0:
  770. room.cancelParticipation(self._user)
  771. if len(room.participatingPlayers) == 0:
  772. room.phase = lobby.RoomState.ROOM_IDLE
  773. room.match = None
  774. else: # there's still a player in the endmatch screen
  775. room.phase = lobby.RoomState.ROOM_MATCH_SERIES_ENDING
  776. # play again different teams
  777. elif payload == 3:
  778. room.phase = lobby.RoomState.ROOM_MATCH_TEAM_SELECT
  779. room.match = None
  780. # play again same teams
  781. elif payload == 4:
  782. room.phase = lobby.RoomState.ROOM_MATCH_FORMATION_SELECT
  783. room.match = None
  784. self.sendRoomUpdate(room)
  785. for usr in room.players:
  786. if usr == self._user:
  787. continue
  788. data = '%s%s' % (
  789. struct.pack('!i',self._user.profile.id),
  790. pkt.data[0])
  791. usr.sendData(0x4371, data)
  792. self.sendZeros(0x4370,4)
  793. # if all participating players are ready, next screen
  794. if room.isAtPregameSettings(room):
  795. self.updateRoomPhase(room)
  796. @defer.inlineCallbacks
  797. def setPlayerSettings_4369(self, pkt):
  798. # Packet contains which players are in team1 & team2
  799. self.sendZeros(0x436a, 4)
  800. room = self._user.state.room
  801. for usr in room.players:
  802. data = '%s%s' % (
  803. '\0',
  804. pkt.data)
  805. usr.sendData(0x436b, data)
  806. # create new TeamSelection object
  807. room.teamSelection = lobby.TeamSelection()
  808. for x in range(4):
  809. profile_id = struct.unpack('!i',pkt.data[x*8:x*8+4])[0]
  810. away = '\x01'==pkt.data[x*8+4]
  811. if profile_id!=0:
  812. profile = yield self.factory.getPlayerProfile(profile_id)
  813. if x in [0,1]:
  814. if not away:
  815. room.teamSelection.home_captain = profile
  816. else:
  817. room.teamSelection.away_captain = profile
  818. else:
  819. if not away:
  820. room.teamSelection.home_more_players.append(profile)
  821. else:
  822. room.teamSelection.away_more_players.append(profile)
  823. self.sendRoomUpdate(room)
  824. def setGameSettings_436c(self, pkt):
  825. # Packet contains game settings(time,injuries,penalty etcetera)
  826. self.sendZeros(0x436d, 4)
  827. room = self._user.state.room
  828. data = '%s' % (pkt.data)
  829. room.matchSettings = lobby.MatchSettings(*pkt.data)
  830. for usr in room.players:
  831. usr.sendData(0x436e, data)
  832. self.sendRoomUpdate(room)
  833. def goalScored_4375(self, pkt):
  834. room = self._user.state.room
  835. if not room.match:
  836. log.msg('ERROR: Goal reported, but no match in the room.')
  837. else:
  838. if pkt.data[0] == '\0':
  839. log.msg('GOAL SCORED by HOME team %d (%s)' % (
  840. room.teamSelection.home_team_id,
  841. getHomePlayerNames(room.match)))
  842. room.match.goalHome()
  843. else:
  844. log.msg('GOAL SCORED by AWAY team %d (%s)' % (
  845. room.teamSelection.away_team_id,
  846. getAwayPlayerNames(room.match)))
  847. room.match.goalAway()
  848. log.msg(
  849. 'UPDATE: Team %d (%s) vs Team %d (%s) - %d:%d (in progress)' % (
  850. room.teamSelection.home_team_id,
  851. getHomePlayerNames(room.match),
  852. room.teamSelection.away_team_id,
  853. getAwayPlayerNames(room.match),
  854. room.match.score_home, room.match.score_away))
  855. self.sendZeros(0x4376, 4)
  856. # let others in the lobby know
  857. self.sendRoomUpdate(room)
  858. def matchClockUpdate_4385(self, pkt):
  859. clock = struct.unpack('!B', pkt.data[0])[0]
  860. room = self._user.state.room
  861. if not room or not room.match:
  862. log.msg('ERROR: got clock update, but no match')
  863. else:
  864. room.match.clock = clock
  865. log.msg('CLOCK: Team %d (%s) vs Team %d (%s). Minute: %d' % (
  866. room.teamSelection.home_team_id,
  867. getHomePlayerNames(room.match),
  868. room.teamSelection.away_team_id,
  869. getAwayPlayerNames(room.match),
  870. room.match.clock))
  871. self.sendZeros(0x4386, 4)
  872. # let others in the lobby know
  873. self.sendRoomUpdate(room)
  874. @defer.inlineCallbacks
  875. def recordMatchResult(self, room):
  876. match = room.match
  877. duration = datetime.now() - match.startDatetime
  878. log.msg('MATCH FINISHED: '
  879. 'Team %d (%s) - Team %d (%s) %d:%d. '
  880. 'Match time: %s.' % (
  881. match.teamSelection.home_team_id, getHomePlayerNames(match),
  882. match.teamSelection.away_team_id, getAwayPlayerNames(match),
  883. match.score_home, match.score_away,
  884. duration))
  885. # check if match result should be stored
  886. thisLobby = self.factory.getLobbies()[
  887. self._user.state.lobbyId]
  888. if thisLobby.typeCode != 0x20: # no-stats
  889. # record the match in DB
  890. yield self.factory.matchData.store(match)
  891. participants = [match.teamSelection.home_captain,
  892. match.teamSelection.away_captain]
  893. participants.extend(match.teamSelection.home_more_players)
  894. participants.extend(match.teamSelection.away_more_players)
  895. for profile in participants:
  896. # update player play time
  897. profile.playTime += duration
  898. # re-calculate points
  899. stats = yield self.getStats(profile.id)
  900. rm = self.factory.ratingMath
  901. profile.points = rm.getPoints(stats)
  902. # store updated profile
  903. yield self.factory.storeProfile(profile)
  904. else:
  905. yield defer.succeed(None)
  906. def matchStateUpdate_4377(self, pkt):
  907. state = struct.unpack('!B', pkt.data[0])[0]
  908. room = self._user.state.room
  909. if not room or not room.teamSelection:
  910. log.msg(
  911. 'ERROR: got match state update, '
  912. 'but no room or team-selection')
  913. else:
  914. if room.match is not None:
  915. room.match.state = state
  916. # check if match just started
  917. if state == lobby.MatchState.FIRST_HALF:
  918. match = lobby.Match6(room.teamSelection)
  919. log.msg('NEW MATCH started: Team %d (%s) vs Team %d (%s)' % (
  920. room.teamSelection.home_team_id,
  921. getHomePlayerNames(room),
  922. room.teamSelection.away_team_id,
  923. getAwayPlayerNames(room)))
  924. match.startDatetime = datetime.now()
  925. match.home_team_id = match.teamSelection.home_team_id
  926. match.away_team_id = match.teamSelection.away_team_id
  927. room.match = match
  928. room.match.state = state
  929. # check if match is done
  930. elif state == lobby.MatchState.FINISHED and room.match:
  931. room.phase = lobby.RoomState.ROOM_MATCH_FINISHED
  932. self.recordMatchResult(room)
  933. # let others in the lobby know
  934. self.sendRoomUpdate(room)
  935. self.sendZeros(0x4378, 4)
  936. def teamSelected_4373(self, pkt):
  937. team = struct.unpack('!H', pkt.data[0:2])[0]
  938. log.msg('Team selected: %d' % team)
  939. room = self._user.state.room
  940. if not room.teamSelection:
  941. log.msg('ERROR: room has no TeamSelection object')
  942. else:
  943. ts = room.teamSelection
  944. if self._user.profile.id == ts.home_captain.id:
  945. ts.home_team_id = team
  946. elif self._user.profile.id == ts.away_captain.id:
  947. ts.away_team_id = team
  948. self.sendData(0x4374,'\0\0\0\0')
  949. self.sendRoomUpdate(room)
  950. @defer.inlineCallbacks
  951. def setComment_4110(self, pkt):
  952. self._user.profile.comment = pkt.data
  953. yield self.factory.storeProfile(self._user.profile)
  954. self.sendZeros(0x4111,4)
  955. def relayRoomSettings_4350(self, pkt):
  956. if not self._user.state:
  957. return
  958. room = self._user.state.room
  959. if room:
  960. if pkt.data[0:4] == '\0\0\1\3': #TODO clean this up (3 - phase?)
  961. # extract info that we care about
  962. room.matchTime = 5*(ord(pkt.data[12]) + 1)
  963. log.msg('match time set to: %d minutes' % room.matchTime)
  964. # send to others
  965. for usr in self._user.state.room.players:
  966. if usr == self._user:
  967. continue
  968. usr.sendData(0x4350, pkt.data)
  969. def do_3087(self, pkt):
  970. """
  971. Do nothing.
  972. Overriden here to mask pes5 logic
  973. """
  974. def register(self):
  975. pes5.MainService.register(self)
  976. self.addHandler(0x6020, self.quickGameSearch_6020)
  977. self.addHandler(0x4110, self.setComment_4110)
  978. self.addHandler(0x4345, self.getStunInfo_4345)
  979. self.addHandler(0x4400, self.chat_4400)
  980. self.addHandler(0x4310, self.createRoom_4310)
  981. self.addHandler(0x4300, self.getRoomList_4300)
  982. self.addHandler(0x4320, self.joinRoom_4320)
  983. self.addHandler(0x4363, self.toggleParticipate_4363)
  984. self.addHandler(0x4360, self.startMatch_4360)
  985. self.addHandler(0x436f, self.toggleReady_436f)
  986. self.addHandler(0x4369, self.setPlayerSettings_4369)
  987. self.addHandler(0x436c, self.setGameSettings_436c)
  988. self.addHandler(0x4373, self.teamSelected_4373)
  989. self.addHandler(0x4375, self.goalScored_4375)
  990. self.addHandler(0x4377, self.matchStateUpdate_4377)
  991. self.addHandler(0x4385, self.matchClockUpdate_4385)
  992. self.addHandler(0x4349, self.setOwner_4349)
  993. self.addHandler(0x434d, self.setRoomName_434d)
  994. self.addHandler(0x4366, self.becomeSpectator_4366)
  995. self.addHandler(0x4351, self.do_4351)
  996. self.addHandler(0x4383, self.backToMatchMenu_4383)
  997. self.addHandler(0x4380, self.forcedCancelParticipation_4380)