Support SRTools web
This commit is contained in:
@@ -11,9 +11,9 @@ from pydantic import BaseModel
|
||||
from rail_proto.lib import (
|
||||
Equipment,
|
||||
Relic,
|
||||
RelicAffix,
|
||||
BattleRelic,
|
||||
RelicAffix
|
||||
RelicAffix,
|
||||
PlayerSyncScNotify
|
||||
)
|
||||
|
||||
items_collection = get_collection("items")
|
||||
@@ -125,6 +125,18 @@ class InventoryData(BaseDatabaseData):
|
||||
)
|
||||
return proto
|
||||
|
||||
def RelicSyncProto(self):
|
||||
proto = PlayerSyncScNotify(
|
||||
relic_list=[self.ToProto()]
|
||||
)
|
||||
return proto
|
||||
|
||||
def LightconeSyncProto(self):
|
||||
proto = PlayerSyncScNotify(
|
||||
equipment_list=[self.ToProto()]
|
||||
)
|
||||
return proto
|
||||
|
||||
def save_item(self):
|
||||
item_data = self.model_dump()
|
||||
item_data["uid"] = self.uid
|
||||
|
||||
@@ -10,7 +10,7 @@ from rail_proto.lib import (
|
||||
PlayerSyncScNotify,
|
||||
AvatarSync
|
||||
)
|
||||
from pymongo import UpdateOne
|
||||
from pymongo import UpdateOne, DeleteMany
|
||||
from utils.logger import Error
|
||||
|
||||
|
||||
@@ -82,12 +82,14 @@ class PlayerManager(BaseModel):
|
||||
return None
|
||||
|
||||
def add_lineup(self,avatar_ids: list[int],index=0,name=""):
|
||||
LineupData(
|
||||
lineup = LineupData(
|
||||
uid=self.data.uid,
|
||||
index=index,
|
||||
name=name,
|
||||
avatar_list=avatar_ids
|
||||
).add_lineup()
|
||||
if lineup:
|
||||
self.lineup_manager[lineup.index] = lineup
|
||||
|
||||
def add_lightcone(self,lightcone_id: int, rank=1) -> Optional[InventoryData]:
|
||||
item = InventoryData(
|
||||
@@ -110,6 +112,25 @@ class PlayerManager(BaseModel):
|
||||
return item
|
||||
return None
|
||||
|
||||
async def unequip_items(self,avatar_id):
|
||||
avatar_data = self.avatar_manager.get(avatar_id)
|
||||
if not avatar_data:return
|
||||
item_data = self.inventory_manager.get(avatar_data.lightcone_id)
|
||||
if not item_data:return
|
||||
item_data.equip_avatar = 0
|
||||
|
||||
match item_data.main_type:
|
||||
case 1:
|
||||
avatar_data.lightcone_id = 0
|
||||
case 2:
|
||||
avatar_data.relic_ids[str(item_data.sub_type)] = 0
|
||||
return
|
||||
|
||||
async def remove_items(self,count,unique_id):
|
||||
item_data = self.inventory_manager.get(unique_id)
|
||||
if not item_data:return
|
||||
if count == 0: self.inventory_manager.pop(unique_id)
|
||||
return
|
||||
|
||||
def PlayerSyncProto(self) -> PlayerSyncScNotify:
|
||||
avatars = []
|
||||
@@ -135,6 +156,33 @@ class PlayerManager(BaseModel):
|
||||
basic_info=self.data.ToProto()
|
||||
)
|
||||
|
||||
def SrToolAvatarSync(self):
|
||||
avatars = []
|
||||
for avatar_id, avatar in self.avatar_manager.items():
|
||||
avatars.append(avatar.ToProto())
|
||||
|
||||
return PlayerSyncScNotify(
|
||||
avatar_sync=
|
||||
AvatarSync(
|
||||
avatar_list=avatars
|
||||
)
|
||||
)
|
||||
|
||||
def SrToolItemsSync(self):
|
||||
equipment = []
|
||||
for unique_id, item in self.inventory_manager.items():
|
||||
if item.main_type == 1:
|
||||
equipment.append(item.ToProto())
|
||||
|
||||
relics = []
|
||||
for unique_id, item in self.inventory_manager.items():
|
||||
if item.main_type == 2:
|
||||
relics.append(item.ToProto())
|
||||
return PlayerSyncScNotify(
|
||||
equipment_list=equipment,
|
||||
relic_list=relics
|
||||
)
|
||||
|
||||
def save_player_data_bulk(self):
|
||||
try:
|
||||
player_data = self.data.model_dump()
|
||||
@@ -168,11 +216,20 @@ class PlayerManager(BaseModel):
|
||||
|
||||
def save_all_items_bulk(self):
|
||||
operations = []
|
||||
current_unique_ids = set()
|
||||
|
||||
for item in self.inventory_manager.values():
|
||||
item_data = item.model_dump()
|
||||
item_data["uid"] = item.uid
|
||||
query = {"uid": item.uid, "unique_id": item.unique_id}
|
||||
operations.append(UpdateOne(query, {"$set": item_data}, upsert=True))
|
||||
current_unique_ids.add(item.unique_id)
|
||||
|
||||
delete_query = {
|
||||
"unique_id": {"$nin": list(current_unique_ids)},
|
||||
"uid": self.data.uid
|
||||
}
|
||||
operations.append(DeleteMany(delete_query))
|
||||
|
||||
if operations:
|
||||
items_collection.bulk_write(operations)
|
||||
@@ -18,21 +18,17 @@ async def handle(session: PlayerSession, msg: DressAvatarCsReq) -> betterproto.M
|
||||
target_equipment = session.player.inventory_manager.get(target_avatar.lightcone_id)
|
||||
target_avatar.lightcone_id, previous_avatar.lightcone_id = previous_avatar.lightcone_id, target_avatar.lightcone_id
|
||||
target_equipment.equip_avatar, equipment.equip_avatar = previous_avatar.avatar_id, target_avatar.avatar_id
|
||||
#target_equipment.save_item()
|
||||
else:
|
||||
previous_avatar.lightcone_id = 0
|
||||
equipment.equip_avatar = target_avatar.avatar_id
|
||||
target_avatar.lightcone_id = equipment.unique_id
|
||||
#previous_avatar.save_avatar()
|
||||
else:
|
||||
if target_avatar.lightcone_id > 0:
|
||||
previous_equipment = session.player.inventory_manager.get(target_avatar.lightcone_id)
|
||||
previous_equipment.equip_avatar = 0
|
||||
#previous_equipment.save_item()
|
||||
equipment.equip_avatar = target_avatar.avatar_id
|
||||
target_avatar.lightcone_id = equipment.unique_id
|
||||
|
||||
#equipment.save_item()
|
||||
#target_avatar.save_avatar()
|
||||
await session.notify(session.player.PlayerSyncProto())
|
||||
return DressAvatarScRsp(retcode=0)
|
||||
|
||||
session.pending_notify(session.player.PlayerSyncProto())
|
||||
return DressAvatarScRsp()
|
||||
@@ -33,41 +33,30 @@ async def handle(session: PlayerSession, msg: DressRelicAvatarCsReq) -> betterpr
|
||||
target_avatar.relic_ids[relic_sub_type] = previous_relic.unique_id
|
||||
previous_relic.equip_avatar = target_avatar.avatar_id
|
||||
|
||||
#current_relic.save_item()
|
||||
#previous_relic.save_item()
|
||||
|
||||
elif previous_relic_id > 0:
|
||||
previous_relic = session.player.inventory_manager.get(previous_relic_id)
|
||||
if previous_relic:
|
||||
previous_relic.equip_avatar = 0
|
||||
#previous_relic.save_item()
|
||||
previous_avatar.relic_ids[relic_sub_type] = 0
|
||||
|
||||
elif current_relic_id > 0:
|
||||
current_relic = session.player.inventory_manager.get(current_relic_id)
|
||||
if current_relic:
|
||||
current_relic.equip_avatar = 0
|
||||
#current_relic.save_item()
|
||||
target_avatar.relic_ids[relic_sub_type] = 0
|
||||
|
||||
target_avatar.relic_ids[relic_sub_type] = relic.unique_id
|
||||
relic.equip_avatar = target_avatar.avatar_id
|
||||
#relic.save_item()
|
||||
#previous_avatar.save_avatar()
|
||||
|
||||
else:
|
||||
if current_relic_id > 0:
|
||||
current_relic = session.player.inventory_manager.get(current_relic_id)
|
||||
if current_relic:
|
||||
current_relic.equip_avatar = 0
|
||||
#current_relic.save_item()
|
||||
|
||||
target_avatar.relic_ids[relic_sub_type] = relic.unique_id
|
||||
relic.equip_avatar = target_avatar.avatar_id
|
||||
#relic.save_item()
|
||||
|
||||
#target_avatar.save_avatar()
|
||||
session.pending_notify(session.player.PlayerSyncProto())
|
||||
|
||||
await session.notify(session.player.PlayerSyncProto())
|
||||
|
||||
return DressRelicAvatarScRsp(retcode=0)
|
||||
return DressRelicAvatarScRsp()
|
||||
|
||||
33
game_server/handlers/GetBigDataAllRecommendCsReq.py
Normal file
33
game_server/handlers/GetBigDataAllRecommendCsReq.py
Normal file
@@ -0,0 +1,33 @@
|
||||
import betterproto
|
||||
from game_server.net.session import PlayerSession
|
||||
from rail_proto.lib import (
|
||||
GetBigDataAllRecommendCsReq,
|
||||
GetBigDataAllRecommendScRsp,
|
||||
PIIIPHEFDJO,
|
||||
MKJALMKMPGL,
|
||||
OFNGPLJKLOJ,
|
||||
KNNFPFKCABE
|
||||
)
|
||||
|
||||
async def handle(session: PlayerSession, msg: GetBigDataAllRecommendCsReq) -> betterproto.Message:
|
||||
unk1 = PIIIPHEFDJO()
|
||||
unk2 = MKJALMKMPGL()
|
||||
list1 = []
|
||||
for _,avatar_data in session.player.avatar_manager.items():
|
||||
id = avatar_data.avatar_id
|
||||
list1.append(id)
|
||||
|
||||
unk2unk2 = OFNGPLJKLOJ()
|
||||
unk2unk2.avatar_id = id
|
||||
unk2.bfdmginboib.append(unk2unk2)
|
||||
unk1.apfecoopnkn.append(
|
||||
KNNFPFKCABE(
|
||||
avatar_id_list=list1
|
||||
)
|
||||
)
|
||||
|
||||
return GetBigDataAllRecommendScRsp(
|
||||
big_data_recommend_type=msg.big_data_recommend_type,
|
||||
dklbnbdpmpo=unk1,
|
||||
pfopjpjkklk=unk2,
|
||||
)
|
||||
13
game_server/handlers/GetBigDataRecommendCsReq.py
Normal file
13
game_server/handlers/GetBigDataRecommendCsReq.py
Normal file
@@ -0,0 +1,13 @@
|
||||
import betterproto
|
||||
from game_server.net.session import PlayerSession
|
||||
from rail_proto.lib import (
|
||||
GetBigDataRecommendCsReq,
|
||||
GetBigDataRecommendScRsp
|
||||
)
|
||||
|
||||
async def handle(session: PlayerSession, msg: GetBigDataRecommendCsReq) -> betterproto.Message:
|
||||
return GetBigDataRecommendScRsp(
|
||||
big_data_recommend_type=msg.big_data_recommend_type,
|
||||
has_recommand=True,
|
||||
equip_avatar=msg.equip_avatar
|
||||
)
|
||||
@@ -1,4 +1,6 @@
|
||||
import asyncio
|
||||
from threading import Thread
|
||||
from game_server.srtools.server import run_flask
|
||||
from game_server.net.gateway import KCPGateway
|
||||
from utils.config import Config
|
||||
from game_server.resource import ResourceManager
|
||||
@@ -6,7 +8,7 @@ from game_server.resource import ResourceManager
|
||||
def fn_main():
|
||||
try:
|
||||
ResourceManager.instance().load_resources()
|
||||
asyncio.run(KCPGateway.new(Config.GameServer.IP,Config.GameServer.Port))
|
||||
Thread(target=run_flask, args=(Config.SRToolsServer.IP, Config.SRToolsServer.Port), daemon=True).start()
|
||||
asyncio.run(KCPGateway.new(Config.GameServer.IP, Config.GameServer.Port))
|
||||
except Exception as e:
|
||||
print(f"Error: {e}")
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@ from game_server.net.packet import NetOperation
|
||||
from game_server.net.session import PlayerSession
|
||||
from utils.logger import Info,Warn,Debug
|
||||
from database.mongodb import get_database
|
||||
from game_server.srtools.gateway import set_gateway_instance
|
||||
|
||||
|
||||
class KCPGateway(asyncio.DatagramProtocol):
|
||||
@@ -17,6 +18,12 @@ class KCPGateway(asyncio.DatagramProtocol):
|
||||
self.timeout_check_interval = 5
|
||||
self.save_duration = 60
|
||||
|
||||
def get_active_connection(self,uid) -> PlayerSession:
|
||||
for key, session in self.sessions.items():
|
||||
if session.player.data.uid == uid:
|
||||
return session
|
||||
return None
|
||||
|
||||
async def check_sessions_timeout(self):
|
||||
while self.running:
|
||||
for conv_id, session in list(self.sessions.items()):
|
||||
@@ -134,6 +141,8 @@ class KCPGateway(asyncio.DatagramProtocol):
|
||||
lambda: KCPGateway(db), local_addr=(host, port)
|
||||
)
|
||||
|
||||
set_gateway_instance(protocol)
|
||||
|
||||
try:
|
||||
await protocol.shutdown_event.wait()
|
||||
except asyncio.CancelledError:
|
||||
|
||||
8
game_server/srtools/gateway.py
Normal file
8
game_server/srtools/gateway.py
Normal file
@@ -0,0 +1,8 @@
|
||||
gateway_instance = None
|
||||
|
||||
def set_gateway_instance(instance):
|
||||
global gateway_instance
|
||||
gateway_instance = instance
|
||||
|
||||
def get_gateway_instance():
|
||||
return gateway_instance
|
||||
74
game_server/srtools/models/srtools_data.py
Normal file
74
game_server/srtools/models/srtools_data.py
Normal file
@@ -0,0 +1,74 @@
|
||||
from pydantic import BaseModel
|
||||
from typing import Optional
|
||||
|
||||
class SRToolData(BaseModel):
|
||||
class RelicSubAffixData(BaseModel):
|
||||
sub_affix_id: int
|
||||
count: int
|
||||
step: int
|
||||
|
||||
class RelicData(BaseModel):
|
||||
level: int
|
||||
relic_id: int
|
||||
relic_set_id: int
|
||||
main_affix_id: int
|
||||
sub_affixes: Optional[list["SRToolData.RelicSubAffixData"]] = None
|
||||
internal_uid: int
|
||||
equip_avatar: int
|
||||
|
||||
class LightconeData(BaseModel):
|
||||
level: int
|
||||
internal_uid: int
|
||||
equip_avatar: int
|
||||
item_id: int
|
||||
rank: int
|
||||
promotion: int
|
||||
|
||||
class AvatarData(BaseModel):
|
||||
avatar_id: int
|
||||
data: Optional["SRToolData.AvatarInnerData"] = None
|
||||
level: int
|
||||
promotion: int
|
||||
techniques: Optional[list[int]] = []
|
||||
sp_value: int
|
||||
sp_max: int
|
||||
|
||||
class AvatarInnerData(BaseModel):
|
||||
rank: int
|
||||
skills: dict[str,int]
|
||||
|
||||
class DynamicKey(BaseModel):
|
||||
key: Optional[str] = ""
|
||||
value: int
|
||||
|
||||
class BlessingData(BaseModel):
|
||||
level: int
|
||||
id: int
|
||||
dynamic_key: Optional["SRToolData.DynamicKey"] = None
|
||||
|
||||
class MonsterData(BaseModel):
|
||||
level: int
|
||||
monster_id: int
|
||||
amount: int
|
||||
|
||||
class BattleConfigData(BaseModel):
|
||||
battle_type: Optional[str] = ""
|
||||
blessings: Optional[list["SRToolData.BlessingData"]] = None
|
||||
custom_stats: Optional[list["SRToolData.RelicSubAffixData"]] = None
|
||||
cycle_count: int
|
||||
monsters: Optional[list["SRToolData.MonsterData"]] = None
|
||||
path_resonance_id: int
|
||||
stage_id: int
|
||||
|
||||
relics: Optional[list[RelicData]] = None
|
||||
lightcones: Optional[list[LightconeData]] = None
|
||||
avatars: Optional[dict[int,AvatarData]] = {}
|
||||
|
||||
class SRToolDataReq(BaseModel):
|
||||
data: Optional[SRToolData] = None
|
||||
username: Optional[str] = ""
|
||||
password: Optional[str] = ""
|
||||
|
||||
class SRToolDataRsp(BaseModel):
|
||||
status: int
|
||||
message: Optional[str] = ""
|
||||
119
game_server/srtools/server.py
Normal file
119
game_server/srtools/server.py
Normal file
@@ -0,0 +1,119 @@
|
||||
|
||||
import logging
|
||||
from flask import Flask, request, jsonify
|
||||
from flask_cors import CORS
|
||||
import traceback
|
||||
import asyncio
|
||||
|
||||
from database.inventory.inventory_data import SubAffix
|
||||
from game_server.game.player.player_manager import PlayerManager
|
||||
from game_server.net.session import PlayerSession
|
||||
from game_server.srtools.models.srtools_data import SRToolDataRsp,SRToolDataReq,SRToolData
|
||||
from database.account.account_data import find_account_by_name
|
||||
from utils.logger import Info
|
||||
from game_server.srtools.gateway import get_gateway_instance
|
||||
flask_app = Flask(__name__)
|
||||
log = logging.getLogger('werkzeug')
|
||||
log.setLevel(logging.ERROR)
|
||||
CORS(flask_app, resources={r"/*": {"origins": "https://srtools.pages.dev"}})
|
||||
|
||||
|
||||
async def ClearInventory(conn: PlayerSession):
|
||||
# un equip all lightcones and relic after that remove it
|
||||
for _, items in list(conn.player.inventory_manager.items()):
|
||||
if items.equip_avatar != 0 and items.main_type in (1, 2):
|
||||
await conn.player.unequip_items(items.equip_avatar)
|
||||
if items.main_type == 1:
|
||||
await conn.notify(items.LightconeSyncProto())
|
||||
if items.main_type == 2:
|
||||
await conn.notify(items.RelicSyncProto())
|
||||
await conn.player.remove_items(0, items.unique_id)
|
||||
|
||||
async def AddAllData(player: PlayerManager, data: SRToolData):
|
||||
for key, avatar_data in data.avatars.items():
|
||||
avatar = player.avatar_manager.get(avatar_data.avatar_id)
|
||||
if avatar_data.avatar_id >= 8000:continue
|
||||
if not avatar:
|
||||
avatar = player.add_avatar(avatar_data.avatar_id)
|
||||
if avatar:
|
||||
player.avatar_manager[avatar_data.avatar_id] = avatar
|
||||
|
||||
avatar.level = avatar_data.level
|
||||
avatar.promotion = avatar_data.promotion
|
||||
avatar.rank = avatar_data.data.rank if avatar_data.data else 0
|
||||
avatar.skills = avatar_data.data.skills
|
||||
|
||||
for lightcone in data.lightcones:
|
||||
if lightcone.equip_avatar > 8000:continue
|
||||
lightcone_data = player.add_lightcone(
|
||||
lightcone.item_id,
|
||||
lightcone.rank
|
||||
)
|
||||
if lightcone_data:
|
||||
if lightcone.equip_avatar > 0:
|
||||
player.avatar_manager[lightcone.equip_avatar].lightcone_id = lightcone_data.unique_id
|
||||
lightcone_data.equip_avatar = lightcone.equip_avatar
|
||||
player.inventory_manager[lightcone_data.unique_id] = lightcone_data
|
||||
|
||||
for relic in data.relics:
|
||||
if relic.equip_avatar > 8000:continue
|
||||
relic_data = player.add_relic(
|
||||
relic_id=relic.relic_id,
|
||||
main_affix=relic.main_affix_id,
|
||||
sub_affix=[
|
||||
SubAffix(
|
||||
id=affix.sub_affix_id,
|
||||
count=affix.count,
|
||||
step=affix.step
|
||||
)
|
||||
for affix in relic.sub_affixes
|
||||
]
|
||||
)
|
||||
if relic_data:
|
||||
if relic.equip_avatar > 0:
|
||||
player.avatar_manager[relic.equip_avatar].relic_ids[str(relic_data.sub_type)] = relic_data.unique_id
|
||||
relic_data.equip_avatar = relic.equip_avatar
|
||||
player.inventory_manager[relic_data.unique_id] = relic_data
|
||||
|
||||
|
||||
@flask_app.route("/srtools", methods=["POST"])
|
||||
async def srtools_handler():
|
||||
body = request.get_json()
|
||||
try:
|
||||
req = SRToolDataReq(**body)
|
||||
except Exception:
|
||||
return jsonify({"message": "Invalid input", "status": 400}), 400
|
||||
|
||||
rsp = SRToolDataRsp(message="OK", status=200)
|
||||
|
||||
account = find_account_by_name(req.username)
|
||||
if not account:
|
||||
rsp.message = "Invalid account"
|
||||
rsp.status = 400
|
||||
return jsonify(rsp.model_dump()), 401
|
||||
|
||||
if req.data is None:
|
||||
return jsonify(rsp.model_dump()), 200
|
||||
|
||||
gateway = get_gateway_instance()
|
||||
conn : PlayerSession = gateway.get_active_connection(account.id)
|
||||
if not conn:
|
||||
rsp.message = "Player Not Online"
|
||||
rsp.status = 401
|
||||
return jsonify(rsp.model_dump()), 401
|
||||
|
||||
try:
|
||||
await ClearInventory(conn)
|
||||
await AddAllData(conn.player,req.data)
|
||||
conn.pending_notify(conn.player.SrToolItemsSync())
|
||||
conn.pending_notify(conn.player.SrToolAvatarSync())
|
||||
except:
|
||||
rsp.message = "Internal Server Error"
|
||||
rsp.status = 500
|
||||
print(traceback.print_exc())
|
||||
return jsonify(rsp.model_dump()), 500
|
||||
return jsonify(rsp.model_dump()), 200
|
||||
|
||||
def run_flask(host, port):
|
||||
Info(f"SRTools Server Starting on {host}:{port}")
|
||||
flask_app.run(host=host, port=port)
|
||||
@@ -15,12 +15,14 @@ class ConfigData:
|
||||
LogLevel: str
|
||||
GameServer: ServerConfig
|
||||
SDKServer: ServerConfig
|
||||
SRToolsServer: ServerConfig
|
||||
RegionName: str
|
||||
def write_default_config():
|
||||
config = ConfigData(
|
||||
LogLevel="INFO",
|
||||
GameServer=ServerConfig(IP="127.0.0.1", Port=23301),
|
||||
SDKServer=ServerConfig(IP="127.0.0.1", Port=21000),
|
||||
SRToolsServer=ServerConfig(IP="127.0.0.1", Port=25000),
|
||||
RegionName="NeonSR",
|
||||
)
|
||||
with open("Config.json", "w") as f:
|
||||
|
||||
Reference in New Issue
Block a user