forked from Dniel97/artemis
round database implenments
round database implenments idac: database upgrade script idac: unused upgrade script idac: even more round event implements idac: some bugfixes idac: convert round event file to UTF-8 idac: async round info loading idac: last round ranking impl idac: bugfixes idac: bugfixes idac: bugfixes idac: 160~240 number plate implemented idac: remove SQL comment idac: sort things out and make multi-season compatibility
This commit is contained in:
parent
1bb6891411
commit
e50fedad49
3
.gitignore
vendored
3
.gitignore
vendored
@ -161,3 +161,6 @@ deliver/*
|
||||
*.gz
|
||||
|
||||
dbdump-*.json
|
||||
/.vs
|
||||
/titles/id8
|
||||
/titles/idac/battle.py
|
||||
|
@ -0,0 +1,72 @@
|
||||
"""idac rounds event info added
|
||||
|
||||
Revision ID: 202d1ada1b39
|
||||
Revises: e4e8d89c9b02
|
||||
Create Date: 2024-05-03 15:51:02.384863
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
from sqlalchemy.dialects import mysql
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '202d1ada1b39'
|
||||
down_revision = 'e4e8d89c9b02'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table(
|
||||
"idac_round_info",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("round_id_in_json", sa.Integer(), nullable=True),
|
||||
sa.Column("name", String(64), nullable=True),
|
||||
sa.Column("season", sa.Integer(), nullable=True),
|
||||
sa.Column(
|
||||
"start_dt",
|
||||
sa.TIMESTAMP(),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=True,
|
||||
),
|
||||
sa.Column(
|
||||
"end_dt",
|
||||
sa.TIMESTAMP(),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=True,
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
op.create_table(
|
||||
"idac_user_round_info",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("user", sa.Integer(), nullable=False),
|
||||
sa.Column("round_id", sa.Integer(), nullable=True),
|
||||
sa.Column("count", sa.Integer(), nullable=True),
|
||||
sa.Column("win", sa.Integer(), nullable=True),
|
||||
sa.Column("point", sa.Integer(), nullable=True),
|
||||
sa.Column(
|
||||
"play_dt",
|
||||
sa.TIMESTAMP(),
|
||||
server_default=sa.text("now()"),
|
||||
nullable=True,
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["user"], ["aime_user.id"], onupdate="cascade", ondelete="cascade"
|
||||
),
|
||||
sa.ForeignKeyConstraint(
|
||||
["round_id"], ["idac_round_info.id"], onupdate="cascade", ondelete="cascade"
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint(
|
||||
"user", "round_id", name="idac_user_round_info_uk"
|
||||
),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
def downgrade():
|
||||
op.drop_table("idac_round_info")
|
||||
op.drop_table("idac_user_round_info")
|
@ -0,0 +1,39 @@
|
||||
"""idac plate number lottery added
|
||||
|
||||
Revision ID: 7e98c2c328b1
|
||||
Revises: 202d1ada1b39
|
||||
Create Date: 2024-05-07 12:25:27.606480
|
||||
|
||||
"""
|
||||
from alembic import op
|
||||
import sqlalchemy as sa
|
||||
|
||||
|
||||
# revision identifiers, used by Alembic.
|
||||
revision = '7e98c2c328b1'
|
||||
down_revision = '202d1ada1b39'
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.create_table(
|
||||
"idac_user_lottery",
|
||||
sa.Column("id", sa.Integer(), nullable=False),
|
||||
sa.Column("user", sa.Integer(), nullable=False),
|
||||
sa.Column("version", sa.Integer(), nullable=False),
|
||||
sa.Column("saved_value", sa.Integer(), nullable=False),
|
||||
sa.Column("lottery_count", sa.Integer(), nullable=False),
|
||||
sa.ForeignKeyConstraint(
|
||||
["user"], ["aime_user.id"], onupdate="cascade", ondelete="cascade"
|
||||
),
|
||||
sa.PrimaryKeyConstraint("id"),
|
||||
sa.UniqueConstraint(
|
||||
"user", "version", name="idac_user_lottery_uk"
|
||||
),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
|
||||
def downgrade():
|
||||
op.drop_table("idac_user_lottery")
|
@ -28,3 +28,8 @@ timetrial:
|
||||
battle_event:
|
||||
enabled: True
|
||||
enabled_battle_event: "touhou_1st"
|
||||
|
||||
round_event:
|
||||
enable: True
|
||||
enabled_round: "S2R2"
|
||||
last_round: "S2R1"
|
@ -149,6 +149,36 @@ class IDACTBattleGiftConfig:
|
||||
default="touhou_1st",
|
||||
)
|
||||
|
||||
class IDACRoundConfig:
|
||||
def __init__(self, parent: "IDACConfig") -> None:
|
||||
self.__config = parent
|
||||
|
||||
@property
|
||||
def enable(self) -> bool:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config, "idac", "round_event", "enable", default=True
|
||||
)
|
||||
|
||||
@property
|
||||
def enabled_round(self) -> str:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config,
|
||||
"idac",
|
||||
"round_event",
|
||||
"enabled_round",
|
||||
default="S1R1",
|
||||
)
|
||||
|
||||
@property
|
||||
def last_round(self) -> str:
|
||||
return CoreConfig.get_config_field(
|
||||
self.__config,
|
||||
"idac",
|
||||
"round_event",
|
||||
"last_round",
|
||||
default="S1R1",
|
||||
)
|
||||
|
||||
|
||||
class IDACConfig(dict):
|
||||
def __init__(self) -> None:
|
||||
@ -157,3 +187,4 @@ class IDACConfig(dict):
|
||||
self.stamp = IDACStampConfig(self)
|
||||
self.timetrial = IDACTimetrialConfig(self)
|
||||
self.battle_gift = IDACTBattleGiftConfig(self)
|
||||
self.round_event = IDACRoundConfig(self)
|
||||
|
183
titles/idac/data/rounds/season2/S2R1.json
Normal file
183
titles/idac/data/rounds/season2/S2R1.json
Normal file
@ -0,0 +1,183 @@
|
||||
{
|
||||
"round_event_id": 9,
|
||||
"round_event_nm": "シーズン2 特別ラウンド",
|
||||
"start_dt": 1647468000,
|
||||
"end_dt": 1648062000,
|
||||
"round_start_rank": 0,
|
||||
"save_filename": "",
|
||||
"vscount": [
|
||||
{
|
||||
"reward_upper_limit": 30,
|
||||
"reward_lower_limit": 30,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 21,
|
||||
"reward_type": 379
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 10,
|
||||
"reward_lower_limit": 10,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 21,
|
||||
"reward_type": 367
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"rank": [
|
||||
{
|
||||
"reward_upper_limit": 1,
|
||||
"reward_lower_limit": 1,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4328
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 2,
|
||||
"reward_lower_limit": 10,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4329
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 11,
|
||||
"reward_lower_limit": 50,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4330
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 51,
|
||||
"reward_lower_limit": 100,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4331
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 101,
|
||||
"reward_lower_limit": 1000,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4332
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"point": [
|
||||
|
||||
],
|
||||
"playable_course_list": [
|
||||
{
|
||||
"course_id": 0,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 0,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 2,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 2,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 36,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 36,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 38,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 38,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 4,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 4,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 6,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 6,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 12,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 12,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 14,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 14,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 8,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 8,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 10,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 10,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 16,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 16,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 18,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 18,
|
||||
"course_day": 1
|
||||
}
|
||||
]
|
||||
}
|
229
titles/idac/data/rounds/season2/S2R2.json
Normal file
229
titles/idac/data/rounds/season2/S2R2.json
Normal file
@ -0,0 +1,229 @@
|
||||
{
|
||||
"round_event_id": 10,
|
||||
"round_event_nm": "シーズン2 2ndラウンド",
|
||||
"start_dt": 1648072800,
|
||||
"end_dt": 1651086000,
|
||||
"round_start_rank": 0,
|
||||
"save_filename": "",
|
||||
"vscount": [
|
||||
{
|
||||
"reward_upper_limit": 180,
|
||||
"reward_lower_limit": 180,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 21,
|
||||
"reward_type": 462
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 120,
|
||||
"reward_lower_limit": 120,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 21,
|
||||
"reward_type": 461
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 80,
|
||||
"reward_lower_limit": 80,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 22,
|
||||
"reward_type": 516
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 40,
|
||||
"reward_lower_limit": 40,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 21,
|
||||
"reward_type": 484
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 10,
|
||||
"reward_lower_limit": 10,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 21,
|
||||
"reward_type": 483
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"rank": [
|
||||
{
|
||||
"reward_upper_limit": 1,
|
||||
"reward_lower_limit": 1,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4333
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 2,
|
||||
"reward_lower_limit": 10,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4334
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 11,
|
||||
"reward_lower_limit": 50,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4335
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 51,
|
||||
"reward_lower_limit": 100,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4336
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 101,
|
||||
"reward_lower_limit": 1000,
|
||||
"reward": [
|
||||
{
|
||||
"reward_category": 24,
|
||||
"reward_type": 4337
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"point": [
|
||||
|
||||
],
|
||||
"playable_course_list": [
|
||||
{
|
||||
"course_id": 4,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 4,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 6,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 6,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 12,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 12,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 14,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 14,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 16,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 16,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 18,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 18,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 20,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 20,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 22,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 22,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 24,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 24,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 26,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 26,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 44,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 44,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 46,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 46,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 48,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 48,
|
||||
"course_day": 1
|
||||
},
|
||||
{
|
||||
"course_id": 50,
|
||||
"course_day": 0
|
||||
},
|
||||
{
|
||||
"course_id": 50,
|
||||
"course_day": 1
|
||||
}
|
||||
]
|
||||
}
|
@ -2,6 +2,8 @@ from core.data import Data
|
||||
from core.config import CoreConfig
|
||||
from titles.idac.schema.profile import IDACProfileData
|
||||
from titles.idac.schema.item import IDACItemData
|
||||
from titles.idac.schema.rounds import IDACOnlineRounds
|
||||
from titles.idac.schema.factory import IDACFactoryData
|
||||
|
||||
|
||||
class IDACData(Data):
|
||||
@ -10,3 +12,5 @@ class IDACData(Data):
|
||||
|
||||
self.profile = IDACProfileData(cfg, self.session)
|
||||
self.item = IDACItemData(cfg, self.session)
|
||||
self.rounds = IDACOnlineRounds(cfg, self.session)
|
||||
self.factory = IDACFactoryData(cfg, self.session)
|
||||
|
60
titles/idac/schema/factory.py
Normal file
60
titles/idac/schema/factory.py
Normal file
@ -0,0 +1,60 @@
|
||||
from typing import Dict, Optional, List
|
||||
from sqlalchemy import (
|
||||
Table,
|
||||
Column,
|
||||
UniqueConstraint,
|
||||
PrimaryKeyConstraint,
|
||||
and_,
|
||||
update,
|
||||
)
|
||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON
|
||||
from sqlalchemy.schema import ForeignKey
|
||||
from sqlalchemy.engine import Row
|
||||
from sqlalchemy.sql import func, select
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
|
||||
from core.data.schema import BaseData, metadata
|
||||
|
||||
lottery = Table(
|
||||
"idac_user_lottery",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("version", Integer, nullable=False),
|
||||
Column("saved_value", Integer, nullable=False),
|
||||
Column("lottery_count", Integer, nullable=False),
|
||||
UniqueConstraint("user", "version", name="idac_user_lottery_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
class IDACFactoryData(BaseData):
|
||||
async def get_lottery(self, aime_id: int, version: int) -> Optional[Row]:
|
||||
sql = select(lottery).where(
|
||||
and_(
|
||||
lottery.c.user == aime_id,
|
||||
lottery.c.version == version
|
||||
)
|
||||
)
|
||||
result = await self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
async def put_lottery(
|
||||
self, aime_id: int, version: int, saved_value: int, lottery_count: int
|
||||
) -> Optional[int]:
|
||||
|
||||
lottery_data = {}
|
||||
lottery_data["user"] = aime_id
|
||||
lottery_data["version"] = version
|
||||
lottery_data["saved_value"] = saved_value
|
||||
lottery_data["lottery_count"] = lottery_count
|
||||
|
||||
sql = insert(lottery).values(**lottery_data)
|
||||
conflict = sql.on_duplicate_key_update(**lottery_data)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warn(f"put_lottery: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
138
titles/idac/schema/rounds.py
Normal file
138
titles/idac/schema/rounds.py
Normal file
@ -0,0 +1,138 @@
|
||||
from typing import Dict, List, Optional
|
||||
from sqlalchemy import Table, Column, UniqueConstraint, PrimaryKeyConstraint, and_, update
|
||||
from sqlalchemy.types import Integer, String, TIMESTAMP, Boolean, JSON, BigInteger
|
||||
from sqlalchemy.engine.base import Connection
|
||||
from sqlalchemy.schema import ForeignKey
|
||||
from sqlalchemy.sql import func, select
|
||||
from sqlalchemy.engine import Row
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
|
||||
from core.data.schema import BaseData, metadata
|
||||
from core.config import CoreConfig
|
||||
import datetime
|
||||
|
||||
round_details = Table(
|
||||
"idac_round_info",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("round_id_in_json", Integer),
|
||||
Column("name", String(64)),
|
||||
Column("season", Integer),
|
||||
Column("start_dt", TIMESTAMP, server_default=func.now()),
|
||||
Column("end_dt", TIMESTAMP, server_default=func.now()),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
round_info = Table(
|
||||
"idac_user_round_info",
|
||||
metadata,
|
||||
Column("id", Integer, primary_key=True, nullable=False),
|
||||
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("round_id", ForeignKey("idac_round_info.id", ondelete="cascade", onupdate="cascade")),
|
||||
Column("count", Integer),
|
||||
Column("win", Integer),
|
||||
Column("point", Integer),
|
||||
Column("play_dt", TIMESTAMP, server_default=func.now()),
|
||||
UniqueConstraint("user", "round_id", name="idac_user_round_info_uk"),
|
||||
mysql_charset="utf8mb4",
|
||||
)
|
||||
|
||||
class IDACOnlineRounds(BaseData):
|
||||
# get player's ranking from a specified round event
|
||||
async def get_round_rank_by_id(self, aime_id: int, round_event_id: int) -> Optional[Row]:
|
||||
subquery = (
|
||||
select([func.group_concat(func.concat(round_info.c.user, '|', round_info.c.point),order_by=[round_info.c.point.desc(), round_info.c.play_dt])])
|
||||
.select_from(round_info)
|
||||
.where(round_info.c.round_id == round_event_id)
|
||||
.as_scalar()
|
||||
)
|
||||
|
||||
sql = (
|
||||
select([func.find_in_set(func.concat(round_info.c.user, '|', round_info.c.point), subquery.label('rank'))])
|
||||
.select_from(round_info)
|
||||
.where(
|
||||
and_(
|
||||
round_info.c.user == aime_id,
|
||||
round_info.c.round_id == round_event_id
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
|
||||
# get player's info from a specified round event
|
||||
async def get_round_info_by_id(self, aime_id: int, round_event_id: int) -> Optional[Row]:
|
||||
sql = select(round_info).where(
|
||||
and_(
|
||||
round_info.c.user == aime_id,
|
||||
round_info.c.round_id == round_event_id
|
||||
)
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchone()
|
||||
|
||||
# get top 5 of a specified round event
|
||||
async def get_round_top_five(self, round_event_id: int) -> Optional[Row]:
|
||||
subquery = (
|
||||
select([func.group_concat(func.concat(round_info.c.user, '|', round_info.c.point),order_by=[round_info.c.point.desc(), round_info.c.play_dt])])
|
||||
.select_from(round_info)
|
||||
.where(round_info.c.round_id == round_event_id)
|
||||
.as_scalar()
|
||||
)
|
||||
|
||||
sql = (
|
||||
select([func.find_in_set(func.concat(round_info.c.user, '|', round_info.c.point), subquery.label('rank'))], round_info.c.user)
|
||||
.select_from(round_info)
|
||||
.where(round_info.c.round_id == round_event_id)
|
||||
.limit(5)
|
||||
)
|
||||
|
||||
result = await self.execute(sql)
|
||||
if result is None:
|
||||
return None
|
||||
return result.fetchall()
|
||||
|
||||
# save players info to a specified round event
|
||||
async def put_round_event(
|
||||
self, aime_id: int, round_id: int, round_data: Dict
|
||||
) -> Optional[int]:
|
||||
round_data["user"] = aime_id
|
||||
round_data["round_id"] = round_id
|
||||
|
||||
sql = insert(round_info).values(**round_data)
|
||||
conflict = sql.on_duplicate_key_update(**round_data)
|
||||
result = await self.execute(conflict)
|
||||
|
||||
if result is None:
|
||||
self.logger.warn(f"putround: Failed to update! aime_id: {aime_id}")
|
||||
return None
|
||||
return result.lastrowid
|
||||
|
||||
# insert if the event does not exist in database
|
||||
async def _try_load_round_event(
|
||||
self, round_id: int, version: int, round_data: Dict
|
||||
) -> Optional[int]:
|
||||
sql = select(round_details).where(
|
||||
round_details.c.round_id_in_json == round_id
|
||||
)
|
||||
result = await self.execute(sql)
|
||||
rid = result.fetchone()
|
||||
if rid is None:
|
||||
tmp = {}
|
||||
tmp["round_id_in_json"] = round_id
|
||||
tmp["name"] = round_data["round_event_nm"]
|
||||
tmp["season"] = version
|
||||
tmp["start_dt"] = datetime.datetime.fromtimestamp(round_data["start_dt"])
|
||||
tmp["end_dt"] = datetime.datetime.fromtimestamp(round_data["end_dt"])
|
||||
sql = insert(round_details).values(**tmp)
|
||||
result = await self.execute(sql)
|
||||
return result.lastrowid
|
||||
return rid["id"]
|
||||
#TODO: get top five players of last round event for Boot/GetConfigData
|
@ -4,6 +4,7 @@ from random import choice, randint
|
||||
from typing import Any, Dict, List
|
||||
import json
|
||||
import logging
|
||||
import asyncio
|
||||
|
||||
from core.config import CoreConfig
|
||||
from core.utils import Utils
|
||||
@ -60,6 +61,9 @@ class IDACSeason2(IDACBase):
|
||||
"timetrial_event_id"
|
||||
)
|
||||
|
||||
# load the user configured round event (only one)
|
||||
asyncio.create_task(self._load_round_event())
|
||||
|
||||
# load the user configured battle gifts (only one)
|
||||
self.battle_gift_event = None
|
||||
if self.game_config.battle_gift.enable:
|
||||
@ -79,6 +83,117 @@ class IDACSeason2(IDACBase):
|
||||
) as f:
|
||||
self.battle_gift_event = self._fix_dates(json.load(f))
|
||||
|
||||
async def _load_round_event(self):
|
||||
self.round_event_id = 0
|
||||
self.round_event = []
|
||||
self.last_round_event_id = 0
|
||||
self.last_round_event = []
|
||||
self.last_round_event_ranking = []
|
||||
if self.game_config.round_event.enable:
|
||||
# Load current round event
|
||||
round = self.game_config.round_event.enabled_round
|
||||
if round is not None:
|
||||
if not os.path.exists(f"./titles/idac/data/rounds/season{self.version+1}/{round}.json"):
|
||||
self.logger.warning(f"Round info {round} is enabled but json file does not exist!")
|
||||
else:
|
||||
with open(
|
||||
f"./titles/idac/data/rounds/season{self.version+1}/{round}.json", encoding="UTF-8"
|
||||
) as f:
|
||||
self.logger.debug(f"Loading round info {round}...")
|
||||
tmp = json.load(f)
|
||||
self.round_event_id = await self.data.rounds._try_load_round_event(tmp["round_event_id"], self.version, tmp)
|
||||
self.round_event.append(self._fix_dates(tmp))
|
||||
self.logger.debug(f"Loaded round id for database: {self.round_event_id}...")
|
||||
|
||||
# Load last round event
|
||||
round = self.game_config.round_event.last_round
|
||||
if round is not None:
|
||||
if not os.path.exists(f"./titles/idac/data/rounds/season{self.version+1}/{round}.json"):
|
||||
self.logger.warning(f"Round info {round} is enabled but json file does not exist!")
|
||||
else:
|
||||
with open(
|
||||
f"./titles/idac/data/rounds/season{self.version+1}/{round}.json", encoding="UTF-8"
|
||||
) as f:
|
||||
self.logger.debug(f"Loading round info {round}...")
|
||||
tmp = json.load(f)
|
||||
self.last_round_event_id = await self.data.rounds._try_load_round_event(tmp["round_event_id"], self.version, tmp)
|
||||
self.last_round_event.append(tmp)
|
||||
self.logger.debug(f"Loaded round id for database: {self.last_round_event_id}...")
|
||||
|
||||
# Load top five of last round event
|
||||
|
||||
# class LastRoundEventRanking(BaseModel):
|
||||
# round_rank: int
|
||||
# round_point: int
|
||||
# round_play_count: int
|
||||
|
||||
ranking = await self.data.rounds.get_round_top_five(self.last_round_event_id)
|
||||
if ranking is not None:
|
||||
rank_profile = {
|
||||
"round_rank": 0,
|
||||
"round_point": 0,
|
||||
"round_play_count": 0,
|
||||
"username": "DUMMY",
|
||||
"country": 9,
|
||||
"store": self.core_cfg.server.name,
|
||||
"online_battle_rank": 0,
|
||||
"mytitle_id": 0,
|
||||
"mytitle_effect_id": 0,
|
||||
"car_data": [],
|
||||
"user_avatar": []
|
||||
}
|
||||
for user in ranking:
|
||||
# get the user's profile
|
||||
p = await self.data.profile.get_profile(user["user"], self.version)
|
||||
user_data = p._asdict()
|
||||
arcade = await self.data.arcade.get_arcade(user_data["store"])
|
||||
user_data["store_name"] = (
|
||||
self.core_cfg.server.name if arcade is None else arcade["name"]
|
||||
)
|
||||
rank_profile["username"] = user_data["username"]
|
||||
rank_profile["country"] = user_data["country"]
|
||||
rank_profile["store"] = user_data["store_name"]
|
||||
rank_profile["mytitle_id"] = user_data["mytitle_id"]
|
||||
rank_profile["mytitle_effect_id"] = user_data["mytitle_effect_id"]
|
||||
|
||||
# get the user's avatar
|
||||
a = await self.data.profile.get_profile_avatar(user["user"])
|
||||
avatar_data = a._asdict()
|
||||
del avatar_data["id"]
|
||||
del avatar_data["user"]
|
||||
rank_profile["user_avatar"] = avatar_data
|
||||
|
||||
# get the user's rank
|
||||
r = await self.data.profile.get_profile_rank(user_id, self.version)
|
||||
rank_data = r._asdict()
|
||||
del rank_data["id"]
|
||||
del rank_data["user"]
|
||||
del rank_data["version"]
|
||||
rank_profile["online_battle_rank"] = rank_data["online_battle_rank"]
|
||||
|
||||
# get the user's car
|
||||
cars = await self.data.item.get_cars(self.version, user_id, only_pickup=True)
|
||||
fulltune_count = 0
|
||||
total_car_parts_count = 0
|
||||
car_data = []
|
||||
for car in cars:
|
||||
tmp = car._asdict()
|
||||
del tmp["id"]
|
||||
del tmp["user"]
|
||||
del tmp["version"]
|
||||
car_data.append(tmp)
|
||||
rank_profile["car_data"] = car_data
|
||||
|
||||
# get round info
|
||||
ri = await self.data.rounds.get_round_info_by_id(user["user"], self.last_round_event_id)
|
||||
if ri is not None:
|
||||
ri = ri._asdict()
|
||||
rank_profile["round_rank"] = user["find_in_set_1"]
|
||||
rank_profile["round_point"] = ri["point"]
|
||||
rank_profile["round_play_count"] = ri["count"]
|
||||
|
||||
self.last_round_event_ranking.append(rank_profile)
|
||||
|
||||
async def handle_alive_get_request(self, data: Dict, headers: Dict):
|
||||
return {
|
||||
"status_code": "0",
|
||||
@ -170,106 +285,9 @@ class IDACSeason2(IDACBase):
|
||||
[self.battle_gift_event] if self.battle_gift_event else []
|
||||
),
|
||||
# online battle round event
|
||||
"round_event": [
|
||||
{
|
||||
"round_event_id": 30,
|
||||
"round_event_nm": f"{self.core_cfg.server.name} Event",
|
||||
"start_dt": int(
|
||||
datetime.strptime("2023-01-01", "%Y-%m-%d").timestamp()
|
||||
),
|
||||
"end_dt": int(
|
||||
datetime.strptime("2029-01-01", "%Y-%m-%d").timestamp()
|
||||
),
|
||||
"round_start_rank": 0,
|
||||
"save_filename": "0",
|
||||
# https://info-initialdac.sega.jp/1898/
|
||||
"vscount": [
|
||||
{
|
||||
"reward_upper_limit": 10,
|
||||
"reward_lower_limit": 10,
|
||||
"reward": [{"reward_category": 21, "reward_type": 483}],
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 40,
|
||||
"reward_lower_limit": 40,
|
||||
"reward": [{"reward_category": 21, "reward_type": 484}],
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 80,
|
||||
"reward_lower_limit": 80,
|
||||
"reward": [{"reward_category": 22, "reward_type": 516}],
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 120,
|
||||
"reward_lower_limit": 120,
|
||||
"reward": [{"reward_category": 21, "reward_type": 461}],
|
||||
},
|
||||
{
|
||||
"reward_upper_limit": 180,
|
||||
"reward_lower_limit": 180,
|
||||
"reward": [{"reward_category": 21, "reward_type": 462}],
|
||||
},
|
||||
],
|
||||
"rank": [],
|
||||
"point": [],
|
||||
"playable_course_list": [
|
||||
{"course_id": 4, "course_day": 0},
|
||||
{"course_id": 4, "course_day": 1},
|
||||
{"course_id": 6, "course_day": 0},
|
||||
{"course_id": 6, "course_day": 1},
|
||||
{"course_id": 8, "course_day": 0},
|
||||
{"course_id": 8, "course_day": 1},
|
||||
{"course_id": 10, "course_day": 0},
|
||||
{"course_id": 10, "course_day": 1},
|
||||
{"course_id": 12, "course_day": 0},
|
||||
{"course_id": 12, "course_day": 1},
|
||||
{"course_id": 14, "course_day": 0},
|
||||
{"course_id": 14, "course_day": 1},
|
||||
{"course_id": 16, "course_day": 0},
|
||||
{"course_id": 16, "course_day": 1},
|
||||
{"course_id": 18, "course_day": 0},
|
||||
{"course_id": 18, "course_day": 1},
|
||||
{"course_id": 20, "course_day": 0},
|
||||
{"course_id": 20, "course_day": 1},
|
||||
{"course_id": 22, "course_day": 0},
|
||||
{"course_id": 22, "course_day": 1},
|
||||
{"course_id": 24, "course_day": 0},
|
||||
{"course_id": 24, "course_day": 1},
|
||||
{"course_id": 26, "course_day": 0},
|
||||
{"course_id": 26, "course_day": 1},
|
||||
{"course_id": 36, "course_day": 0},
|
||||
{"course_id": 36, "course_day": 1},
|
||||
{"course_id": 38, "course_day": 0},
|
||||
{"course_id": 38, "course_day": 1},
|
||||
{"course_id": 40, "course_day": 0},
|
||||
{"course_id": 40, "course_day": 1},
|
||||
{"course_id": 42, "course_day": 0},
|
||||
{"course_id": 42, "course_day": 1},
|
||||
{"course_id": 44, "course_day": 0},
|
||||
{"course_id": 44, "course_day": 1},
|
||||
{"course_id": 46, "course_day": 0},
|
||||
{"course_id": 46, "course_day": 1},
|
||||
{"course_id": 48, "course_day": 0},
|
||||
{"course_id": 48, "course_day": 1},
|
||||
{"course_id": 50, "course_day": 0},
|
||||
{"course_id": 50, "course_day": 1},
|
||||
{"course_id": 52, "course_day": 0},
|
||||
{"course_id": 52, "course_day": 1},
|
||||
{"course_id": 54, "course_day": 0},
|
||||
{"course_id": 54, "course_day": 1},
|
||||
{"course_id": 56, "course_day": 0},
|
||||
{"course_id": 56, "course_day": 1},
|
||||
{"course_id": 58, "course_day": 0},
|
||||
{"course_id": 58, "course_day": 1},
|
||||
{"course_id": 68, "course_day": 0},
|
||||
{"course_id": 68, "course_day": 1},
|
||||
{"course_id": 70, "course_day": 0},
|
||||
{"course_id": 70, "course_day": 1},
|
||||
],
|
||||
}
|
||||
],
|
||||
"last_round_event": [],
|
||||
"last_round_event_ranking": [],
|
||||
"round_event": self.round_event,
|
||||
"last_round_event": self.last_round_event,
|
||||
"last_round_event_ranking": self.last_round_event_ranking,
|
||||
"round_event_exp": [],
|
||||
"stamp_info": self.stamp_info,
|
||||
# 0 = use default data, 1+ = server version of timereleasedata response
|
||||
@ -843,6 +861,57 @@ class IDACSeason2(IDACBase):
|
||||
vs_info["course_select_priority"] = data.get("course_select_priority")
|
||||
return vs_info
|
||||
|
||||
async def _update_round_info(self, user_id: int, round_event_id: int, point: int, win: int) -> Dict:
|
||||
# get the round info data from database
|
||||
round_info = []
|
||||
ri = await self.data.rounds.get_round_info_by_id(user_id, round_event_id)
|
||||
if ri is not None:
|
||||
tmp = ri._asdict()
|
||||
|
||||
del tmp["id"]
|
||||
|
||||
# calculate new round points and info
|
||||
tmp["point"] = 0 if tmp["point"] + point < 1 else tmp["point"] + point
|
||||
tmp["count"] += 1
|
||||
tmp["win"] += win
|
||||
tmp["play_dt"] = datetime.now()
|
||||
|
||||
# update players round info
|
||||
await self.data.rounds.put_round_event(user_id, round_event_id, tmp)
|
||||
|
||||
del tmp["play_dt"]
|
||||
|
||||
# get players new round ranking
|
||||
r = await self.data.rounds.get_round_rank_by_id(user_id, round_event_id)
|
||||
round_ranking = r._asdict()
|
||||
tmp["rank"] = round_ranking["find_in_set_1"] if tmp["point"] > 0 else 0
|
||||
|
||||
# TODO: get players historical earned points
|
||||
tmp["total_round_point"] = 0
|
||||
|
||||
round_info.append(tmp)
|
||||
else:
|
||||
# new player of now-going round event
|
||||
tmp = {}
|
||||
tmp["point"] = 0 if point < 1 else point
|
||||
tmp["count"] = 1
|
||||
tmp["win"] = win
|
||||
tmp["play_dt"] = datetime.now()
|
||||
await self.data.rounds.put_round_event(user_id, round_event_id, tmp)
|
||||
|
||||
del tmp["play_dt"]
|
||||
|
||||
r = await self.data.rounds.get_round_rank_by_id(user_id, round_event_id)
|
||||
round_ranking = r._asdict()
|
||||
tmp["rank"] = round_ranking["find_in_set_1"] if tmp["point"] > 0 else 0
|
||||
|
||||
# TODO: get players historical earned points
|
||||
tmp["total_round_point"] = 0
|
||||
|
||||
round_info.append(tmp)
|
||||
return round_info
|
||||
|
||||
|
||||
def _choose_gift_id(self) -> Dict:
|
||||
gift_data = self.battle_gift_event["gift_data"]
|
||||
# calculate the total_rate based on the first_distribution_rate
|
||||
@ -925,6 +994,25 @@ class IDACSeason2(IDACBase):
|
||||
}
|
||||
)
|
||||
|
||||
# get the users'd round data
|
||||
|
||||
round_info = []
|
||||
ri = await self.data.rounds.get_round_info_by_id(user_id, self.round_event_id)
|
||||
if ri is not None:
|
||||
r = await self.data.rounds.get_round_rank_by_id(user_id, self.round_event_id)
|
||||
round_ranking = r._asdict()
|
||||
tmp = ri._asdict()
|
||||
del tmp["id"]
|
||||
del tmp["user"]
|
||||
del tmp["round_id"]
|
||||
del tmp["play_dt"]
|
||||
|
||||
tmp["rank"] = round_ranking["find_in_set_1"]
|
||||
# TODO: calculate this
|
||||
tmp["total_round_point"] = 0
|
||||
|
||||
round_info.append(tmp)
|
||||
|
||||
# get the user's course, required for the "course proeficiency"
|
||||
courses = await self.data.item.get_courses(user_id)
|
||||
course_data = []
|
||||
@ -1206,7 +1294,7 @@ class IDACSeason2(IDACBase):
|
||||
# first_distribution related are useless since this is all handled by server
|
||||
"battle_gift_data": battle_gift_data,
|
||||
"ticket_data": ticket_data,
|
||||
"round_event": [],
|
||||
"round_event": round_info,
|
||||
"last_round_event": [],
|
||||
"past_round_event": [],
|
||||
"total_round_point": 0,
|
||||
@ -2849,6 +2937,7 @@ class IDACSeason2(IDACBase):
|
||||
}
|
||||
|
||||
async def handle_user_updateonlinebattle_request(self, data: Dict, headers: Dict):
|
||||
#TODO: voiding cheaters' result. which will need to analysis historical battle results returned by the game.
|
||||
return {
|
||||
"status_code": "0",
|
||||
"bothwin_penalty": 1,
|
||||
@ -2922,18 +3011,12 @@ class IDACSeason2(IDACBase):
|
||||
user_id, self.version, {"online_battle_play_count": tips["online_battle_play_count"] + 1}
|
||||
)
|
||||
|
||||
round_info = await self._update_round_info(user_id, self.round_event_id, data.pop("round_point"), data.pop("win_flg"))
|
||||
|
||||
return {
|
||||
"status_code": "0",
|
||||
"vsinfo_data": vs_info,
|
||||
"round_event": [
|
||||
{
|
||||
"count": 1,
|
||||
"win": 1,
|
||||
"rank": 1,
|
||||
"point": 1,
|
||||
"total_round_point": 1,
|
||||
}
|
||||
],
|
||||
"round_event": round_info,
|
||||
"car_use_count": [],
|
||||
"maker_use_count": [],
|
||||
}
|
||||
@ -3026,3 +3109,79 @@ class IDACSeason2(IDACBase):
|
||||
"car_use_count": [],
|
||||
"maker_use_count": [],
|
||||
}
|
||||
|
||||
async def handle_factory_numberlotterybefore_request(self, data: Dict, headers: Dict):
|
||||
user_id = headers["session"]
|
||||
win_list = []
|
||||
lottery_count = 0
|
||||
|
||||
p = await self.data.factory.get_lottery(user_id, self.version)
|
||||
if p is not None:
|
||||
lottery_data = p._asdict()
|
||||
lottery_count = lottery_data["lottery_count"]
|
||||
number = 0
|
||||
while number < 10:
|
||||
if int(lottery_data["saved_value"]) & 1:
|
||||
win_list_data = {
|
||||
"m_number_lottery_schedule_no": 1,
|
||||
"win_number": 0
|
||||
}
|
||||
|
||||
win_list_data["win_number"] = number*1111
|
||||
win_list.append(win_list_data)
|
||||
|
||||
lottery_data["saved_value"] = lottery_data["saved_value"] / 2
|
||||
number = number + 1
|
||||
|
||||
return {
|
||||
"status_code": "0",
|
||||
"lottery_info": {
|
||||
"lottery_count": lottery_count,
|
||||
"win_list": win_list
|
||||
}
|
||||
}
|
||||
|
||||
async def handle_factory_numberlotteryresult_request(self, data: Dict, headers: Dict):
|
||||
user_id = headers["session"]
|
||||
win_number = data.pop("win_number")
|
||||
lottery_count = data.pop("lottery_count")
|
||||
|
||||
# save count if not a lucky number otherwise save all
|
||||
if win_number != 10000:
|
||||
shifted = win_number / 1111
|
||||
number = 1 << int(shifted)
|
||||
p = await self.data.factory.get_lottery(user_id, self.version)
|
||||
if p is not None:
|
||||
lottery_data = p._asdict()
|
||||
saved_value = lottery_data["saved_value"] + number
|
||||
else:
|
||||
saved_value = number
|
||||
|
||||
await self.data.factory.put_lottery(user_id, self.version, saved_value, lottery_count)
|
||||
else:
|
||||
p = await self.data.factory.get_lottery(user_id, self.version)
|
||||
if p is not None:
|
||||
lottery_data = p._asdict()
|
||||
saved_value = lottery_data["saved_value"]
|
||||
else:
|
||||
saved_value = 0
|
||||
await self.data.factory.put_lottery(user_id, self.version, saved_value, lottery_count)
|
||||
|
||||
# save car data if lottery ended
|
||||
car = {}
|
||||
car["style_car_id"] = data.pop("style_car_id")
|
||||
car["l_no"] = data.pop("l_no")
|
||||
if data.pop("isEnd") == 1:
|
||||
await self.data.item.put_car(user_id, self.version, car)
|
||||
|
||||
# save ticket data
|
||||
for ticket in data.pop("ticket_data"):
|
||||
await self.data.item.put_ticket(user_id, ticket)
|
||||
|
||||
# save cash
|
||||
await self.data.profile.put_profile(user_id, self.version, data)
|
||||
|
||||
return {
|
||||
"status_code": "0"
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user