mirror of
https://github.com/Lost-MSth/Arcaea-server.git
synced 2025-12-14 08:06:23 +08:00
- Fix a small bug that `best30` of API cannot have scores whose songs are not in database - At present the setting file can be a module or a file with some of options - Limiter can have multiple rules together now
391 lines
12 KiB
Python
391 lines
12 KiB
Python
from .config_manager import Config
|
||
from .error import InputError, ItemNotEnough, ItemUnavailable, NoData
|
||
|
||
|
||
class Item:
|
||
item_type = None
|
||
|
||
def __init__(self) -> None:
|
||
self.item_id = None
|
||
self.__amount = None
|
||
self.is_available = None
|
||
|
||
@property
|
||
def amount(self):
|
||
return self.__amount
|
||
|
||
@amount.setter
|
||
def amount(self, value: int):
|
||
self.__amount = int(value)
|
||
|
||
def to_dict(self, has_is_available: bool = False) -> dict:
|
||
r = {
|
||
'id': self.item_id,
|
||
'amount': self.amount,
|
||
'type': self.item_type
|
||
}
|
||
if has_is_available:
|
||
r['is_available'] = self.is_available
|
||
return r
|
||
|
||
def user_claim_item(self, user):
|
||
# parameter: user - User类或子类的实例
|
||
pass
|
||
|
||
|
||
class UserItem(Item):
|
||
|
||
def __init__(self, c=None) -> None:
|
||
super().__init__()
|
||
self.c = c
|
||
self.user = None
|
||
|
||
def select(self, user=None):
|
||
'''
|
||
查询用户item\
|
||
parameter: `user` - `User`类或子类的实例
|
||
'''
|
||
if user is not None:
|
||
self.user = user
|
||
self.c.execute('''select amount from user_item where user_id=? and item_id=? and type=?''',
|
||
(self.user.user_id, self.item_id, self.item_type))
|
||
x = self.c.fetchone()
|
||
if x:
|
||
self.amount = x[0] if x[0] is not None else 1
|
||
else:
|
||
self.amount = 0
|
||
|
||
|
||
class NormalItem(UserItem):
|
||
def __init__(self, c) -> None:
|
||
super().__init__()
|
||
self.c = c
|
||
|
||
def user_claim_item(self, user):
|
||
self.user = user
|
||
if not self.is_available:
|
||
self.c.execute(
|
||
'''select is_available from item where item_id=? and type=?''', (self.item_id, self.item_type))
|
||
x = self.c.fetchone()
|
||
if x:
|
||
if x[0] == 0:
|
||
self.is_available = False
|
||
raise ItemUnavailable('The item is unavailable.')
|
||
else:
|
||
self.is_available = True
|
||
else:
|
||
raise NoData('No item data.')
|
||
|
||
self.c.execute('''select exists(select * from user_item where user_id=? and item_id=? and type=?)''',
|
||
(self.user.user_id, self.item_id, self.item_type))
|
||
if self.c.fetchone() == (0,):
|
||
self.c.execute('''insert into user_item values(:a,:b,:c,1)''',
|
||
{'a': self.user.user_id, 'b': self.item_id, 'c': self.item_type})
|
||
|
||
|
||
class PositiveItem(UserItem):
|
||
def __init__(self, c) -> None:
|
||
super().__init__()
|
||
self.c = c
|
||
|
||
def user_claim_item(self, user):
|
||
'''添加或使用用户item,注意是+amount'''
|
||
self.user = user
|
||
self.c.execute('''select amount from user_item where user_id=? and item_id=? and type=?''',
|
||
(self.user.user_id, self.item_id, self.item_type))
|
||
x = self.c.fetchone()
|
||
if x:
|
||
if x[0] + self.amount < 0: # 数量不足
|
||
raise ItemNotEnough(
|
||
'The user does not have enough `%s`.' % self.item_id)
|
||
self.c.execute('''update user_item set amount=? where user_id=? and item_id=? and type=?''',
|
||
(x[0]+self.amount, self.user.user_id, self.item_id, self.item_type))
|
||
else:
|
||
if self.amount < 0: # 添加数量错误
|
||
raise InputError(
|
||
'The amount of `%s` is wrong.' % self.item_id)
|
||
self.c.execute('''insert into user_item values(?,?,?,?)''',
|
||
(self.user.user_id, self.item_id, self.item_type, self.amount))
|
||
|
||
|
||
class ItemCore(PositiveItem):
|
||
item_type = 'core'
|
||
|
||
def __init__(self, c, core=None, reverse=False) -> None:
|
||
super().__init__(c)
|
||
self.is_available = True
|
||
if core:
|
||
self.item_id = core.item_id
|
||
self.amount = - core.amount if reverse else core.amount
|
||
|
||
def __str__(self) -> str:
|
||
return self.item_id + '_' + str(self.amount)
|
||
|
||
|
||
class ItemCharacter(UserItem):
|
||
item_type = 'character'
|
||
|
||
def __init__(self, c) -> None:
|
||
super().__init__()
|
||
self.c = c
|
||
self.is_available = True
|
||
|
||
def set_id(self, character_id: str) -> None:
|
||
# 将name: str转为character_id: int存到item_id里
|
||
if character_id.isdigit():
|
||
self.item_id = int(character_id)
|
||
else:
|
||
self.c.execute(
|
||
'''select character_id from character where name=?''', (character_id,))
|
||
x = self.c.fetchone()
|
||
if x:
|
||
self.item_id = x[0]
|
||
else:
|
||
raise NoData('No character `%s`.' % character_id)
|
||
|
||
def user_claim_item(self, user):
|
||
if not isinstance(self.item_id, int):
|
||
self.set_id(self.item_id)
|
||
|
||
self.c.execute(
|
||
'''select exists(select * from user_char where user_id=? and character_id=?)''', (user.user_id, self.item_id))
|
||
if self.c.fetchone() == (0,):
|
||
self.c.execute(
|
||
'''insert into user_char values(?,?,1,0,0,0)''', (user.user_id, self.item_id))
|
||
|
||
|
||
class Memory(UserItem):
|
||
item_type = 'memory'
|
||
|
||
def __init__(self, c) -> None:
|
||
super().__init__()
|
||
self.c = c
|
||
self.is_available = True
|
||
|
||
def user_claim_item(self, user):
|
||
self.c.execute(
|
||
'''select ticket from user where user_id=?''', (user.user_id,))
|
||
x = self.c.fetchone()
|
||
if x is not None:
|
||
self.c.execute('''update user set ticket=? where user_id=?''',
|
||
(x[0]+self.amount, user.user_id))
|
||
else:
|
||
raise NoData('The ticket of the user is null.')
|
||
|
||
|
||
class Fragment(UserItem):
|
||
item_type = 'fragment'
|
||
|
||
def __init__(self, c) -> None:
|
||
super().__init__()
|
||
self.c = c
|
||
self.is_available = True
|
||
|
||
def user_claim_item(self, user):
|
||
pass
|
||
|
||
def __str__(self) -> str:
|
||
return 'fragment' + str(self.amount)
|
||
|
||
|
||
class Anni5tix(PositiveItem):
|
||
item_type = 'anni5tix'
|
||
|
||
def __init__(self, c) -> None:
|
||
super().__init__(c)
|
||
self.is_available = True
|
||
|
||
|
||
class WorldSong(NormalItem):
|
||
item_type = 'world_song'
|
||
|
||
def __init__(self, c) -> None:
|
||
super().__init__(c)
|
||
self.is_available = True
|
||
|
||
|
||
class WorldUnlock(NormalItem):
|
||
item_type = 'world_unlock'
|
||
|
||
def __init__(self, c) -> None:
|
||
super().__init__(c)
|
||
self.is_available = True
|
||
|
||
|
||
class CourseBanner(NormalItem):
|
||
item_type = 'course_banner'
|
||
|
||
def __init__(self, c) -> None:
|
||
super().__init__(c)
|
||
self.is_available = True
|
||
|
||
def __str__(self) -> str:
|
||
return str(self.item_id)
|
||
|
||
|
||
class Single(NormalItem):
|
||
item_type = 'single'
|
||
|
||
def __init__(self, c) -> None:
|
||
super().__init__(c)
|
||
|
||
|
||
class Pack(NormalItem):
|
||
item_type = 'pack'
|
||
|
||
def __init__(self, c) -> None:
|
||
super().__init__(c)
|
||
|
||
|
||
class ProgBoost(UserItem):
|
||
item_type = 'prog_boost_300'
|
||
|
||
def __init__(self, c) -> None:
|
||
super().__init__(c)
|
||
|
||
def user_claim_item(self, user):
|
||
'''
|
||
世界模式prog_boost\
|
||
parameters: `user` - `UserOnline`类或子类的实例
|
||
'''
|
||
user.update_user_one_column('prog_boost', 300)
|
||
|
||
|
||
class Stamina6(UserItem):
|
||
item_type = 'stamina6'
|
||
|
||
def __init__(self, c) -> None:
|
||
super().__init__(c)
|
||
|
||
def user_claim_item(self, user):
|
||
'''
|
||
世界模式记忆源点或残片买体力+6\
|
||
顺手清一下世界模式过载状态
|
||
'''
|
||
user.select_user_about_stamina()
|
||
user.stamina.stamina += 6
|
||
user.stamina.update()
|
||
user.update_user_one_column('world_mode_locked_end_ts', -1)
|
||
|
||
|
||
class ItemFactory:
|
||
def __init__(self, c=None) -> None:
|
||
self.c = c
|
||
|
||
def get_item(self, item_type: str):
|
||
'''
|
||
根据item_type实例化对应的item类
|
||
return: Item类或子类的实例
|
||
'''
|
||
if item_type == 'core':
|
||
return ItemCore(self.c)
|
||
elif item_type == 'character':
|
||
return ItemCharacter(self.c)
|
||
elif item_type == 'memory':
|
||
return Memory(self.c)
|
||
elif item_type == 'anni5tix':
|
||
return Anni5tix(self.c)
|
||
elif item_type == 'world_song':
|
||
return WorldSong(self.c)
|
||
elif item_type == 'world_unlock':
|
||
return WorldUnlock(self.c)
|
||
elif item_type == 'single':
|
||
return Single(self.c)
|
||
elif item_type == 'pack':
|
||
return Pack(self.c)
|
||
elif item_type == 'fragment':
|
||
return Fragment(self.c)
|
||
elif item_type == 'prog_boost_300':
|
||
return ProgBoost(self.c)
|
||
elif item_type == 'stamina6':
|
||
return Stamina6(self.c)
|
||
elif item_type == 'course_banner':
|
||
return CourseBanner(self.c)
|
||
else:
|
||
raise InputError('The item type `%s` is wrong.' % item_type)
|
||
|
||
@classmethod
|
||
def from_dict(cls, d: dict, c=None):
|
||
'''注意这里没有处理character_id的转化,是为了世界模式的map服务的'''
|
||
if 'item_type' in d:
|
||
item_type = d['item_type']
|
||
elif 'type' in d:
|
||
item_type = d['type']
|
||
else:
|
||
raise InputError('The dict of item is wrong.')
|
||
i = cls().get_item(item_type)
|
||
if c is not None:
|
||
i.c = c
|
||
if 'item_id' in d:
|
||
i.item_id = d['item_id']
|
||
elif 'id' in d:
|
||
i.item_id = d['id']
|
||
else:
|
||
i.item_id = item_type
|
||
i.amount = d.get('amount', 1)
|
||
i.is_available = d.get('is_available', True)
|
||
return i
|
||
|
||
@classmethod
|
||
def from_str(cls, s: str, c=None):
|
||
if s.startswith('fragment'):
|
||
item_type = 'fragment'
|
||
item_id = 'fragment'
|
||
amount = int(s[8:])
|
||
elif s.startswith('core'):
|
||
item_type = 'core'
|
||
x = s.split('_')
|
||
item_id = x[0] + '_' + x[1]
|
||
amount = int(x[-1])
|
||
elif s.startswith('course_banner'):
|
||
item_type = 'course_banner'
|
||
item_id = s
|
||
amount = 1
|
||
else:
|
||
raise InputError('The string of item is wrong.')
|
||
i = cls().get_item(item_type)
|
||
if c is not None:
|
||
i.c = c
|
||
i.item_id = item_id
|
||
i.amount = amount
|
||
i.is_available = True
|
||
return i
|
||
|
||
|
||
class UserItemList:
|
||
'''
|
||
用户的item列表\
|
||
注意只能查在user_item里面的,character不行\
|
||
properties: `user` - `User`类或子类的实例
|
||
'''
|
||
|
||
def __init__(self, c=None, user=None):
|
||
self.c = c
|
||
self.user = user
|
||
|
||
self.items: list = []
|
||
|
||
def select_from_type(self, item_type: str) -> 'UserItemList':
|
||
'''
|
||
根据item_type搜索用户的item
|
||
'''
|
||
if Config.WORLD_SONG_FULL_UNLOCK and item_type == 'world_song' or Config.WORLD_SCENERY_FULL_UNLOCK and item_type == 'world_unlock':
|
||
self.c.execute(
|
||
'''select item_id from item where type=?''', (item_type,))
|
||
else:
|
||
self.c.execute('''select item_id, amount from user_item where type = :a and user_id = :b''', {
|
||
'a': item_type, 'b': self.user.user_id})
|
||
x = self.c.fetchall()
|
||
if not x:
|
||
return self
|
||
|
||
self.items: list = []
|
||
for i in x:
|
||
if len(i) > 1:
|
||
amount = i[1] if i[1] is not None else 1
|
||
else:
|
||
amount = 1
|
||
self.items.append(ItemFactory.from_dict(
|
||
{'item_id': i[0], 'amount': amount, 'item_type': item_type}, self.c))
|
||
return self
|