122 lines
4.0 KiB
Go
122 lines
4.0 KiB
Go
package network
|
|
|
|
import (
|
|
"encoding/binary"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"google.golang.org/protobuf/proto"
|
|
|
|
pb "a301_game_server/proto/gen/pb"
|
|
)
|
|
|
|
// Message type IDs — the wire protocol uses 2-byte type prefixes.
|
|
const (
|
|
// Auth
|
|
MsgLoginRequest uint16 = 0x0001
|
|
MsgLoginResponse uint16 = 0x0002
|
|
MsgEnterWorldRequest uint16 = 0x0003
|
|
MsgEnterWorldResponse uint16 = 0x0004
|
|
|
|
// Movement
|
|
MsgMoveRequest uint16 = 0x0010
|
|
MsgStateUpdate uint16 = 0x0011
|
|
MsgSpawnEntity uint16 = 0x0012
|
|
MsgDespawnEntity uint16 = 0x0013
|
|
|
|
// Zone Transfer
|
|
MsgZoneTransferNotify uint16 = 0x0014
|
|
|
|
// System
|
|
MsgPing uint16 = 0x0020
|
|
MsgPong uint16 = 0x0021
|
|
|
|
// Combat
|
|
MsgUseSkillRequest uint16 = 0x0040
|
|
MsgUseSkillResponse uint16 = 0x0041
|
|
MsgCombatEvent uint16 = 0x0042
|
|
MsgBuffApplied uint16 = 0x0043
|
|
MsgBuffRemoved uint16 = 0x0044
|
|
MsgRespawnRequest uint16 = 0x0045
|
|
MsgRespawnResponse uint16 = 0x0046
|
|
|
|
// Admin / Debug
|
|
MsgAOIToggleRequest uint16 = 0x0030
|
|
MsgAOIToggleResponse uint16 = 0x0031
|
|
MsgMetricsRequest uint16 = 0x0032
|
|
MsgServerMetrics uint16 = 0x0033
|
|
)
|
|
|
|
var (
|
|
ErrUnknownMessageType = errors.New("unknown message type")
|
|
ErrMessageTooShort = errors.New("message too short")
|
|
)
|
|
|
|
// Packet is a decoded network message.
|
|
type Packet struct {
|
|
Type uint16
|
|
Payload proto.Message
|
|
}
|
|
|
|
// messageFactory maps type IDs to protobuf message constructors.
|
|
var messageFactory = map[uint16]func() proto.Message{
|
|
MsgLoginRequest: func() proto.Message { return &pb.LoginRequest{} },
|
|
MsgLoginResponse: func() proto.Message { return &pb.LoginResponse{} },
|
|
MsgEnterWorldRequest: func() proto.Message { return &pb.EnterWorldRequest{} },
|
|
MsgEnterWorldResponse: func() proto.Message { return &pb.EnterWorldResponse{} },
|
|
MsgMoveRequest: func() proto.Message { return &pb.MoveRequest{} },
|
|
MsgStateUpdate: func() proto.Message { return &pb.StateUpdate{} },
|
|
MsgSpawnEntity: func() proto.Message { return &pb.SpawnEntity{} },
|
|
MsgDespawnEntity: func() proto.Message { return &pb.DespawnEntity{} },
|
|
MsgPing: func() proto.Message { return &pb.Ping{} },
|
|
MsgPong: func() proto.Message { return &pb.Pong{} },
|
|
MsgZoneTransferNotify: func() proto.Message { return &pb.ZoneTransferNotify{} },
|
|
MsgUseSkillRequest: func() proto.Message { return &pb.UseSkillRequest{} },
|
|
MsgUseSkillResponse: func() proto.Message { return &pb.UseSkillResponse{} },
|
|
MsgCombatEvent: func() proto.Message { return &pb.CombatEvent{} },
|
|
MsgBuffApplied: func() proto.Message { return &pb.BuffApplied{} },
|
|
MsgBuffRemoved: func() proto.Message { return &pb.BuffRemoved{} },
|
|
MsgRespawnRequest: func() proto.Message { return &pb.RespawnRequest{} },
|
|
MsgRespawnResponse: func() proto.Message { return &pb.RespawnResponse{} },
|
|
MsgAOIToggleRequest: func() proto.Message { return &pb.AOIToggleRequest{} },
|
|
MsgAOIToggleResponse: func() proto.Message { return &pb.AOIToggleResponse{} },
|
|
MsgMetricsRequest: func() proto.Message { return &pb.MetricsRequest{} },
|
|
MsgServerMetrics: func() proto.Message { return &pb.ServerMetrics{} },
|
|
}
|
|
|
|
// Encode serializes a packet into a wire-format byte slice: [2-byte type][protobuf payload].
|
|
func Encode(msgType uint16, msg proto.Message) ([]byte, error) {
|
|
payload, err := proto.Marshal(msg)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("marshal message 0x%04X: %w", msgType, err)
|
|
}
|
|
|
|
buf := make([]byte, 2+len(payload))
|
|
binary.BigEndian.PutUint16(buf[:2], msgType)
|
|
copy(buf[2:], payload)
|
|
return buf, nil
|
|
}
|
|
|
|
// Decode parses a wire-format byte slice into a Packet.
|
|
func Decode(data []byte) (*Packet, error) {
|
|
if len(data) < 2 {
|
|
return nil, ErrMessageTooShort
|
|
}
|
|
|
|
msgType := binary.BigEndian.Uint16(data[:2])
|
|
|
|
factory, ok := messageFactory[msgType]
|
|
if !ok {
|
|
return nil, fmt.Errorf("%w: 0x%04X", ErrUnknownMessageType, msgType)
|
|
}
|
|
|
|
msg := factory()
|
|
if len(data) > 2 {
|
|
if err := proto.Unmarshal(data[2:], msg); err != nil {
|
|
return nil, fmt.Errorf("unmarshal message 0x%04X: %w", msgType, err)
|
|
}
|
|
}
|
|
|
|
return &Packet{Type: msgType, Payload: msg}, nil
|
|
}
|