package packets import ( "encoding/binary" "math" ) // Message type constants const ( // Login server messages MSG_LOGIN_REQUEST = 0x20 MSG_REGISTER_REQUEST = 0x21 MSG_LOGIN_RESPONSE = 0x22 // World server messages MSG_AUTH = 0x30 MSG_AUTH_RESPONSE = 0x31 MSG_SPAWN = 0x03 MSG_MOVE = 0x04 MSG_UPDATE = 0x05 MSG_PLAYER_JOINED = 0x06 MSG_PLAYER_LEFT = 0x07 MSG_PLAYER_LIST = 0x08 MSG_HEARTBEAT = 0x0D MSG_TIME_SYNC = 0x0E MSG_LOGOUT = 0x0C ) // EncodeLoginResponse creates a login response packet func EncodeLoginResponse(success bool, token []byte, worldHost string, worldPort uint16, playerID uint32, message string) []byte { msgBytes := []byte(message) hostBytes := []byte(worldHost) // Calculate total size size := 1 + 1 + 1 + len(msgBytes) // type + success + msgLen + message if success { size += 32 + 1 + len(hostBytes) + 2 + 4 // token + hostLen + host + port + playerID } msg := make([]byte, size) offset := 0 msg[offset] = MSG_LOGIN_RESPONSE offset++ if success { msg[offset] = 1 } else { msg[offset] = 0 } offset++ msg[offset] = uint8(len(msgBytes)) offset++ copy(msg[offset:], msgBytes) offset += len(msgBytes) if success { copy(msg[offset:offset+32], token) offset += 32 msg[offset] = uint8(len(hostBytes)) offset++ copy(msg[offset:], hostBytes) offset += len(hostBytes) binary.LittleEndian.PutUint16(msg[offset:], worldPort) offset += 2 binary.LittleEndian.PutUint32(msg[offset:], playerID) } return msg } // DecodeLoginRequest decodes a login request packet func DecodeLoginRequest(data []byte) (username, password string, ok bool) { if len(data) < 3 { return "", "", false } offset := 1 // Skip message type usernameLen := data[offset] offset++ if len(data) < offset+int(usernameLen)+1 { return "", "", false } username = string(data[offset : offset+int(usernameLen)]) offset += int(usernameLen) passwordLen := data[offset] offset++ if len(data) < offset+int(passwordLen) { return "", "", false } password = string(data[offset : offset+int(passwordLen)]) return username, password, true } // DecodeRegisterRequest decodes a register request packet func DecodeRegisterRequest(data []byte) (username, password string, ok bool) { // Same format as login request return DecodeLoginRequest(data) } // EncodeAuthResponse creates an auth response packet func EncodeAuthResponse(success bool, message string) []byte { msgBytes := []byte(message) msg := make([]byte, 3+len(msgBytes)) msg[0] = MSG_AUTH_RESPONSE if success { msg[1] = 1 } else { msg[1] = 0 } msg[2] = uint8(len(msgBytes)) copy(msg[3:], msgBytes) return msg } // DecodeAuth decodes an auth packet with token func DecodeAuth(data []byte) (token []byte, ok bool) { if len(data) < 33 { // 1 byte type + 32 byte token return nil, false } token = make([]byte, 32) copy(token, data[1:33]) return token, true } // EncodeSpawnPacket creates a spawn packet func EncodeSpawnPacket(playerID uint32, position Vec3) []byte { msg := make([]byte, 17) 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)) 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, username string) []byte { usernameBytes := []byte(username) msg := make([]byte, 18+len(usernameBytes)) 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(usernameBytes)) copy(msg[18:], usernameBytes) 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 } // 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 } // 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 } // 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 } // 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 }