# Coverage for wire/msg.py : 97%

## 272 statements 266 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: Any = {}

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'] # type: ignore

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, total=False):

253 contest: bool

254 invite: bool

255 chat: bool

256

257

258@message

259@dataclass

260 class CreateRoom(Message, ClientToServer):

261 name: str

262 mode: str

263 flags: CreateRoomFlags

264

265

266@message

267@dataclass

268 class JoinRoom(Message, ClientToServer):

269 gid: int

270 slot: Optional[int]

271

272

273@message

274@dataclass

275 class LeaveRoom(Message, ClientToServer):

276 pass

277

278

279@message

280@dataclass

281 class GetRoomUsers(Message, ClientToServer):

282 gid: int

283

284

285@message

286@dataclass

287 class GetReady(Message, ClientToServer):

288 pass

289

290

291@message

292@dataclass

293 class CancelReady(Message, ClientToServer):

294 pass

295

296

297@message

298@dataclass

299 class ChangeLocation(Message, ClientToServer):

300 loc: int

301

302

303# ----- matching -----

304@message

305@dataclass

306 class StartMatching(Message, BiDirectional):

307 modes: List[str]

308

309

310# ----- observe -----

311@message

312@dataclass

313 class Observe(Message, ClientToServer):

314 uid: int

315

316

317@message

318@dataclass

319 class GrantObserve(Message, ClientToServer):

320 uid: int

321 grant: bool

322

323

324@message

325@dataclass

326 class KickObserver(Message, ClientToServer):

327 uid: int

328

329

330@message

331@dataclass

332 class ObserverEnter(Message, ServerToClient):

333 observer: int

334 observee: int

335

336

337@message

338@dataclass

339 class ObserverLeave(Message, ServerToClient):

340 observer: int

341 observee: int

342

343

344# ----- invite -----

345@message

346@dataclass

347 class Invite(Message, ClientToServer):

348 uid: int

349

350

351@message

352@dataclass

353 class Kick(Message, ClientToServer):

354 uid: int

355

356

357# ----- item -----

358@message

359@dataclass

360 class UseItem(Message, ClientToServer):

361 sku: str

362

363

364# ----- admin -----

365@message

366@dataclass

367 class AdminStacktrace(Message, ClientToServer):

368 pass

369

370

371@message

372@dataclass

373 class AdminClearZombies(Message, ClientToServer):

374 pass

375

376

377@message

378@dataclass

379 class AdminMigrate(Message, ClientToServer):

380 pass

381

382

383@message

384@dataclass

385 class AdminKick(Message, ClientToServer):

386 uid: int

387

388

389@message

390@dataclass

391 class AdminKillGame(Message, ClientToServer):

392 gid: int

393

394

395@message

396@dataclass

397 class AdminAdd(Message, ClientToServer):

398 uid: int

399

400

401@message

402@dataclass

403 class AdminRemove(Message, ClientToServer):

404 uid: int

405

406

407@message

408@dataclass

409 class AdminAddBigbrother(Message, ClientToServer):

410 uid: int

411

412

413@message

414@dataclass

415 class AdminRemoveBigbrother(Message, ClientToServer):

416 uid: int

417

418

419# ----- contest -----

420@message

421@dataclass

422 class SetupContest(Message, ClientToServer):

423 name: str

424 mode: str

425 uids: List[int]