├── saturnine ├── __init__.py └── __main__.py ├── requirements.txt ├── game_server ├── utils │ └── time.py ├── game │ ├── world.py │ ├── entity │ │ ├── entity.py │ │ └── avatar.py │ ├── gacha.py │ └── player.py ├── encryption │ ├── __init__.py │ └── mt64.py ├── handlers │ ├── ping.py │ ├── clock.py │ ├── entity.py │ ├── gacha.py │ ├── map_commands.py │ ├── auth.py │ ├── avatar.py │ └── scene.py ├── resource │ ├── __init__.py │ ├── binoutput.py │ ├── excel.py │ └── enums.py ├── protocol │ ├── reader.py │ ├── packet.py │ └── cmd_id.py └── __init__.py ├── .gitignore ├── lib ├── packet_head.py └── retcode.py ├── README.md └── sdk_server └── app.py /saturnine/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "0.1.0" 2 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | betterproto==2.0.0b5 2 | bottle==0.12.23 3 | Cython==0.29.32 4 | loguru==0.6.0 5 | -------------------------------------------------------------------------------- /game_server/utils/time.py: -------------------------------------------------------------------------------- 1 | import time 2 | 3 | def current_milli_time(): 4 | return round(time.time() * 1000) -------------------------------------------------------------------------------- /game_server/game/world.py: -------------------------------------------------------------------------------- 1 | from lib.proto import ProtEntityType,SceneEntityAppearNotify,SceneEntityDisappearNotify,VisionType 2 | 3 | class World: 4 | next_entity_id: int = 0 5 | 6 | def get_next_entity_id(self, type: ProtEntityType): 7 | self.next_entity_id += 1 8 | return (type << 24) + self.next_entity_id 9 | -------------------------------------------------------------------------------- /game_server/encryption/__init__.py: -------------------------------------------------------------------------------- 1 | from itertools import cycle 2 | 3 | from .mt64 import mt64 4 | 5 | def new_key(seed: int) -> bytes: 6 | mt = mt64() 7 | mt.seed(seed) 8 | return bytes(byte for _ in range(512) for byte in mt.int64().to_bytes(8, "big")) 9 | 10 | def xor(data: bytes, key: bytes) -> bytes: 11 | return bytes(v ^ k for (v, k) in zip(data, cycle(key))) -------------------------------------------------------------------------------- /game_server/handlers/ping.py: -------------------------------------------------------------------------------- 1 | from game_server.protocol.cmd_id import CmdID 2 | from game_server import HandlerRouter,Connection 3 | from lib.proto import PingReq,PingRsp 4 | import enet 5 | 6 | router = HandlerRouter() 7 | 8 | @router(CmdID.PingReq) 9 | def handle_ping(conn: Connection, msg: PingReq): 10 | rsp = PingRsp() 11 | rsp.client_time = msg.client_time 12 | rsp.seq = msg.seq 13 | conn.send(rsp) 14 | -------------------------------------------------------------------------------- /saturnine/__main__.py: -------------------------------------------------------------------------------- 1 | from sdk_server.app import run_http_server 2 | from game_server import GameServer; 3 | from game_server.handlers import auth, ping, scene, avatar, entity, map_commands, clock, gacha 4 | 5 | if __name__ == "__main__": 6 | gameserver = GameServer("0.0.0.0", 22102) 7 | gameserver.add(auth.router) 8 | gameserver.add(ping.router) 9 | gameserver.add(scene.router) 10 | gameserver.add(avatar.router) 11 | gameserver.add(entity.router) 12 | gameserver.add(map_commands.router) 13 | gameserver.add(clock.router) 14 | gameserver.add(gacha.router) 15 | 16 | gameserver.start() 17 | run_http_server('0.0.0.0') -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | 3 | # Packages 4 | *.egg 5 | !/tests/**/*.egg 6 | /*.egg-info 7 | /dist/* 8 | build 9 | _build 10 | .cache 11 | *.so 12 | 13 | # Installer logs 14 | pip-log.txt 15 | 16 | # Unit test / coverage reports 17 | .coverage 18 | .pytest_cache 19 | 20 | .DS_Store 21 | .idea/* 22 | .python-version 23 | .vscode/* 24 | 25 | /test.py 26 | /test_*.* 27 | 28 | /setup.cfg 29 | MANIFEST.in 30 | /setup.py 31 | /docs/site/* 32 | /tests/fixtures/simple_project/setup.py 33 | /tests/fixtures/project_with_extras/setup.py 34 | .mypy_cache 35 | 36 | .venv 37 | /releases/* 38 | pip-wheel-metadata 39 | /poetry.toml 40 | 41 | poetry/core/* 42 | 43 | /proto 44 | /server_data -------------------------------------------------------------------------------- /game_server/resource/__init__.py: -------------------------------------------------------------------------------- 1 | from game_server.resource.excel import ExcelOutput 2 | from game_server.resource.binoutput import BinOutput 3 | import dataclasses 4 | from os import path 5 | from loguru import logger 6 | 7 | class ResourceManager: 8 | def __init__(self, dir: str): 9 | if path.exists(dir): 10 | self.excels = ExcelOutput.load_all_excels(dir) 11 | self.binoutput = BinOutput.load_all_bins(dir) 12 | else: 13 | logger.opt(colors=True).debug(f'Resources directory does not exist, running with minimal resources') 14 | 15 | resources: ResourceManager = ResourceManager("server_data") -------------------------------------------------------------------------------- /game_server/handlers/clock.py: -------------------------------------------------------------------------------- 1 | from game_server.protocol.cmd_id import CmdID 2 | from game_server import HandlerRouter,Connection 3 | from lib.retcode import Retcode 4 | from lib.proto import ChangeGameTimeReq, SceneTimeNotify, ChangeGameTimeRsp 5 | import enet 6 | 7 | router = HandlerRouter() 8 | @router(CmdID.ChangeGameTimeReq) 9 | def handle_clock(conn: Connection, msg: ChangeGameTimeReq): 10 | scenetimenotify = SceneTimeNotify() 11 | scenetimenotify.scene_id = conn.player.scene_id 12 | scenetimenotify.scene_time = msg.game_time 13 | scenetimenotify.is_paused = False 14 | conn.send(scenetimenotify) 15 | 16 | changegametimersp = ChangeGameTimeRsp() 17 | changegametimersp.cur_game_time = msg.game_time 18 | changegametimersp.retcode = 0 19 | conn.send(changegametimersp) -------------------------------------------------------------------------------- /game_server/protocol/reader.py: -------------------------------------------------------------------------------- 1 | import io 2 | import struct 3 | 4 | 5 | class BinaryReader(io.BytesIO): 6 | def read_i16b(self) -> int: 7 | return struct.unpack(">h", self.read(2))[0] 8 | 9 | def write_i16b(self, value: int) -> None: 10 | self.write(struct.pack(">h", value)) 11 | 12 | def read_u16b(self) -> int: 13 | return struct.unpack(">H", self.read(2))[0] 14 | 15 | def write_u16b(self, value: int) -> None: 16 | self.write(struct.pack(">H", value)) 17 | 18 | def read_i32b(self) -> int: 19 | return struct.unpack(">i", self.read(4))[0] 20 | 21 | def write_i32b(self, value: int) -> None: 22 | self.write(struct.pack(">i", value)) 23 | 24 | def read_u32b(self) -> int: 25 | return struct.unpack(">I", self.read(4))[0] 26 | 27 | def write_u32b(self, value: int) -> None: 28 | self.write(struct.pack(">I", value)) -------------------------------------------------------------------------------- /game_server/handlers/entity.py: -------------------------------------------------------------------------------- 1 | from game_server.protocol.cmd_id import CmdID 2 | from game_server import HandlerRouter,Connection 3 | from lib.proto import SceneEntitiesMovesReq,EntityMoveInfo 4 | from lib.retcode import Retcode 5 | from game_server.protocol.reader import BinaryReader 6 | from game_server.resource.enums import PropType 7 | import enet 8 | 9 | router = HandlerRouter() 10 | 11 | @router(CmdID.SceneEntitiesMovesReq) 12 | def handle_entity_move_req(conn: Connection, msg: SceneEntitiesMovesReq): 13 | for entity_move_info in msg.entity_move_info_list: 14 | if int(str(entity_move_info.entity_id)[:5]) == 16777: #toolazy kek 15 | avatar = conn.player.get_avatar_by_entity_id(entity_move_info.entity_id) 16 | avatar.motion = entity_move_info.motion_info.pos 17 | avatar.rotation = entity_move_info.motion_info.rot 18 | avatar.speed = entity_move_info.motion_info.speed 19 | avatar.motion_state = entity_move_info.motion_info.state -------------------------------------------------------------------------------- /game_server/handlers/gacha.py: -------------------------------------------------------------------------------- 1 | from game_server.protocol.cmd_id import CmdID 2 | from game_server import HandlerRouter,Connection 3 | from lib.proto import GetGachaInfoReq,GetGachaInfoRsp,GachaInfo,DoGachaReq,DoGachaRsp,GachaItem,GachaTransferItem,ItemParam 4 | from lib.retcode import Retcode 5 | from game_server.protocol.reader import BinaryReader 6 | import enet 7 | 8 | 9 | router = HandlerRouter() 10 | @router(CmdID.GetGachaInfoReq) 11 | def handle_gacha_info_req(conn: Connection, msg: GetGachaInfoReq): 12 | #global history 13 | rsp = GetGachaInfoRsp() 14 | #try: 15 | rsp.gacha_info_list.extend(conn.gacha.get_banners()) 16 | rsp.gacha_random = 0 17 | rsp.retcode = 0 18 | #except: 19 | # rsp.retcode = 1 20 | conn.send(rsp) 21 | 22 | @router(CmdID.DoGachaReq) 23 | def handle_do_gacha_req(conn: Connection, msg: DoGachaReq): 24 | rsp = DoGachaRsp() 25 | rsp.gacha_item_list.extend(conn.gacha.do_pull(msg.gacha_schedule_id, msg.gacha_times)) 26 | rsp.retcode = 0 27 | conn.send(rsp) 28 | -------------------------------------------------------------------------------- /game_server/game/entity/entity.py: -------------------------------------------------------------------------------- 1 | from lib.proto import MotionState, Vector, SceneEntityInfo, MotionInfo, SceneEntityAiInfo 2 | from game_server.game.world import World 3 | import dataclasses 4 | 5 | class Entity(object): 6 | world: World 7 | motion: Vector 8 | rotation: Vector 9 | speed: Vector 10 | 11 | entity_id: int 12 | guid: int 13 | motion_state: MotionState = MotionState.MOTION_STANDBY 14 | 15 | def __init__(self, world: World, motion: Vector = Vector(0, 400, 0), rotation: Vector = Vector(0, 0, 0), speed: Vector = Vector(0, 0, 0)) -> None: 16 | self.world = world 17 | self.motion = motion 18 | self.rotation = rotation 19 | self.speed = speed 20 | pass 21 | 22 | def get_motion_info(self): 23 | return MotionInfo(pos=self.motion, rot=self.rotation, speed=self.speed, state=self.motion_state) 24 | 25 | def get_scene_entity_info(self): 26 | scene_entity_info = SceneEntityInfo() 27 | scene_entity_info.entity_id = self.entity_id 28 | scene_entity_info.life_state = 1 29 | scene_entity_info.motion_info = self.get_motion_info() 30 | scene_entity_info.ai_info = SceneEntityAiInfo(is_ai_open=True) 31 | return scene_entity_info -------------------------------------------------------------------------------- /lib/packet_head.py: -------------------------------------------------------------------------------- 1 | # Generated by the protocol buffer compiler. DO NOT EDIT! 2 | # sources: pb/packet_head.proto 3 | # plugin: python-betterproto 4 | from dataclasses import dataclass 5 | from typing import Dict 6 | 7 | import betterproto 8 | 9 | 10 | class DebugNotifyRetcode(betterproto.Enum): 11 | SUCC = 0 12 | FAIL = 1 13 | 14 | 15 | class DebugNotifyEnum(betterproto.Enum): 16 | ZERO = 0 17 | CMD_ID = 101 18 | TARGET_SERVICE = 101 19 | ENET_CHANNEL_ID = 2 20 | ENET_IS_RELIABLE = 1 21 | 22 | 23 | @dataclass 24 | class PacketHead(betterproto.Message): 25 | packet_id: int = betterproto.uint32_field(1) 26 | rpc_id: int = betterproto.uint32_field(2) 27 | client_sequence_id: int = betterproto.uint32_field(3) 28 | enet_channel_id: int = betterproto.uint32_field(4) 29 | enet_is_reliable: int = betterproto.uint32_field(5) 30 | user_id: int = betterproto.uint32_field(11) 31 | user_ip: int = betterproto.uint32_field(12) 32 | user_session_id: int = betterproto.uint32_field(13) 33 | recv_time_ms: int = betterproto.uint64_field(21) 34 | rpc_begin_time_ms: int = betterproto.uint32_field(22) 35 | ext_map: Dict[int, int] = betterproto.map_field( 36 | 23, betterproto.TYPE_UINT32, betterproto.TYPE_UINT32 37 | ) 38 | sender_app_id: int = betterproto.uint32_field(24) 39 | source_service: int = betterproto.uint32_field(31) 40 | target_service: int = betterproto.uint32_field(32) 41 | service_app_id_map: Dict[int, int] = betterproto.map_field( 42 | 33, betterproto.TYPE_UINT32, betterproto.TYPE_UINT32 43 | ) 44 | is_set_game_thread: bool = betterproto.bool_field(34) 45 | game_thread_index: int = betterproto.uint32_field(35) 46 | 47 | 48 | @dataclass 49 | class DebugNotify(betterproto.Message): 50 | id: int = betterproto.uint32_field(1) 51 | name: str = betterproto.string_field(2) 52 | retcode: "DebugNotifyRetcode" = betterproto.enum_field(3) 53 | -------------------------------------------------------------------------------- /game_server/protocol/packet.py: -------------------------------------------------------------------------------- 1 | 2 | from __future__ import annotations 3 | from lib.packet_head import PacketHead 4 | from game_server.protocol.reader import BinaryReader 5 | from game_server.protocol.cmd_id import CmdID 6 | from lib import proto 7 | import betterproto 8 | 9 | PACKET_MAGIC = (0x4567, 0x89ab) 10 | 11 | class Packet: 12 | def __init__(self, head: PacketHead = None, body: betterproto.Message = None): 13 | self.head = head 14 | if not head: 15 | self.head = PacketHead() 16 | 17 | self.body = body 18 | if not body == None: 19 | self.cmdid = CmdID[body.__class__.__name__] 20 | self.head.packet_id = self.cmdid 21 | 22 | def parse(self, data: bytes) -> Packet: 23 | buf = BinaryReader(data) 24 | 25 | magic1 = buf.read_u16b() 26 | if magic1 != PACKET_MAGIC[0]: 27 | raise Exception 28 | 29 | self.cmdid = CmdID(buf.read_u16b()) 30 | metadata_len = buf.read_u16b() 31 | data_len = buf.read_u32b() 32 | 33 | self.head = PacketHead().parse(buf.read(metadata_len)) 34 | 35 | proto_class = getattr(proto, self.cmdid.name, None) 36 | 37 | if data_len: 38 | self.body = proto_class().parse(buf.read(data_len)) 39 | else: 40 | self.body = proto_class() 41 | 42 | magic2 = buf.read_u16b() 43 | if magic2 != PACKET_MAGIC[1]: 44 | raise Exception 45 | 46 | return self 47 | 48 | def __bytes__(self) -> bytes: 49 | if self.body == None: 50 | raise Exception 51 | 52 | head_bytes = bytes(self.head) 53 | body_bytes = bytes(self.body) 54 | 55 | buf = BinaryReader() 56 | 57 | buf.write_u16b(PACKET_MAGIC[0]) 58 | buf.write_u16b(self.cmdid) 59 | 60 | buf.write_u16b(len(head_bytes)) 61 | buf.write_u32b(len(body_bytes)) 62 | 63 | buf.write(head_bytes) 64 | 65 | if len(body_bytes) > 0: 66 | buf.write(body_bytes) 67 | 68 | buf.write_u16b(PACKET_MAGIC[1]) 69 | 70 | return buf.getvalue() -------------------------------------------------------------------------------- /game_server/handlers/map_commands.py: -------------------------------------------------------------------------------- 1 | from game_server.protocol.cmd_id import CmdID 2 | from game_server.utils.time import current_milli_time 3 | from game_server import HandlerRouter,Connection 4 | from lib.proto import MarkMapReq, PlayerEnterSceneNotify ,CutSceneBeginNotify ,Vector ,EnterType 5 | import enet 6 | 7 | router = HandlerRouter() 8 | @router(CmdID.MarkMapReq) 9 | def handle_map_tp(conn: Connection, msg: MarkMapReq): 10 | if msg.mark: 11 | if msg.mark.point_type == 5: 12 | pos_y = 500 13 | if msg.mark.name: 14 | try: 15 | pos_y = int(msg.mark.name) 16 | except: 17 | pass 18 | 19 | scene_id = msg.mark.scene_id 20 | pos = Vector(msg.mark.pos.x, pos_y, msg.mark.pos.z) 21 | 22 | conn.send(conn.player.get_teleport_packet(scene_id, pos, EnterType.ENTER_GOTO)) 23 | 24 | conn.player.pos = pos 25 | conn.player.scene_id = scene_id 26 | conn.player.get_cur_avatar().motion = pos 27 | elif msg.mark.point_type == 4: 28 | if msg.mark.name: 29 | try: 30 | scene_id = int(msg.mark.name) 31 | except: 32 | scene_id = msg.mark.scene_id 33 | 34 | pos = Vector(0, 500, 0) 35 | 36 | conn.send(conn.player.get_teleport_packet(scene_id, pos, EnterType.ENTER_JUMP)) 37 | 38 | conn.player.pos = pos 39 | conn.player.scene_id = scene_id 40 | conn.player.get_cur_avatar().motion = pos 41 | elif msg.mark: 42 | if msg.mark.point_type == 3: 43 | global cg_id 44 | cg_id = 1 45 | if msg.mark.name: 46 | try: 47 | cg_id = int(msg.mark.name) 48 | except: 49 | pass 50 | 51 | playcg = CutSceneBeginNotify() 52 | playcg.cutscene_id = cg_id 53 | playcg.is_wait_others = 0 54 | conn.send(playcg) 55 | else: 56 | pass 57 | 58 | 59 | -------------------------------------------------------------------------------- /game_server/game/entity/avatar.py: -------------------------------------------------------------------------------- 1 | from lib.proto import AvatarInfo, Vector, ProtEntityType, SceneWeaponInfo, SceneAvatarInfo, SceneTeamAvatar 2 | from game_server.game.world import World 3 | from game_server.game.entity.entity import Entity 4 | 5 | class AvatarEntity(Entity): 6 | 7 | avatar_info: AvatarInfo 8 | scene_weapon_info: SceneWeaponInfo 9 | 10 | def __init__(self, world: World, avatar_info: AvatarInfo, motion: Vector, rotation: Vector = Vector(0, 0, 0), speed: Vector = Vector(0, 0, 0)) -> None: 11 | self.avatar_info = avatar_info 12 | self.entity_id = world.get_next_entity_id(ProtEntityType.PROT_ENTITY_AVATAR) 13 | self.guid = avatar_info.guid 14 | self.scene_weapon_info = SceneWeaponInfo() 15 | self.scene_weapon_info.entity_id = world.get_next_entity_id(ProtEntityType.PROT_ENTITY_GADGET) 16 | self.scene_weapon_info.item_id = 11406 17 | self.scene_weapon_info.level = 90 18 | self.scene_weapon_info.promote_level = 6 19 | self.scene_weapon_info.gadget_id = 50011406 20 | super().__init__(world, motion, rotation, speed) 21 | 22 | def get_scene_entity_info(self, uid: int): 23 | scene_entity_info = super().get_scene_entity_info() 24 | scene_entity_info.entity_type = ProtEntityType.PROT_ENTITY_AVATAR 25 | scene_entity_info.prop_map = self.avatar_info.prop_map 26 | scene_entity_info.fight_prop_map = self.avatar_info.fight_prop_map 27 | scene_entity_info.avatar = self.get_scene_avatar_info(uid) 28 | return scene_entity_info 29 | 30 | def get_scene_avatar_info(self, uid: int): 31 | scene_avatar_info = SceneAvatarInfo() 32 | scene_avatar_info.guid = self.guid 33 | scene_avatar_info.uid = uid 34 | scene_avatar_info.inherent_proud_skill_list = self.avatar_info.inherent_proud_skill_list 35 | scene_avatar_info.proud_skill_extra_level_map = self.avatar_info.proud_skill_extra_level_map 36 | scene_avatar_info.skill_level_map = self.avatar_info.skill_level_map 37 | scene_avatar_info.talent_id_list = self.avatar_info.talent_id_list 38 | scene_avatar_info.core_proud_skill_level = self.avatar_info.core_proud_skill_level 39 | scene_avatar_info.skill_depot_id = self.avatar_info.skill_depot_id 40 | scene_avatar_info.avatar_id = self.avatar_info.avatar_id 41 | scene_avatar_info.peer_id = 1 42 | scene_avatar_info.equip_id_list = [11406] 43 | scene_avatar_info.weapon = self.scene_weapon_info 44 | return scene_avatar_info 45 | -------------------------------------------------------------------------------- /game_server/resource/binoutput.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | import sys, os, json 3 | from lib.proto import Vector 4 | from game_server.resource.enums import * 5 | 6 | @dataclasses.dataclass() 7 | class ScenePoint: 8 | id: int 9 | # type: str 10 | # $type: str 11 | # gadgetId: int 12 | # pos: dir 13 | # rot: dir 14 | # areaId: int 15 | # unlocked: bool 16 | # cutsceneList: list[int] 17 | # groupLimit: bool 18 | # npcId: int 19 | # maxSpringVolume: float 20 | # entryPointId: int 21 | # size: list[int] 22 | dungeonIds: list[int] 23 | # titleTextID: str 24 | # hideOnMap: bool 25 | # showLevel: int 26 | # dungeonRandomList: int 27 | # dungeonEntryType: str 28 | # mapVisibility: str 29 | # forbidSimpleUnlock: bool 30 | tranSceneId: int 31 | tranPos: Vector 32 | tranRot: Vector 33 | 34 | @dataclasses.dataclass() 35 | class ConfigScene: 36 | id: int 37 | points: dict[int, ScenePoint] 38 | 39 | @dataclasses.dataclass() 40 | class BinOutput: 41 | config_scene: dict[int, ConfigScene] = dataclasses.field(default_factory=dict) 42 | 43 | @classmethod 44 | def load_all_bins(cls, path: str): 45 | cls_inst = cls() 46 | 47 | with os.scandir(os.path.join(path, "binoutput", "Scene", "Point")) as files: 48 | bins = [file.name for file in files] 49 | 50 | for bin_json in bins: 51 | with open(os.path.join(path, "binoutput", "Scene", "Point", bin_json), 'r', encoding='utf-8') as f: 52 | try: 53 | scene_id = int(bin_json.replace('_point.json','').replace('scene','')) 54 | reader = json.load(f) 55 | if 'points' in reader: 56 | if len(reader['points']) != 0: 57 | scene_point = dict() 58 | for point_id in reader['points']: 59 | row = reader['points'][point_id] 60 | point = ScenePoint( 61 | int(point_id), 62 | row["dungeonIds"] if 'dungeonIds' in row else None, 63 | int(row['tranSceneId']) if 'tranSceneId' in row else scene_id, 64 | Vector(row['tranPos']['x'],row['tranPos']['y'],row['tranPos']['z'])if 'tranPos' in row else Vector(0,0,0), 65 | Vector(row['tranRot']['x'],row['tranRot']['y'],row['tranRot']['z'])if 'tranRot' in row else Vector(0,0,0) 66 | ) 67 | scene_point[int(point_id)] = point 68 | 69 | cls_inst.config_scene[scene_id] = ConfigScene(scene_id, scene_point) 70 | except: 71 | print('Error_read: '+bin_json) 72 | 73 | return cls_inst -------------------------------------------------------------------------------- /game_server/encryption/mt64.py: -------------------------------------------------------------------------------- 1 | class mt64: 2 | def __init__(self): 3 | self.mt = [0]*312 4 | self.mti = 313 5 | 6 | def seed(self, seed): 7 | self.mt[0] = seed & 0xffffffffffffffff 8 | for i in range(1,312): 9 | self.mt[i] = (6364136223846793005 * (self.mt[i-1] ^ (self.mt[i-1] >> 62)) + i) & 0xffffffffffffffff 10 | self.mti = 312 11 | 12 | def init_by_array(self, key): 13 | self.seed(19650218) 14 | i = 1 15 | j = 0 16 | k = max(312, len(key)) 17 | for ki in range(k): 18 | self.mt[i] = ((self.mt[i] ^ ((self.mt[i-1] ^ (self.mt[i-1] >> 62)) * 3935559000370003845)) + key[j] + j) & 0xffffffffffffffff 19 | i += 1 20 | j += 1 21 | if i >= 312: 22 | self.mt[0] = self.mt[311] 23 | i = 1 24 | if j >= len(key): 25 | j = 0 26 | for ki in range(312): 27 | self.mt[i] = ((self.mt[i] ^ ((self.mt[i-1] ^ (self.mt[i-1] >> 62)) * 2862933555777941757)) - i) & 0xffffffffffffffff 28 | i += 1 29 | if i >= 312: 30 | self.mt[0] = self.mt[311] 31 | i = 1 32 | self.mt[0] = 1 << 63 33 | 34 | def int64(self): 35 | if self.mti >= 312: 36 | if self.mti == 313: 37 | self.seed(5489) 38 | 39 | for k in range(311): 40 | y = (self.mt[k] & 0xFFFFFFFF80000000) | (self.mt[k+1] & 0x7fffffff) 41 | if k < 312 - 156: 42 | self.mt[k] = self.mt[k+156] ^ (y >> 1) ^ (0xB5026F5AA96619E9 if y & 1 else 0) 43 | else: 44 | self.mt[k] = self.mt[k+156-624] ^ (y >> 1) ^ (0xB5026F5AA96619E9 if y & 1 else 0) 45 | 46 | y = (self.mt[311] & 0xFFFFFFFF80000000) | (self.mt[0] & 0x7fffffff) 47 | self.mt[311] = self.mt[155] ^ (y >> 1) ^ (0xB5026F5AA96619E9 if y & 1 else 0) 48 | self.mti = 0 49 | 50 | y = self.mt[self.mti] 51 | self.mti += 1 52 | 53 | y ^= (y >> 29) & 0x5555555555555555 54 | y ^= (y << 17) & 0x71D67FFFEDA60000 55 | y ^= (y << 37) & 0xFFF7EEE000000000 56 | y ^= (y >> 43) 57 | 58 | return y 59 | 60 | def int64b(self): 61 | if self.mti == 313: 62 | self.seed(5489) 63 | 64 | k = self.mti 65 | 66 | if k == 312: 67 | k = 0 68 | self.mti = 0 69 | 70 | if k == 311: 71 | y = (self.mt[311] & 0xFFFFFFFF80000000) | (self.mt[0] & 0x7fffffff) 72 | self.mt[311] = self.mt[155] ^ (y >> 1) ^ (0xB5026F5AA96619E9 if y & 1 else 0) 73 | else: 74 | y = (self.mt[k] & 0xFFFFFFFF80000000) | (self.mt[k+1] & 0x7fffffff) 75 | if k < 312 - 156: 76 | self.mt[k] = self.mt[k+156] ^ (y >> 1) ^ (0xB5026F5AA96619E9 if y & 1 else 0) 77 | else: 78 | self.mt[k] = self.mt[k+156-624] ^ (y >> 1) ^ (0xB5026F5AA96619E9 if y & 1 else 0) 79 | 80 | y = self.mt[self.mti] 81 | self.mti += 1 82 | 83 | y ^= (y >> 29) & 0x5555555555555555 84 | y ^= (y << 17) & 0x71D67FFFEDA60000 85 | y ^= (y << 37) & 0xFFF7EEE000000000 86 | y ^= (y >> 43) 87 | 88 | return y -------------------------------------------------------------------------------- /game_server/handlers/auth.py: -------------------------------------------------------------------------------- 1 | from game_server.protocol.packet import Packet 2 | from game_server.protocol.cmd_id import CmdID 3 | from game_server.encryption import new_key 4 | from game_server import HandlerRouter,Connection 5 | from lib.proto import GetPlayerTokenReq,GetPlayerTokenRsp,PlayerLoginReq,PlayerLoginRsp,OpenStateUpdateNotify,StoreWeightLimitNotify,StoreType,PlayerStoreNotify,Vector,PlayerDataNotify,PropValue, AvatarDataNotify 6 | from game_server.game.player import Player 7 | from game_server.game.gacha import Gacha 8 | from game_server.utils.time import current_milli_time 9 | from game_server.resource.enums import PropType 10 | import enet 11 | 12 | router = HandlerRouter() 13 | 14 | @router(CmdID.GetPlayerTokenReq) 15 | def handle_token_req(conn: Connection, msg: GetPlayerTokenReq): 16 | mt_key = new_key(int(msg.account_uid)) 17 | rsp = GetPlayerTokenRsp() 18 | rsp.uid = int(msg.account_uid) 19 | rsp.account_type = msg.account_type 20 | rsp.account_uid = msg.account_uid 21 | rsp.token = msg.account_token 22 | rsp.gm_uid = int(msg.account_uid) 23 | rsp.secret_key_seed = int(msg.account_uid) 24 | conn.send(rsp) 25 | conn.key = mt_key 26 | conn.player = Player(uid=rsp.uid, name="saturnine") 27 | conn.player.scene_id = 3 28 | conn.player.pos = Vector(100, 400, 100) 29 | conn.player.init_default() 30 | conn.gacha = Gacha() 31 | 32 | @router(CmdID.PlayerLoginReq) 33 | def handle_login(conn: Connection, msg: PlayerLoginReq): 34 | open_state = OpenStateUpdateNotify() 35 | open_state.open_state_map = {} 36 | for x in range(600): 37 | open_state.open_state_map[x] = 1 38 | 39 | store_weight_limit = StoreWeightLimitNotify() 40 | store_weight_limit.store_type = StoreType.STORE_PACK 41 | store_weight_limit.weight_limit = 10000 42 | 43 | player_store_notify = PlayerStoreNotify() 44 | player_store_notify.weight_limit = 10000 45 | player_store_notify.store_type = StoreType.STORE_PACK 46 | player_store_notify.item_list = [] 47 | 48 | player_data_notify = PlayerDataNotify() 49 | player_data_notify.nick_name = conn.player.name 50 | player_data_notify.server_time = current_milli_time() 51 | player_data_notify.is_first_login_today = False 52 | player_data_notify.region_id = 1 53 | player_data_notify.prop_map = {} 54 | 55 | avatar_data_notify = AvatarDataNotify() 56 | avatar_data_notify.avatar_list = [] 57 | for avatar_entity in conn.player.avatars: 58 | avatar_data_notify.avatar_list.append(avatar_entity.avatar_info) 59 | 60 | avatar_data_notify.avatar_team_map = conn.player.teams 61 | avatar_data_notify.cur_avatar_team_id = conn.player.cur_avatar_team_id 62 | avatar_data_notify.choose_avatar_guid = conn.player.cur_avatar_guid 63 | for prop, value in conn.player.prop_map.items(): 64 | #dont ask me please 65 | player_data_notify.prop_map[prop._value_] = PropValue(type=prop._value_, val=value, ival=value) 66 | 67 | rsp = PlayerLoginRsp() 68 | rsp.game_biz = "hk4e" 69 | rsp.is_use_ability_hash = False 70 | rsp.is_new_player = True 71 | rsp.target_uid = conn.player.uid 72 | 73 | conn.send(open_state) 74 | conn.send(store_weight_limit) 75 | conn.send(player_store_notify) 76 | conn.send(player_data_notify) 77 | conn.send(avatar_data_notify) 78 | conn.send(conn.player.get_teleport_packet(conn.player.scene_id, conn.player.pos)) 79 | conn.send(rsp) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## DEPRECATED 2 | Please move to [this](https://github.com/NickTheHuy/CockPY) for future updates. 3 | 4 | 5 | # saturnine 6 | 7 | 8 | a minimal cbt2 ps 9 | 10 | # Installation 11 | 0. (Optional but recommended) Create a virtual environment 12 | ``` 13 | python -m venv venv 14 | venv\Scripts\activate.bat or venv\Scripts\activate.ps1 for PowerShell (Windows) 15 | source venv/bin/activate (Linux) 16 | ``` 17 | 1. Install dependencies 18 | ```pip install -r ./requirements.txt``` 19 | 20 | 2. Install enet 21 | 22 | Clone the repo recursively and move to that directory 23 | 24 | ```git clone https://github.com/lilmayofuksu/pyenet --recursive && cd pyenet``` 25 | 26 | Compile the ENet Cython module 27 | 28 | ```python setup.py build``` 29 | 30 | Install the new pyenet module 31 | 32 | ```python setup.py install``` 33 | 34 | # Running 35 | ```py -m saturnine``` would do the trick 36 | 37 | # Troubleshooting 38 | 1. Client doesn't run? Replace mhyprot with this [mhyprot2](https://cdn.discordapp.com/attachments/991093426055442522/1044336940905922580/mhyprot2.Sys) 39 | 2. If after logging in and clicking Tap to begin an next error occurs: `FormatException: Unknown char: . `, just select English as the system language and try again. 40 | 41 | # Redirecting 42 | Use fiddler with this script 43 | ``` 44 | import System; 45 | import System.Windows.Forms; 46 | import Fiddler; 47 | import System.Text.RegularExpressions; 48 | var list = [ 49 | "https://api-os-takumi.mihoyo.com/", 50 | "https://hk4e-api-os-static.mihoyo.com/", 51 | "https://hk4e-sdk-os.mihoyo.com/", 52 | "https://dispatchosglobal.yuanshen.com/", 53 | "https://osusadispatch.yuanshen.com/", 54 | "https://account.mihoyo.com/", 55 | "https://log-upload-os.mihoyo.com/", 56 | "https://dispatchcntest.yuanshen.com/", 57 | "https://devlog-upload.mihoyo.com/", 58 | "https://webstatic.mihoyo.com/", 59 | "https://log-upload.mihoyo.com/", 60 | "https://hk4e-sdk.mihoyo.com/", 61 | "https://api-beta-sdk.mihoyo.com/", 62 | "https://api-beta-sdk-os.mihoyo.com/", 63 | "https://cnbeta01dispatch.yuanshen.com/", 64 | "https://dispatchcnglobal.yuanshen.com/", 65 | "https://cnbeta02dispatch.yuanshen.com/", 66 | "https://sdk-os-static.mihoyo.com/", 67 | "https://webstatic-sea.mihoyo.com/", 68 | "https://webstatic-sea.hoyoverse.com/", 69 | "https://hk4e-sdk-os-static.hoyoverse.com/", 70 | "https://sdk-os-static.hoyoverse.com/", 71 | "http://dispatch.osglobal.yuanshen.com", 72 | "https://sandbox-sdk.mihoyo.com/", 73 | "https://dispatch.osglobal.yuanshen.com/", 74 | "https://hk4e-sdk-os.hoyoverse.com/", 75 | "https://api-sdk.mihoyo.com"// Line 24 76 | ]; 77 | class Handlers{ 78 | static function OnBeforeRequest(oS: Session) { 79 | var active = true; 80 | if(active) { 81 | if(oS.uriContains("http://overseauspider.yuanshen.com:8888/log")){ 82 | oS.oRequest.FailSession(404, "Blocked", "yourmom"); 83 | } 84 | for(var i = 0; i < 24 ;i++) { 85 | if(oS.uriContains(list[i])) { 86 | oS.host = "localhost"; 87 | oS.oRequest.headers.UriScheme = "http"; 88 | oS.port = 80; // This can also be replaced with another IP address. 89 | } 90 | } 91 | } 92 | } 93 | }; 94 | ``` 95 | 96 | -------------------------------------------------------------------------------- /game_server/__init__.py: -------------------------------------------------------------------------------- 1 | from __future__ import annotations 2 | import threading 3 | import enet 4 | from game_server.game.player import Player 5 | from game_server.protocol.packet import Packet 6 | from game_server.protocol.cmd_id import CmdID 7 | from game_server.encryption import xor, mt64 8 | from game_server.resource import ResourceManager 9 | from typing import Callable 10 | import betterproto 11 | from loguru import logger 12 | import traceback 13 | 14 | class Connection: 15 | game_server: GameServer 16 | peer: enet.Peer 17 | key: bytes 18 | player: Player 19 | 20 | def __init__(self, game_server: GameServer, peer: enet.Peer) -> None: 21 | self.peer = peer 22 | self.game_server = game_server 23 | 24 | def handle(self, data: bytes): 25 | if hasattr(self, 'key'): 26 | data = xor(data, self.key) 27 | packet = Packet() 28 | packet.parse(data) 29 | 30 | logger.opt(colors=True).debug(f'{self.peer.address} Receive: {packet.body}') 31 | if handler := self.game_server.router.get(packet.cmdid): 32 | handler(self, packet.body) 33 | 34 | def send(self, msg: betterproto.Message): 35 | packet = bytes(Packet(body=msg)) 36 | logger.opt(colors=True).debug(f'{self.peer.address} Send: {msg}') 37 | self.send_raw(bytes(packet)) 38 | 39 | def send_raw(self, data: bytes): 40 | if hasattr(self, 'key'): 41 | data = xor(data, self.key) 42 | self.peer.send(0, enet.Packet(data)) 43 | 44 | 45 | Handler = Callable[[Connection, Packet], None] 46 | 47 | class HandlerRouter: 48 | _handlers: dict[CmdID, HandlerRouter] 49 | 50 | def __init__(self): 51 | self._handlers = {} 52 | 53 | def add(self, router: HandlerRouter): 54 | self._handlers |= router._handlers 55 | 56 | def get(self, cmdid: CmdID) -> Handler | None: 57 | return self._handlers.get(cmdid) 58 | 59 | def __call__(self, cmdid: CmdID): 60 | def wrapper(handler: HandlerRouter): 61 | self._handlers[cmdid] = handler 62 | return handler 63 | return wrapper 64 | 65 | class GameServer: 66 | router: HandlerRouter = HandlerRouter() 67 | conns: dict[str, Connection] = {} 68 | 69 | def __init__(self, host, port) -> None: 70 | self.host = enet.Host(enet.Address(host, port), 10, 0, 0, 0) 71 | self.host.checksum = enet.ENET_CRC32 72 | self.host.compress_with_range_coder() 73 | 74 | def add(self, router: HandlerRouter): 75 | self.router.add(router) 76 | 77 | def loop(self) -> None: 78 | while True: 79 | try: 80 | event = self.host.service(20) 81 | if event is not None: 82 | if event.type == enet.EVENT_TYPE_CONNECT: 83 | self.conns[str(event.peer.address)] = Connection(self, event.peer) 84 | elif event.type == enet.EVENT_TYPE_DISCONNECT: 85 | del self.conns[str(event.peer.address)] 86 | elif event.type == enet.EVENT_TYPE_RECEIVE: 87 | msg = event.packet.data 88 | conn = self.conns[str(event.peer.address)] 89 | conn.handle(msg) 90 | except: 91 | traceback.print_exc() 92 | 93 | def start(self): 94 | b = threading.Thread(name='GameServer', target=self.loop) 95 | b.daemon=True 96 | b.start() -------------------------------------------------------------------------------- /game_server/handlers/avatar.py: -------------------------------------------------------------------------------- 1 | from game_server.protocol.cmd_id import CmdID 2 | from game_server import HandlerRouter,Connection 3 | from lib.proto import SetUpAvatarTeamReq,SetUpAvatarTeamRsp,SceneTeamUpdateNotify,AvatarTeamUpdateNotify,SceneTeamAvatar,SceneEntityAppearNotify,SceneEntityDisappearNotify,VisionType,ChangeAvatarReq,ChangeAvatarRsp 4 | from lib.retcode import Retcode 5 | from game_server.protocol.reader import BinaryReader 6 | from game_server.resource.enums import PropType 7 | import enet 8 | 9 | router = HandlerRouter() 10 | 11 | @router(CmdID.ChangeAvatarReq) 12 | def handle_change_avatar(conn: Connection, msg: ChangeAvatarReq): 13 | old_avatar = conn.player.get_cur_avatar() 14 | scene_entity_disappear_notify = SceneEntityDisappearNotify() 15 | scene_entity_disappear_notify.disappear_type = VisionType.VISION_REPLACE 16 | scene_entity_disappear_notify.entity_list = [old_avatar.entity_id] 17 | conn.send(scene_entity_disappear_notify) 18 | 19 | conn.player.cur_avatar_guid = msg.guid 20 | 21 | new_avatar = conn.player.get_cur_avatar() 22 | new_avatar.motion = old_avatar.motion 23 | new_avatar.rotation = old_avatar.rotation 24 | new_avatar.speed = old_avatar.speed 25 | new_avatar.motion_state = old_avatar.motion_state 26 | 27 | scene_entity_appear_notify = SceneEntityAppearNotify() 28 | scene_entity_appear_notify.appear_type = VisionType.VISION_REPLACE 29 | scene_entity_appear_notify.entity_list = [new_avatar.get_scene_entity_info(conn.player.uid)] 30 | conn.send(scene_entity_appear_notify) 31 | 32 | rsp = ChangeAvatarRsp() 33 | rsp.cur_guid = msg.guid 34 | rsp.skill_id = msg.skill_id 35 | conn.send(rsp) 36 | 37 | 38 | @router(CmdID.SetUpAvatarTeamReq) 39 | def handle_set_up_avatr_team(conn: Connection, msg: SetUpAvatarTeamReq): 40 | conn.player.teams[msg.team_id].avatar_guid_list = msg.avatar_team_guid_list 41 | 42 | avatar_team_update_notify = AvatarTeamUpdateNotify() 43 | avatar_team_update_notify.avatar_team_map = conn.player.teams 44 | conn.send(avatar_team_update_notify) 45 | 46 | if conn.player.cur_avatar_team_id == msg.team_id: 47 | scene_team_update_notify = SceneTeamUpdateNotify(scene_team_avatar_list=[], display_cur_avatar_list=[], is_in_mp=False) 48 | for avatar_guid in msg.avatar_team_guid_list: 49 | avatar = conn.player.get_avatar_by_guid(avatar_guid) 50 | avatar.scene_weapon_info.guid = conn.player.get_next_guid() 51 | scene_team_avatar = SceneTeamAvatar() 52 | scene_team_avatar.scene_id = conn.player.scene_id 53 | scene_team_avatar.player_uid = conn.player.uid 54 | scene_team_avatar.avatar_guid = avatar.guid 55 | scene_team_avatar.entity_id = avatar.entity_id 56 | scene_team_avatar.avatar_info = avatar.avatar_info 57 | scene_team_update_notify.scene_team_avatar_list.append(scene_team_avatar) 58 | conn.send(scene_team_update_notify) 59 | 60 | if conn.player.cur_avatar_guid != msg.cur_avatar_guid: 61 | old_avatar = conn.player.get_cur_avatar() 62 | scene_entity_disappear_notify = SceneEntityDisappearNotify() 63 | scene_entity_disappear_notify.disappear_type = VisionType.VISION_REPLACE 64 | scene_entity_disappear_notify.entity_list = [old_avatar.entity_id] 65 | conn.send(scene_entity_disappear_notify) 66 | 67 | conn.player.cur_avatar_guid = msg.cur_avatar_guid 68 | 69 | new_avatar = conn.player.get_cur_avatar() 70 | new_avatar.motion = old_avatar.motion 71 | new_avatar.rotation = old_avatar.rotation 72 | new_avatar.speed = old_avatar.speed 73 | new_avatar.motion_state = old_avatar.motion_state 74 | 75 | scene_entity_appear_notify = SceneEntityAppearNotify() 76 | scene_entity_appear_notify.appear_type = VisionType.VISION_REPLACE 77 | scene_entity_appear_notify.entity_list = [new_avatar.get_scene_entity_info(conn.player.uid)] 78 | conn.send(scene_entity_appear_notify) 79 | 80 | rsp = SetUpAvatarTeamRsp() 81 | rsp.avatar_team_guid_list = msg.avatar_team_guid_list 82 | rsp.cur_avatar_guid = msg.cur_avatar_guid 83 | rsp.team_id = msg.team_id 84 | conn.send(rsp) -------------------------------------------------------------------------------- /game_server/game/gacha.py: -------------------------------------------------------------------------------- 1 | from lib.proto import GachaInfo, GachaItem, ItemParam 2 | from game_server.resource.enums import GachaItemType 3 | from game_server.resource import resources 4 | from game_server.resource.excel import * 5 | from random import Random 6 | from loguru import logger 7 | 8 | class History: 9 | def __init__(self): 10 | self.rolls_since5S = 0 11 | self.rolls_since4S = 0 12 | self.total_rolls = 0 13 | 14 | def tick(self): 15 | self.total_rolls += 1 16 | self.rolls_since5S += 1 17 | self.rolls_since4S += 1 18 | 19 | def s5tick(self): 20 | self.tick() 21 | self.rolls_since5S = 0 22 | 23 | def s4tick(self): 24 | self.tick() 25 | self.rolls_since4S = 0 26 | 27 | class Banner: 28 | #data: GachaData 29 | #pool: dict[int, list[GachaPoolData]] # prob, pool_list 30 | 31 | def __init__(self, gacha_data: GachaData): 32 | self.data: GachaData() = gacha_data 33 | self.pool: dict[int, list[GachaPoolData]] = dict() 34 | self.prob: dict[int, int] = dict() 35 | logger.debug("loading banner with sort_id: {}", gacha_data.sort_id) 36 | to_del=dict() 37 | for prob_tuple, prob_data in resources.excels.gacha_prob_datas.items(): 38 | if prob_data.id != gacha_data.prob_rule_id: 39 | continue 40 | pool = list() 41 | for pool_tuple, pool_data in resources.excels.gacha_pool_datas.items(): 42 | if pool_data.id != gacha_data.pool_id: 43 | continue 44 | if prob_data.item_type != pool_data.item_type: 45 | continue 46 | if pool_data in pool: 47 | continue 48 | pool.append(pool_data) 49 | if prob_data.base_prob in self.pool.keys(): 50 | if prob_data.base_prob in to_del.keys(): 51 | to_del[prob_data.base_prob] += 1 52 | else: 53 | to_del[prob_data.base_prob] = 2 54 | self.pool[prob_data.base_prob].extend(pool) 55 | else: 56 | self.pool[prob_data.base_prob]=pool 57 | 58 | for key, value in to_del.items(): 59 | self.pool[key*value] = self.pool.pop(key) 60 | 61 | for k, v in self.pool.items(): 62 | logger.debug("loaded {} pool items with probabilty {}", len(v), k/100) 63 | p = sorted(self.pool.keys()) 64 | for pv in p: 65 | v = sum(filter(lambda x: x<=pv, p)) 66 | self.prob[v] = pv 67 | def as_gacha_info(self): 68 | info = GachaInfo( 69 | gacha_type=(300 if self.data.sort_id!=9999 else 100), #since there is no gacha type in newbie data, i use hardcoded 100 for newbie and 300 for others 70 | schedule_id=self.data.sort_id, 71 | begin_time=0, 72 | end_time=0xffffffff, 73 | cost_item_id=self.data.cost_item_id, 74 | cost_item_num=self.data.cost_item_num, 75 | gacha_prefab_path=self.data.gacha_prefab_path, 76 | gacha_prob_url=self.data.gacha_prob_url, 77 | gacha_record_url="",#gachadata.gacha_prob_url, #protection for fools 78 | gacha_preview_prefab_path=self.data.gacha_preview_prefab_path, 79 | ten_cost_item_id=self.data.ten_cost_item_id, 80 | ten_cost_item_num=self.data.ten_cost_item_num, 81 | left_gacha_times=0xFFFFFFFF,#(0xFFFFFFFF if self.data.sort_id!=9999 else gachadata.gacha_times_limit-history.total_rolls), 82 | gacha_times_limit=0xFFFFFFFF,#(0xFFFFFFFF if self.data.sort_id!=9999 else gachadata.gacha_times_limit), 83 | gacha_sort_id=self.data.sort_id 84 | ) 85 | return info 86 | def do_pull(self, seed: float, history: History): #im too lazy to implement an accurate gacha for shit cb server, that doesnt even have a database 87 | rnd = Random() 88 | rnd.seed(seed) 89 | pk = self.pool.keys() 90 | val = 10000 91 | if (history.rolls_since4S >= 9): 92 | val = rnd.randint(0,510+60) #there is still chanses to get 5star 93 | elif (history.rolls_since5S >= 89): 94 | val = 0 95 | else: 96 | val = rnd.randint(0,9430+510+60) 97 | logger.debug("do_pull_result: {}/100 (5S:{} 4S:{} T:{})", val/100, history.rolls_since5S, history.rolls_since4S, history.total_rolls) 98 | prob = list(filter(lambda x: x>=val, self.prob)) 99 | 100 | return rnd.choice(self.pool[self.prob[min(prob)]]) 101 | 102 | 103 | 104 | 105 | 106 | class Gacha: 107 | #banners: dict[int, Banner] = dict() 108 | def __init__(self): 109 | self.banners: dict[int, Banner] = dict() 110 | self.history: History = History() 111 | for gacha_id, gachadata in resources.excels.gacha_datas.items(): 112 | banner = Banner(gachadata) 113 | self.banners[gacha_id] = banner 114 | logger.debug("loaded {} banners", len(self.banners)) 115 | def get_banners(self): 116 | banner_list = list() 117 | for banner in self.banners.values(): 118 | banner_list.append(banner.as_gacha_info()) 119 | return banner_list 120 | def do_pull(self, banner_id:int, times: int): 121 | result: list(GachaItem) = list() 122 | rnd = Random() 123 | for p in range(times): 124 | res = self.banners[banner_id].do_pull(rnd.random(), self.history) 125 | if res.item_type==GachaItemType.GACHA_ITEM_AVATAR_S5 or res.item_type==GachaItemType.GACHA_ITEM_WEAPON_S5: 126 | self.history.s5tick() 127 | elif res.item_type==GachaItemType.GACHA_ITEM_AVATAR_S4 or res.item_type==GachaItemType.GACHA_ITEM_WEAPON_S4: 128 | self.history.s4tick() 129 | else: 130 | self.history.tick() 131 | item = GachaItem( 132 | gacha_item=ItemParam( 133 | item_id=res.item_id, 134 | count=1, 135 | ), 136 | transfer_items=[], 137 | is_flash_card=False, 138 | is_gacha_item_new=True, 139 | token_item_list=[] 140 | ) 141 | result.append(item) 142 | logger.debug("{}",self.history) 143 | return result 144 | 145 | 146 | -------------------------------------------------------------------------------- /sdk_server/app.py: -------------------------------------------------------------------------------- 1 | from bottle import route, run, request, Bottle 2 | import json 3 | import base64 4 | from lib.proto import ( 5 | QueryRegionListHttpRsp, 6 | QueryCurrRegionHttpRsp, 7 | RegionSimpleInfo, 8 | RegionInfo, 9 | ) 10 | 11 | app = Bottle() 12 | 13 | ACCOUNT_INFO = { 14 | "retcode": 0, 15 | "message": "OK", 16 | "data": { 17 | "account": { 18 | "uid": "1337", 19 | "name": "saturnine", 20 | "email": "saturnine", 21 | "mobile": "", 22 | "is_email_verify": 1, 23 | "realname": "", 24 | "identity_card": "", 25 | "token": "token", 26 | "safe_mobile": "", 27 | "facebook_name": "", 28 | "google_name": "", 29 | "twitter_name": "", 30 | "game_center_name": "", 31 | "apple_name": "", 32 | "sony_name": "", 33 | "tap_name": "", 34 | "country": "US", 35 | "reactivate_ticket": "", 36 | "area_code": "US", 37 | "device_grant_ticket": "", 38 | "steam_name": "", 39 | }, 40 | "device_grant_required": False, 41 | "safe_mobile_required": False, 42 | "realperson_required": False, 43 | "realname_operation": "None", 44 | }, 45 | } 46 | 47 | 48 | @app.route("/query_region_list") 49 | def query_region_list(): 50 | rsp = QueryRegionListHttpRsp() 51 | rsp.region_list.append( 52 | RegionSimpleInfo( 53 | name="Test", 54 | title="os_euro", 55 | type="DEV_PUBLIC", 56 | dispatch_url="http://127.0.0.1:80/query_cur_region", 57 | ) 58 | ) 59 | rsp.retcode = 0 60 | 61 | rsp.client_config.sdkenv = "2" 62 | rsp.client_config.showexception = True 63 | 64 | custom_config = { 65 | "sdkenv": "2", 66 | "checkdevice": "False", 67 | "loadPatch": "False", 68 | "showexception": "true", 69 | "regionConfig": "pm|fk|add", 70 | "downloadMode": "0", 71 | } 72 | 73 | rsp.client_custom_config = json.dumps(custom_config) 74 | rsp.client_secret_key = b"" 75 | rsp.client_custom_config_encrypted = json.dumps(custom_config).encode() 76 | 77 | return base64.b64encode(rsp.SerializeToString()).decode() 78 | 79 | 80 | @app.route("/query_cur_region") 81 | def query_cur_region(): 82 | rsp = QueryCurrRegionHttpRsp() 83 | rsp.retcode = 0 84 | 85 | rsp.region_info = RegionInfo() 86 | 87 | rsp.region_info.gateserver_ip = "127.0.0.1" 88 | rsp.region_info.gateserver_port = 22102 89 | 90 | rsp.client_secret_key = b"" 91 | rsp.region_info.secret_key = b"" 92 | 93 | custom_config = { 94 | "sdkenv": "2", 95 | "checkdevice": "False", 96 | "loadPatch": "False", 97 | "showexception": "True", 98 | "regionConfig": "pm|fk|add", 99 | "downloadMode": "0", 100 | } 101 | 102 | rsp.client_config.sdkenv = "2" 103 | rsp.client_config.showexception = True 104 | 105 | rsp.region_custom_config_encrypted = json.dumps(custom_config).encode() 106 | rsp.client_region_custom_config_encrypted = json.dumps(custom_config).encode() 107 | 108 | rsp.region_info.resource_url = "http://127.0.0.1:80/client_game_res/client_game_res" 109 | rsp.region_info.data_url = "http://127.0.0.1:80/client_design_data" 110 | 111 | return base64.b64encode(rsp.SerializeToString()).decode() 112 | 113 | 114 | @app.route("/mdk/shield/api/login", method="POST") 115 | def route_mdk_shield_api_login(): 116 | return ACCOUNT_INFO 117 | 118 | 119 | @app.route("/mdk/shield/api/verify", method="POST") 120 | def route_mdk_vertify(): 121 | return ACCOUNT_INFO 122 | 123 | 124 | @app.route("/mdk/shield/api/loadConfig", method="POST") 125 | def route_mdk_shield_api_loadConfig(): 126 | return { 127 | "retcode": 0, 128 | "message": "OK", 129 | "data": { 130 | "id": 6, 131 | "game_key": "hk4e_cn", 132 | "client": "PC", 133 | "identity": "I_IDENTITY", 134 | "guest": True, 135 | "ignore_versions": "", 136 | "scene": "S_NORMAL", 137 | "name": "原神海外", 138 | "disable_regist": False, 139 | "enable_email_captcha": False, 140 | "thirdparty": ["fb", "tw"], 141 | "disable_mmt": False, 142 | "server_guest": True, 143 | "thirdparty_ignore": {"tw": "", "fb": ""}, 144 | "enable_ps_bind_account": False, 145 | "thirdparty_login_configs": { 146 | "tw": {"token_type": "TK_GAME_TOKEN", "game_token_expires_in": 604800}, 147 | "fb": {"token_type": "TK_GAME_TOKEN", "game_token_expires_in": 604800}, 148 | }, 149 | }, 150 | } 151 | 152 | 153 | @app.route( 154 | "/admin/mi18n/bh3_usa/20190628_5d15ba66cd922/20190628_5d15ba66cd922-version.json" 155 | ) 156 | def route_20190628_5d15ba66cd922(): 157 | return {"version": 52} 158 | 159 | 160 | @app.route("/mdk/shield/api/loginCaptcha", method="POST") 161 | def route_mdk_shield_api_loginCaptcha(): 162 | return { 163 | "retcode": 0, 164 | "message": "OK", 165 | "data": {"protocol": True, "qr_enabled": True, "log_level": "INFO"}, 166 | } 167 | 168 | 169 | @app.route("/combo/granter/login/login", method="POST") 170 | def route_combo_granter_login_login(): 171 | return { 172 | "retcode": 0, 173 | "message": "OK", 174 | "data": { 175 | "combo_id": 1337, 176 | "open_id": 1337, 177 | "combo_token": "token", 178 | "data": '{"guest":true}', 179 | "heartbeat": False, 180 | "account_type": 1, 181 | }, 182 | } 183 | 184 | 185 | @app.route("/combo/granter/api/getConfig") 186 | def route_combo_granter_api_getConfig(): 187 | return { 188 | "retcode": 0, 189 | "message": "OK", 190 | "data": { 191 | "protocol": True, 192 | "qr_enabled": True, 193 | "log_level": "DEBUG", 194 | "announce_url": "https://webstatic-sea.hoyoverse.com/hk4e/announcement/index.html?sdk_presentation_style=fullscreen\u0026sdk_screen_transparent=true\u0026game_biz=hk4e_global\u0026auth_appid=announcement\u0026game=hk4e#/", 195 | "push_alias_type": 2, 196 | "disable_ysdk_guard": False, 197 | "enable_announce_pic_popup": True, 198 | }, 199 | } 200 | 201 | 202 | @app.route("/combo/granter/api/getProtocol") 203 | def route_combo_granter_api_getProtocol(): 204 | return { 205 | "retcode": 0, 206 | "message": "OK", 207 | "data": { 208 | "modified": True, 209 | "protocol": { 210 | "id": 0, 211 | "app_id": 4, 212 | "language": "zh-cn", 213 | "user_proto": "", 214 | "priv_proto": "", 215 | "major": 31, 216 | "minimum": 0, 217 | "create_time": "-", 218 | "teenager_proto": "asd", 219 | "third_proto": "asd", 220 | }, 221 | }, 222 | } 223 | 224 | 225 | @app.route("/log/sdk/upload", method="POST") 226 | def route_log_sdk_upload(): 227 | return "" 228 | 229 | 230 | @app.route("/pcSdkLogin.html") 231 | def route_pcSdkLogin(): 232 | return "" 233 | 234 | 235 | @app.route("/client_game_res/:#.*#") 236 | def client_game_res(): 237 | return "" 238 | 239 | 240 | @app.route("/client_design_data/:#.*#") 241 | def client_design_data(): 242 | return "" 243 | 244 | 245 | @app.route("/sdk/login") 246 | def login(): 247 | return { 248 | "retcode": 0, 249 | "data": { 250 | "uid": "1337", 251 | "token": "token", 252 | "email": "saturnine", 253 | }, 254 | } 255 | 256 | 257 | def run_http_server(host): 258 | app.run(host=host, port="80", debug=True) 259 | -------------------------------------------------------------------------------- /game_server/game/player.py: -------------------------------------------------------------------------------- 1 | from lib.proto import Vector,EnterType,PlayerEnterSceneNotify, AvatarTeam, AvatarInfo, AvatarType, PropValue, AvatarFetterInfo 2 | from game_server.utils.time import current_milli_time 3 | from game_server.resource.enums import PropType 4 | from game_server.game.world import World 5 | from game_server.game.entity.avatar import AvatarEntity 6 | from game_server.resource.enums import PropType, FightProp 7 | from game_server.resource import resources 8 | import dataclasses 9 | 10 | @dataclasses.dataclass() 11 | class Player: 12 | uid: int 13 | name: str 14 | avatar_id: int = 10000005 15 | world: World = World() 16 | 17 | scene_id: int = 0 18 | pos: Vector = dataclasses.field(default_factory=Vector) 19 | 20 | prop_map: dict[PropType, int] = dataclasses.field(default_factory=dict) 21 | next_guid: int = 0 22 | 23 | teams: dict[int, AvatarTeam] = dataclasses.field(default_factory=dict) 24 | 25 | avatars: list[AvatarEntity] = dataclasses.field(default_factory=list) 26 | cur_avatar_guid: int = 0 27 | cur_avatar_team_id: int = 0 28 | 29 | def init_default(self): 30 | self.prop_map = { 31 | PropType.PROP_IS_SPRING_AUTO_USE: 1, 32 | PropType.PROP_SPRING_AUTO_USE_PERCENT: 50, 33 | PropType.PROP_IS_FLYABLE: 1, 34 | PropType.PROP_IS_TRANSFERABLE: 1, 35 | PropType.PROP_CUR_PERSIST_STAMINA: 24000, 36 | PropType.PROP_MAX_STAMINA: 24000, 37 | PropType.PROP_PLAYER_LEVEL: 60, 38 | PropType.PROP_PLAYER_EXP: 0, 39 | PropType.PROP_PLAYER_HCOIN: 10, 40 | } 41 | self.teams = { 42 | 0: AvatarTeam(avatar_guid_list=[], team_name="Team 1"), 43 | 1: AvatarTeam(avatar_guid_list=[], team_name="Team 2"), 44 | 2: AvatarTeam(avatar_guid_list=[], team_name="Team 3"), 45 | 3: AvatarTeam(avatar_guid_list=[], team_name="Team 4"), 46 | } 47 | traveler = AvatarInfo() 48 | traveler.avatar_id = 10000005 49 | traveler.avatar_type = AvatarType.AVATAR_TYPE_FORMAL 50 | traveler.skill_depot_id = 504 51 | traveler.talent_id_list = [] 52 | traveler.prop_map = { 53 | PropType.PROP_LEVEL._value_: PropValue(type=PropType.PROP_LEVEL._value_, val=80, ival=80), 54 | PropType.PROP_EXP._value_: PropValue(type=PropType.PROP_EXP._value_, val=0, ival=0), 55 | PropType.PROP_BREAK_LEVEL._value_: PropValue(type=PropType.PROP_BREAK_LEVEL._value_, val=4, ival=4), 56 | } 57 | 58 | traveler.fight_prop_map = { 59 | FightProp.FIGHT_PROP_BASE_HP._value_: 20000, 60 | FightProp.FIGHT_PROP_MAX_HP._value_: 20000, 61 | FightProp.FIGHT_PROP_BASE_DEFENSE._value_: 3000, 62 | FightProp.FIGHT_PROP_BASE_ATTACK._value_: 3000, 63 | FightProp.FIGHT_PROP_CRITICAL._value_: 1, 64 | FightProp.FIGHT_PROP_CRITICAL_HURT._value_: 2, 65 | FightProp.FIGHT_PROP_CHARGE_EFFICIENCY._value_: 2, 66 | FightProp.FIGHT_PROP_MAX_ROCK_ENERGY._value_: 1, 67 | FightProp.FIGHT_PROP_MAX_ICE_ENERGY._value_: 1, 68 | FightProp.FIGHT_PROP_MAX_WATER_ENERGY._value_: 1, 69 | FightProp.FIGHT_PROP_MAX_FIRE_ENERGY._value_: 1, 70 | FightProp.FIGHT_PROP_MAX_ELEC_ENERGY._value_: 1, 71 | FightProp.FIGHT_PROP_MAX_GRASS_ENERGY._value_: 1, 72 | FightProp.FIGHT_PROP_MAX_WIND_ENERGY._value_: 1, 73 | FightProp.FIGHT_PROP_CUR_ROCK_ENERGY._value_: 1, 74 | FightProp.FIGHT_PROP_CUR_ICE_ENERGY._value_: 1, 75 | FightProp.FIGHT_PROP_CUR_WATER_ENERGY._value_: 1, 76 | FightProp.FIGHT_PROP_CUR_ELEC_ENERGY._value_: 1, 77 | FightProp.FIGHT_PROP_CUR_FIRE_ENERGY._value_: 1, 78 | FightProp.FIGHT_PROP_CUR_WIND_ENERGY._value_: 1, 79 | FightProp.FIGHT_PROP_CUR_GRASS_ENERGY._value_: 1, 80 | FightProp.FIGHT_PROP_CUR_HP._value_: 20000, 81 | FightProp.FIGHT_PROP_CUR_DEFENSE._value_: 3000, 82 | FightProp.FIGHT_PROP_CUR_ATTACK._value_: 3000, 83 | FightProp.FIGHT_PROP_SPEED_PERCENT._value_: 2, 84 | FightProp.FIGHT_PROP_SKILL_CD_MINUS_RATIO._value_: 1, 85 | } 86 | 87 | traveler.fetter_info = AvatarFetterInfo(exp_level=10, exp_number=10) 88 | traveler.equip_guid_list = [] 89 | traveler.proud_skill_extra_level_map = {} 90 | traveler.inherent_proud_skill_list = [] 91 | traveler.skill_level_map = {} 92 | traveler.life_state = 1 93 | traveler.core_proud_skill_level = 0 94 | traveler.guid = self.get_next_guid() 95 | self.avatars.append(AvatarEntity(self.world, traveler, self.pos)) 96 | self.teams[self.cur_avatar_team_id].avatar_guid_list.append(traveler.guid) 97 | self.cur_avatar_guid = traveler.guid 98 | self.add_all_avatars() 99 | 100 | def add_all_avatars(self): 101 | for avatar_id, avatar_data in resources.excels.avatar_datas.items(): 102 | print(avatar_id) 103 | print(avatar_data) 104 | if avatar_id == 10000005: 105 | continue 106 | skill_depot = resources.excels.avatar_skill_depot_datas[avatar_data.skill_depot_id] 107 | print(skill_depot) 108 | talents = skill_depot.talent_groups 109 | talents.append(skill_depot.leader_talent) 110 | avatar = AvatarInfo() 111 | avatar.avatar_id = int(avatar_id) 112 | avatar.avatar_type = AvatarType.AVATAR_TYPE_FORMAL 113 | avatar.skill_depot_id = int(avatar_data.skill_depot_id) 114 | avatar.talent_id_list = [] 115 | avatar.prop_map = { 116 | PropType.PROP_LEVEL._value_: PropValue(type=PropType.PROP_LEVEL._value_, val=80, ival=80), 117 | PropType.PROP_EXP._value_: PropValue(type=PropType.PROP_EXP._value_, val=0, ival=0), 118 | PropType.PROP_BREAK_LEVEL._value_: PropValue(type=PropType.PROP_BREAK_LEVEL._value_, val=4, ival=4), 119 | } 120 | avatar.fight_prop_map = { 121 | FightProp.FIGHT_PROP_BASE_HP._value_: 20000, 122 | FightProp.FIGHT_PROP_MAX_HP._value_: 20000, 123 | FightProp.FIGHT_PROP_BASE_DEFENSE._value_: 3000, 124 | FightProp.FIGHT_PROP_BASE_ATTACK._value_: 3000, 125 | FightProp.FIGHT_PROP_CRITICAL._value_: 1, 126 | FightProp.FIGHT_PROP_CRITICAL_HURT._value_: 2, 127 | FightProp.FIGHT_PROP_CHARGE_EFFICIENCY._value_: 2, 128 | FightProp.FIGHT_PROP_MAX_ROCK_ENERGY._value_: 1, 129 | FightProp.FIGHT_PROP_MAX_ICE_ENERGY._value_: 1, 130 | FightProp.FIGHT_PROP_MAX_WATER_ENERGY._value_: 1, 131 | FightProp.FIGHT_PROP_MAX_FIRE_ENERGY._value_: 1, 132 | FightProp.FIGHT_PROP_MAX_ELEC_ENERGY._value_: 1, 133 | FightProp.FIGHT_PROP_MAX_GRASS_ENERGY._value_: 1, 134 | FightProp.FIGHT_PROP_MAX_WIND_ENERGY._value_: 1, 135 | FightProp.FIGHT_PROP_CUR_ROCK_ENERGY._value_: 1, 136 | FightProp.FIGHT_PROP_CUR_ICE_ENERGY._value_: 1, 137 | FightProp.FIGHT_PROP_CUR_WATER_ENERGY._value_: 1, 138 | FightProp.FIGHT_PROP_CUR_ELEC_ENERGY._value_: 1, 139 | FightProp.FIGHT_PROP_CUR_FIRE_ENERGY._value_: 1, 140 | FightProp.FIGHT_PROP_CUR_WIND_ENERGY._value_: 1, 141 | FightProp.FIGHT_PROP_CUR_GRASS_ENERGY._value_: 1, 142 | FightProp.FIGHT_PROP_CUR_HP._value_: 20000, 143 | FightProp.FIGHT_PROP_CUR_DEFENSE._value_: 3000, 144 | FightProp.FIGHT_PROP_CUR_ATTACK._value_: 3000, 145 | FightProp.FIGHT_PROP_SPEED_PERCENT._value_: 2, 146 | FightProp.FIGHT_PROP_SKILL_CD_MINUS_RATIO._value_: 1, 147 | } 148 | avatar.fetter_info = AvatarFetterInfo(exp_level=10, exp_number=10) 149 | avatar.equip_guid_list = [] 150 | avatar.proud_skill_extra_level_map = {} 151 | avatar.inherent_proud_skill_list = [] 152 | avatar.skill_level_map = {} 153 | avatar.life_state = 1 154 | avatar.core_proud_skill_level = 0 155 | avatar.guid = self.get_next_guid() 156 | self.avatars.append(AvatarEntity(self.world, avatar, self.pos)) 157 | 158 | def get_cur_avatar(self): 159 | return self.get_avatar_by_guid(self.cur_avatar_guid) 160 | 161 | def get_avatar_by_guid(self, guid: int): 162 | for avatar_entity in self.avatars: 163 | if avatar_entity.guid == guid: 164 | return avatar_entity 165 | 166 | def get_avatar_by_entity_id(self, entity_id: int): 167 | for avatar_entity in self.avatars: 168 | if avatar_entity.entity_id == entity_id: 169 | return avatar_entity 170 | 171 | def get_teleport_packet(self, scene_id: int, pos: Vector, enter_type: EnterType = EnterType.ENTER_SELF): 172 | player_enter_scene_notify = PlayerEnterSceneNotify() 173 | player_enter_scene_notify.scene_id = scene_id 174 | player_enter_scene_notify.pos = pos 175 | player_enter_scene_notify.scene_begin_time = current_milli_time() 176 | player_enter_scene_notify.type = enter_type 177 | player_enter_scene_notify.enter_scene_token = 1000 178 | player_enter_scene_notify.world_level = 8 179 | player_enter_scene_notify.target_uid = self.uid 180 | return player_enter_scene_notify 181 | 182 | def get_next_guid(self): 183 | self.next_guid += 1 184 | return self.next_guid 185 | -------------------------------------------------------------------------------- /lib/retcode.py: -------------------------------------------------------------------------------- 1 | # Generated by the protocol buffer compiler. DO NOT EDIT! 2 | # sources: retcode.proto 3 | # plugin: python-betterproto 4 | from dataclasses import dataclass 5 | 6 | import betterproto 7 | 8 | 9 | class Retcode(betterproto.Enum): 10 | RET_SUCC = 0 11 | RET_FAIL = -1 12 | RET_SVR_ERROR = 1 13 | RET_UNKNOWN_ERROR = 2 14 | RET_FREQUENT = 3 15 | RET_NODE_FORWARD_ERROR = 4 16 | RET_NOT_FOUND_CONFIG = 5 17 | RET_SYSTEM_BUSY = 6 18 | RET_GM_UID_BIND = 7 19 | RET_STOP_REGISTER = 10 20 | RET_STOP_SERVER = 11 21 | RET_ACCOUNT_VEIRFY_ERROR = 12 22 | RET_ACCOUNT_FREEZE = 13 23 | RET_REPEAT_LOGIN = 14 24 | RET_CLIENT_VERSION_ERROR = 15 25 | RET_TOKEN_ERROR = 16 26 | RET_ACCOUNT_NOT_EXIST = 17 27 | RET_WAIT_OTHER_LOGIN = 18 28 | RET_ANOTHER_LOGIN = 19 29 | RET_CLIENT_FORCE_UPDATE = 20 30 | RET_BLACK_UID = 21 31 | RET_LOGIN_DB_FAIL = 22 32 | RET_LOGIN_INIT_FAIL = 23 33 | RET_MYSQL_DUPLICATE = 24 34 | RET_MAX_PLAYER = 25 35 | RET_AVATAR_IN_CD = 101 36 | RET_AVATAR_NOT_ALIVE = 102 37 | RET_AVATAR_NOT_ON_SCENE = 103 38 | RET_CAN_NOT_FIND_AVATAR = 104 39 | RET_CAN_NOT_DEL_CUR_AVATAR = 105 40 | RET_DUPLICATE_AVATAR = 106 41 | RET_AVATAR_IS_SAME_ONE = 107 42 | RET_AVATAR_LEVEL_LESS_THAN = 108 43 | RET_AVATAR_CAN_NOT_CHANGE_ELEMENT = 109 44 | RET_AVATAR_BREAK_LEVEL_LESS_THAN = 110 45 | RET_AVATAR_ON_MAX_BREAK_LEVEL = 111 46 | RET_AVATAR_ID_ALREADY_EXIST = 112 47 | RET_AVATAR_NOT_DEAD = 113 48 | RET_AVATAR_IS_REVIVING = 114 49 | RET_AVATAR_ID_ERROR = 115 50 | RET_REPEAT_SET_PLAYER_BORN_DATA = 116 51 | RET_PLAYER_LEVEL_LESS_THAN = 117 52 | RET_AVATAR_LIMIT_LEVEL_ERROR = 118 53 | RET_CAN_NOT_FIND_TEAM = 120 54 | RET_CAN_NOT_FIND_CUR_TEAM = 121 55 | RET_AVATAR_NOT_EXIST_IN_TEAM = 122 56 | RET_CAN_NOT_REMOVE_CUR_AVATAR_FROM_TEAM = 123 57 | RET_CAN_NOT_USE_REVIVE_ITEM_FOR_CUR_AVATAR = 124 58 | RET_TEAM_COST_EXCEED_LIMIT = 125 59 | RET_TEAM_AVATAR_IN_EXPEDITION = 126 60 | RET_TEAM_CAN_NOT_CHOSE_REPLACE_USE = 127 61 | RET_NICKNAME_UTF8_ERROR = 130 62 | RET_NICKNAME_TOO_LONG = 131 63 | RET_NICKNAME_WORD_ILLEGAL = 132 64 | RET_PLAYER_NOT_ONLINE = 140 65 | RET_OPEN_STATE_NOT_OPEN = 141 66 | RET_AVATAR_EXPEDITION_AVATAR_DIE = 152 67 | RET_AVATAR_EXPEDITION_COUNT_LIMIT = 153 68 | RET_AVATAR_EXPEDITION_MAIN_FORBID = 154 69 | RET_AVATAR_EXPEDITION_TRIAL_FORBID = 155 70 | RET_TEAM_NAME_ILLEGAL = 156 71 | RET_IS_NOT_IN_STANDBY = 157 72 | RET_IS_IN_DUNGEON = 158 73 | RET_IS_IN_LOCK_AVATAR_QUEST = 159 74 | RET_IS_USING_TRIAL_AVATAR = 160 75 | RET_IS_USING_TEMP_AVATAR = 161 76 | RET_NPC_NOT_EXIST = 301 77 | RET_NPC_TOO_FAR = 302 78 | RET_NOT_CURRENT_TALK = 303 79 | RET_NPC_CREATE_FAIL = 304 80 | RET_NPC_MOVE_FAIL = 305 81 | RET_QUEST_NOT_EXIST = 401 82 | RET_QUEST_IS_FAIL = 402 83 | RET_QUEST_CONTENT_ERROR = 403 84 | RET_POINT_NOT_UNLOCKED = 501 85 | RET_POINT_TOO_FAR = 502 86 | RET_POINT_ALREAY_UNLOCKED = 503 87 | RET_ENTITY_NOT_EXIST = 504 88 | RET_ENTER_SCENE_FAIL = 505 89 | RET_PLAYER_IS_ENTER_SCENE = 506 90 | RET_CITY_MAX_LEVEL = 507 91 | RET_AREA_LOCKED = 508 92 | RET_JOIN_OTHER_WAIT = 509 93 | RET_WEATHER_AREA_NOT_FOUND = 510 94 | RET_WEATHER_IS_LOCKED = 511 95 | RET_NOT_IN_SELF_SCENE = 512 96 | RET_GROUP_NOT_EXIST = 513 97 | RET_MARK_NAME_ILLEGAL = 514 98 | RET_MARK_ALREADY_EXISTS = 515 99 | RET_MARK_OVERFLOW = 516 100 | RET_MARK_NOT_EXISTS = 517 101 | RET_MARK_UNKNOWN_TYPE = 518 102 | RET_MARK_NAME_TOO_LONG = 519 103 | RET_DISTANCE_LONG = 520 104 | RET_ENTER_SCENE_TOKEN_INVALID = 521 105 | RET_ITEM_NOT_EXIST = 601 106 | RET_PACK_EXCEED_MAX_WEIGHT = 602 107 | RET_ITEM_NOT_DROPABLE = 603 108 | RET_ITEM_NOT_USABLE = 604 109 | RET_ITEM_INVALID_USE_COUNT = 605 110 | RET_ITEM_INVALID_DROP_COUNT = 606 111 | RET_ITEM_ALREADY_EXIST = 607 112 | RET_ITEM_IN_COOLDOWN = 608 113 | RET_ITEM_COUNT_NOT_ENOUGH = 609 114 | RET_ITEM_INVALID_TARGET = 610 115 | RET_RECIPE_NOT_EXIST = 611 116 | RET_RECIPE_LOCKED = 612 117 | RET_RECIPE_UNLOCKED = 613 118 | RET_COMPOUND_QUEUE_FULL = 614 119 | RET_COMPOUND_NOT_FINISH = 615 120 | RET_MAIL_ITEM_NOT_GET = 616 121 | RET_ITEM_EXCEED_LIMIT = 617 122 | RET_AVATAR_CAN_NOT_USE = 618 123 | RET_ITEM_NEED_PLAYER_LEVEL = 619 124 | RET_RECIPE_NOT_AUTO_QTE = 620 125 | RET_COMPOUND_BUSY_QUEUE = 621 126 | RET_NEED_MORE_SCOIN = 622 127 | RET_SKILL_DEPOT_NOT_FOUND = 623 128 | RET_HCOIN_NOT_ENOUGH = 624 129 | RET_SCOIN_NOT_ENOUGH = 625 130 | RET_HCOIN_EXCEED_LIMIT = 626 131 | RET_SCOIN_EXCEED_LIMIT = 627 132 | RET_MAIL_EXPIRED = 628 133 | RET_REWARD_HAS_TAKEN = 629 134 | RET_COMBINE_COUNT_TOO_LARGE = 630 135 | RET_GIVING_ITEM_WRONG = 631 136 | RET_GIVING_IS_FINISHED = 632 137 | RET_GIVING_NOT_ACTIVED = 633 138 | RET_FORGE_QUEUE_FULL = 634 139 | RET_FORGE_QUEUE_CAPACITY = 635 140 | RET_FORGE_QUEUE_NOT_FOUND = 636 141 | RET_FORGE_QUEUE_EMPTY = 637 142 | RET_NOT_SUPPORT_ITEM = 638 143 | RET_ITEM_EMPTY = 639 144 | RET_VIRTUAL_EXCEED_LIMIT = 640 145 | RET_MATERIAL_EXCEED_LIMIT = 641 146 | RET_EQUIP_EXCEED_LIMIT = 642 147 | RET_ITEM_SHOULD_HAVE_NO_LEVEL = 643 148 | RET_WEAPON_PROMOTE_LEVEL_EXCEED_LIMIT = 644 149 | RET_WEAPON_LEVEL_INVALID = 645 150 | RET_UNKNOW_ITEM_TYPE = 646 151 | RET_ITEM_COUNT_IS_ZERO = 647 152 | RET_ITEM_IS_EXPIRED = 648 153 | RET_EQUIP_LEVEL_HIGHER = 650 154 | RET_EQUIP_CAN_NOT_WAKE_OFF_WEAPON = 651 155 | RET_EQUIP_HAS_BEEN_WEARED = 652 156 | RET_EQUIP_WEARED_CANNOT_DROP = 653 157 | RET_AWAKEN_LEVEL_MAX = 654 158 | RET_RESIN_NOT_ENOUGH = 660 159 | RET_RESIN_EXCEED_LIMIT = 661 160 | RET_RESIN_OPENSTATE_OFF = 662 161 | RET_RESIN_BOUGHT_COUNT_EXCEEDED = 663 162 | RET_WORLD_RESIN_NOT_ENOUGH = 664 163 | RET_WORLD_RESIN_EXCEED_LIMIT = 665 164 | RET_WORLD_RESIN_OPENSTATE_OFF = 666 165 | RET_WORLD_RESIN_BOUGHT_COUNT_EXCEEDED = 667 166 | RET_AUTO_RECOVER_OPENSTATE_OFF = 668 167 | RET_AUTO_RECOVER_BOUGHT_COUNT_EXCEEDED = 669 168 | RET_RESIN_GAIN_FAILED = 670 169 | RET_WORLD_RESIN_GAIN_FAILED = 671 170 | RET_GOODS_NOT_EXIST = 701 171 | RET_GOODS_MATERIAL_NOT_ENOUGH = 702 172 | RET_GOODS_NOT_IN_TIME = 703 173 | RET_GOODS_BUY_NUM_NOT_ENOUGH = 704 174 | RET_GOODS_BUY_NUM_ERROR = 705 175 | RET_SHOP_NOT_OPEN = 706 176 | RET_CHAT_CD = 799 177 | RET_CHAT_FREQUENTLY = 800 178 | RET_GADGET_NOT_EXIST = 801 179 | RET_GADGET_NOT_INTERACTIVE = 802 180 | RET_GADGET_NOT_GATHERABLE = 803 181 | RET_CHEST_IS_LOCKED = 804 182 | RET_GADGET_CREATE_FAIL = 805 183 | RET_WORKTOP_OPTION_NOT_EXIST = 806 184 | RET_GADGET_STATUE_NOT_ACTIVE = 807 185 | RET_GADGET_STATUE_OPENED = 808 186 | RET_BOSS_CHEST_NO_QUALIFICATION = 809 187 | RET_BOSS_CHEST_LIFE_TIME_OVER = 810 188 | RET_BOSS_CHEST_WEEK_NUM_LIMIT = 811 189 | RET_BOSS_CHEST_GUEST_WORLD_LEVEL = 812 190 | RET_BOSS_CHEST_HAS_TAKEN = 813 191 | RET_ACTIVITY_CLOSE = 860 192 | RET_ACTIVITY_ITEM_ERROR = 861 193 | RET_ACTIVITY_CONTRIBUTION_NOT_ENOUGH = 862 194 | RET_SEA_LAMP_PHASE_NOT_FINISH = 863 195 | RET_SEA_LAMP_FLY_NUM_LIMIT = 864 196 | RET_SEA_LAMP_FLY_LAMP_WORD_ILLEGAL = 865 197 | RET_TALENT_ALREAY_UNLOCKED = 901 198 | RET_PREV_TALENT_NOT_UNLOCKED = 902 199 | RET_BIG_TALENT_POINT_NOT_ENOUGH = 903 200 | RET_SMALL_TALENT_POINT_NOT_ENOUGH = 904 201 | RET_PROUD_SKILL_ALREADY_GOT = 905 202 | RET_PREV_PROUD_SKILL_NOT_GET = 906 203 | RET_PROUD_SKILL_MAX_LEVEL = 907 204 | RET_CANDIDATE_SKILL_DEPOT_ID_NOT_FIND = 910 205 | RET_SKILL_DEPOT_IS_THE_SAME = 911 206 | RET_MONSTER_NOT_EXIST = 1001 207 | RET_MONSTER_CREATE_FAIL = 1002 208 | RET_DUNGEON_ENTER_FAIL = 1101 209 | RET_DUNGEON_QUIT_FAIL = 1102 210 | RET_DUNGEON_ENTER_EXCEED_DAY_COUNT = 1103 211 | RET_DUNGEON_REVIVE_EXCEED_MAX_COUNT = 1104 212 | RET_DUNGEON_REVIVE_FAIL = 1105 213 | RET_DUNGEON_NOT_SUCCEED = 1106 214 | RET_DUNGEON_CAN_NOT_CANCEL = 1107 215 | RET_DEST_DUNGEON_SETTLED = 1108 216 | RET_DUNGEON_CANDIDATE_TEAM_IS_FULL = 1109 217 | RET_DUNGEON_CANDIDATE_TEAM_IS_DISMISS = 1110 218 | RET_DUNGEON_CANDIDATE_TEAM_NOT_ALL_READY = 1111 219 | RET_DUNGEON_CANDIDATE_TEAM_HAS_REPEAT_AVATAR = 1112 220 | RET_DUNGEON_CANDIDATE_NOT_SINGEL_PASS = 1113 221 | RET_DUNGEON_REPLAY_NEED_ALL_PLAYER_DIE = 1114 222 | RET_DUNGEON_REPLAY_HAS_REVIVE_COUNT = 1115 223 | RET_DUNGEON_OTHERS_LEAVE = 1116 224 | RET_DUNGEON_ENTER_LEVEL_LIMIT = 1117 225 | RET_DUNGEON_CANNOT_ENTER_PLOT_IN_MP = 1118 226 | RET_DUNGEON_DROP_SUBFIELD_LIMIT = 1119 227 | RET_MP_NOT_IN_MY_WORLD = 1201 228 | RET_MP_IN_MP_MODE = 1202 229 | RET_MP_SCENE_IS_FULL = 1203 230 | RET_MP_MODE_NOT_AVAILABLE = 1204 231 | RET_MP_PLAYER_NOT_ENTERABLE = 1205 232 | RET_MP_QUEST_BLOCK_MP = 1206 233 | RET_MP_IN_ROOM_SCENE = 1207 234 | RET_MP_WORLD_IS_FULL = 1208 235 | RET_MP_PLAYER_NOT_ALLOW_ENTER = 1209 236 | RET_MP_PLAYER_DISCONNECTED = 1210 237 | RET_MP_NOT_IN_MP_MODE = 1211 238 | RET_MP_OWNER_NOT_ENTER = 1212 239 | RET_MP_ALLOW_ENTER_PLAYER_FULL = 1213 240 | RET_MP_TARGET_PLAYER_IN_TRANSFER = 1214 241 | RET_MP_TARGET_ENTERING_OTHER = 1215 242 | RET_MP_OTHER_ENTERING = 1216 243 | RET_MP_ENTER_MAIN_PLAYER_IN_PLOT = 1217 244 | RET_MAIL_PARA_ERR = 1301 245 | RET_MAIL_MAX_NUM = 1302 246 | RET_MAIL_ITEM_NUM_EXCEED = 1303 247 | RET_MAIL_TITLE_LEN_EXCEED = 1304 248 | RET_MAIL_CONTENT_LEN_EXCEED = 1305 249 | RET_MAIL_SENDER_LEN_EXCEED = 1306 250 | RET_MAIL_PARSE_PACKET_FAIL = 1307 251 | RET_OFFLINE_MSG_MAX_NUM = 1308 252 | RET_OFFLINE_MSG_SAME_TICKET = 1309 253 | RET_MAIL_EXCEL_MAIL_TYPE_ERROR = 1310 254 | RET_DAILY_TASK_NOT_FINISH = 1330 255 | RET_DAILY_TAKS_HAS_TAKEN = 1331 256 | RET_GACHA_INAVAILABLE = 1401 257 | RET_GACHA_RANDOM_NOT_MATCH = 1402 258 | RET_GACHA_SCHEDULE_NOT_MATCH = 1403 259 | RET_GACHA_INVALID_TIMES = 1404 260 | RET_GACHA_COST_ITEM_NOT_ENOUGH = 1405 261 | RET_GACHA_TIMES_LIMIT = 1406 262 | RET_INVESTIGAITON_NOT_IN_PROGRESS = 1501 263 | RET_INVESTIGAITON_UNCOMPLETE = 1502 264 | RET_INVESTIGAITON_REWARD_TAKEN = 1503 265 | RET_INVESTIGAITON_TARGET_STATE_ERROR = 1504 266 | RET_PUSH_TIPS_NOT_FOUND = 1505 267 | RET_TOWER_NOT_OPEN = 1521 268 | RET_TOWER_HAVE_DAILY_RECORD = 1522 269 | RET_TOWER_NOT_RECORD = 1523 270 | RET_TOWER_HAVE_RECORD = 1524 271 | RET_TOWER_TEAM_NUM_ERROR = 1525 272 | RET_TOWER_FLOOR_NOT_OPEN = 1526 273 | RET_TOWER_NO_FLOOR_STAR_RECORD = 1527 274 | RET_ALREADY_HAS_TOWER_BUFF = 1528 275 | RET_DUPLICATE_ENTER_LEVEL = 1529 276 | RET_NOT_IN_TOWER_LEVEL = 1530 277 | RET_IN_TOWER_LEVEL = 1531 278 | RET_TOWER_PREV_FLOOR_NOT_FINISH = 1532 279 | RET_TOWER_STAR_NOT_ENOUGH = 1533 280 | RET_UID_NOT_EXIST = 2001 281 | RET_PARSE_BIN_ERROR = 2002 282 | RET_ACCOUNT_INFO_NOT_EXIST = 2003 283 | RET_ORDER_INFO_NOT_EXIST = 2004 284 | RET_SNAPSHOT_INDEX_ERROR = 2005 285 | RET_MAIL_HAS_BEEN_SENT = 2006 286 | RET_PRODUCT_NOT_EXIST = 2007 287 | RET_UNFINISH_ORDER = 2008 288 | RET_ID_NOT_EXIST = 2009 289 | RET_REDIS_MODIFIED = 5001 290 | RET_PATHFINDING_DATA_NOT_EXIST = 6001 291 | RET_PATHFINDING_DESTINATION_NOT_EXIST = 6002 292 | RET_PATHFINDING_ERROR_SCENE = 6003 293 | RET_FRIEND_COUNT_EXCEEDED = 7001 294 | -------------------------------------------------------------------------------- /game_server/resource/excel.py: -------------------------------------------------------------------------------- 1 | import dataclasses 2 | import sys, os, csv 3 | from game_server.resource.enums import * 4 | 5 | @dataclasses.dataclass() 6 | class AvatarData: 7 | id: int 8 | skill_depot_id: int 9 | initial_weapon_id: int 10 | weapon_type: WeaponType 11 | use_type: AvatarUseType 12 | 13 | @dataclasses.dataclass() 14 | class AvatarSkillDepotData: 15 | id: int 16 | leader_talent: int 17 | talent_groups: list[int] 18 | 19 | @dataclasses.dataclass() 20 | class AvatarSkillData: 21 | id: int 22 | energy_type: ElementType 23 | energy_cost: int 24 | max_charges: int = 1 25 | 26 | @dataclasses.dataclass() 27 | class ItemData: 28 | id: int 29 | type: ItemType 30 | weight: int 31 | rank: int 32 | gadget_id: int 33 | dropable: bool 34 | #use_level: int 35 | #global_item_limit: int 36 | 37 | @dataclasses.dataclass() 38 | class WeaponProperty: 39 | prop_type: FightProp 40 | init_value: float 41 | type: GrowCurveType 42 | 43 | @dataclasses.dataclass() 44 | class WeaponData(ItemData): 45 | weapon_type: WeaponType 46 | rank_level: int 47 | #weapon_material_type: WeaponMaterialType 48 | #weapon_element_type: ElementType 49 | #is_gold: bool 50 | #weapon_base_exp: int 51 | #skill_affix: list[int] 52 | #weapon_prop: list[WeaponProperty] 53 | #weapon_promote_id: int 54 | #awaken_costs: list[int] 55 | 56 | @dataclasses.dataclass() 57 | class MaterialData(ItemData): 58 | ... 59 | 60 | @dataclasses.dataclass() 61 | class ReliquaryData(ItemData): 62 | ... 63 | 64 | @dataclasses.dataclass() 65 | class TalentSkillData: 66 | id: int = 0 #天赋ID 67 | talent_group_id: int = 0 #天赋组ID 68 | rank: int = 0 #等级 69 | 70 | @dataclasses.dataclass() 71 | class DungeonData: 72 | id: int = 0 #ID 73 | scene_id: int = 0 #场景ID 74 | 75 | @dataclasses.dataclass() 76 | class GachaData: #GachaNewbieData (actually i wanna use it for another gachas) 77 | cost_item_id: int #单抽消耗物品ID 78 | cost_item_num: int #单抽消耗物品数量 79 | ten_cost_item_id: int #十连消耗物品ID 80 | ten_cost_item_num: int #十连消耗物品数量 81 | ten_cost_item_id_first: int #首次十连消耗物品 82 | ten_cost_item_num_first: int #首次十连消耗数量 83 | gacha_times_limit: int #扭蛋次数上限 84 | pool_id: int #蛋池ID 85 | prob_rule_id: int #概率规则ID 86 | unknown_field_1: object #[UP配置]1父类型 87 | unknown_field_2: object #[UP配置]1概率 88 | unknown_field_3: object #[UP配置]1物品列表 89 | guarantee_rule_list: list[int] #保底规则列表 90 | gacha_prefab_path: str #扭蛋Prefab路径 91 | gacha_preview_prefab_path: str #扭蛋预览Prefab路径 92 | gacha_prob_url: str #扭蛋概率公示URL 93 | gacha_record_url: str #扭蛋记录URL 94 | sort_id: int #排序id 95 | 96 | @dataclasses.dataclass() 97 | class GachaProbData: 98 | id: int #扭蛋概率规则ID 99 | prop_parent_type: GachaItemParentType #道具父类型 100 | item_type: GachaItemType #道具类型 (seems it GachaItemType, at least values looks like GachaItemType) 101 | round_table_priority: int #圆桌优先级 102 | base_prob: int #基础概率 103 | is_guaranteed: bool #是否保底 104 | init_guarantee_times: int #起始保底次数 105 | guaranteed_single_increase_probability: int #保底单次递增概率 106 | 107 | 108 | @dataclasses.dataclass() 109 | class GachaPoolData: 110 | id: int #Gacha根ID 111 | item_id: int #道具ID 112 | item_type: GachaItemType #类型 113 | weight: int #概率权重 114 | flash_card_prob: int #闪卡概率 115 | 116 | @dataclasses.dataclass() 117 | class GachaRuleData: 118 | id: int #规则ID 119 | priority: int #优先级 120 | gacha_guarantee_type: GachaGuaranteeType #保底类型 121 | gacha_guarantee_times: int #保底触发次数 122 | gacha_guarantee_params: list[int] #保底参数1-4 123 | gacha_guarantee_reset_type: GachaGuaranteeResetType #保底重置类型 124 | gacha_guarantee_reset_param: str #保底重置参数 125 | 126 | 127 | 128 | 129 | @dataclasses.dataclass() 130 | class ExcelOutput: 131 | avatar_datas: dict[int, AvatarData] = dataclasses.field(default_factory=dict) 132 | avatar_skill_depot_datas: dict[int, AvatarSkillDepotData] = dataclasses.field(default_factory=dict) 133 | avatar_skill_datas: dict[int, AvatarSkillData] = dataclasses.field(default_factory=dict) 134 | item_datas: dict[int, ItemData] = dataclasses.field(default_factory=dict) 135 | #shop_plan_datas: dict[int, ShopPlanData] = dataclasses.field(default_factory=dict) 136 | #shop_goods_datas: dict[int, ShopGoodsData] = dataclasses.field(default_factory=dict) 137 | talent_skill_datas: dict[int, TalentSkillData] = dataclasses.field(default_factory=dict) 138 | #gadget_datas: dict[int, GadgetData] = dataclasses.field(default_factory=dict) 139 | #monster_datas: dict[int, MonsterData] = dataclasses.field(default_factory=dict) 140 | #npc_datas: dict[int, NpcData] = dataclasses.field(default_factory=dict) 141 | 142 | gacha_datas:dict[int, GachaData] =dataclasses.field(default_factory=dict) 143 | gacha_prob_datas:dict[(int,GachaItemType), GachaProbData] = dataclasses.field(default_factory=dict) 144 | gacha_pool_datas:dict[(int,int), GachaPoolData] = dataclasses.field(default_factory=dict) #(gachaId, itemId) 145 | gacha_rule_datas:dict[int, GachaRuleData] = dataclasses.field(default_factory=dict) 146 | 147 | dungeon_data: dict[int, DungeonData] = dataclasses.field(default_factory=dict) 148 | 149 | @classmethod 150 | def load_all_excels(cls, path: str): 151 | cls_inst = cls() 152 | 153 | with open(os.path.join(path, "txt", "AvatarData.txt"), encoding="utf-8") as f: 154 | reader = csv.DictReader(f, delimiter="\t") 155 | for row in reader: 156 | avatar = AvatarData( 157 | int(row["ID"]), 158 | int(row["技能库ID"]), 159 | int(row["初始武器"]), 160 | WeaponType(int(row["武器种类"])), 161 | AvatarUseType(int(row["是否使用"])) 162 | ) 163 | 164 | cls_inst.avatar_datas[avatar.id] = avatar 165 | 166 | with open(os.path.join(path, "txt", "AvatarSkillDepotData.txt"), encoding="utf-8") as f: 167 | reader = csv.DictReader(f, delimiter="\t") 168 | for row in reader: 169 | leader_talent = row["队长天赋"] 170 | talent_groups = [] 171 | 172 | for i in range(1, 6+1): 173 | if talent := row[f"天赋{i}"]: 174 | talent_groups.append(talent) 175 | 176 | depot = AvatarSkillDepotData( 177 | int(row["ID"]), 178 | int(leader_talent) if leader_talent else 0, 179 | talent_groups 180 | ) 181 | 182 | cls_inst.avatar_skill_depot_datas[depot.id] = depot 183 | 184 | with open(os.path.join(path, "txt", "AvatarSkillData.txt"), encoding="utf-8") as f: 185 | reader = csv.DictReader(f, delimiter="\t") 186 | for row in reader: 187 | max_charges = row["可累积次数"] 188 | element_type = row["消耗能量类型"] 189 | energy_cost = row["消耗能量值"] 190 | avatar_skill = AvatarSkillData( 191 | int(row["ID"]), 192 | ElementType(int(element_type)) if element_type else None, 193 | int(energy_cost) if energy_cost else None, 194 | int(max_charges) if max_charges else 1, 195 | ) 196 | 197 | cls_inst.avatar_skill_datas[avatar_skill.id] = avatar_skill 198 | 199 | with open(os.path.join(path, "txt", "WeaponData.txt"), encoding="utf-8") as f: 200 | reader = csv.DictReader(f, delimiter="\t") 201 | for row in reader: 202 | weapon = WeaponData( 203 | id=int(row["ID"]), 204 | type=ItemType(int(row["类型"])), 205 | weight=int(row["重量"]), 206 | rank=int(row["排序权重"]), 207 | gadget_id=int(row["物件ID"]), 208 | dropable=bool(int(row["可丢弃"])), 209 | weapon_type=WeaponType(int(row["武器种类"])), 210 | rank_level=int(row["武器阶数"]) 211 | ) 212 | cls_inst.item_datas[weapon.id] = weapon 213 | 214 | with open(os.path.join(path, "txt", "WeaponData.txt"), encoding="utf-8") as f: 215 | reader = csv.DictReader(f, delimiter="\t") 216 | for row in reader: 217 | talent = None 218 | #cls_inst.talent_skill_datas[talent.id] = talent 219 | 220 | with open(os.path.join(path, "txt", "DungeonData.txt"), encoding="utf-8") as f: 221 | reader = csv.DictReader(f, delimiter="\t") 222 | for row in reader: 223 | dungeon = DungeonData( 224 | int(row["ID"]), 225 | int(row["场景ID"]) 226 | ) 227 | cls_inst.dungeon_data[dungeon.id] = dungeon 228 | 229 | with open(os.path.join(path, "txt", "GachaNewbieData.txt"), encoding="utf-8") as f: 230 | reader = csv.DictReader(f, delimiter="\t") 231 | for row in reader: 232 | gacha = GachaData( 233 | cost_item_id=int(row["单抽消耗物品ID"]), 234 | cost_item_num=int(row["单抽消耗物品数量"]), 235 | ten_cost_item_id=int(row["十连消耗物品ID"]), 236 | ten_cost_item_num=int(row["十连消耗物品数量"]), 237 | ten_cost_item_id_first=int(row["首次十连消耗物品"]), 238 | ten_cost_item_num_first=int(row["首次十连消耗数量"]), 239 | gacha_times_limit=int(row["扭蛋次数上限"]), 240 | pool_id=int(row["蛋池ID"]), 241 | prob_rule_id=int(row["概率规则ID"]), 242 | unknown_field_1=row["[UP配置]1父类型"], 243 | unknown_field_2=row["[UP配置]1概率"], 244 | unknown_field_3=row["[UP配置]1物品列表"], 245 | guarantee_rule_list=[], 246 | gacha_prefab_path=str(row["扭蛋Prefab路径"]), 247 | gacha_preview_prefab_path=str(row["扭蛋预览Prefab路径"]), 248 | gacha_prob_url=str(row["扭蛋概率公示URL"]), 249 | gacha_record_url=str(row["扭蛋记录URL"]), 250 | sort_id=int(row["排序id"]) 251 | ) 252 | for i in str(row["保底规则列表"]).split(','): 253 | gacha.guarantee_rule_list.append(int(i)) 254 | cls_inst.gacha_datas[gacha.sort_id] = gacha 255 | 256 | with open(os.path.join(path, "txt", "GachaProbData.txt"), encoding="utf-8") as f: 257 | reader = csv.DictReader(f, delimiter="\t") 258 | for row in reader: 259 | gacha_prob = GachaProbData( 260 | id=int(row["扭蛋概率规则ID"]), 261 | prop_parent_type=GachaItemParentType(int(row["道具父类型"])), 262 | item_type=GachaItemType(int(row["道具类型"])), 263 | round_table_priority=int(row["圆桌优先级"]), 264 | base_prob=int('0'+row["基础概率"]), 265 | is_guaranteed=bool('0'+row["是否保底"]), 266 | init_guarantee_times=int('0'+row["起始保底次数"]), 267 | guaranteed_single_increase_probability=int('0'+row["保底单次递增概率"]) 268 | ) 269 | cls_inst.gacha_prob_datas[(gacha_prob.id,gacha_prob.item_type)]=gacha_prob 270 | 271 | with open(os.path.join(path, "txt", "GachaPoolData.txt"), encoding="utf-8") as f: 272 | reader = csv.DictReader(f, delimiter="\t") 273 | for row in reader: 274 | pool = GachaPoolData( 275 | id=int(row["Gacha根ID"]), 276 | item_id=int(row["道具ID"]), 277 | item_type=GachaItemType(int(row["类型"])), 278 | weight=int('0'+row["概率权重"]), 279 | flash_card_prob=int(row["闪卡概率"]) 280 | ) 281 | cls_inst.gacha_pool_datas[(pool.id,pool.item_id)]=pool 282 | return cls_inst 283 | -------------------------------------------------------------------------------- /game_server/resource/enums.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | class LifeState(Enum): 4 | LIFE_NONE = 0 5 | LIFE_ALIVE = 1 6 | LIFE_DEAD = 2 7 | LIFE_REVIVE = 3 8 | 9 | class EquipType(Enum): 10 | EQUIP_NONE = 0 11 | EQUIP_BRACER = 1 12 | EQUIP_DRESS = 2 13 | EQUIP_SHOES = 3 14 | EQUIP_RING = 4 15 | EQUIP_NECKLACE = 5 16 | EQUIP_WEAPON = 6 17 | 18 | class PropType(Enum): 19 | PROP_NONE = 0 20 | PROP_EXP = 1001 21 | PROP_BREAK_LEVEL = 1002 22 | PROP_SMALL_TALENT_POINT = 1004 23 | PROP_BIG_TALENT_POINT = 1005 24 | PROP_GEAR_START_VAL = 2001 25 | PROP_GEAR_STOP_VAL = 2002 26 | PROP_LEVEL = 4001 27 | PROP_LAST_CHANGE_AVATAR_TIME = 10001 28 | PROP_MAX_SPRING_VOLUME = 10002 29 | PROP_CUR_SPRING_VOLUME = 10003 30 | PROP_IS_SPRING_AUTO_USE = 10004 31 | PROP_SPRING_AUTO_USE_PERCENT = 10005 32 | PROP_IS_FLYABLE = 10006 33 | PROP_IS_WEATHER_LOCKED = 10007 34 | PROP_IS_GAME_TIME_LOCKED = 10008 35 | PROP_IS_TRANSFERABLE = 10009 36 | PROP_MAX_STAMINA = 10010 37 | PROP_CUR_PERSIST_STAMINA = 10011 38 | PROP_CUR_TEMPORARY_STAMINA = 10012 39 | PROP_PLAYER_LEVEL = 10013 40 | PROP_PLAYER_EXP = 10014 41 | PROP_PLAYER_HCOIN = 10015 42 | PROP_PLAYER_SCOIN = 10016 43 | PROP_IS_WORLD_ENTERABLE = 10017 44 | PROP_IS_MP_MODE_AVAILABLE = 10018 45 | 46 | class FightProp(Enum): 47 | FIGHT_PROP_NONE = 0 48 | FIGHT_PROP_BASE_HP = 1 49 | FIGHT_PROP_HP = 2 50 | FIGHT_PROP_HP_PERCENT = 3 51 | FIGHT_PROP_BASE_ATTACK = 4 52 | FIGHT_PROP_ATTACK = 5 53 | FIGHT_PROP_ATTACK_PERCENT = 6 54 | FIGHT_PROP_BASE_DEFENSE = 7 55 | FIGHT_PROP_DEFENSE = 8 56 | FIGHT_PROP_DEFENSE_PERCENT = 9 57 | FIGHT_PROP_BASE_SPEED = 10 58 | FIGHT_PROP_SPEED_PERCENT = 11 59 | FIGHT_PROP_HP_MP_PERCENT = 12 60 | FIGHT_PROP_ATTACK_MP_PERCENT = 13 61 | FIGHT_PROP_CRITICAL = 20 62 | FIGHT_PROP_ANTI_CRITICAL = 21 63 | FIGHT_PROP_CRITICAL_HURT = 22 64 | FIGHT_PROP_CHARGE_EFFICIENCY = 23 65 | FIGHT_PROP_ADD_HURT = 24 66 | FIGHT_PROP_SUB_HURT = 25 67 | FIGHT_PROP_HEAL_ADD = 26 68 | FIGHT_PROP_HEALED_ADD = 27 69 | FIGHT_PROP_ELEMENT_MASTERY = 28 70 | FIGHT_PROP_PHYSICAL_SUB_HURT = 29 71 | FIGHT_PROP_PHYSICAL_ADD_HURT = 30 72 | FIGHT_PROP_DEFENCE_IGNORE_RATIO = 31 73 | FIGHT_PROP_DEFENCE_IGNORE_DELTA = 32 74 | FIGHT_PROP_FIRE_ADD_HURT = 40 75 | FIGHT_PROP_ELEC_ADD_HURT = 41 76 | FIGHT_PROP_WATER_ADD_HURT = 42 77 | FIGHT_PROP_GRASS_ADD_HURT = 43 78 | FIGHT_PROP_WIND_ADD_HURT = 44 79 | FIGHT_PROP_ROCK_ADD_HURT = 45 80 | FIGHT_PROP_ICE_ADD_HURT = 46 81 | FIGHT_PROP_HIT_HEAD_ADD_HURT = 47 82 | FIGHT_PROP_FIRE_SUB_HURT = 50 83 | FIGHT_PROP_ELEC_SUB_HURT = 51 84 | FIGHT_PROP_WATER_SUB_HURT = 52 85 | FIGHT_PROP_GRASS_SUB_HURT = 53 86 | FIGHT_PROP_WIND_SUB_HURT = 54 87 | FIGHT_PROP_ROCK_SUB_HURT = 55 88 | FIGHT_PROP_ICE_SUB_HURT = 56 89 | FIGHT_PROP_EFFECT_HIT = 60 90 | FIGHT_PROP_EFFECT_RESIST = 61 91 | FIGHT_PROP_FREEZE_RESIST = 62 92 | FIGHT_PROP_TORPOR_RESIST = 63 93 | FIGHT_PROP_DIZZY_RESIST = 64 94 | FIGHT_PROP_FREEZE_SHORTEN = 65 95 | FIGHT_PROP_TORPOR_SHORTEN = 66 96 | FIGHT_PROP_DIZZY_SHORTEN = 67 97 | FIGHT_PROP_MAX_FIRE_ENERGY = 70 98 | FIGHT_PROP_MAX_ELEC_ENERGY = 71 99 | FIGHT_PROP_MAX_WATER_ENERGY = 72 100 | FIGHT_PROP_MAX_GRASS_ENERGY = 73 101 | FIGHT_PROP_MAX_WIND_ENERGY = 74 102 | FIGHT_PROP_MAX_ICE_ENERGY = 75 103 | FIGHT_PROP_MAX_ROCK_ENERGY = 76 104 | FIGHT_PROP_SKILL_CD_MINUS_RATIO = 80 105 | FIGHT_PROP_SHIELD_COST_MINUS_RATIO = 81 106 | FIGHT_PROP_CUR_FIRE_ENERGY = 1000 107 | FIGHT_PROP_CUR_ELEC_ENERGY = 1001 108 | FIGHT_PROP_CUR_WATER_ENERGY = 1002 109 | FIGHT_PROP_CUR_GRASS_ENERGY = 1003 110 | FIGHT_PROP_CUR_WIND_ENERGY = 1004 111 | FIGHT_PROP_CUR_ICE_ENERGY = 1005 112 | FIGHT_PROP_CUR_ROCK_ENERGY = 1006 113 | FIGHT_PROP_CUR_HP = 1010 114 | FIGHT_PROP_MAX_HP = 2000 115 | FIGHT_PROP_CUR_ATTACK = 2001 116 | FIGHT_PROP_CUR_DEFENSE = 2002 117 | FIGHT_PROP_CUR_SPEED = 2003 118 | 119 | class GrowCurveType(Enum): 120 | GROW_CURVE_NONE = 0 121 | GROW_CURVE_HP = 1 122 | GROW_CURVE_ATTACK = 2 123 | GROW_CURVE_STAMINA = 3 124 | GROW_CURVE_STRIKE = 4 125 | GROW_CURVE_ANTI_STRIKE = 5 126 | GROW_CURVE_ANTI_STRIKE1 = 6 127 | GROW_CURVE_ANTI_STRIKE2 = 7 128 | GROW_CURVE_ANTI_STRIKE3 = 8 129 | GROW_CURVE_STRIKE_HURT = 9 130 | GROW_CURVE_ELEMENT = 10 131 | GROW_CURVE_KILL_EXP = 11 132 | GROW_CURVE_DEFENSE = 12 133 | GROW_CURVE_ATTACK_BOMB = 13 134 | GROW_CURVE_HP_LITTLEMONSTER = 14 135 | GROW_CURVE_ELEMENT_MASTERY = 15 136 | GROW_CURVE_PROGRESSION = 16 137 | GROW_CURVE_DEFENDING = 17 138 | GROW_CURVE_MHP = 18 139 | GROW_CURVE_MATK = 19 140 | GROW_CURVE_TOWERATK = 20 141 | GROW_CURVE_HP_S5 = 21 142 | GROW_CURVE_HP_S4 = 22 143 | GROW_CURVE_HP_2 = 23 144 | GROW_CURVE_ATTACK_2 = 24 145 | GROW_CURVE_ATTACK_S5 = 31 146 | GROW_CURVE_ATTACK_S4 = 32 147 | GROW_CURVE_ATTACK_S3 = 33 148 | GROW_CURVE_STRIKE_S5 = 34 149 | GROW_CURVE_DEFENSE_S5 = 41 150 | GROW_CURVE_DEFENSE_S4 = 42 151 | GROW_CURVE_ATTACK_101 = 1101 152 | GROW_CURVE_ATTACK_102 = 1102 153 | GROW_CURVE_ATTACK_103 = 1103 154 | GROW_CURVE_ATTACK_104 = 1104 155 | GROW_CURVE_ATTACK_105 = 1105 156 | GROW_CURVE_ATTACK_201 = 1201 157 | GROW_CURVE_ATTACK_202 = 1202 158 | GROW_CURVE_ATTACK_203 = 1203 159 | GROW_CURVE_ATTACK_204 = 1204 160 | GROW_CURVE_ATTACK_205 = 1205 161 | GROW_CURVE_ATTACK_301 = 1301 162 | GROW_CURVE_ATTACK_302 = 1302 163 | GROW_CURVE_ATTACK_303 = 1303 164 | GROW_CURVE_ATTACK_304 = 1304 165 | GROW_CURVE_ATTACK_305 = 1305 166 | GROW_CURVE_CRITICAL_101 = 2101 167 | GROW_CURVE_CRITICAL_102 = 2102 168 | GROW_CURVE_CRITICAL_103 = 2103 169 | GROW_CURVE_CRITICAL_104 = 2104 170 | GROW_CURVE_CRITICAL_105 = 2105 171 | GROW_CURVE_CRITICAL_201 = 2201 172 | GROW_CURVE_CRITICAL_202 = 2202 173 | GROW_CURVE_CRITICAL_203 = 2203 174 | GROW_CURVE_CRITICAL_204 = 2204 175 | GROW_CURVE_CRITICAL_205 = 2205 176 | GROW_CURVE_CRITICAL_301 = 2301 177 | GROW_CURVE_CRITICAL_302 = 2302 178 | GROW_CURVE_CRITICAL_303 = 2303 179 | GROW_CURVE_CRITICAL_304 = 2304 180 | GROW_CURVE_CRITICAL_305 = 2305 181 | 182 | class RuntimeIDBits(Enum): 183 | RUNTIME_ID_SEQUENCE_BITS = 0x17 184 | RUNTIME_ID_SEQUENCE_SHIFT = 0x0 185 | RUNTIME_ID_SEQUENCE_MASK = 0x7fffff 186 | RUNTIME_ID_IS_SYNCED_BITS = 0x1 187 | RUNTIME_ID_IS_SYNCED_SHIFT = 0x17 188 | RUNTIME_ID_IS_SYNCED_MASK = 0x800000 189 | RUNTIME_ID_CATEGORY_BITS = 0x5 190 | RUNTIME_ID_CATEGORY_SHIFT = 0x18 191 | RUNTIME_ID_CATEGORY_MASK = 0x1f000000 192 | RUNTIME_ID_PEER_BITS = 0x3 193 | RUNTIME_ID_PEER_MASK = 0xe0000000 194 | RUNTIME_ID_PEER_SHIFT = 0x1d 195 | 196 | class RuntimeIDCategory(Enum): 197 | INVALID_RUNTIMEID = 0 198 | AVATAR_CATE = 1 199 | MONSTER_CATE = 2 200 | NPC_CATE = 3 201 | GADGET_CATE = 4 202 | MAX_SERVER_CATE = 15 203 | EFFECT_CATE = 16 204 | ATTACKUNIT_CATE = 17 205 | CAMERA_CATE = 18 206 | MANAGER_CATE = 19 207 | LOCALGADGET_CATE = 20 208 | LOCALMASSIVE_CATE = 21 209 | 210 | class AvatarUseType(Enum): 211 | AVATAR_TEST = 0 212 | AVATAR_SYNC_TEST = 1 213 | AVATAR_FORMAL = 2 214 | AVATAR_ABANDON = 3 215 | 216 | class WeaponType(Enum): 217 | WEAPON_NONE = 0 218 | WEAPON_SWORD_ONE_HAND = 1 219 | WEAPON_CROSSBOW = 2 220 | WEAPON_STAFF = 3 221 | WEAPON_DOUBLE_DAGGER = 4 222 | WEAPON_KATANA = 5 223 | WEAPON_SHURIKEN = 6 224 | WEAPON_STICK = 7 225 | WEAPON_SPEAR = 8 226 | WEAPON_SHIELD_SMALL = 9 227 | WEAPON_CATALYST = 10 228 | WEAPON_CLAYMORE = 11 229 | WEAPON_BOW = 12 230 | WEAPON_POLE = 13 231 | 232 | class WeaponMaterialType(Enum): 233 | WEAPON_MATERIAL_NONE = 0 234 | 235 | class ItemType(Enum): 236 | ITEM_NONE = 0 237 | ITEM_VIRTUAL = 1 238 | ITEM_MATERIAL = 2 239 | ITEM_RELIQUARY = 3 240 | ITEM_WEAPON = 4 241 | 242 | class ShopType(Enum): 243 | SHOP_TYPE_NONE = 0 244 | SHOP_TYPE_PAIMON = 1 245 | SHOP_TYPE_CITY = 2 246 | SHOP_TYPE_BLACKSMITH = 3 247 | SHOP_TYPE_GROCERY = 4 248 | SHOP_TYPE_FOOD = 5 249 | 250 | class ElementType(Enum): 251 | ELEMENT_None = 0 252 | ELEMENT_Fire = 1 253 | ELEMENT_Water = 2 254 | ELEMENT_Grass = 3 255 | ELEMENT_Electric = 4 256 | ELEMENT_Ice = 5 257 | ELEMENT_Frozen = 6 258 | ELEMENT_Wind = 7 259 | ELEMENT_Rock = 8 260 | ELEMENT_AntiFire = 9 261 | ELEMENT_VehicleMuteIce = 10 262 | 263 | class SceneType(Enum): 264 | SCENE_NONE = 0 265 | SCENE_WORLD = 1 266 | SCENE_DUNGEON = 2 267 | SCENE_ROOM = 3 268 | 269 | class GadgetState(Enum): 270 | GADGET_STATE_Default = 0 271 | GADGET_STATE_GatherDrop = 1 272 | GADGET_STATE_ChestLocked = 101 273 | GADGET_STATE_ChestOpened = 102 274 | GADGET_STATE_ChestTrap = 103 275 | GADGET_STATE_ChestBramble = 104 276 | GADGET_STATE_ChestFrozen = 105 277 | GADGET_STATE_GearStart = 201 278 | GADGET_STATE_GearStop = 202 279 | GADGET_STATE_GearAction1 = 203 280 | GADGET_STATE_GearAction2 = 204 281 | GADGET_STATE_CrystalResonate1 = 301 282 | GADGET_STATE_CrystalResonate2 = 302 283 | GADGET_STATE_CrystalExplode = 303 284 | GADGET_STATE_CrystalDrain = 304 285 | GADGET_STATE_Action01 = 901 286 | GADGET_STATE_Action02 = 902 287 | GADGET_STATE_Action03 = 903 288 | 289 | class GadgetType(Enum): 290 | GADGET_NONE = 0 291 | GADGET_WORLD_CHECT = 1 292 | GADGET_DUNGEON_SECRET_CHEST = 2 293 | GADGET_DUNGEON_PASS_CHEST = 3 294 | 295 | class EntityType(Enum): 296 | ENTITY_None = 0 297 | ENTITY_Avatar = 1 298 | ENTITY_Monster = 2 299 | ENTITY_Bullet = 3 300 | ENTITY_AttackPhyisicalUnit = 4 301 | ENTITY_AOE = 5 302 | ENTITY_Camera = 6 303 | ENTITY_EnviroArea = 7 304 | ENTITY_Equip = 8 305 | ENTITY_MonsterEquip = 9 306 | ENTITY_Grass = 10 307 | ENTITY_Level = 11 308 | ENTITY_NPC = 12 309 | ENTITY_TransPointFirst = 13 310 | ENTITY_TransPointFirstGadget = 14 311 | ENTITY_TransPointSecond = 15 312 | ENTITY_TransPointSecondGadget = 16 313 | ENTITY_DropItem = 17 314 | ENTITY_Field = 18 315 | ENTITY_Gadget = 19 316 | ENTITY_Water = 20 317 | ENTITY_GatherPoint = 21 318 | ENTITY_GatherObject = 22 319 | ENTITY_AirflowField = 23 320 | ENTITY_SpeedupField = 24 321 | ENTITY_Gear = 25 322 | ENTITY_Chest = 26 323 | ENTITY_EnergyBall = 27 324 | ENTITY_ElemCrystal = 28 325 | ENTITY_Timeline = 29 326 | ENTITY_Worktop = 30 327 | ENTITY_Team = 31 328 | ENTITY_Platform = 32 329 | ENTITY_AmberWind = 33 330 | ENTITY_EnvAnimal = 34 331 | ENTITY_SealGadget = 35 332 | ENTITY_Tree = 36 333 | ENTITY_Bush = 37 334 | ENTITY_PlaceHolder = 99 335 | 336 | class EventType(Enum): 337 | EVENT_NONE = 0 338 | EVENT_ANY_MONSTER_DIE = 1 339 | EVENT_ANY_GADGET_DIE = 2 340 | EVENT_VARIABLE_CHANGE = 3 341 | EVENT_ENTER_REGION = 4 342 | EVENT_LEAVE_REGION = 5 343 | EVENT_GADGET_CREATE = 6 344 | EVENT_GADGET_STATE_CHANGE = 7 345 | EVENT_DUNGEON_SETTLE = 8 346 | EVENT_SELECT_OPTION = 9 347 | EVENT_CLIENT_EXECUTE = 10 348 | EVENT_ANY_MONSTER_LIVE = 11 349 | EVENT_SPECIFIC_MONSTER_HP_CHANGE = 12 350 | EVENT_AREA_LEVELUP_UNLOCK_DUNGEON_ENTRY = 13 351 | EVENT_DUNGEON_BROADCAST_ONTIMER = 14 352 | EVENT_TIMER_EVENT = 15 353 | EVENT_CHALLENGE_SUCCESS = 16 354 | EVENT_CHALLENGE_FAIL = 17 355 | EVENT_SEAL_BATTLE_BEGIN = 18 356 | EVENT_SEAL_BATTLE_END = 19 357 | EVENT_GATHER = 20 358 | EVENT_QUEST_FINISH = 21 359 | EVENT_MONSTER_BATTLE = 22 360 | EVENT_AREA_LEVELUP = 23 361 | EVENT_CUTSCENE_END = 24 362 | EVENT_AVATAR_NEAR_PLATFORM = 25 363 | 364 | class GachaItemType(Enum): 365 | GACHA_ITEM_INVALID = 0 366 | GACHA_ITEM_AVATAR_S5 = 11 367 | GACHA_ITEM_AVATAR_S4 = 12 368 | GACHA_ITEM_AVATAR_S3 = 13 369 | GACHA_ITEM_WEAPON_S5 = 21 370 | GACHA_ITEM_WEAPON_S4 = 22 371 | GACHA_ITEM_WEAPON_S3 = 23 372 | GACHA_ITEM_COMMON_MATERIAL = 31 373 | 374 | class GachaGuaranteeType(Enum): 375 | GACHA_GUARANTEE_NONE = 0 376 | GACHA_GUARANTEE_SPECIFIED_COUNT = 1 377 | GACHA_GUARANTEE_LOOP_COUNT = 2 378 | GACHA_GUARANTEE_N_COUNT = 3 379 | GACHA_GUARANTEE_LOOP_COUNT_WITH_CHILDS = 4 380 | GACHA_GUARANTEE_N_COUNT_WITH_CHILDS = 5 381 | 382 | class GachaGuaranteeResetType(Enum): 383 | GACHA_GUARANTEE_RESET_NONE = 0 384 | GACHA_GUARANTEE_RESET_ACTIVITY_CHANGE = 1 385 | 386 | class GachaItemParentType(Enum): 387 | GACHA_ITEM_PARENT_INVALID = 0 388 | GACHA_ITEM_PARENT_S5 = 1 389 | GACHA_ITEM_PARENT_S4 = 2 390 | GACHA_ITEM_PARENT_S3 = 3 -------------------------------------------------------------------------------- /game_server/handlers/scene.py: -------------------------------------------------------------------------------- 1 | from game_server.protocol.cmd_id import CmdID 2 | from game_server import HandlerRouter,Connection 3 | from lib.proto import TeamEnterSceneInfo, EnterSceneDoneReq, EnterWorldAreaReq,SceneGetAreaExplorePercentReq,SceneGetAreaExplorePercentRsp, EnterWorldAreaRsp, ScenePlayerLocationNotify, PlayerLocationInfo, VisionType, SceneEntityAppearNotify, SceneTeamAvatar, MpDisplayCurAvatar,AvatarEnterSceneInfo, SceneTeamUpdateNotify, ProtEntityType, AbilitySyncStateInfo, MpLevelEntityInfo,EnterSceneReadyReq,EnterScenePeerNotify,SceneInitFinishReq,MpSettingType,WorldDataNotify,PropValue,HostPlayerNotify,PlayerGameTimeNotify,SceneTimeNotify,SceneDataNotify,WorldPlayerInfoNotify,OnlinePlayerInfo,ScenePlayerInfoNotify,ScenePlayerInfo,PlayerEnterSceneInfoNotify,SceneInitFinishRsp,GetScenePointReq, GetSceneAreaReq,GetScenePointRsp,GetSceneAreaRsp,SceneForceUnlockNotify,SceneInitFinishRsp,EnterSceneDoneRsp,EnterType,PostEnterSceneReq,PostEnterSceneRsp, SceneTransToPointReq, SceneTransToPointRsp, DungeonEntryInfoReq, DungeonEntryInfoRsp, DungeonEntryInfo, PersonalSceneJumpReq, PersonalSceneJumpRsp, PlayerEnterDungeonReq, PlayerEnterDungeonRsp, PlayerEnterSceneNotify 4 | from lib.retcode import Retcode 5 | from game_server.resource import resources 6 | from game_server.utils.time import current_milli_time 7 | from game_server.protocol.reader import BinaryReader 8 | from game_server.resource.enums import PropType 9 | import enet 10 | 11 | router = HandlerRouter() 12 | 13 | @router(CmdID.EnterSceneReadyReq) 14 | def handle_scene_ready(conn: Connection, msg: EnterSceneReadyReq): 15 | enter_scene_peer_notify = EnterScenePeerNotify() 16 | enter_scene_peer_notify.peer_id = 1 17 | enter_scene_peer_notify.host_peer_id = 1 18 | enter_scene_peer_notify.dest_scene_id = conn.player.scene_id 19 | conn.send(enter_scene_peer_notify) 20 | 21 | @router(CmdID.SceneInitFinishReq) 22 | def handle_scene_init(conn: Connection, msg: SceneInitFinishReq): 23 | 24 | online_player_info = OnlinePlayerInfo( 25 | uid=conn.player.uid, 26 | nickname=conn.player.name, 27 | player_level=conn.player.prop_map[PropType.PROP_PLAYER_LEVEL], 28 | avatar_id=conn.player.avatar_id, 29 | mp_setting_type=MpSettingType.MP_SETTING_NO_ENTER, 30 | cur_player_num_in_world=1, 31 | world_level=8 32 | ) 33 | 34 | world_data_notify = WorldDataNotify() 35 | world_data_notify.world_prop_map = { 36 | 1: PropValue(1, ival=8), 37 | 2: PropValue(2, ival=0) 38 | } 39 | 40 | host_player_notify = HostPlayerNotify() 41 | host_player_notify.host_uid = conn.player.uid 42 | host_player_notify.host_peer_id = 1 43 | 44 | player_game_time_notify = PlayerGameTimeNotify() 45 | player_game_time_notify.game_time = 0 46 | player_game_time_notify.uid = conn.player.uid 47 | 48 | scene_time_notify = SceneTimeNotify() 49 | scene_time_notify.scene_id = conn.player.scene_id 50 | scene_time_notify.scene_time = 0 51 | scene_time_notify.is_paused = False 52 | 53 | scene_data_notify = SceneDataNotify() 54 | scene_data_notify.level_config_name_list = ["Level_BigWorld"] 55 | 56 | world_player_info_notify = WorldPlayerInfoNotify() 57 | world_player_info_notify.player_info_list = [online_player_info] 58 | world_player_info_notify.player_uid_list = [conn.player.uid] 59 | 60 | scene_player_info_notify = ScenePlayerInfoNotify() 61 | scene_player_info_notify.player_info_list = [ScenePlayerInfo( 62 | uid=conn.player.uid, 63 | peer_id=1, 64 | name=conn.player.name, 65 | is_connected=True, 66 | scene_id=conn.player.scene_id, 67 | online_player_info=online_player_info 68 | )] 69 | 70 | cur_avatar = conn.player.get_cur_avatar() 71 | scene_team_update_notify = SceneTeamUpdateNotify(scene_team_avatar_list=[], display_cur_avatar_list=[], is_in_mp=False) 72 | enter_scene_info_notify = PlayerEnterSceneInfoNotify() 73 | enter_scene_info_notify.cur_avatar_entity_id = cur_avatar.entity_id 74 | enter_scene_info_notify.team_enter_info = TeamEnterSceneInfo(team_entity_id=conn.player.world.get_next_entity_id(ProtEntityType.PROT_ENTITY_TEAM), team_ability_info=AbilitySyncStateInfo()) 75 | enter_scene_info_notify.mp_level_entity_info = MpLevelEntityInfo(entity_id=conn.player.world.get_next_entity_id(ProtEntityType.PROT_ENTITY_MP_LEVEL), authority_peer_id=1, ability_info=AbilitySyncStateInfo()) 76 | 77 | for avatar_guid in conn.player.teams[conn.player.cur_avatar_team_id].avatar_guid_list: 78 | avatar = conn.player.get_avatar_by_guid(avatar_guid) 79 | avatar.scene_weapon_info.guid = conn.player.get_next_guid() 80 | scene_team_avatar = SceneTeamAvatar() 81 | scene_team_avatar.scene_id = conn.player.scene_id 82 | scene_team_avatar.player_uid = conn.player.uid 83 | scene_team_avatar.avatar_guid = avatar.guid 84 | scene_team_avatar.entity_id = avatar.entity_id 85 | scene_team_avatar.avatar_info = avatar.avatar_info 86 | 87 | scene_team_update_notify.scene_team_avatar_list.append(scene_team_avatar) 88 | enter_scene_info_notify.avatar_enter_info.append(AvatarEnterSceneInfo( 89 | avatar_guid=avatar.guid, 90 | avatar_entity_id=avatar.entity_id, 91 | weapon_guid=avatar.scene_weapon_info.guid, 92 | weapon_entity_id=avatar.scene_weapon_info.entity_id 93 | )) 94 | 95 | conn.send(world_data_notify) 96 | conn.send(scene_data_notify) 97 | conn.send(host_player_notify) 98 | conn.send(player_game_time_notify) 99 | conn.send(scene_time_notify) 100 | conn.send(world_player_info_notify) 101 | conn.send(scene_player_info_notify) 102 | conn.send(scene_team_update_notify) 103 | conn.send(enter_scene_info_notify) 104 | 105 | @router(CmdID.GetScenePointReq) 106 | def handle_scene_point(conn: Connection, msg: GetScenePointReq): 107 | rsp = GetScenePointRsp() 108 | rsp.scene_id = msg.scene_id 109 | rsp.unlocked_point_list = [] 110 | rsp.unlock_area_list = [] 111 | for x in range(200): 112 | rsp.unlock_area_list.append(x) 113 | rsp.unlocked_point_list.append(x) 114 | conn.send(rsp) 115 | 116 | @router(CmdID.GetSceneAreaReq) 117 | def handle_scene_area(conn: Connection, msg: GetSceneAreaReq): 118 | rsp = GetSceneAreaRsp() 119 | rsp.scene_id = msg.scene_id 120 | rsp.area_id_list = [] 121 | for x in range(200): 122 | rsp.area_id_list.append(x) 123 | conn.send(rsp) 124 | 125 | @router(CmdID.EnterSceneDoneReq) 126 | def handle_scene_done(conn: Connection, msg: EnterSceneDoneReq): 127 | cur_avatar = conn.player.get_cur_avatar() 128 | conn.player.get_cur_avatar().motion = conn.player.pos 129 | scene_entity_appear_notify = SceneEntityAppearNotify() 130 | scene_entity_appear_notify.appear_type = VisionType.VISION_NONE 131 | scene_entity_appear_notify.entity_list = [cur_avatar.get_scene_entity_info(conn.player.uid)] 132 | 133 | scene_player_location_notify = ScenePlayerLocationNotify() 134 | scene_player_location_notify.scene_id = conn.player.scene_id 135 | scene_player_location_notify.player_loc_list = [ 136 | PlayerLocationInfo(uid=conn.player.uid, pos=cur_avatar.motion, rot=cur_avatar.rotation) 137 | ] 138 | 139 | conn.send(scene_entity_appear_notify) 140 | conn.send(scene_player_location_notify) 141 | conn.send(EnterSceneDoneRsp(retcode=0)) 142 | 143 | @router(CmdID.PostEnterSceneReq) 144 | def handle_enter_world(conn: Connection, msg: PostEnterSceneReq): 145 | conn.send(PostEnterSceneRsp(retcode=0)) 146 | 147 | 148 | @router(CmdID.EnterWorldAreaReq) 149 | def handle_enter_world(conn: Connection, msg: EnterWorldAreaReq): 150 | rsp = EnterWorldAreaRsp() 151 | rsp.area_id = msg.area_id 152 | rsp.area_type = msg.area_type 153 | conn.send(rsp) 154 | 155 | @router(CmdID.SceneGetAreaExplorePercentReq) 156 | def area_explore_percent_handle(conn: Connection, msg: SceneGetAreaExplorePercentReq): 157 | rsp = SceneGetAreaExplorePercentRsp() 158 | rsp.area_id = msg.area_id 159 | rsp.explore_percent = 100 160 | conn.send(rsp) 161 | 162 | #++ 163 | 164 | @router(CmdID.SceneTransToPointReq) 165 | def handle_SceneTransToPoint(conn: Connection, msg: SceneTransToPointReq): 166 | if hasattr(resources, 'binoutput'): 167 | point_data = resources.binoutput.config_scene[msg.scene_id].points 168 | 169 | scene_id = point_data[msg.point_id].tranSceneId 170 | pos = point_data[msg.point_id].tranPos 171 | 172 | conn.player.pos = pos 173 | 174 | conn.send(conn.player.get_teleport_packet(scene_id, pos, EnterType.ENTER_GOTO)) 175 | 176 | conn.player.scene_id = scene_id 177 | 178 | scene_trans_to_point_rsp = SceneTransToPointRsp() 179 | scene_trans_to_point_rsp.retcode = 0 180 | scene_trans_to_point_rsp.scene_id = msg.scene_id 181 | scene_trans_to_point_rsp.point_id = msg.point_id 182 | conn.send(scene_trans_to_point_rsp) 183 | 184 | @router(CmdID.DungeonEntryInfoReq) 185 | def handle_DungeonEntryInfo(conn: Connection, msg: DungeonEntryInfoReq): 186 | if hasattr(resources, 'binoutput'): 187 | point_data = resources.binoutput.config_scene[3].points 188 | 189 | dungeon_entry_info_rsp = DungeonEntryInfoRsp() 190 | dungeon_entry_info_rsp.point_id = msg.point_id 191 | 192 | dungeon_entry_list = [] 193 | 194 | rec = None 195 | 196 | for dungeonId in point_data[msg.point_id].dungeonIds: 197 | rec = dungeonId 198 | dungeon_info = DungeonEntryInfo() 199 | dungeon_info.dungeon_id = dungeonId 200 | dungeon_info.is_passed = True 201 | dungeon_info.left_times = current_milli_time() 202 | dungeon_info.start_time = current_milli_time() + 500 203 | dungeon_info.end_time = current_milli_time() + 1000 204 | dungeon_info.max_boss_chest_num = 2 205 | dungeon_info.boss_chest_num = 19000000 206 | dungeon_entry_list.append(dungeon_info) 207 | 208 | dungeon_entry_info_rsp.dungeon_entry_list = dungeon_entry_list 209 | dungeon_entry_info_rsp.recommend_dungeon_id = rec 210 | 211 | conn.send(dungeon_entry_info_rsp) 212 | 213 | @router(CmdID.PersonalSceneJumpReq) 214 | def handle_PersonalSceneJump(conn: Connection, msg: PersonalSceneJumpReq): 215 | if hasattr(resources, 'binoutput'): 216 | point_data = resources.binoutput.config_scene[conn.player.scene_id].points 217 | 218 | scene_id = point_data[msg.point_id].tranSceneId 219 | pos = point_data[msg.point_id].tranPos 220 | 221 | conn.player.pos = pos 222 | 223 | player_enter_scene_notify = PlayerEnterSceneNotify() 224 | player_enter_scene_notify.scene_id = scene_id 225 | player_enter_scene_notify.pos = pos 226 | player_enter_scene_notify.scene_begin_time = current_milli_time() 227 | player_enter_scene_notify.type = EnterType.ENTER_JUMP 228 | player_enter_scene_notify.enter_scene_token = 1000 229 | player_enter_scene_notify.world_level = 8 230 | player_enter_scene_notify.target_uid = conn.player.uid 231 | player_enter_scene_notify.prev_scene_id = conn.player.scene_id 232 | player_enter_scene_notify.prev_pos = conn.player.pos 233 | conn.send(player_enter_scene_notify) 234 | 235 | conn.player.scene_id = scene_id 236 | 237 | personal_scene_jump = PersonalSceneJumpRsp() 238 | personal_scene_jump.retcode = 0 239 | personal_scene_jump.dest_scene_id = scene_id 240 | personal_scene_jump.dest_pos = pos 241 | conn.send(personal_scene_jump) 242 | 243 | 244 | @router(CmdID.PlayerEnterDungeonReq) 245 | def handle_PlayerEnterDungeonReq(conn: Connection, msg: PlayerEnterDungeonReq): 246 | if hasattr(resources, 'binoutput'): 247 | dungeon_data = resources.excels.dungeon_data[msg.dungeon_id] 248 | point_data = resources.binoutput.config_scene[3].points 249 | 250 | scene_id = dungeon_data.scene_id 251 | pos = point_data[msg.point_id].tranPos 252 | 253 | # conn.player.pos = pos 254 | 255 | # conn.send(conn.player.get_teleport_packet(scene_id, pos, EnterType.ENTER_DUNGEON)) 256 | 257 | # conn.player.scene_id = scene_id 258 | 259 | player_enter_dungeon = PlayerEnterDungeonRsp() 260 | player_enter_dungeon.retcode = 0 261 | player_enter_dungeon.point_id = msg.point_id 262 | player_enter_dungeon.dungeon_id = msg.dungeon_id 263 | conn.send(player_enter_dungeon) -------------------------------------------------------------------------------- /game_server/protocol/cmd_id.py: -------------------------------------------------------------------------------- 1 | from enum import IntEnum 2 | 3 | class CmdID(IntEnum): 4 | AbilityInvocationFixedNotify = 1101 5 | AbilityInvocationsNotify = 1102 6 | ClientAbilityInitBeginNotify = 1103 7 | ClientAbilityInitFinishNotify = 1104 8 | AbilityInvocationFailNotify = 1105 9 | AvatarAbilityResetNotify = 1106 10 | ClientAbilitiesInitFinishCombineNotify = 1107 11 | ElementReactionLogNotify = 1108 12 | AvatarAbilityResetFinishNotify = 1109 13 | WindSeedClientNotify = 1110 14 | GetActivityScheduleReq = 2001 15 | GetActivityScheduleRsp = 2002 16 | GetActivityInfoReq = 2003 17 | GetActivityInfoRsp = 2004 18 | ActivityPlayOpenAnimNotify = 2005 19 | ActivityInfoNotify = 2006 20 | ActivityScheduleInfoNotify = 2007 21 | SeaLampFlyLampReq = 2014 22 | SeaLampFlyLampRsp = 2015 23 | SeaLampTakeContributionRewardReq = 2016 24 | SeaLampTakeContributionRewardRsp = 2017 25 | SeaLampTakePhaseRewardReq = 2018 26 | SeaLampTakePhaseRewardRsp = 2019 27 | SeaLampContributeItemReq = 2020 28 | SeaLampContributeItemRsp = 2021 29 | ServerAnnounceNotify = 2022 30 | ServerAnnounceRevokeNotify = 2023 31 | LoadActivityTerrainNotify = 2024 32 | AvatarAddNotify = 1701 33 | AvatarDelNotify = 1702 34 | SetUpAvatarTeamReq = 1703 35 | SetUpAvatarTeamRsp = 1704 36 | ChooseCurAvatarTeamReq = 1705 37 | ChooseCurAvatarTeamRsp = 1706 38 | ChangeAvatarReq = 1707 39 | ChangeAvatarRsp = 1708 40 | AvatarPromoteReq = 1709 41 | AvatarPromoteRsp = 1710 42 | SpringUseReq = 1711 43 | SpringUseRsp = 1712 44 | RefreshBackgroundAvatarReq = 1713 45 | RefreshBackgroundAvatarRsp = 1714 46 | AvatarTeamUpdateNotify = 1715 47 | AvatarDataNotify = 1716 48 | AvatarUpgradeReq = 1717 49 | AvatarUpgradeRsp = 1718 50 | AvatarDieAnimationEndReq = 1719 51 | AvatarDieAnimationEndRsp = 1720 52 | AvatarChangeElementTypeReq = 1721 53 | AvatarChangeElementTypeRsp = 1722 54 | AvatarFetterDataNotify = 1723 55 | AvatarExpeditionDataNotify = 1724 56 | AvatarExpeditionAllDataReq = 1725 57 | AvatarExpeditionAllDataRsp = 1726 58 | AvatarExpeditionStartReq = 1727 59 | AvatarExpeditionStartRsp = 1728 60 | AvatarExpeditionCallBackReq = 1729 61 | AvatarExpeditionCallBackRsp = 1730 62 | AvatarExpeditionGetRewardReq = 1731 63 | AvatarExpeditionGetRewardRsp = 1732 64 | ChangeMpTeamAvatarReq = 1734 65 | ChangeMpTeamAvatarRsp = 1735 66 | ChangeTeamNameReq = 1736 67 | ChangeTeamNameRsp = 1737 68 | SceneTeamUpdateNotify = 1738 69 | SceneTeamMPDisplayCurAvatarNotify = 1739 70 | FocusAvatarReq = 1740 71 | FocusAvatarRsp = 1741 72 | DungeonEntryInfoReq = 901 73 | DungeonEntryInfoRsp = 902 74 | PlayerEnterDungeonReq = 903 75 | PlayerEnterDungeonRsp = 904 76 | PlayerQuitDungeonReq = 905 77 | PlayerQuitDungeonRsp = 906 78 | DungeonWayPointNotify = 907 79 | DungeonWayPointActivateReq = 908 80 | DungeonWayPointActivateRsp = 909 81 | DungeonSettleNotify = 910 82 | DungeonPlayerDieNotify = 911 83 | DungeonDieOptionReq = 912 84 | DungeonDieOptionRsp = 913 85 | DungeonShowReminderNotify = 914 86 | DungeonPlayerDieReq = 915 87 | DungeonPlayerDieRsp = 916 88 | DungeonDataNotify = 917 89 | DungeonChallengeBeginNotify = 918 90 | DungeonChallengeFinishNotify = 919 91 | ChallengeDataNotify = 920 92 | DungeonFollowNotify = 921 93 | DungeonGetStatueDropReq = 922 94 | DungeonGetStatueDropRsp = 923 95 | ChallengeRecordNotify = 924 96 | DungeonCandidateTeamInfoNotify = 925 97 | DungeonCandidateTeamInviteNotify = 926 98 | DungeonCandidateTeamRefuseNotify = 927 99 | DungeonCandidateTeamPlayerLeaveNotify = 928 100 | DungeonCandidateTeamDismissNotify = 929 101 | DungeonCandidateTeamCreateReq = 930 102 | DungeonCandidateTeamCreateRsp = 931 103 | DungeonCandidateTeamInviteReq = 932 104 | DungeonCandidateTeamInviteRsp = 933 105 | DungeonCandidateTeamKickReq = 934 106 | DungeonCandidateTeamKickRsp = 935 107 | DungeonCandidateTeamLeaveReq = 936 108 | DungeonCandidateTeamLeaveRsp = 937 109 | DungeonCandidateTeamReplyInviteReq = 938 110 | DungeonCandidateTeamReplyInviteRsp = 939 111 | DungeonCandidateTeamSetReadyReq = 940 112 | DungeonCandidateTeamSetReadyRsp = 941 113 | DungeonCandidateTeamChangeAvatarReq = 942 114 | DungeonCandidateTeamChangeAvatarRsp = 943 115 | GetDailyDungeonEntryInfoReq = 944 116 | GetDailyDungeonEntryInfoRsp = 945 117 | EvtBeingHitNotify = 301 118 | EvtAnimatorParameterNotify = 302 119 | HostPlayerNotify = 303 120 | EvtDoSkillSuccNotify = 304 121 | EvtCreateGadgetNotify = 305 122 | EvtDestroyGadgetNotify = 306 123 | EvtFaceToEntityNotify = 307 124 | EvtFaceToDirNotify = 308 125 | EvtCostStaminaNotify = 309 126 | EvtSetAttackTargetNotify = 310 127 | EvtAnimatorStateChangedNotify = 311 128 | EvtRushMoveNotify = 312 129 | EvtBulletHitNotify = 313 130 | EvtBulletDeactiveNotify = 314 131 | EvtEntityStartDieEndNotify = 315 132 | EvtBulletMoveNotify = 322 133 | EvtAvatarEnterFocusNotify = 323 134 | EvtAvatarExitFocusNotify = 324 135 | EvtAvatarUpdateFocusNotify = 325 136 | EntityAuthorityChangeNotify = 326 137 | AvatarBuffAddNotify = 327 138 | AvatarBuffDelNotify = 328 139 | MonsterAlertChangeNotify = 329 140 | MonsterForceAlertNotify = 330 141 | MonsterForceAiNotify = 331 142 | AvatarEnterElementViewNotify = 332 143 | TriggerCreateGadgetToEquipPartNotify = 333 144 | EvtEntityRenderersChangedNotify = 334 145 | AnimatorForceSetAirMoveNotify = 335 146 | EvtAiSyncSkillCdNotify = 336 147 | EvtBeingHitsCombineNotify = 337 148 | EvtAvatarSitDownNotify = 341 149 | EvtAvatarStandUpNotify = 342 150 | CreateMassiveEntityReq = 343 151 | CreateMassiveEntityRsp = 344 152 | CreateMassiveEntityNotify = 345 153 | DestroyMassiveEntityNotify = 346 154 | MassiveEntityStateChangedNotify = 347 155 | SyncTeamEntityNotify = 348 156 | DelTeamEntityNotify = 349 157 | CombatInvocationsNotify = 350 158 | GetGachaInfoReq = 1501 159 | GetGachaInfoRsp = 1502 160 | DoGachaReq = 1503 161 | DoGachaRsp = 1504 162 | GadgetInteractReq = 801 163 | GadgetInteractRsp = 802 164 | GadgetStateNotify = 803 165 | WorktopOptionNotify = 804 166 | SelectWorktopOptionReq = 805 167 | SelectWorktopOptionRsp = 806 168 | BossChestActivateNotify = 807 169 | PlayerInvestigationAllInfoNotify = 1901 170 | TakeInvestigationRewardReq = 1902 171 | TakeInvestigationRewardRsp = 1903 172 | TakeInvestigationTargetRewardReq = 1904 173 | TakeInvestigationTargetRewardRsp = 1905 174 | GetInvestigationMonsterReq = 1906 175 | GetInvestigationMonsterRsp = 1907 176 | PlayerInvestigationNotify = 1908 177 | PlayerInvestigationTargetNotify = 1909 178 | PlayerStoreNotify = 601 179 | StoreWeightLimitNotify = 602 180 | StoreItemChangeNotify = 603 181 | StoreItemDelNotify = 604 182 | ItemAddHintNotify = 605 183 | UseItemReq = 608 184 | UseItemRsp = 609 185 | DropItemReq = 610 186 | DropItemRsp = 611 187 | WearEquipReq = 614 188 | WearEquipRsp = 615 189 | TakeoffEquipReq = 616 190 | TakeoffEquipRsp = 617 191 | AvatarEquipChangeNotify = 618 192 | WeaponUpgradeReq = 619 193 | WeaponUpgradeRsp = 620 194 | WeaponPromoteReq = 621 195 | WeaponPromoteRsp = 622 196 | ReliquaryUpgradeReq = 623 197 | ReliquaryUpgradeRsp = 624 198 | ReliquaryPromoteReq = 625 199 | ReliquaryPromoteRsp = 626 200 | AvatarCardChangeReq = 627 201 | AvatarCardChangeRsp = 628 202 | GrantRewardNotify = 629 203 | WeaponAwakenReq = 630 204 | WeaponAwakenRsp = 631 205 | ItemCdGroupTimeNotify = 632 206 | DropHintNotify = 633 207 | CombineReq = 634 208 | CombineRsp = 635 209 | ForgeQueueDataNotify = 636 210 | ForgeGetQueueDataReq = 637 211 | ForgeGetQueueDataRsp = 638 212 | ForgeStartReq = 639 213 | ForgeStartRsp = 640 214 | ForgeQueueManipulateReq = 641 215 | ForgeQueueManipulateRsp = 642 216 | ResinChangeNotify = 643 217 | WorldResinChangeNotify = 644 218 | BuyWorldResinReq = 647 219 | BuyWorldResinRsp = 648 220 | BuyResinReq = 649 221 | BuyResinRsp = 650 222 | MaterialDeleteReturnNotify = 651 223 | TakeMaterialDeleteReturnReq = 652 224 | TakeMaterialDeleteReturnRsp = 653 225 | MaterialDeleteUpdateNotify = 654 226 | MailChangeNotify = 1402 227 | ReadMailNotify = 1403 228 | GetMailItemReq = 1404 229 | GetMailItemRsp = 1405 230 | DelMailReq = 1406 231 | DelMailRsp = 1407 232 | GetAuthkeyReq = 1408 233 | GetAuthkeyRsp = 1409 234 | ClientNewMailNotify = 1410 235 | GetAllMailReq = 1411 236 | GetAllMailRsp = 1412 237 | KeepAliveNotify = 1 238 | GmTalkReq = 2 239 | GmTalkRsp = 3 240 | ShowMessageNotify = 4 241 | PingReq = 5 242 | PingRsp = 6 243 | GetOnlinePlayerListReq = 8 244 | GetOnlinePlayerListRsp = 9 245 | ServerTimeNotify = 10 246 | ServerLogNotify = 11 247 | ClientReconnectNotify = 12 248 | ClientFpsStatusNotify = 13 249 | RobotPushPlayerDataNotify = 14 250 | ClientReportNotify = 15 251 | MonsterSummonTagNotify = 1301 252 | PlayerApplyEnterMpNotify = 1801 253 | PlayerApplyEnterMpReq = 1802 254 | PlayerApplyEnterMpRsp = 1803 255 | PlayerApplyEnterMpResultNotify = 1804 256 | PlayerApplyEnterMpResultReq = 1805 257 | PlayerApplyEnterMpResultRsp = 1806 258 | PlayerQuitFromMpNotify = 1807 259 | PlayerPreEnterMpNotify = 1808 260 | GetPlayerMpModeAvailabilityReq = 1809 261 | GetPlayerMpModeAvailabilityRsp = 1810 262 | NpcTalkReq = 501 263 | NpcTalkRsp = 502 264 | GetSceneNpcPositionReq = 504 265 | GetSceneNpcPositionRsp = 505 266 | QueryPathReq = 2301 267 | QueryPathRsp = 2302 268 | ObstacleModifyNotify = 2303 269 | PathfindingPingNotify = 2304 270 | PathfindingEnterSceneReq = 2305 271 | PathfindingEnterSceneRsp = 2306 272 | GMShowObstacleReq = 2351 273 | GMShowObstacleRsp = 2352 274 | GMShowNavMeshReq = 2353 275 | GMShowNavMeshRsp = 2354 276 | GetPlayerTokenReq = 101 277 | GetPlayerTokenRsp = 102 278 | PlayerLoginReq = 103 279 | PlayerLoginRsp = 104 280 | PlayerLogoutReq = 105 281 | PlayerLogoutRsp = 106 282 | PlayerLogoutNotify = 107 283 | PlayerDataNotify = 108 284 | ChangeGameTimeReq = 109 285 | ChangeGameTimeRsp = 110 286 | PlayerGameTimeNotify = 111 287 | PlayerPropNotify = 112 288 | ClientTriggerEventNotify = 113 289 | SetPlayerPropReq = 114 290 | SetPlayerPropRsp = 115 291 | SetPlayerBornDataReq = 116 292 | SetPlayerBornDataRsp = 117 293 | DoSetPlayerBornDataNotify = 118 294 | PlayerPropChangeNotify = 119 295 | SetPlayerNameReq = 120 296 | SetPlayerNameRsp = 121 297 | SetOpenStateReq = 122 298 | SetOpenStateRsp = 123 299 | OpenStateUpdateNotify = 124 300 | OpenStateChangeNotify = 125 301 | PlayerCookReq = 126 302 | PlayerCookRsp = 127 303 | PlayerRandomCookReq = 128 304 | PlayerRandomCookRsp = 129 305 | CookDataNotify = 130 306 | CookRecipeDataNotify = 131 307 | CookGradeDataNotify = 132 308 | PlayerCompoundMaterialReq = 133 309 | PlayerCompoundMaterialRsp = 134 310 | TakeCompoundOutputReq = 135 311 | TakeCompoundOutputRsp = 136 312 | CompoundDataNotify = 137 313 | GetCompoundDataReq = 138 314 | GetCompoundDataRsp = 139 315 | PlayerTimeNotify = 140 316 | PlayerSetPauseReq = 141 317 | PlayerSetPauseRsp = 142 318 | PlayerSetLanguageReq = 143 319 | PlayerSetLanguageRsp = 144 320 | DataResVersionNotify = 145 321 | DailyTaskDataNotify = 146 322 | DailyTaskProgressNotify = 147 323 | DailyTaskScoreRewardNotify = 148 324 | WorldOwnerDailyTaskNotify = 149 325 | AddRandTaskInfoNotify = 150 326 | RemoveRandTaskInfoNotify = 151 327 | TakePlayerLevelRewardReq = 152 328 | TakePlayerLevelRewardRsp = 153 329 | PlayerLevelRewardUpdateNotify = 154 330 | GivingRecordNotify = 155 331 | GivingRecordChangeNotify = 156 332 | ItemGivingReq = 157 333 | ItemGivingRsp = 158 334 | PlayerCookArgsReq = 159 335 | PlayerCookArgsRsp = 160 336 | PlayerLuaShellNotify = 161 337 | ServerDisconnectClientNotify = 162 338 | EntityPropNotify = 1201 339 | LifeStateChangeNotify = 1202 340 | EntityFightPropNotify = 1203 341 | EntityFightPropUpdateNotify = 1204 342 | AvatarFightPropNotify = 1205 343 | AvatarFightPropUpdateNotify = 1206 344 | EntityFightPropChangeReasonNotify = 1207 345 | AvatarLifeStateChangeNotify = 1208 346 | AvatarPropChangeReasonNotify = 1209 347 | PlayerPropChangeReasonNotify = 1210 348 | AvatarPropNotify = 1211 349 | MarkNewNotify = 1212 350 | QuestListNotify = 401 351 | QuestListUpdateNotify = 402 352 | QuestDelNotify = 403 353 | FinishedParentQuestNotify = 404 354 | FinishedParentQuestUpdateNotify = 405 355 | AddQuestContentProgressReq = 406 356 | AddQuestContentProgressRsp = 407 357 | GetQuestTalkHistoryReq = 408 358 | GetQuestTalkHistoryRsp = 409 359 | QuestCreateEntityReq = 410 360 | QuestCreateEntityRsp = 411 361 | QuestDestroyEntityReq = 412 362 | QuestDestroyEntityRsp = 413 363 | LogTalkNotify = 414 364 | LogCutsceneNotify = 415 365 | ChapterStateNotify = 416 366 | QuestProgressUpdateNotify = 417 367 | QuestUpdateQuestVarReq = 418 368 | QuestUpdateQuestVarRsp = 419 369 | QuestUpdateQuestVarNotify = 420 370 | QuestDestroyNpcReq = 421 371 | QuestDestroyNpcRsp = 422 372 | PlayerEnterSceneNotify = 201 373 | LeaveSceneReq = 202 374 | LeaveSceneRsp = 203 375 | SceneInitFinishReq = 204 376 | SceneInitFinishRsp = 205 377 | SceneEntityAppearNotify = 206 378 | SceneEntityDisappearNotify = 207 379 | SceneEntityMoveReq = 208 380 | SceneEntityMoveRsp = 209 381 | SceneAvatarStaminaStepReq = 210 382 | SceneAvatarStaminaStepRsp = 211 383 | SceneEntityMoveNotify = 212 384 | ScenePlayerLocationNotify = 213 385 | GetScenePointReq = 214 386 | GetScenePointRsp = 215 387 | EnterTransPointRegionNotify = 216 388 | ExitTransPointRegionNotify = 217 389 | ScenePointUnlockNotify = 218 390 | SceneTransToPointReq = 219 391 | SceneTransToPointRsp = 220 392 | EntityJumpNotify = 221 393 | GetSceneAreaReq = 222 394 | GetSceneAreaRsp = 223 395 | SceneAreaUnlockNotify = 224 396 | SceneEntityDrownReq = 225 397 | SceneEntityDrownRsp = 226 398 | SceneCreateEntityReq = 227 399 | SceneCreateEntityRsp = 228 400 | SceneDestroyEntityReq = 229 401 | SceneDestroyEntityRsp = 230 402 | SceneForceUnlockNotify = 231 403 | SceneForceLockNotify = 232 404 | EnterWorldAreaReq = 233 405 | EnterWorldAreaRsp = 234 406 | EntityForceSyncReq = 235 407 | EntityForceSyncRsp = 236 408 | SceneAreaExploreNotify = 237 409 | SceneGetAreaExplorePercentReq = 238 410 | SceneGetAreaExplorePercentRsp = 239 411 | ClientTransmitReq = 240 412 | ClientTransmitRsp = 241 413 | EnterSceneWeatherAreaNotify = 242 414 | ExitSceneWeatherAreaNotify = 243 415 | SceneAreaWeatherNotify = 244 416 | ScenePlayerInfoNotify = 245 417 | WorldPlayerLocationNotify = 246 418 | BeginCameraSceneLookNotify = 247 419 | EndCameraSceneLookNotify = 248 420 | MarkEntityInMinMapNotify = 249 421 | UnmarkEntityInMinMapNotify = 250 422 | DropSubfieldReq = 251 423 | DropSubfieldRsp = 252 424 | ExecuteGroupTriggerReq = 253 425 | ExecuteGroupTriggerRsp = 254 426 | LevelupCityReq = 255 427 | LevelupCityRsp = 256 428 | SceneRouteChangeNotify = 257 429 | PlatformStartRouteNotify = 258 430 | PlatformStopRouteNotify = 259 431 | PlatformChangeRouteNotify = 260 432 | ScenePlayerSoundNotify = 261 433 | PersonalSceneJumpReq = 262 434 | PersonalSceneJumpRsp = 263 435 | SealBattleBeginNotify = 264 436 | SealBattleEndNotify = 265 437 | SealBattleProgressNotify = 266 438 | ClientPauseNotify = 267 439 | PlayerEnterSceneInfoNotify = 268 440 | JoinPlayerSceneReq = 269 441 | JoinPlayerSceneRsp = 270 442 | SceneKickPlayerReq = 271 443 | SceneKickPlayerRsp = 272 444 | SceneKickPlayerNotify = 273 445 | HitClientTrivialNotify = 274 446 | BackMyWorldReq = 275 447 | BackMyWorldRsp = 276 448 | SeeMonsterReq = 277 449 | SeeMonsterRsp = 278 450 | AddSeenMonsterNotify = 279 451 | AllSeenMonsterNotify = 280 452 | SceneTimeNotify = 281 453 | EnterSceneReadyReq = 282 454 | EnterSceneReadyRsp = 283 455 | EnterScenePeerNotify = 284 456 | EnterSceneDoneReq = 285 457 | EnterSceneDoneRsp = 286 458 | WorldPlayerDieNotify = 287 459 | WorldPlayerReviveReq = 288 460 | WorldPlayerReviveRsp = 289 461 | JoinPlayerFailNotify = 290 462 | SetSceneWeatherAreaReq = 291 463 | SetSceneWeatherAreaRsp = 292 464 | ExecuteGadgetLuaReq = 293 465 | ExecuteGadgetLuaRsp = 294 466 | CutSceneBeginNotify = 295 467 | CutSceneFinishNotify = 296 468 | CutSceneEndNotify = 297 469 | ClientScriptEventNotify = 298 470 | SceneEntitiesMovesReq = 299 471 | SceneEntitiesMovesRsp = 300 472 | SceneEntitiesMoveCombineNotify = 3001 473 | UnlockTransPointReq = 3002 474 | UnlockTransPointRsp = 3003 475 | PlatformRouteStateNotify = 3004 476 | SceneWeatherForcastReq = 3005 477 | SceneWeatherForcastRsp = 3006 478 | MarkMapReq = 3010 479 | MarkMapRsp = 3011 480 | AllMarkPointNotify = 3012 481 | WorldDataNotify = 3013 482 | EntityMoveRoomNotify = 3014 483 | WorldPlayerInfoNotify = 3015 484 | PostEnterSceneReq = 3016 485 | PostEnterSceneRsp = 3017 486 | PlayerChatReq = 3018 487 | PlayerChatRsp = 3019 488 | PlayerChatNotify = 3020 489 | PlayerChatCDNotify = 3021 490 | ChatHistoryNotify = 3022 491 | SceneDataNotify = 3023 492 | DungeonEntryToBeExploreNotify = 3024 493 | GetDungeonEntryExploreConditionReq = 3035 494 | GetDungeonEntryExploreConditionRsp = 3036 495 | UnfreezeGroupLimitNotify = 3037 496 | GetShopReq = 701 497 | GetShopRsp = 702 498 | BuyGoodsReq = 703 499 | BuyGoodsRsp = 704 500 | UnlockAvatarTalentReq = 1001 501 | UnlockAvatarTalentRsp = 1002 502 | AvatarUnlockTalentNotify = 1003 503 | AvatarSkillDepotChangeNotify = 1004 504 | BigTalentPointConvertReq = 1005 505 | BigTalentPointConvertRsp = 1006 506 | AvatarSkillMaxChargeCountNotify = 1007 507 | AvatarSkillInfoNotify = 1008 508 | ProudSkillUpgradeReq = 1009 509 | ProudSkillUpgradeRsp = 1010 510 | ProudSkillChangeNotify = 1011 511 | AvatarSkillUpgradeReq = 1012 512 | AvatarSkillUpgradeRsp = 1013 513 | AvatarSkillChangeNotify = 1014 514 | ProudSkillExtraLevelNotify = 1015 515 | CanUseSkillNotify = 1016 516 | GetPlayerFriendListReq = 4001 517 | GetPlayerFriendListRsp = 4002 518 | AskAddFriendReq = 4005 519 | AskAddFriendRsp = 4006 520 | AddFriendReq = 4007 521 | AddFriendRsp = 4008 522 | TowerBriefDataNotify = 2401 523 | TowerFloorRecordChangeNotify = 2402 524 | TowerCurLevelRecordChangeNotify = 2403 525 | TowerDailyRewardProgressChangeNotify = 2404 526 | TowerTeamSelectReq = 2406 527 | TowerTeamSelectRsp = 2407 528 | TowerAllDataReq = 2408 529 | TowerAllDataRsp = 2409 530 | TowerEnterLevelReq = 2411 531 | TowerEnterLevelRsp = 2412 532 | TowerBuffSelectReq = 2413 533 | TowerBuffSelectRsp = 2414 534 | TowerSurrenderReq = 2421 535 | TowerSurrenderRsp = 2422 536 | TowerGetFloorStarRewardReq = 2423 537 | TowerGetFloorStarRewardRsp = 2424 538 | TowerLevelEndNotify = 2430 539 | TowerLevelStarCondNotify = 2431 540 | TowerMiddleLevelChangeTeamNotify = 2432 541 | WatcherAllDataNotify = 2201 542 | WatcherChangeNotify = 2202 543 | WatcherEventNotify = 2203 544 | WatcherEventTypeNotify = 2204 545 | PushTipsAllDataNotify = 2221 546 | PushTipsChangeNotify = 2222 547 | PushTipsReadFinishReq = 2223 548 | PushTipsReadFinishRsp = 2224 549 | GetPushTipsRewardReq = 2225 550 | GetPushTipsRewardRsp = 2226 551 | SeaLampPlayerContributionNotify = 10601 552 | SeaLampProgressNotify = 10602 553 | SeaLampBroadcastNotify = 10603 554 | SeaLampSetProgressByMuipReq = 10604 555 | SeaLampSetProgressByMuipRsp = 10605 556 | SeaLampProgressImplementNotify = 10606 557 | SeaLampClearProgressByGmNotify = 10607 558 | SeaLampAddProgressByMuipReq = 10608 559 | SeaLampAddProgressByMuipRsp = 10609 560 | GetActivityDataByMuipReq = 10610 561 | GetActivityDataByMuipRsp = 10611 562 | SendMailReq = 10301 563 | SendMailRsp = 10302 564 | NewMailNotify = 10303 565 | ReceiveMailReq = 10304 566 | ReceiveMailRsp = 10305 567 | UpdateMailNotify = 10306 568 | ClearMailBoxDataNotify = 10307 569 | SendOfflineMsgReq = 10308 570 | SendOfflineMsgRsp = 10309 571 | NewOfflineMsgNotify = 10310 572 | GetOfflineMsgReq = 10311 573 | GetOfflineMsgRsp = 10312 574 | RemoveOfflineMsgNotify = 10313 575 | ClearOfflineMsgNotify = 10314 576 | DelRedisMailByMuipReq = 10315 577 | DelRedisMailByMuipRsp = 10316 578 | StopServerConfigNotify = 10001 579 | NodeserverConnectedAndRegisteredNotify = 10002 580 | MultiPlayerMsg = 10003 581 | AddGateserverNotify = 10004 582 | RegisterServiceNotify = 10005 583 | PlayerTransferNotify = 10006 584 | PacketFreqencyExceedNotify = 10007 585 | SceneAsyncLoadGroupBatchNotify = 10008 586 | ClientVersionSyncNotify = 10009 587 | RegisterServiceSuccessNotify = 10010 588 | ReloadConfigNotify = 10011 589 | UpdateMpStatusNotify = 10401 590 | DelMpStatusNotify = 10402 591 | GetPlayerMpStatusListReq = 10403 592 | GetPlayerMpStatusListRsp = 10404 593 | OnlinePlayerNumReq = 10201 594 | OnlinePlayerNumRsp = 10202 595 | KickoutPlayerNotify = 10203 596 | CheckOnlinePlayerReq = 10204 597 | CheckOnlinePlayerRsp = 10205 598 | PlayerCombatForceReq = 10206 599 | PlayerCombatForceRsp = 10207 600 | DataAndResVersionReq = 10208 601 | DataAndResVersionRsp = 10209 602 | PlatformPlayerNumReq = 10210 603 | PlatformPlayerNumRsp = 10211 604 | QueryPlayerMemDataByMuipReq = 10212 605 | QueryPlayerMemDataByMuipRsp = 10213 606 | BindGmUidNotify = 10214 607 | UnbindGmUidNotify = 10215 608 | GetBindGmUidReq = 10216 609 | GetBindGmUidRsp = 10217 610 | SavePlayerDataReq = 10102 611 | SavePlayerDataRsp = 10103 612 | DisconnectClientNotify = 10109 613 | AddAskFriendNotify = 10801 --------------------------------------------------------------------------------