forked from Hay1tsme/artemis
feat(chuni): Implement GameLoginApi/GameLogoutApi
This commit is contained in:
@ -0,0 +1,36 @@
|
|||||||
|
"""Implement GameLoginApi/GameLogoutApi
|
||||||
|
|
||||||
|
Revision ID: 263169f3a129
|
||||||
|
Revises: 48f4acc43a7e
|
||||||
|
Create Date: 2024-06-23 12:16:57.881129
|
||||||
|
|
||||||
|
"""
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
from sqlalchemy.dialects import mysql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision = '263169f3a129'
|
||||||
|
down_revision = '48f4acc43a7e'
|
||||||
|
branch_labels = None
|
||||||
|
depends_on = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade():
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('aime_user_game_locks',
|
||||||
|
sa.Column('id', sa.Integer(), autoincrement=True, nullable=False),
|
||||||
|
sa.Column('user', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('game', sa.String(length=4), nullable=False),
|
||||||
|
sa.Column('expires_at', sa.TIMESTAMP(), server_default=sa.text('date_add(now(), INTERVAL 15 MINUTE)'), nullable=True),
|
||||||
|
sa.Column('extra', sa.JSON(), nullable=True),
|
||||||
|
sa.ForeignKeyConstraint(['user'], ['aime_user.id'], onupdate='cascade', ondelete='cascade'),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('user', 'game', name='aime_user_title_locks'),
|
||||||
|
mysql_charset='utf8mb4',
|
||||||
|
)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade():
|
||||||
|
op.drop_table("aime_user_game_locks")
|
@ -1,7 +1,9 @@
|
|||||||
from typing import Optional, List
|
from typing import Optional, List
|
||||||
from sqlalchemy import Table, Column
|
from sqlalchemy import Table, Column, text, UniqueConstraint
|
||||||
from sqlalchemy.types import Integer, String, TIMESTAMP
|
from sqlalchemy.dialects import mysql
|
||||||
|
from sqlalchemy.types import Integer, String, TIMESTAMP, JSON
|
||||||
from sqlalchemy.sql import func
|
from sqlalchemy.sql import func
|
||||||
|
from sqlalchemy.sql.schema import ForeignKey
|
||||||
from sqlalchemy.dialects.mysql import insert
|
from sqlalchemy.dialects.mysql import insert
|
||||||
from sqlalchemy.sql import func, select
|
from sqlalchemy.sql import func, select
|
||||||
from sqlalchemy.engine import Row
|
from sqlalchemy.engine import Row
|
||||||
@ -23,6 +25,18 @@ aime_user = Table(
|
|||||||
mysql_charset="utf8mb4",
|
mysql_charset="utf8mb4",
|
||||||
)
|
)
|
||||||
|
|
||||||
|
game_locks = Table(
|
||||||
|
"aime_user_game_locks",
|
||||||
|
metadata,
|
||||||
|
Column("id", Integer, nullable=False, primary_key=True, autoincrement=True),
|
||||||
|
Column("user", ForeignKey("aime_user.id", ondelete="cascade", onupdate="cascade"), nullable=False),
|
||||||
|
Column("game", String(4), nullable=False),
|
||||||
|
Column("expires_at", TIMESTAMP, server_default=func.date_add(func.now(), text("INTERVAL 15 MINUTE"))),
|
||||||
|
Column("extra", JSON),
|
||||||
|
UniqueConstraint("user", "game", name="aime_user_title_locks"),
|
||||||
|
mysql_charset="utf8mb4",
|
||||||
|
)
|
||||||
|
|
||||||
class UserData(BaseData):
|
class UserData(BaseData):
|
||||||
async def create_user(
|
async def create_user(
|
||||||
self,
|
self,
|
||||||
@ -123,4 +137,33 @@ class UserData(BaseData):
|
|||||||
|
|
||||||
async def get_user_by_username(self, username: str) -> Optional[Row]:
|
async def get_user_by_username(self, username: str) -> Optional[Row]:
|
||||||
result = await self.execute(aime_user.select(aime_user.c.username == username))
|
result = await self.execute(aime_user.select(aime_user.c.username == username))
|
||||||
if result: return result.fetchone()
|
if result:
|
||||||
|
return result.fetchone()
|
||||||
|
|
||||||
|
async def acquire_lock_for_game(self, user_id: int, game: str, extra: dict | None = None):
|
||||||
|
sql = game_locks.select(
|
||||||
|
(game_locks.c.user == user_id)
|
||||||
|
& (game_locks.c.game == game)
|
||||||
|
& func.timestampdiff(text("SECOND"), func.now(), game_locks.c.expires_at) > 0)
|
||||||
|
result = await self.execute(sql)
|
||||||
|
|
||||||
|
if result:
|
||||||
|
return result.fetchone()
|
||||||
|
|
||||||
|
sql = (
|
||||||
|
insert(game_locks)
|
||||||
|
.values(user=user_id, game=game, extra=extra)
|
||||||
|
.on_duplicate_key_update(
|
||||||
|
expires_at=func.date_add(func.now(), text("INTERVAL 15 MINUTE")),
|
||||||
|
extra=extra,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
await self.execute(sql)
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
async def release_lock_for_game(self, user_id: int, game: str):
|
||||||
|
sql = game_locks.delete(game_locks.c.user == user_id & game_locks.c.game == game)
|
||||||
|
|
||||||
|
await self.execute(sql)
|
||||||
|
@ -31,12 +31,21 @@ class ChuniBase:
|
|||||||
loginBonus 30 gets looped, only show the login banner every 24 hours,
|
loginBonus 30 gets looped, only show the login banner every 24 hours,
|
||||||
adds the bonus to items (itemKind 6)
|
adds the bonus to items (itemKind 6)
|
||||||
"""
|
"""
|
||||||
|
user_id = int(data["userId"])
|
||||||
|
lock_result = await self.data.user.acquire_lock_for_game(user_id, self.game, data)
|
||||||
|
|
||||||
|
if lock_result is not None:
|
||||||
|
self.logger.warn(
|
||||||
|
"User ID %d attempted to log in while having a profile lock expiring on %s.",
|
||||||
|
user_id, lock_result["expires_at"],
|
||||||
|
extra=lock_result["extra"]
|
||||||
|
)
|
||||||
|
return {"returnCode": 0}
|
||||||
|
|
||||||
# ignore the login bonus if disabled in config
|
# ignore the login bonus if disabled in config
|
||||||
if not self.game_cfg.mods.use_login_bonus:
|
if not self.game_cfg.mods.use_login_bonus:
|
||||||
return {"returnCode": 1}
|
return {"returnCode": 1}
|
||||||
|
|
||||||
user_id = data["userId"]
|
|
||||||
login_bonus_presets = await self.data.static.get_login_bonus_presets(self.version)
|
login_bonus_presets = await self.data.static.get_login_bonus_presets(self.version)
|
||||||
|
|
||||||
for preset in login_bonus_presets:
|
for preset in login_bonus_presets:
|
||||||
@ -120,6 +129,9 @@ class ChuniBase:
|
|||||||
|
|
||||||
async def handle_game_logout_api_request(self, data: Dict) -> Dict:
|
async def handle_game_logout_api_request(self, data: Dict) -> Dict:
|
||||||
# self.data.base.log_event("chuni", "logout", logging.INFO, {"version": self.version, "user": data["userId"]})
|
# self.data.base.log_event("chuni", "logout", logging.INFO, {"version": self.version, "user": data["userId"]})
|
||||||
|
user_id = int(data["userId"])
|
||||||
|
|
||||||
|
await self.data.user.release_lock_for_game(user_id, self.game)
|
||||||
return {"returnCode": 1}
|
return {"returnCode": 1}
|
||||||
|
|
||||||
async def handle_get_game_charge_api_request(self, data: Dict) -> Dict:
|
async def handle_get_game_charge_api_request(self, data: Dict) -> Dict:
|
||||||
|
Reference in New Issue
Block a user