# Coverage for server/core.py : 84%

## 102 statements 99 run 3 missing 0 excluded 20 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, Dict, Tuple, Type, TypeVar

6

7# -- third party --

8# -- own --

9 from . import parts

10 from .base import Game

11 from .endpoint import Client

12 from game.base import Packet

13 from utils.events import EventHub

14 import core

15 import wire

16

17

18# -- code --

19 class Options(object):

20 def __init__(self, options: Dict[str, Any]):

21 self.node = options.get('node', 'localhost') # Current node name

22 self.listen = options.get('listen', '') # Listen

23 self.backend = options.get('backend', '') # Backend URI

24 self.interconnect = options.get('interconnect', '') # URI of chat server

25 self.archive_path = options.get('archive_path', '') # file:// URI of dir for storing game archives

26 self.disables = options.get('disables', []) # disabled core components, will assign a None value

27 self.testing = options.get('testing', False) # In testing?

28

29

30 T = TypeVar('T', bound=wire.Message)

31

32

33 class _ClientCommandMapping(dict):

34 __slots__ = ('core',)

35

36 def __init__(self, core: Core):

37 self.core = core

38 super().__init__()

39

40 def __getitem__(self, k: Type[T]) -> EventHub[Tuple[Client, T]]:

41 if k in self:

42 return dict.__getitem__(self, k)

43

44 hub = EventHub[Tuple[Client, T]]()

45 hub.name = f'{self.core}::client_command'

46 self[k] = hub

47 return hub

48

49

50 class Events(object):

51 def __init__(self, core: Core) -> None:

52 self.core = core

53

54 # ev = (core: Core)

55 self.core_initialized = EventHub[Core]()

56

57 # Fires when user state changes,

58 # ev = (c: Client, from: str, to: str)

59 self.user_state_transition = EventHub[Tuple[Client, str, str]]()

60

61 # Client connected

62 self.client_connected = EventHub[Client]()

63

64 # Client dropped(connection lost)

65 self.client_dropped = EventHub[Client]()

66

67 # Client logged in when previous login still online, or still in active game

68 # ev = c: Client # old client obj with new connection `pivot_to`ed to it

69 self.client_pivot = EventHub[Client]()

70

71 # Client send some command

72 # ev = (c: Client, args: (...))

73 self.client_command = _ClientCommandMapping(core)

74

75 # Game is created

76 # ev = g: Game

77 self.game_created = EventHub[Game]()

78

79 # Sent client game data

80 self.game_data_send = EventHub[Tuple[Game, Client, Packet]]()

81

82 # Received client game data

83 self.game_data_recv = EventHub[Tuple[Game, Client, Packet]]()

84

85 # Fires after old game ended and new game created.

86 # Actors should copy settings from old to new

87 # ev = (old: Game, g: Game)

88 self.game_successive_create = EventHub[Tuple[Game, Game]]()

89

90 # Game started running

91 # ev = (g: Game)

92 self.game_started = EventHub[Game]()

93

94 # Client joined a game

95 # ev = (g: Game, c: Client)

96 self.game_joined = EventHub[Tuple[Game, Client]]()

97

98 # Client left a game

99 # ev = (g: Game, c: Client)

100 self.game_left = EventHub[Tuple[Game, Client]]()

101

102 # Game was ended, successfully or not.

103 # ev = (g: Game)

104 self.game_ended = EventHub[Game]()

105

106 # Game ended in half way.

107 # This fires before GAME_ENDED

108 # ev = (g: Game)

109 self.game_aborted = EventHub[Game]()

110

111 # Game crashed

112 # ev = (g: Game)

113 self.game_crashed = EventHub[Game]()

114

115 def __setattr__(self, name: str, v: Any) -> None:

116 if hasattr(v, 'name'):

117 v.name = f'{repr(self.core)}::{name}'

118 object.__setattr__(self, name, v)

119

120

121 class Core(core.Core):

122 core_type = 'S'

123

124 def __init__(self, **options: Dict[str, Any]) -> None:

125 super().__init__()

126

127 self.options = Options(options)

128 self.events = Events(self)

129 disables = self.options.disables

130

131 if 'serve' not in disables: 131 ↛ 134 line 131 didn't jump to line 134, because the condition on line 131 was never false

132 self.serve = parts.serve.Serve(self)

133

134 if 'auth' not in disables: 134 ↛ 137 line 134 didn't jump to line 137, because the condition on line 134 was never false

135 self.auth = parts.auth.Auth(self)

136

137 if 'lobby' not in disables: 137 ↛ 140 line 137 didn't jump to line 140, because the condition on line 137 was never false

138 self.lobby = parts.lobby.Lobby(self)

139

140 if 'room' not in disables: 140 ↛ 143 line 140 didn't jump to line 143, because the condition on line 140 was never false

141 self.room = parts.room.Room(self)

142

143 if 'game' not in disables: 143 ↛ 146 line 143 didn't jump to line 146, because the condition on line 143 was never false

144 self.game = parts.game.GamePart(self)

145

146 if 'matching' not in disables: 146 ↛ 149 line 146 didn't jump to line 149, because the condition on line 146 was never false

147 self.matching = parts.matching.Matching(self)

148

149 if 'observe' not in disables: 149 ↛ 152 line 149 didn't jump to line 152, because the condition on line 149 was never false

150 self.observe = parts.observe.Observe(self)

151

152 if 'invite' not in disables: 152 ↛ 155 line 152 didn't jump to line 155, because the condition on line 152 was never false

153 self.invite = parts.invite.Invite(self)

154

155 if 'item' not in disables: 155 ↛ 158 line 155 didn't jump to line 158, because the condition on line 155 was never false

156 self.item = parts.item.Item(self)

157

158 if 'reward' not in disables: 158 ↛ 161 line 158 didn't jump to line 161, because the condition on line 158 was never false

159 self.reward = parts.reward.Reward(self)

160

161 if 'contest' not in disables: 161 ↛ 164 line 161 didn't jump to line 164, because the condition on line 161 was never false

162 self.contest = parts.contest.Contest(self)

163

164 if 'admin' not in disables: 164 ↛ 167 line 164 didn't jump to line 167, because the condition on line 164 was never false

165 self.admin = parts.admin.Admin(self)

166

167 if 'kedama' not in disables: 167 ↛ 170 line 167 didn't jump to line 170, because the condition on line 167 was never false

168 self.kedama = parts.kedama.Kedama(self)

169

170 if 'archive' not in disables: 170 ↛ 173 line 170 didn't jump to line 173, because the condition on line 170 was never false

171 self.archive = parts.archive.Archive(self)

172

173 if 'hooks' not in disables: 173 ↛ 176 line 173 didn't jump to line 176, because the condition on line 173 was never false

174 self.hooks = parts.hooks.Hooks(self)

175

176 if 'connect' not in disables: 176 ↛ 177 line 176 didn't jump to line 177, because the condition on line 176 was never true

177 self.connect = parts.connect.Connect(self)

178

179 if 'backend' not in disables: 179 ↛ 180 line 179 didn't jump to line 180, because the condition on line 179 was never true

180 self.backend = parts.backend.Backend(self)

181

182 if 'log' not in disables: 182 ↛ 185 line 182 didn't jump to line 185, because the condition on line 182 was never false

183 self.log = parts.log.Log(self)

184

185 if 'stats' not in disables: 185 ↛ 186 line 185 didn't jump to line 186, because the condition on line 185 was never true

186 self.stats = parts.stats.Stats(self)

187

188 if 'view' not in disables: 188 ↛ 191 line 188 didn't jump to line 191, because the condition on line 188 was never false

189 self.view = parts.view.View(self)

190

191 self.events.core_initialized.emit(self)