Coverage for server/parts/item.py : 88%
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 -*-
2from __future__ import annotations
4# -- stdlib --
5from collections import defaultdict
6from typing import Dict, List, TYPE_CHECKING, Tuple
7import logging
9# -- third party --
10from mypy_extensions import TypedDict
12# -- own --
13from game.base import GameItem
14from server.base import Game
15from server.endpoint import Client
16from server.utils import command
17from utils.misc import BusinessException
18import wire
20# -- typing --
21if TYPE_CHECKING:
22 from server.core import Core # noqa: F401
25# -- code --
26log = logging.getLogger('server.parts.item')
29class ItemAssocOnGame(TypedDict):
30 items: Dict[int, List[GameItem]]
33def A(self: Item, g: Game) -> ItemAssocOnGame:
34 return g._[self]
37class Item(object):
38 def __init__(self, core: Core):
39 self.core = core
40 core.events.game_created += self.handle_game_created
41 core.events.game_started += self.handle_game_started
42 _ = core.events.client_command
43 _[wire.UseItem] += self._use_item
45 def __repr__(self) -> str:
46 return self.__class__.__name__
48 def handle_game_started(self, g: Game) -> Game:
49 core = self.core
50 final: Dict[int, List[GameItem]] = {}
52 for uid, l in A(self, g)['items'].items():
53 consumed = []
54 for i in l:
55 try:
56 rst = core.backend.query('''
57 mutation($id: Int!, $sku: String, $r: String) {
58 item {
59 remove(player: $id, sku: $sku, reason: $r)
60 }
61 }
62 ''', id=uid, sku=i.sku, reason="Use in game %s" % core.room.gid_of(g))
64 if rst['item']['remove']: 64 ↛ 54line 64 didn't jump to line 54, because the condition on line 64 was never false
65 consumed.append(i)
66 except Exception:
67 log.exception('Error consuming item')
69 final[uid] = consumed
71 A(self, g)['items'] = final
73 return g
75 def handle_game_created(self, g: Game) -> Game:
76 assoc: ItemAssocOnGame = {
77 'items': defaultdict(list),
78 }
79 g._[self] = assoc
80 return g
82 def handle_game_left(self, ev: Tuple[Game, Client]) -> Tuple[Game, Client]:
83 g, u = ev
84 core = self.core
85 if not core.room.is_started(g) and not g.ended:
86 A(self, g)['items'].pop(core.auth.uid_of(u), None)
88 return ev
90 # ----- Command -----
91 @command('room')
92 def _use_item(self, u: Client, ev: wire.UseItem) -> None:
93 core = self.core
94 g = core.game.current(u)
95 assert g
97 try:
98 uid = core.auth.uid_of(u)
99 i = GameItem.from_sku(ev.sku)
100 i.should_usable(core, g, u)
102 have_item = core.backend.query('''
103 query($uid: Int!, $sku: String!) {
104 player(id: $id) {
105 haveItem(sku: $sku)
106 }
107 }
108 ''', uid=uid, sku=ev.sku)['player']['haveItem']
110 if not have_item:
111 from utils.misc import exceptions
112 raise exceptions.ItemNotFound
114 A(self, g)['items'][uid].append(i)
115 u.write(wire.Info('use_item_success'))
116 except BusinessException as e:
117 uid = core.auth.uid_of(u)
118 log.error('User %s failed to use item %s: %s', uid, ev.sku, e.name)
119 u.write(wire.Error(e.snake_case))
121 # ----- Methods ------
122 # ----- Public Methods -----
123 def items_of(self, g: Game) -> Dict[int, List[GameItem]]:
124 return A(self, g)['items']
126 def item_skus_of(self, g: Game) -> Dict[int, List[str]]:
127 return {k: [i.sku for i in v] for k, v in self.items_of(g).items()}