├── 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
--------------------------------------------------------------------------------