mirror of
https://github.com/Wesley-DeMontigny/WLUS.git
synced 2026-04-30 21:39:13 -05:00
Implemented World Loading
The character can now load entirely into a world instance. Next step is to implement all replica components so the world can have objects, and to figure out a way how to determine what to serialize/where to store object data.
This commit is contained in:
@@ -21,6 +21,8 @@ class GameServer(pyraknet.server.Server):
|
||||
self.add_handler(pyraknet.server.Event.UserPacket, self.handle_packet)
|
||||
self.add_handler(pyraknet.server.Event.Disconnect, self.handle_disconnect)
|
||||
|
||||
self.additional_config = {}
|
||||
self.address = address
|
||||
self.name = name
|
||||
self.zone = zone
|
||||
self.config = configparser.ConfigParser()
|
||||
|
||||
@@ -6,6 +6,7 @@ CREATE TABLE `character_stats` (
|
||||
`quick_builds_done` int(11) DEFAULT NULL,
|
||||
`enemies_smashed` int(11) DEFAULT NULL,
|
||||
`rockets_used` int(11) DEFAULT NULL,
|
||||
`missions_completed` int(11) DEFAULT NULL,
|
||||
`pets_tamed` int(11) DEFAULT NULL,
|
||||
`imagination_collected` int(11) DEFAULT NULL,
|
||||
`health_collected` int(11) DEFAULT NULL,
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
CREATE TABLE `completed_missions` (
|
||||
`player_id` bigint(40) DEFAULT NULL,
|
||||
`mission_id` int(11) DEFAULT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
@@ -0,0 +1,5 @@
|
||||
CREATE TABLE `current_missions` (
|
||||
`mission_id` int(11) DEFAULT NULL,
|
||||
`progress` int(11) DEFAULT NULL,
|
||||
`player_id` bigint(40) DEFAULT NULL
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
|
||||
@@ -3,6 +3,7 @@ This will handle all of the character related packets.
|
||||
"""
|
||||
from ..serializables import packet_enum, world_to_client, client_to_world, global_packets, misc_serializables
|
||||
from pyraknet.bitstream import *
|
||||
import os
|
||||
|
||||
|
||||
class Plugin:
|
||||
@@ -43,19 +44,20 @@ class Plugin:
|
||||
stream = ReadStream(data)
|
||||
player_id = stream.read(client_to_world.JoinWorldPacket).object_id
|
||||
c = server.db_connection.cursor()
|
||||
c.execute("SELECT zone FROM wlus.character WHERE character_id = %s", (player_id,))
|
||||
zone_id = c.fetchone()[0]
|
||||
c.execute("SELECT zone, current_name FROM wlus.character WHERE character_id = %s", (player_id,))
|
||||
char_data = c.fetchone()
|
||||
session = server.lookup_session_by_ip(address[0])
|
||||
zone_id = char_data[0]
|
||||
if zone_id == 0:
|
||||
zone_id = 1000
|
||||
|
||||
player = server.lookup_player_by_ip(address[0])
|
||||
if player is None:
|
||||
server.execute_master_query("""INSERT INTO online_players(id, session_id, current_zone, name)
|
||||
VALUES (%s, %s, %s, "%s")""" % (player_id, session["id"], zone_id, session["username"]), do_return=False)
|
||||
VALUES (%s, %s, %s, "%s")""" % (player_id, session["id"], zone_id, char_data[1]), do_return=False)
|
||||
else:
|
||||
server.execute_master_query("""UPDATE online_players SET id = %s, zone_id = %s, name = "%s"
|
||||
WHERE session_id = %s""" % (player_id, zone_id, session["username"], player["session_id"]), do_return=False)
|
||||
WHERE session_id = %s""" % (player_id, zone_id, char_data[1], player["session_id"]), do_return=False)
|
||||
|
||||
if zone_id != server.zone:
|
||||
ip_address, port, instance_name = server.redirect_request("zone", str(zone_id))
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
from pyraknet.bitstream import *
|
||||
from ..serializables import misc_serializables, packet_enum
|
||||
|
||||
@@ -12,7 +12,7 @@ class HandshakePacket(bitstream.Serializable):
|
||||
"""
|
||||
def __init__(self):
|
||||
self.game_version = 171022
|
||||
self.unknown_0 = 0x93
|
||||
self.unknown_0 = 0
|
||||
self.remote_connection_type = 0 # For auth this is 1, otherwise it is 4
|
||||
self.process_id = 1124
|
||||
self.local_port = 0xff
|
||||
|
||||
@@ -32,6 +32,69 @@ class PacketHeader(Enum):
|
||||
REDIRECT_TO_NEW_SERVER = b'S\x05\x00\x0e\x00\x00\x00\x00'
|
||||
|
||||
|
||||
class ReplicaTypes(IntEnum):
|
||||
CONSTRUCTION = 0
|
||||
SERIALIZATION = 1
|
||||
|
||||
|
||||
class GameMessages(IntEnum):
|
||||
SERVER_DONE_LOADING_OBJECTS = 1642
|
||||
PLAYER_READY = 509
|
||||
READY_FOR_UPDATES = 888
|
||||
PLAY_FX_EFFECT = 154
|
||||
STOP_FX_EFFECT = 155
|
||||
PARSE_CHAT_MSG = 850
|
||||
SET_JETPACK_MODE = 561
|
||||
ADD_SKILL = 127
|
||||
REMOVE_SKILL = 128
|
||||
PLAYER_LOADED = 505
|
||||
ADD_ITEM_TO_INVENTORY_CLIENT_SYNC = 227
|
||||
EQUIP_INVENTORY = 231
|
||||
UNEQUIP_INVENTORY = 233
|
||||
RESYNC_EQUIPMENT = 1238
|
||||
START_SKILL = 119
|
||||
ECHO_START_SKILL = 118
|
||||
ECHO_SYNC_SKILL = 1144
|
||||
SYNC_SKILL = 1145
|
||||
DIE = 37
|
||||
REQUEST_DIE = 38
|
||||
PLAYER_REQUEST_SMASH = 1202
|
||||
RESSURECT_REQUEST = 159
|
||||
RESSURECT = 160
|
||||
REQUEST_USE = 364
|
||||
OFFER_MISSION = 248
|
||||
|
||||
|
||||
class SerializedComponents(IntEnum):
|
||||
POSSESSABLE = 108
|
||||
MODULE_ASSEMBLY = 61
|
||||
CONTROLLABLE_PHYSICS = 1
|
||||
SIMPLE_PHYSICS = 3
|
||||
RIGID_BODY_PHANTOM_PHYSICS = 20
|
||||
VEHICLE_PHYSICS = 30
|
||||
PHANTOM_PHYSICS = 40
|
||||
DESTRUCTIBLE = 7
|
||||
COLLECTIBLE = 23
|
||||
PET = 26
|
||||
CHARACTER = 4
|
||||
INVENTORY = 17
|
||||
SCRIPT = 5
|
||||
SKILL = 9
|
||||
BASE_COMBAT_AI = 60
|
||||
REBUILD = 48
|
||||
MOVING_PLATFORM = 25
|
||||
SWITCH = 49
|
||||
VENDOR = 16
|
||||
BOUNCER = 6
|
||||
SCRIPTED_ACTIVITY = 39
|
||||
RACING_CONTROL = 71
|
||||
EXHIBIT = 75
|
||||
MODEL = 42
|
||||
RENDER = 2
|
||||
COMPONENT_107 = 107
|
||||
TRIGGER = 69
|
||||
|
||||
|
||||
class DisconnectionNotify(IntEnum):
|
||||
"""
|
||||
Contains various reasons for disconnect notify.
|
||||
|
||||
@@ -1,10 +1,97 @@
|
||||
"""
|
||||
This file contains all of the replica components and the structs withing them
|
||||
"""
|
||||
from pyraknet import bitstream
|
||||
from pyraknet.bitstream import *
|
||||
from pyraknet.replicamanager import Replica
|
||||
from plugins.serializables.packet_enum import ReplicaTypes, SerializedComponents
|
||||
from plugins.serializables.misc_serializables import Vector3, Vector4
|
||||
import sqlite3
|
||||
|
||||
|
||||
class BaseDataComponent(bitstream.Serializable):
|
||||
class ReplicaObject(Replica):
|
||||
|
||||
def __init__(self):
|
||||
self.base_data = BaseDataComponent()
|
||||
self.components = {}
|
||||
|
||||
def write_construction(self, stream: WriteStream) -> None:
|
||||
self.write(stream, ReplicaTypes.CONSTRUCTION.value)
|
||||
|
||||
def serialize(self, stream: WriteStream) -> None:
|
||||
self.write(stream, ReplicaTypes.SERIALIZATION.value)
|
||||
|
||||
def write(self, stream: WriteStream, replica_mode) -> None:
|
||||
# Not Networked: 12, 31, 35, 36, 45, 55, 56, 64, 65, 68, 73, 104, 113, 114
|
||||
self.base_data.replica_mode = replica_mode # TODO: There has to be a better way to do this
|
||||
for comp in self.components:
|
||||
self.components[comp].replica_mode = ReplicaTypes.CONSTRUCTION.value
|
||||
conn = sqlite3.connect("./res/cdclient.sqlite")
|
||||
c = conn.cursor()
|
||||
c.execute("SELECT component_type FROM ComponentsRegistry WHERE id = ?", (self.base_data.lot,))
|
||||
comp_table = c.fetchall()
|
||||
components = []
|
||||
for component in comp_table:
|
||||
components.append(component[0])
|
||||
stream.write(self.base_data)
|
||||
|
||||
if SerializedComponents.POSSESSABLE.value in components:
|
||||
pass
|
||||
if SerializedComponents.MODULE_ASSEMBLY.value in components:
|
||||
pass
|
||||
if SerializedComponents.CONTROLLABLE_PHYSICS.value in components:
|
||||
stream.write(self.components[SerializedComponents.CONTROLLABLE_PHYSICS.value])
|
||||
if SerializedComponents.SIMPLE_PHYSICS.value in components:
|
||||
pass
|
||||
if SerializedComponents.RIGID_BODY_PHANTOM_PHYSICS.value in components:
|
||||
pass
|
||||
if SerializedComponents.VEHICLE_PHYSICS.value in components:
|
||||
pass
|
||||
if SerializedComponents.PHANTOM_PHYSICS.value in components:
|
||||
pass
|
||||
if SerializedComponents.DESTRUCTIBLE.value in components:
|
||||
stream.write(self.components[SerializedComponents.DESTRUCTIBLE.value].destructible_index)
|
||||
stream.write(self.components[SerializedComponents.DESTRUCTIBLE.value].stats_index)
|
||||
if SerializedComponents.COLLECTIBLE.value in components:
|
||||
pass
|
||||
if SerializedComponents.PET.value in components:
|
||||
pass
|
||||
if SerializedComponents.CHARACTER.value in components:
|
||||
stream.write(self.components[SerializedComponents.CHARACTER.value])
|
||||
if SerializedComponents.INVENTORY.value in components:
|
||||
stream.write(self.components[SerializedComponents.INVENTORY.value])
|
||||
if SerializedComponents.SCRIPT.value in components:
|
||||
pass
|
||||
if SerializedComponents.SKILL.value in components:
|
||||
stream.write(self.components[SerializedComponents.SKILL.value])
|
||||
if SerializedComponents.BASE_COMBAT_AI.value in components:
|
||||
pass
|
||||
if SerializedComponents.REBUILD.value in components:
|
||||
pass
|
||||
if SerializedComponents.MOVING_PLATFORM.value in components:
|
||||
pass
|
||||
if SerializedComponents.SWITCH.value in components:
|
||||
pass
|
||||
if SerializedComponents.VENDOR.value in components:
|
||||
pass
|
||||
if SerializedComponents.BOUNCER.value in components:
|
||||
pass
|
||||
if SerializedComponents.SCRIPTED_ACTIVITY.value in components:
|
||||
pass
|
||||
if SerializedComponents.RACING_CONTROL.value in components:
|
||||
pass
|
||||
if SerializedComponents.EXHIBIT.value in components:
|
||||
pass
|
||||
if SerializedComponents.MODEL.value in components:
|
||||
pass
|
||||
if SerializedComponents.RENDER.value in components:
|
||||
stream.write(self.components[SerializedComponents.RENDER.value])
|
||||
if SerializedComponents.COMPONENT_107.value in components:
|
||||
stream.write(self.components[SerializedComponents.COMPONENT_107.value])
|
||||
if SerializedComponents.MODEL.value in components:
|
||||
pass
|
||||
|
||||
|
||||
class BaseDataComponent(Serializable):
|
||||
"""
|
||||
Base Data should be supplied with every replica.
|
||||
replica_mode refers to whether or not it is construction, serialization or destruction.
|
||||
@@ -12,7 +99,7 @@ class BaseDataComponent(bitstream.Serializable):
|
||||
Serialization - 1
|
||||
Destruction - 2
|
||||
"""
|
||||
def __init__(self, replica_mode: int = 0):
|
||||
def __init__(self, replica_mode=0):
|
||||
self.replica_mode = replica_mode
|
||||
self.object_id = 0
|
||||
self.lot = 0
|
||||
@@ -22,5 +109,334 @@ class BaseDataComponent(bitstream.Serializable):
|
||||
self.spawner_id = 0
|
||||
self.spawner_node_id = 0
|
||||
self.scale = 1
|
||||
self.object_world_state = 0
|
||||
self.gm_level = 0
|
||||
self.object_world_state = -1
|
||||
self.gm_level = 0
|
||||
|
||||
def serialize(self, stream: WriteStream) -> None:
|
||||
if self.replica_mode == ReplicaTypes.CONSTRUCTION.value:
|
||||
stream.write(c_int64(self.object_id))
|
||||
stream.write(c_int32(self.lot))
|
||||
stream.write(self.name, length_type=c_uint8)
|
||||
stream.write(c_uint32(self.time_since_created))
|
||||
stream.write(c_bit(False)) # Unimplemented struct
|
||||
stream.write(c_bit(self.has_trigger))
|
||||
stream.write(c_bit(self.spawner_id != 0))
|
||||
if self.spawner_id != 0:
|
||||
stream.write(c_int64(self.spawner_id))
|
||||
stream.write(c_bit(self.spawner_node_id != 0))
|
||||
if self.spawner_node_id != 0:
|
||||
stream.write(c_uint32(self.spawner_node_id))
|
||||
stream.write(c_bit(self.scale != 1))
|
||||
if self.scale != 1:
|
||||
stream.write(c_float(self.scale))
|
||||
stream.write(c_bit(self.object_world_state != -1))
|
||||
if self.object_world_state != -1:
|
||||
stream.write(c_uint8(self.object_world_state))
|
||||
stream.write(c_bit(self.gm_level != 0))
|
||||
if self.gm_level != 0:
|
||||
stream.write(c_uint8(self.gm_level))
|
||||
stream.write(c_bit(False)) # Unimplemented struct
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls, stream: ReadStream) -> Serializable:
|
||||
raise Exception("This struct cannot be deserialized")
|
||||
|
||||
|
||||
class ControllablePhysicsComponent(Serializable):
|
||||
|
||||
def __init__(self, replica_mode=0):
|
||||
self.replica_mode = replica_mode
|
||||
self.jetpack_equipped = False
|
||||
self.jetpack_effect = 0
|
||||
self.in_air = False
|
||||
self.gravity_multiplier = 1
|
||||
self.speed_multiplier = 1
|
||||
self.position = Vector3()
|
||||
self.rotation = Vector4()
|
||||
self.on_ground = True
|
||||
self.on_rail = False
|
||||
self.velocity = Vector3()
|
||||
self.angular_velocity = Vector3()
|
||||
|
||||
def serialize(self, stream: WriteStream) -> None:
|
||||
if self.replica_mode == ReplicaTypes.CONSTRUCTION.value:
|
||||
stream.write(c_bit(self.jetpack_equipped))
|
||||
if self.jetpack_equipped:
|
||||
stream.write(c_uint32(self.jetpack_effect))
|
||||
stream.write(c_bit(self.in_air))
|
||||
stream.write(c_bit(False))
|
||||
stream.write(c_bit(False)) # Undiscovered struct?
|
||||
stream.write(c_bit(self.gravity_multiplier != 1 or self.speed_multiplier != 1))
|
||||
if self.gravity_multiplier != 1 or self.speed_multiplier != 1:
|
||||
stream.write(c_float(self.gravity_multiplier))
|
||||
stream.write(c_float(self.speed_multiplier))
|
||||
stream.write(c_bit(False)) # Undiscovered struct?
|
||||
stream.write(c_bit(False)) # Undiscovered struct?
|
||||
stream.write(c_bit(True))
|
||||
stream.write(self.position)
|
||||
stream.write(self.rotation)
|
||||
stream.write(c_bit(self.on_ground))
|
||||
stream.write(c_bit(self.on_rail))
|
||||
stream.write(c_bit(self.velocity != Vector3()))
|
||||
if self.velocity != Vector3():
|
||||
stream.write(self.velocity)
|
||||
stream.write(c_bit(self.angular_velocity != Vector3()))
|
||||
if self.angular_velocity != Vector3():
|
||||
stream.write(self.angular_velocity)
|
||||
stream.write(c_bit(False)) # Some struct relating to moving platforms
|
||||
if self.replica_mode == ReplicaTypes.SERIALIZATION.value:
|
||||
stream.write(c_bit(False))
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls, stream: ReadStream) -> Serializable:
|
||||
raise Exception("This struct cannot be deserialized")
|
||||
|
||||
|
||||
class DestructibleIndex(Serializable):
|
||||
"""
|
||||
This index is fairly unresearched.
|
||||
"""
|
||||
def __init__(self, replica_mode=0):
|
||||
self.replica_mode = replica_mode
|
||||
|
||||
def serialize(self, stream: WriteStream) -> None:
|
||||
if self.replica_mode == ReplicaTypes.CONSTRUCTION.value:
|
||||
stream.write(c_bit(False))
|
||||
stream.write(c_bit(False))
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls, stream: ReadStream) -> Serializable:
|
||||
raise Exception("This struct cannot be deserialized")
|
||||
|
||||
|
||||
class StatsIndex(Serializable):
|
||||
|
||||
def __init__(self, replica_mode=0):
|
||||
self.replica_mode = replica_mode
|
||||
self.health = 0
|
||||
self.max_health = 0
|
||||
self.armor = 0
|
||||
self.max_armor = 0
|
||||
self.imagination = 0
|
||||
self.max_imagination = 0
|
||||
self.absorbtion_points = 0
|
||||
self.is_gm_immune = False
|
||||
self.immune = False
|
||||
self.shielded = False
|
||||
self.factions = []
|
||||
self.is_smashable = False
|
||||
|
||||
def serialize(self, stream: WriteStream) -> None:
|
||||
if self.replica_mode == ReplicaTypes.CONSTRUCTION.value:
|
||||
stream.write(c_bit(False))
|
||||
stream.write(c_bit(True))
|
||||
stream.write(c_uint32(self.health))
|
||||
stream.write(c_float(self.max_health))
|
||||
stream.write(c_uint32(self.armor))
|
||||
stream.write(c_float(self.max_armor))
|
||||
stream.write(c_uint32(self.imagination))
|
||||
stream.write(c_float(self.max_imagination))
|
||||
stream.write(c_uint32(self.absorbtion_points))
|
||||
stream.write(c_bit(self.immune))
|
||||
stream.write(c_bit(self.is_gm_immune))
|
||||
stream.write(c_bit(self.shielded))
|
||||
stream.write(c_float(self.max_health))
|
||||
stream.write(c_float(self.max_armor))
|
||||
stream.write(c_float(self.max_imagination))
|
||||
stream.write(c_uint32(len(self.factions)))
|
||||
for faction in self.factions:
|
||||
stream.write(c_int32(faction))
|
||||
stream.write(c_bit(self.is_smashable))
|
||||
if self.replica_mode == ReplicaTypes.CONSTRUCTION.value:
|
||||
stream.write(c_bit(False))
|
||||
stream.write(c_bit(False))
|
||||
if self.is_smashable:
|
||||
stream.write(c_bit(False))
|
||||
stream.write(c_bit(False))
|
||||
stream.write(c_bit(False))
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls, stream: ReadStream) -> Serializable:
|
||||
raise Exception("This struct cannot be deserialized")
|
||||
|
||||
|
||||
class CharacterComponent(Serializable):
|
||||
|
||||
def __init__(self, replica_mode=0):
|
||||
self.replica_mode = replica_mode
|
||||
self.level = 0
|
||||
self.hair_color = 0
|
||||
self.hair_style = 0
|
||||
self.shirt_color = 0
|
||||
self.pants_color = 0
|
||||
self.eyebrows = 0
|
||||
self.eyes = 0
|
||||
self.mouth = 0
|
||||
self.account_id = 0
|
||||
self.lego_score = 0
|
||||
self.ftp = False
|
||||
self.character_stats = []
|
||||
for _ in range(27):
|
||||
self.character_stats.append(0)
|
||||
self.world_transition_state = 0
|
||||
self.ldf_rocket_info = "1:9746;1:9747;1:9748;"
|
||||
self.pvp = False
|
||||
self.is_gm = False
|
||||
self.gm_level = 0
|
||||
self.activity = 0
|
||||
# TODO: Add guild struct and vehicle struct
|
||||
|
||||
def serialize(self, stream: WriteStream) -> None:
|
||||
stream.write(c_bit(False)) # Vehicle struct
|
||||
stream.write(c_bit(False))
|
||||
# stream.write(c_uint32(self.level))
|
||||
stream.write(c_bit(False))
|
||||
if self.replica_mode == ReplicaTypes.CONSTRUCTION.value:
|
||||
stream.write(c_bit(False))
|
||||
stream.write(c_bit(False))
|
||||
stream.write(c_bit(False))
|
||||
stream.write(c_bit(False))
|
||||
stream.write(c_uint32(self.hair_color))
|
||||
stream.write(c_uint32(self.hair_style))
|
||||
stream.write(c_uint32(0))
|
||||
stream.write(c_uint32(self.shirt_color))
|
||||
stream.write(c_uint32(self.pants_color))
|
||||
stream.write(c_uint32(0))
|
||||
stream.write(c_uint32(0))
|
||||
stream.write(c_uint32(self.eyebrows))
|
||||
stream.write(c_uint32(self.eyes))
|
||||
stream.write(c_uint32(self.mouth))
|
||||
stream.write(c_uint64(self.account_id))
|
||||
stream.write(c_uint64(0))
|
||||
stream.write(c_uint64(0))
|
||||
stream.write(c_uint64(self.lego_score))
|
||||
stream.write(c_bit(self.ftp))
|
||||
for stat in self.character_stats:
|
||||
stream.write(c_uint64(stat))
|
||||
if self.world_transition_state == 1:
|
||||
stream.write(c_bit(True))
|
||||
stream.write(c_bit(False))
|
||||
stream.write(self.ldf_rocket_info, length_type=c_uint16)
|
||||
elif self.world_transition_state == 2:
|
||||
stream.write(c_bit(False))
|
||||
stream.write(c_bit(True))
|
||||
else:
|
||||
stream.write(c_bit(False))
|
||||
stream.write(c_bit(False))
|
||||
stream.write(c_bit(True))
|
||||
stream.write(c_bit(self.pvp))
|
||||
stream.write(c_bit(self.is_gm))
|
||||
stream.write(c_uint8(self.gm_level))
|
||||
stream.write(c_bit(False))
|
||||
stream.write(c_uint8(0))
|
||||
stream.write(c_bit(self.activity != 0))
|
||||
if self.activity != 0:
|
||||
stream.write(c_uint32(self.activity))
|
||||
stream.write(c_bit(False)) # Guild struct
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls, stream: ReadStream) -> Serializable:
|
||||
raise Exception("This struct cannot be deserialized")
|
||||
|
||||
|
||||
class InventoryItem(Serializable):
|
||||
|
||||
def __init__(self):
|
||||
self.object_id = 0
|
||||
self.lot = 0
|
||||
self.count = 1
|
||||
self.slot = 0
|
||||
self.inventory_type = 4
|
||||
|
||||
def serialize(self, stream: WriteStream) -> None:
|
||||
stream.write(c_int64(self.object_id))
|
||||
stream.write(c_int32(self.lot))
|
||||
stream.write(c_bit(False))
|
||||
stream.write(c_bit(True))
|
||||
stream.write(c_uint32(self.count))
|
||||
stream.write(c_bit(True))
|
||||
stream.write(c_uint16(self.slot))
|
||||
stream.write(c_bit(True))
|
||||
stream.write(c_uint32(self.inventory_type))
|
||||
stream.write(c_bit(False))
|
||||
stream.write(c_bit(True))
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls, stream: ReadStream) -> Serializable:
|
||||
raise Exception("This struct cannot be deserialized")
|
||||
|
||||
|
||||
class InventoryComponent(Serializable):
|
||||
|
||||
def __init__(self, replica_mode=0):
|
||||
self.replica_mode = replica_mode
|
||||
self.items = []
|
||||
|
||||
def serialize(self, stream: WriteStream) -> None:
|
||||
stream.write(c_bit(True))
|
||||
stream.write(c_uint32(len(self.items)))
|
||||
for item in self.items:
|
||||
stream.write(item)
|
||||
stream.write(c_bit(False))
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls, stream: ReadStream) -> Serializable:
|
||||
raise Exception("This struct cannot be deserialized")
|
||||
|
||||
|
||||
class SkillComponent(Serializable):
|
||||
"""
|
||||
Not even really sure what this is for
|
||||
"""
|
||||
def __init__(self, replica_mode=0):
|
||||
self.replica_mode = replica_mode
|
||||
|
||||
def serialize(self, stream: WriteStream) -> None:
|
||||
if self.replica_mode == ReplicaTypes.CONSTRUCTION.value:
|
||||
stream.write(c_bit(False))
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls, stream: ReadStream) -> Serializable:
|
||||
raise Exception("This struct cannot be deserialized")
|
||||
|
||||
|
||||
class RenderComponent(Serializable):
|
||||
"""
|
||||
Not even really sure what this is for
|
||||
"""
|
||||
def __init__(self, replica_mode=0):
|
||||
self.replica_mode = replica_mode
|
||||
# TODO: I should probably at least implement this struct
|
||||
|
||||
def serialize(self, stream: WriteStream) -> None:
|
||||
if self.replica_mode == ReplicaTypes.CONSTRUCTION.value:
|
||||
stream.write(c_int32(0))
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls, stream: ReadStream) -> Serializable:
|
||||
raise Exception("This struct cannot be deserialized")
|
||||
|
||||
|
||||
class Component107(Serializable):
|
||||
|
||||
def __init__(self, replica_mode = 0):
|
||||
self.replica_mode = replica_mode
|
||||
|
||||
def serialize(self, stream: WriteStream) -> None:
|
||||
stream.write(c_bit(False))
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls, stream: ReadStream) -> Serializable:
|
||||
raise Exception("This struct cannot be deserialized")
|
||||
|
||||
|
||||
class DestructibleComponent:
|
||||
|
||||
def __int__(self, replica_mode=0):
|
||||
self.replica_mode = replica_mode
|
||||
self.destructible_index = DestructibleIndex()
|
||||
self.stats_index = StatsIndex()
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
Contains all the packets that the world/char server would send to the client
|
||||
"""
|
||||
from pyraknet import bitstream
|
||||
from plugins.serializables.misc_serializables import CString, Vector3, LUZ
|
||||
from plugins.serializables.misc_serializables import CString, Vector3, LUZ, LDF
|
||||
from plugins.easy_cdclient.cdclient_objects import Zone
|
||||
import zlib
|
||||
|
||||
|
||||
class MinfigureListPacket(bitstream.Serializable):
|
||||
@@ -82,7 +83,7 @@ class WorldInfoPacket(bitstream.Serializable):
|
||||
self.editor_enabled = False
|
||||
self.editor_level = 0
|
||||
self.player_position = Vector3()
|
||||
self.is_in_battle = 0 # If in battle put 4??
|
||||
self.activity = 0 # If in battle put 4??
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls, stream: bitstream.ReadStream) -> bitstream.Serializable:
|
||||
@@ -96,7 +97,7 @@ class WorldInfoPacket(bitstream.Serializable):
|
||||
stream.write(bitstream.c_bool(self.editor_enabled))
|
||||
stream.write(bitstream.c_uint8(self.editor_level))
|
||||
stream.write(self.player_position)
|
||||
stream.write(bitstream.c_uint32(self.is_in_battle))
|
||||
stream.write(bitstream.c_uint32(self.activity))
|
||||
|
||||
@classmethod
|
||||
def from_cdclient(cls, zone_id, default_spawn=False) -> "WorldInfoPacket":
|
||||
@@ -111,3 +112,28 @@ class WorldInfoPacket(bitstream.Serializable):
|
||||
luz = stream.read(LUZ)
|
||||
world_info.player_position = luz.spawnpoint_position
|
||||
return world_info
|
||||
|
||||
|
||||
class DetailedUserInfoPacket(bitstream.Serializable):
|
||||
"""
|
||||
[53-05-00-04]
|
||||
Send after client load complete packet
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
self.ldf = LDF()
|
||||
|
||||
@classmethod
|
||||
def deserialize(cls, stream: bitstream.ReadStream) -> bitstream.Serializable:
|
||||
raise Exception("This packet cannot be deserialized")
|
||||
|
||||
def serialize(self, stream: bitstream.WriteStream) -> None:
|
||||
temp_stream = bitstream.WriteStream()
|
||||
temp_stream.write(self.ldf)
|
||||
temp_bytes = temp_stream.__bytes__()
|
||||
compressed_bytes = zlib.compress(temp_bytes)
|
||||
stream.write(bitstream.c_ulong(len(compressed_bytes) + 9))
|
||||
stream.write(bitstream.c_bool(True))
|
||||
stream.write(bitstream.c_ulong(len(temp_bytes)))
|
||||
stream.write(bitstream.c_ulong(len(compressed_bytes)))
|
||||
stream.write(compressed_bytes)
|
||||
|
||||
@@ -1,16 +1,19 @@
|
||||
"""
|
||||
This will handle all of the character related packets.
|
||||
"""
|
||||
from ..serializables import packet_enum, world_to_client, client_to_world, global_packets, misc_serializables
|
||||
from ..serializables import packet_enum, world_to_client, client_to_world, global_packets, misc_serializables, replica_components
|
||||
from pyraknet.bitstream import *
|
||||
from pyraknet.replicamanager import *
|
||||
from pyraknet.messages import Message
|
||||
from ..char_handler import char_handlers
|
||||
from xml.etree import ElementTree
|
||||
|
||||
|
||||
class Plugin:
|
||||
def __init__(self, parent):
|
||||
print("World Handler Initiated")
|
||||
parent.register_handler(char_handlers.Plugin.handle_minifigure_list_request,
|
||||
packet_enum.PacketHeader.CLIENT_USER_SESSION_INFO.value)
|
||||
parent.register_handler(Plugin.handle_minifigure_list_request,
|
||||
packet_enum.PacketHeader.CLIENT_MINIFIGURE_LIST_REQUEST.value)
|
||||
parent.register_handler(char_handlers.Plugin.handle_minifigure_creation,
|
||||
packet_enum.PacketHeader.CLIENT_MINIFIGURE_CREATE_REQUEST.value)
|
||||
parent.register_handler(char_handlers.Plugin.handle_load_world,
|
||||
@@ -21,34 +24,265 @@ class Plugin:
|
||||
parent.register_handler(Plugin.handle_handshake, packet_enum.PacketHeader.HANDSHAKE.value)
|
||||
parent.register_handler(Plugin.handle_detailed_user_info,
|
||||
packet_enum.PacketHeader.CLIENT_LOAD_COMPLETE.value)
|
||||
parent.register_handler(Plugin.handle_sesion_info, packet_enum.PacketHeader.CLIENT_USER_SESSION_INFO.value)
|
||||
|
||||
parent.additional_config["replica_manager"] = ReplicaManager(parent)
|
||||
|
||||
@classmethod
|
||||
def handle_handshake(cls, data: bytes, address, server):
|
||||
"""
|
||||
Handles initial connection between server and client.
|
||||
"""
|
||||
player = server.lookup_player_by_ip(address[0])
|
||||
c = server.db_connection.cursor()
|
||||
c.execute("SELECT position FROM wlus.character_info WHERE player_id = %s", (int(player["id"]),))
|
||||
player_pos = c.fetchone()[0]
|
||||
if player_pos == "0,0,0":
|
||||
world_info = world_to_client.WorldInfoPacket.from_cdclient(server.zone, default_spawn=True)
|
||||
c.execute("UPDATE wlus.character_info SET position = %s WHERE player_id = %s",
|
||||
(str(world_info.player_position), int(player["id"])))
|
||||
server.db_connection.commit()
|
||||
else:
|
||||
world_info = world_to_client.WorldInfoPacket.from_cdclient(server.zone, default_spawn=False)
|
||||
points = player_pos.split(",")
|
||||
pos = misc_serializables.Vector3()
|
||||
pos.x = float(points[0])
|
||||
pos.y = float(points[1])
|
||||
pos.z = float(points[2])
|
||||
world_info.player_position = pos
|
||||
stream = ReadStream(data)
|
||||
client_handshake = stream.read(global_packets.HandshakePacket)
|
||||
print(f"{address[0]} has initiated handshake - Client has version {client_handshake.game_version}")
|
||||
|
||||
server_handshake = global_packets.HandshakePacket()
|
||||
server_handshake.remote_connection_type = 4
|
||||
packet = WriteStream()
|
||||
packet.write(packet_enum.PacketHeader.WORLD_INFO.value)
|
||||
packet.write(world_info)
|
||||
packet.write(packet_enum.PacketHeader.HANDSHAKE.value)
|
||||
packet.write(server_handshake)
|
||||
server.send(packet, address)
|
||||
|
||||
@classmethod
|
||||
def handle_sesion_info(cls, data: bytes, address, server):
|
||||
stream = ReadStream(data)
|
||||
session_info = stream.read(client_to_world.UserSessionInfoPacket)
|
||||
c = server.db_connection.cursor()
|
||||
session = server.lookup_session_by_username(session_info.username)
|
||||
|
||||
if session is not None:
|
||||
if session["user_key"] == session_info.user_key:
|
||||
# Add user to replica manager
|
||||
server.additional_config["replica_manager"].add_participant(address)
|
||||
|
||||
# Send world info packet if the session key is correct
|
||||
player = server.lookup_player_by_ip(address[0])
|
||||
c.execute("SELECT position FROM wlus.character_info WHERE player_id = %s", (int(player["id"]),))
|
||||
player_pos = c.fetchone()[0]
|
||||
if player_pos == "0,0,0":
|
||||
world_info = world_to_client.WorldInfoPacket.from_cdclient(server.zone, default_spawn=True)
|
||||
c.execute("UPDATE wlus.character_info SET position = %s WHERE player_id = %s",
|
||||
(str(world_info.player_position), int(player["id"])))
|
||||
server.db_connection.commit()
|
||||
else:
|
||||
world_info = world_to_client.WorldInfoPacket.from_cdclient(server.zone, default_spawn=False)
|
||||
points = player_pos.split(",")
|
||||
pos = misc_serializables.Vector3()
|
||||
pos.x = float(points[0])
|
||||
pos.y = float(points[1])
|
||||
pos.z = float(points[2])
|
||||
world_info.player_position = pos
|
||||
packet = WriteStream()
|
||||
packet.write(packet_enum.PacketHeader.WORLD_INFO.value)
|
||||
packet.write(world_info)
|
||||
server.send(packet, address)
|
||||
|
||||
else:
|
||||
disconnect = global_packets.DisconnectNotifyPacket()
|
||||
disconnect.disconnect_id = packet_enum.DisconnectionNotify.INVALID_SESSION_KEY.value
|
||||
disconnect.send(server, address)
|
||||
else:
|
||||
disconnect = global_packets.DisconnectNotifyPacket()
|
||||
disconnect.disconnect_id = packet_enum.DisconnectionNotify.INVALID_SESSION_KEY.value
|
||||
disconnect.send(server, address)
|
||||
|
||||
@classmethod
|
||||
def handle_detailed_user_info(cls, data: bytes, address, server):
|
||||
print("Client Load Complete")
|
||||
# I'm honestly just going to ignore what the client sends here. I don't need to do anything with it
|
||||
print(f"Sending detailed user info to {address}")
|
||||
player_info = server.lookup_player_by_ip(address[0])
|
||||
c = server.db_connection.cursor()
|
||||
c.execute("SELECT backpack_space, currency, universe_score, level, position, rotation, health,"
|
||||
" max_health, armor, max_armor, imagination, max_imagination FROM character_info WHERE player_id = %s"
|
||||
, (player_info["id"],))
|
||||
character_info = c.fetchone()
|
||||
c.execute("SELECT * FROM wlus.inventory WHERE player_id = %s", (player_info["id"],))
|
||||
inventory = c.fetchall()
|
||||
c.execute("SELECT mission_id FROM wlus.completed_missions WHERE player_id = %s", (player_info["id"],))
|
||||
complete_missions = c.fetchall()
|
||||
c.execute("SELECT mission_id, progress FROM wlus.current_missions WHERE player_id = %s", (player_info["id"],))
|
||||
current_missions = c.fetchall()
|
||||
|
||||
dui = world_to_client.DetailedUserInfoPacket()
|
||||
dui.ldf.register_key("levelid", int(player_info["current_zone"]), 1)
|
||||
dui.ldf.register_key("objid", int(player_info["id"]), 9)
|
||||
dui.ldf.register_key("template", 1, 1)
|
||||
dui.ldf.register_key("name", player_info["name"], 0)
|
||||
|
||||
root = ElementTree.Element("obj")
|
||||
root.set("v", "1")
|
||||
buff = ElementTree.SubElement(root, "buff")
|
||||
skill = ElementTree.SubElement(root, "skill")
|
||||
|
||||
inv = ElementTree.SubElement(root, "inv")
|
||||
bag = ElementTree.SubElement(inv, "bag")
|
||||
bag_info = ElementTree.SubElement(bag, "b")
|
||||
bag_info.set("t", "0")
|
||||
bag_info.set("m", str(character_info[0]))
|
||||
items = ElementTree.SubElement(inv, "items")
|
||||
item_in = ElementTree.SubElement(items, "in")
|
||||
for item in inventory:
|
||||
i = ElementTree.SubElement(item_in, "i")
|
||||
i.set("l", str(item[1]))
|
||||
i.set("id", str(item[0]))
|
||||
i.set("s", str(item[2]))
|
||||
i.set("c", str(item[5]))
|
||||
i.set("b", str(item[4]))
|
||||
i.set("eq", str(item[3]))
|
||||
|
||||
mf = ElementTree.SubElement(root, "mf")
|
||||
char = ElementTree.SubElement(root, "char")
|
||||
char.set("cc", str(character_info[1]))
|
||||
char.set("ls", str(character_info[2]))
|
||||
lvl = ElementTree.SubElement(root, "lvl")
|
||||
lvl.set("l", str(character_info[3]))
|
||||
|
||||
pets = ElementTree.SubElement(root, "pet")
|
||||
|
||||
mis = ElementTree.SubElement(root, "mis")
|
||||
done = ElementTree.SubElement(mis, "done")
|
||||
for mission in complete_missions:
|
||||
m = ElementTree.SubElement(done, "m")
|
||||
m.set("id", str(mission[0]))
|
||||
m.set("cct", "1")
|
||||
m.set("cts", "0")
|
||||
cur = ElementTree.SubElement(mis, "cur")
|
||||
for mission in current_missions:
|
||||
m = ElementTree.SubElement(cur, "m")
|
||||
m.set("id", str(mission[0]))
|
||||
sv = ElementTree.SubElement(m, "sv")
|
||||
sv.set("v", str(mission[1]))
|
||||
|
||||
dui.ldf.register_key("xmlData", root, 13)
|
||||
|
||||
packet = WriteStream()
|
||||
packet.write(packet_enum.PacketHeader.DETAILED_USER_INFO.value)
|
||||
packet.write(dui)
|
||||
server.send(packet, address)
|
||||
|
||||
c.execute("SELECT * FROM character_stats WHERE player_id = %s", (player_info["id"],))
|
||||
character_stats = c.fetchone()
|
||||
c.execute("SELECT * FROM wlus.character WHERE character_id = %s", (player_info["id"],))
|
||||
character_data = c.fetchone()
|
||||
|
||||
player = replica_components.ReplicaObject()
|
||||
player.base_data.lot = 1
|
||||
player.base_data.object_id = int(player_info["id"])
|
||||
player.base_data.name = player_info["name"]
|
||||
|
||||
player.components[packet_enum.SerializedComponents.COMPONENT_107.value] = replica_components.Component107()
|
||||
|
||||
player.components[packet_enum.SerializedComponents.RENDER.value] = replica_components.RenderComponent()
|
||||
|
||||
player.components[packet_enum.SerializedComponents.SKILL.value] = replica_components.SkillComponent()
|
||||
|
||||
inventory_comp = replica_components.InventoryComponent()
|
||||
for item in inventory:
|
||||
inventory_item = replica_components.InventoryItem()
|
||||
inventory_item.lot = item[1]
|
||||
inventory_item.slot = item[2]
|
||||
inventory_item.count = item[5]
|
||||
inventory_item.object_id = item[0]
|
||||
inventory_comp.items.append(inventory_item)
|
||||
player.components[packet_enum.SerializedComponents.INVENTORY.value] = inventory_comp
|
||||
|
||||
controllable_physics = replica_components.ControllablePhysicsComponent()
|
||||
pos = misc_serializables.Vector3()
|
||||
p_points = character_info[4].split(",")
|
||||
pos.x = float(p_points[0])
|
||||
pos.y = float(p_points[1])
|
||||
pos.z = float(p_points[2])
|
||||
controllable_physics.position = pos
|
||||
rot = misc_serializables.Vector4()
|
||||
r_points = character_info[5].split(",")
|
||||
rot.x = float(r_points[0])
|
||||
rot.y = float(r_points[1])
|
||||
rot.z = float(r_points[2])
|
||||
rot.w = float(r_points[3])
|
||||
controllable_physics.rotation = rot
|
||||
player.components[packet_enum.SerializedComponents.CONTROLLABLE_PHYSICS.value] = controllable_physics
|
||||
|
||||
destructible_component = replica_components.DestructibleComponent()
|
||||
stats_index = replica_components.StatsIndex()
|
||||
stats_index.factions.append(1)
|
||||
stats_index.health = character_info[6]
|
||||
stats_index.max_health = character_info[7]
|
||||
stats_index.armor = character_info[8]
|
||||
stats_index.max_armor = character_info[9]
|
||||
stats_index.imagination = character_info[10]
|
||||
stats_index.max_imagination = character_info[11]
|
||||
destructible_component.stats_index = stats_index
|
||||
destructible_component.destructible_index = replica_components.DestructibleIndex()
|
||||
player.components[packet_enum.SerializedComponents.DESTRUCTIBLE.value] = destructible_component
|
||||
|
||||
character_component = replica_components.CharacterComponent()
|
||||
character_component.character_stats = character_stats[1:]
|
||||
character_component.lego_score = character_info[2]
|
||||
character_component.shirt_color = character_data[5]
|
||||
character_component.pants_color = character_data[7]
|
||||
character_component.hair_style = character_data[8]
|
||||
character_component.hair_color = character_data[9]
|
||||
character_component.eyebrows = character_data[12]
|
||||
character_component.eyes = character_data[13]
|
||||
character_component.mouth = character_data[14]
|
||||
character_component.account_id = character_data[15]
|
||||
character_component.activity = 1
|
||||
player.components[packet_enum.SerializedComponents.CHARACTER.value] = character_component
|
||||
|
||||
server.additional_config["replica_manager"].construct(player)
|
||||
|
||||
server_done = WriteStream()
|
||||
server_done.write(packet_enum.PacketHeader.SERVER_GAME_MESSAGE.value)
|
||||
server_done.write(c_int64(int(player_info["id"])))
|
||||
server_done.write(c_uint16(packet_enum.GameMessages.SERVER_DONE_LOADING_OBJECTS.value))
|
||||
server.send(server_done, address)
|
||||
|
||||
player_ready = WriteStream()
|
||||
player_ready.write(packet_enum.PacketHeader.SERVER_GAME_MESSAGE.value)
|
||||
player_ready.write(c_int64(int(player_info["id"])))
|
||||
player_ready.write(c_uint16(packet_enum.GameMessages.PLAYER_READY.value))
|
||||
server.send(player_ready, address)
|
||||
|
||||
# This is just slightly different from the character instance handler
|
||||
@classmethod
|
||||
def handle_minifigure_list_request(cls, data: bytes, address, server):
|
||||
c = server.db_connection.cursor()
|
||||
packet = WriteStream()
|
||||
session = server.lookup_session_by_ip(address[0])
|
||||
c.execute("SELECT account_id FROM account WHERE username = %s", (session["username"],))
|
||||
account_id = c.fetchone()[0]
|
||||
|
||||
characters = []
|
||||
c.execute("SELECT * FROM wlus.character WHERE account_id = %s", (account_id,))
|
||||
minifig_results = c.fetchall()
|
||||
for minifig in minifig_results:
|
||||
char = misc_serializables.LoginCharacter()
|
||||
char.object_id = minifig[0]
|
||||
char.current_name = minifig[1]
|
||||
char.unapproved_name = minifig[2]
|
||||
char.head_color = minifig[3]
|
||||
char.head = minifig[4]
|
||||
char.chest_color = minifig[5]
|
||||
char.chest = minifig[6]
|
||||
char.legs = minifig[7]
|
||||
char.hair_style = minifig[8]
|
||||
char.hair_color = minifig[9]
|
||||
char.left_hand = minifig[10]
|
||||
char.right_hand = minifig[11]
|
||||
char.eyebrows_style = minifig[12]
|
||||
char.eyes_style = minifig[13]
|
||||
char.mouth_style = minifig[14]
|
||||
c.execute("SELECT * FROM wlus.inventory WHERE player_id = %s AND equipped = 1", (char.object_id,))
|
||||
equipped_items = c.fetchall()
|
||||
for i in equipped_items:
|
||||
char.equipped_items.append(i[1])
|
||||
characters.append(char)
|
||||
|
||||
char_list = world_to_client.MinfigureListPacket()
|
||||
for c in characters:
|
||||
char_list.minifigs.append(c)
|
||||
|
||||
packet.write(packet_enum.PacketHeader.MINIFIGURE_LIST.value)
|
||||
packet.write(char_list)
|
||||
server.send(packet, address)
|
||||
|
||||
Reference in New Issue
Block a user