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 sqlalchemy import Table, Column
|
||||
from sqlalchemy.types import Integer, String, TIMESTAMP
|
||||
from sqlalchemy import Table, Column, text, UniqueConstraint
|
||||
from sqlalchemy.dialects import mysql
|
||||
from sqlalchemy.types import Integer, String, TIMESTAMP, JSON
|
||||
from sqlalchemy.sql import func
|
||||
from sqlalchemy.sql.schema import ForeignKey
|
||||
from sqlalchemy.dialects.mysql import insert
|
||||
from sqlalchemy.sql import func, select
|
||||
from sqlalchemy.engine import Row
|
||||
@ -23,6 +25,18 @@ aime_user = Table(
|
||||
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):
|
||||
async def create_user(
|
||||
self,
|
||||
@ -123,4 +137,33 @@ class UserData(BaseData):
|
||||
|
||||
async def get_user_by_username(self, username: str) -> Optional[Row]:
|
||||
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,
|
||||
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
|
||||
if not self.game_cfg.mods.use_login_bonus:
|
||||
return {"returnCode": 1}
|
||||
|
||||
user_id = data["userId"]
|
||||
login_bonus_presets = await self.data.static.get_login_bonus_presets(self.version)
|
||||
|
||||
for preset in login_bonus_presets:
|
||||
@ -120,6 +129,9 @@ class ChuniBase:
|
||||
|
||||
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"]})
|
||||
user_id = int(data["userId"])
|
||||
|
||||
await self.data.user.release_lock_for_game(user_id, self.game)
|
||||
return {"returnCode": 1}
|
||||
|
||||
async def handle_get_game_charge_api_request(self, data: Dict) -> Dict:
|
||||
|
Reference in New Issue
Block a user