1
0
game/server/net/packets.go

206 lines
5.6 KiB
Go

package net
import (
"encoding/binary"
"math"
)
// Message type constants
const (
MSG_LOGIN = 0x01
MSG_POSITION = 0x02
MSG_SPAWN = 0x03
MSG_MOVE = 0x04
MSG_UPDATE = 0x05
MSG_PLAYER_JOINED = 0x06
MSG_PLAYER_LEFT = 0x07
MSG_PLAYER_LIST = 0x08
MSG_CHANGE_COLOR = 0x09
MSG_COLOR_CHANGED = 0x0A
MSG_LOGIN_RESPONSE = 0x0B
MSG_LOGOUT = 0x0C
MSG_HEARTBEAT = 0x0D
MSG_TIME_SYNC = 0x0E
)
// Vec3 represents a 3D vector
type Vec3 struct {
X, Y, Z float32
}
// EncodeSpawnPacket creates a spawn packet
func EncodeSpawnPacket(playerID uint32, position Vec3, color string) []byte {
colorBytes := []byte(color)
msg := make([]byte, 18+len(colorBytes))
msg[0] = MSG_SPAWN
binary.LittleEndian.PutUint32(msg[1:5], playerID)
binary.LittleEndian.PutUint32(msg[5:9], math.Float32bits(position.X))
binary.LittleEndian.PutUint32(msg[9:13], math.Float32bits(position.Y))
binary.LittleEndian.PutUint32(msg[13:17], math.Float32bits(position.Z))
msg[17] = uint8(len(colorBytes))
copy(msg[18:], colorBytes)
return msg
}
// EncodeUpdatePacket creates an update packet
func EncodeUpdatePacket(playerID uint32, position Vec3) []byte {
msg := make([]byte, 17)
msg[0] = MSG_UPDATE
binary.LittleEndian.PutUint32(msg[1:5], playerID)
binary.LittleEndian.PutUint32(msg[5:9], math.Float32bits(position.X))
binary.LittleEndian.PutUint32(msg[9:13], math.Float32bits(position.Y))
binary.LittleEndian.PutUint32(msg[13:17], math.Float32bits(position.Z))
return msg
}
// EncodePlayerJoinedPacket creates a player joined packet
func EncodePlayerJoinedPacket(playerID uint32, position Vec3, color string) []byte {
colorBytes := []byte(color)
msg := make([]byte, 18+len(colorBytes))
msg[0] = MSG_PLAYER_JOINED
binary.LittleEndian.PutUint32(msg[1:5], playerID)
binary.LittleEndian.PutUint32(msg[5:9], math.Float32bits(position.X))
binary.LittleEndian.PutUint32(msg[9:13], math.Float32bits(position.Y))
binary.LittleEndian.PutUint32(msg[13:17], math.Float32bits(position.Z))
msg[17] = uint8(len(colorBytes))
copy(msg[18:], colorBytes)
return msg
}
// EncodePlayerLeftPacket creates a player left packet
func EncodePlayerLeftPacket(playerID uint32) []byte {
msg := make([]byte, 5)
msg[0] = MSG_PLAYER_LEFT
binary.LittleEndian.PutUint32(msg[1:5], playerID)
return msg
}
// EncodeColorChangedPacket creates a color changed packet
func EncodeColorChangedPacket(playerID uint32, color string) []byte {
colorBytes := []byte(color)
msg := make([]byte, 6+len(colorBytes))
msg[0] = MSG_COLOR_CHANGED
binary.LittleEndian.PutUint32(msg[1:5], playerID)
msg[5] = uint8(len(colorBytes))
copy(msg[6:], colorBytes)
return msg
}
// EncodePlayerListPacket creates a player list packet
func EncodePlayerListPacket(players []*Player) []byte {
if len(players) == 0 {
return []byte{MSG_PLAYER_LIST, 0}
}
msg := make([]byte, 1024)
msg[0] = MSG_PLAYER_LIST
msg[1] = uint8(len(players))
offset := 2
for _, p := range players {
binary.LittleEndian.PutUint32(msg[offset:], p.ID)
binary.LittleEndian.PutUint32(msg[offset+4:], math.Float32bits(p.Position.X))
binary.LittleEndian.PutUint32(msg[offset+8:], math.Float32bits(p.Position.Y))
binary.LittleEndian.PutUint32(msg[offset+12:], math.Float32bits(p.Position.Z))
colorBytes := []byte(p.Color)
msg[offset+16] = uint8(len(colorBytes))
copy(msg[offset+17:], colorBytes)
offset += 17 + len(colorBytes)
if offset > 1000 {
break // Prevent overflow
}
}
return msg[:offset]
}
// DecodeMovePacket decodes a move packet
func DecodeMovePacket(data []byte) (playerID uint32, delta Vec3, ok bool) {
if len(data) < 17 {
return 0, Vec3{}, false
}
playerID = binary.LittleEndian.Uint32(data[1:5])
delta.X = math.Float32frombits(binary.LittleEndian.Uint32(data[5:9]))
delta.Y = math.Float32frombits(binary.LittleEndian.Uint32(data[9:13]))
delta.Z = math.Float32frombits(binary.LittleEndian.Uint32(data[13:17]))
return playerID, delta, true
}
// DecodeColorChangePacket decodes a color change packet
func DecodeColorChangePacket(data []byte) (playerID uint32, color string, ok bool) {
if len(data) < 6 {
return 0, "", false
}
playerID = binary.LittleEndian.Uint32(data[1:5])
colorLen := data[5]
if len(data) < 6+int(colorLen) {
return 0, "", false
}
color = string(data[6 : 6+colorLen])
return playerID, color, true
}
// EncodeLoginResponsePacket creates a login response packet
func EncodeLoginResponsePacket(success bool, message string) []byte {
msgBytes := []byte(message)
msg := make([]byte, 3+len(msgBytes))
msg[0] = MSG_LOGIN_RESPONSE
if success {
msg[1] = 1
} else {
msg[1] = 0
}
msg[2] = uint8(len(msgBytes))
copy(msg[3:], msgBytes)
return msg
}
// DecodeLoginPacket decodes a login packet with username
func DecodeLoginPacket(data []byte) (username string, ok bool) {
if len(data) < 2 {
return "", false
}
usernameLen := data[1]
if len(data) < 2+int(usernameLen) {
return "", false
}
username = string(data[2 : 2+usernameLen])
return username, true
}
// DecodeLogoutPacket decodes a logout packet
func DecodeLogoutPacket(data []byte) (playerID uint32, ok bool) {
if len(data) < 5 {
return 0, false
}
playerID = binary.LittleEndian.Uint32(data[1:5])
return playerID, true
}
// DecodeHeartbeatPacket decodes a heartbeat packet
func DecodeHeartbeatPacket(data []byte) (playerID uint32, ok bool) {
if len(data) < 5 {
return 0, false
}
playerID = binary.LittleEndian.Uint32(data[1:5])
return playerID, true
}
// EncodeTimeSyncPacket creates a time sync packet
func EncodeTimeSyncPacket(timeOfDay float32) []byte {
msg := make([]byte, 5)
msg[0] = MSG_TIME_SYNC
binary.LittleEndian.PutUint32(msg[1:5], math.Float32bits(timeOfDay))
return msg
}