Files
Groove_Coaster_2_Server/new_server_7003/api/play.py
UnitedAirforce 10bbd03bb6 v3 push
2025-11-26 13:49:27 +08:00

193 lines
6.5 KiB
Python

from starlette.responses import Response
from starlette.requests import Request
from starlette.routing import Route
import json
import copy
import xml.etree.ElementTree as ET
from datetime import datetime
from config import COIN_REWARD
from api.database import player_database, results, decrypt_fields_to_user_info, set_device_data_using_decrypted_fields, results_query, set_user_data_using_decrypted_fields, clear_rank_cache
from api.crypt import decrypt_fields
from api.template import START_STAGES, EXP_UNLOCKED_SONGS, RESULT_XML
from api.misc import should_serve
async def score_delta(mode, old_score, new_score):
mobile_modes = [1, 2, 3]
arcade_modes = [11, 12, 13]
if mode in mobile_modes:
return new_score - old_score, 0, new_score - old_score
elif mode in arcade_modes:
return 0, new_score - old_score, new_score - old_score
else:
return 0, 0, 0
async def result_request(request: Request):
decrypted_fields, _ = await decrypt_fields(request)
if not decrypted_fields:
return Response("""<response><code>10</code><message>Invalid request data.</message></response>""", media_type="application/xml")
should_serve_result = await should_serve(decrypted_fields)
if not should_serve_result:
return Response("""<response><code>403</code><message>Access denied.</message></response>""", media_type="application/xml")
device_id = decrypted_fields[b'vid'][0].decode()
user_info, device_info = await decrypt_fields_to_user_info(decrypted_fields)
tree = copy.deepcopy(RESULT_XML)
root = tree.getroot()
stts = decrypted_fields[b'stts'][0].decode()
song_id = int(decrypted_fields[b'id'][0].decode())
mode = int(decrypted_fields[b'mode'][0].decode())
avatar = int(decrypted_fields[b'avatar'][0].decode())
score = int(decrypted_fields[b'score'][0].decode())
high_score = decrypted_fields[b'high_score'][0].decode()
play_rslt = decrypted_fields[b'play_rslt'][0].decode()
item = int(decrypted_fields[b'item'][0].decode())
device_os = decrypted_fields[b'os'][0].decode()
os_ver = decrypted_fields[b'os_ver'][0].decode()
ver = decrypted_fields[b'ver'][0].decode()
stts = "[" + stts + "]"
high_score = "[" + high_score + "]"
play_rslt = "[" + play_rslt + "]"
try:
stts = json.loads(stts)
high_score = json.loads(high_score)
play_rslt = json.loads(play_rslt)
except:
return Response("""<response><code>10</code><message>Invalid request data.</message></response>""", media_type="application/xml")
cache_key = f"{song_id}-{mode}"
# delete cache with key
await clear_rank_cache(cache_key)
# Start results processing
target_row_id = 0
rank = None
user_id = user_info['id'] if user_info else None
if user_id:
query_param = {
"song_id": song_id,
"mode": mode,
"user_id": user_id
}
records = await results_query(query_param)
mobile_delta, arcade_delta, total_delta = 0, 0, 0
if len(records) != 0:
# user row exists
target_row_id = records[0]['id']
if score > records[0]['score']:
mobile_delta, arcade_delta, total_delta = await score_delta(mode, records[0]['score'], score)
update_query = results.update().where(results.c.id == records[0]['id']).values(
device_id=device_id,
stts=stts,
avatar=avatar,
score=score,
high_score=high_score,
play_rslt=play_rslt,
item=item,
os=device_os,
os_ver=os_ver,
ver=ver
)
await player_database.execute(update_query)
else:
# insert new row
mobile_delta, arcade_delta, total_delta = await score_delta(mode, 0, score)
insert_query = results.insert().values(
device_id=device_id,
user_id=user_id,
stts=stts,
song_id=song_id,
mode=mode,
avatar=avatar,
score=score,
high_score=high_score,
play_rslt=play_rslt,
item=item,
os=device_os,
os_ver=os_ver,
ver=ver,
created_at=datetime.utcnow(),
updated_at=datetime.utcnow()
)
result = await player_database.execute(insert_query)
target_row_id = result
# Calculate final rank for client display
query_param = {
"song_id": song_id,
"mode": mode
}
records = await results_query(query_param)
rank = None
for idx, record in enumerate(records, start=1):
if record["id"] == target_row_id:
rank = idx
break
# Update user score delta
if total_delta:
update_data = {
"mobile_delta": user_info['mobile_delta'] + mobile_delta,
"arcade_delta": user_info['arcade_delta'] + arcade_delta,
"total_delta": user_info['total_delta'] + total_delta
}
await set_user_data_using_decrypted_fields(decrypted_fields, update_data)
# Unlocking mission stages and updating avatars
my_stage = set(device_info["my_stage"]) if device_info and device_info["my_stage"] else set(START_STAGES)
current_exp = stts[0]
for song in EXP_UNLOCKED_SONGS:
if song["lvl"] <= current_exp:
my_stage.add(song["id"])
my_stage = sorted(my_stage)
update_data = {
"lvl": current_exp,
"avatar": int(avatar),
"my_stage": my_stage
}
# add coins, skip 4max placeholder songs
if int(song_id) not in range(616, 1024) or int(mode) not in range(0, 4):
coin_mp = user_info['coin_mp'] if user_info else 1
current_coin = device_info["coin"] if device_info and device_info["coin"] else 0
updated_coin = current_coin + COIN_REWARD * coin_mp
update_data["coin"] = updated_coin
await set_device_data_using_decrypted_fields(decrypted_fields, update_data)
after_element = root.find('.//after')
after_element.text = str(rank)
xml_response = ET.tostring(tree.getroot(), encoding='unicode')
return Response(xml_response, media_type="application/xml")
routes = [
Route('/result.php', result_request, methods=['GET'])
]