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 ) // 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 }