# Coverage for wire/msg.py : 97%

## 267 statements 261 run 6 missing 0 excluded 2 partial

Hot-keys on this page

r m x p toggle line displays

j k next/prev highlighted chunk

0 (zero) top of page

1 (one) first highlighted chunk

1# -*- coding: utf-8 -*-

2 from __future__ import annotations

3

4# -- stdlib --

5 from typing import Any, ClassVar, Dict, List, Optional, Type, TypeVar

6 from typing_extensions import TypedDict

7

8# -- third party --

9 from dataclasses import dataclass

10 import dataclasses

11

12# -- own --

13 from . import model

14

15

16# -- code --

17 class ServerToClient:

18 pass

19

20

21 class ClientToServer:

22 pass

23

24

25 class BiDirectional(ServerToClient, ClientToServer):

26 pass

27

28

29 class Message:

30 types: ClassVar[Dict[str, Type[Message]]] = {}

31 op: str

32

33 def __init__(self, *args, **kwargs):

34 raise Exception('Abstract')

35

36 def encode(self) -> dict:

37 ...

38

39 @classmethod

40 def decode(cls, data: dict) -> Optional[Message]:

41 data = dict(data)

42 op = data.pop('op', '')

43 if op not in cls.types: 43 ↛ 44 line 43 didn't jump to line 44, because the condition on line 43 was never true

44 return None

45

46 return cls.types[op](**data)

47

48

49 def message(cls: Type[Message]) -> Type[Message]:

50 assert issubclass(cls, ServerToClient) or issubclass(cls, ClientToServer)

51 cls.op = cls.__name__

52 assert cls.op not in Message.types, cls.op

53 Message.types[cls.op] = cls

54

55 if cls.encode is Message.encode: 55 ↛ 68 line 55 didn't jump to line 68, because the condition on line 55 was never false

56 env = {}

57 fields = [f" '{i.name}': self.{i.name}," for i in dataclasses.fields(cls)]

58 code = (

59 "def encode(self) -> dict:"

60 " return {"

61 " 'op': self.op,"

62 " %s"

63 " }"

64 ) % '\n'.join(fields)

65 exec(code, env)

66 cls.encode = env['encode']

67

68 return cls

69

70

71 T = TypeVar('T', bound=Message)

72

73

74 def cast(typ: Type[T], msg: Message) -> Optional[T]:

75 if msg.op == typ.op:

76 return msg # type: ignore

77

78 return None

79

80

81# ----------------------------

82@message

83@dataclass

84 class Info(Message, ServerToClient):

85 msg: str

86

87

88@message

89@dataclass

90 class Error(Message, ServerToClient):

91 msg: str

92

93

94@message

95@dataclass

96 class SystemMsg(Message, ServerToClient):

97 msg: str

98

99

100@message

101@dataclass

102 class CurrentGames(Message, ServerToClient):

103 games: List[model.Game]

104

105

106@message

107@dataclass

108 class CurrentUsers(Message, ServerToClient):

109 users: List[model.User]

110

111

112@message

113@dataclass

114 class RoomUsers(Message, ServerToClient):

115 gid: int

116 users: List[model.User]

117

118

119@message

120@dataclass

121 class GameJoined(Message, ServerToClient):

122 game: model.GameDetail

123

124

125@message

126@dataclass

127 class GameStarted(Message, ServerToClient):

128 game: model.GameDetail

129

130

131@message

132@dataclass

133 class PlayerPresence(Message, ServerToClient):

134 '''

135 Indicates if player is still in the game

136 '''

137 gid: int

138 presence: Dict[int, bool]

139

140

141@message

142@dataclass

143 class GameEnded(Message, ServerToClient):

144 gid: int

145

146

147@message

148@dataclass

149 class GameParams(Message, ServerToClient):

150 gid: int

151 params: Dict[str, Any]

152

153

154@message

155@dataclass

156 class SetGameParam(Message, BiDirectional):

157 gid: int

158 key: str

159 value: Any

160

161

162@message

163@dataclass

164 class GameData(Message, BiDirectional):

165 gid: int

166 tag: str

167 data: Any

168

169

170@message

171@dataclass

172 class InviteRequest(Message, ServerToClient):

173 uid: int

174 name: str

175 gid: int

176 type: str

177

178

179@message

180@dataclass

181 class ObserveRequest(Message, ServerToClient):

182 uid: int

183

184

185@message

186@dataclass

187 class KickRequest(Message, ServerToClient):

188 uid: int

189 victim: int

190 votes: int

191

192

193@message

194@dataclass

195 class ObserveStarted(Message, ServerToClient):

196 game: model.GameDetail

197 observee: int

198

199

200@message

201@dataclass

202 class GameLeft(Message, ServerToClient):

203 gid: int

204

205

206@message

207@dataclass

208 class Greeting(Message, ServerToClient):

209 node: str

210 version: str

211

212

213@message

214@dataclass

215 class AuthError(Message, ServerToClient):

216 reason: str

217

218

219@message

220@dataclass

221 class AuthSuccess(Message, ServerToClient):

222 uid: int

223

224

225@message

226@dataclass

227 class Ping(Message, ServerToClient):

228 pass

229

230

231# --------------------------------

232# ----- system -----

233@message

234@dataclass

235 class Beat(Message, ClientToServer):

236 pass

237

238

239@message

240@dataclass

241 class Pong(Message, ClientToServer):

242 pass

243

244

245@message

246@dataclass

247 class Auth(Message, ClientToServer):

248 token: str

249

250

251# ----- room -----

252 class CreateRoomFlags(TypedDict):

253 contest: bool

254 invite: bool

255

256

257@message

258@dataclass

259 class CreateRoom(Message, ClientToServer):

260 name: str

261 mode: str

262 flags: CreateRoomFlags

263

264

265@message

266@dataclass

267 class JoinRoom(Message, ClientToServer):

268 gid: int

269 slot: Optional[int]

270

271

272@message

273@dataclass

274 class LeaveRoom(Message, ClientToServer):

275 pass

276

277

278@message

279@dataclass

280 class GetRoomUsers(Message, ClientToServer):

281 gid: int

282

283

284@message

285@dataclass

286 class GetReady(Message, ClientToServer):

287 pass

288

289

290@message

291@dataclass

292 class CancelReady(Message, ClientToServer):

293 pass

294

295

296@message

297@dataclass

298 class ChangeLocation(Message, ClientToServer):

299 loc: int

300

301

302# ----- observe -----

303@message

304@dataclass

305 class Observe(Message, ClientToServer):

306 uid: int

307

308

309@message

310@dataclass

311 class GrantObserve(Message, ClientToServer):

312 uid: int

313 grant: bool

314

315

316@message

317@dataclass

318 class KickObserver(Message, ClientToServer):

319 uid: int

320

321

322@message

323@dataclass

324 class ObserverEnter(Message, ServerToClient):

325 observer: int

326 observee: int

327

328

329@message

330@dataclass

331 class ObserverLeave(Message, ServerToClient):

332 observer: int

333 observee: int

334

335

336# ----- invite -----

337@message

338@dataclass

339 class Invite(Message, ClientToServer):

340 uid: int

341

342

343@message

344@dataclass

345 class Kick(Message, ClientToServer):

346 uid: int

347

348

349# ----- item -----

350@message

351@dataclass

352 class UseItem(Message, ClientToServer):

353 sku: str

354

355

356# ----- admin -----

357@message

358@dataclass

359 class AdminStacktrace(Message, ClientToServer):

360 pass

361

362

363@message

364@dataclass

365 class AdminClearZombies(Message, ClientToServer):

366 pass

367

368

369@message

370@dataclass

371 class AdminMigrate(Message, ClientToServer):

372 pass

373

374

375@message

376@dataclass

377 class AdminKick(Message, ClientToServer):

378 uid: int

379

380

381@message

382@dataclass

383 class AdminKillGame(Message, ClientToServer):

384 gid: int

385

386

387@message

388@dataclass

389 class AdminAdd(Message, ClientToServer):

390 uid: int

391

392

393@message

394@dataclass

395 class AdminRemove(Message, ClientToServer):

396 uid: int

397

398

399@message

400@dataclass

401 class AdminAddBigbrother(Message, ClientToServer):

402 uid: int

403

404

405@message

406@dataclass

407 class AdminRemoveBigbrother(Message, ClientToServer):

408 uid: int

409

410

411# ----- contest -----

412@message

413@dataclass

414 class SetupContest(Message, ClientToServer):

415 name: str

416 mode: str

417 uids: List[int]