big ol refactor

This commit is contained in:
2025-04-16 12:13:29 +02:00
parent b866ac879e
commit 9d60d5e9cd
10 changed files with 747 additions and 382 deletions

View File

@ -26,6 +26,138 @@ func SetServerAddr(addr string) {
log.Printf("Server address set to: %s", serverAddr)
}
// MessageHandler handles reading and writing protobuf messages
type MessageHandler struct {
conn net.Conn
reader *bufio.Reader
}
// NewMessageHandler creates a new message handler
func NewMessageHandler(conn net.Conn) *MessageHandler {
return &MessageHandler{
conn: conn,
reader: bufio.NewReader(conn),
}
}
// ReadMessage reads a single message from the network
func (mh *MessageHandler) ReadMessage() (*pb.ServerMessage, error) {
// Read message length
lengthBuf := make([]byte, 4)
if _, err := io.ReadFull(mh.reader, lengthBuf); err != nil {
return nil, fmt.Errorf("failed to read message length: %v", err)
}
messageLength := binary.BigEndian.Uint32(lengthBuf)
// Sanity check message size
if messageLength > 1024*1024 { // 1MB max message size
return nil, fmt.Errorf("message size too large: %d bytes", messageLength)
}
// Read message body
messageBuf := make([]byte, messageLength)
if _, err := io.ReadFull(mh.reader, messageBuf); err != nil {
return nil, fmt.Errorf("failed to read message body: %v", err)
}
// Unmarshal the message
var message pb.ServerMessage
if err := proto.Unmarshal(messageBuf, &message); err != nil {
return nil, fmt.Errorf("failed to unmarshal message: %v", err)
}
return &message, nil
}
// WriteMessage writes a protobuf message to the network
func (mh *MessageHandler) WriteMessage(msg proto.Message) error {
data, err := proto.Marshal(msg)
if err != nil {
return err
}
// Write length prefix
lengthBuf := make([]byte, 4)
binary.BigEndian.PutUint32(lengthBuf, uint32(len(data)))
if _, err := mh.conn.Write(lengthBuf); err != nil {
return err
}
// Write message body
_, err = mh.conn.Write(data)
return err
}
// UpdateGameState processes a server message and updates game state
func UpdateGameState(serverMessage *pb.ServerMessage, player *types.Player, otherPlayers map[int32]*types.Player) {
playerID := player.ID
player.Lock()
player.CurrentTick = serverMessage.CurrentTick
tickDiff := serverMessage.CurrentTick - player.CurrentTick
if tickDiff > types.MaxTickDesync {
for _, state := range serverMessage.Players {
if state.PlayerId == playerID {
player.ForceResync(state)
break
}
}
}
player.Unlock()
// Process player states
validPlayerIds := make(map[int32]bool)
for _, state := range serverMessage.Players {
validPlayerIds[state.PlayerId] = true
if state.PlayerId == playerID {
player.Lock()
// Update initial position if not set
if player.PosActual.X == 0 && player.PosActual.Z == 0 {
player.PosActual = rl.Vector3{
X: float32(state.X * types.TileSize),
Y: 0,
Z: float32(state.Y * types.TileSize),
}
player.PosTile = types.Tile{X: int(state.X), Y: int(state.Y)}
}
player.Unlock()
continue
}
// Update or create other players
if otherPlayer, exists := otherPlayers[state.PlayerId]; exists {
otherPlayer.UpdatePosition(state, types.ServerTickRate)
} else {
log.Printf("Creating new player with ID: %d", state.PlayerId)
otherPlayers[state.PlayerId] = types.NewPlayer(state)
}
}
// Remove players no longer in the server state
for id := range otherPlayers {
if id != playerID && !validPlayerIds[id] {
log.Printf("Removing player with ID: %d", id)
delete(otherPlayers, id)
}
}
// Handle chat messages
if handler, ok := player.UserData.(types.ChatMessageHandler); ok && len(serverMessage.ChatMessages) > 0 {
log.Printf("Received %d chat messages from server", len(serverMessage.ChatMessages))
handler.HandleServerMessages(serverMessage.ChatMessages)
// Update the last seen message timestamp to the most recent message
if len(serverMessage.ChatMessages) > 0 {
lastMsg := serverMessage.ChatMessages[len(serverMessage.ChatMessages)-1]
lastSeenMessageTimestamp = lastMsg.Timestamp
log.Printf("Updated last seen message timestamp to %d", lastSeenMessageTimestamp)
}
}
}
func ConnectToServer(username, password string, isRegistering bool) (net.Conn, int32, error) {
log.Printf("Connecting to server at %s...", serverAddr)
@ -57,6 +189,9 @@ func ConnectToServer(username, password string, isRegistering bool) (net.Conn, i
log.Println("Connected to server. Authenticating...")
// Create a message handler
msgHandler := NewMessageHandler(conn)
// Send auth message
authAction := &pb.Action{
Type: pb.Action_LOGIN,
@ -72,45 +207,24 @@ func ConnectToServer(username, password string, isRegistering bool) (net.Conn, i
ProtocolVersion: protoVersion,
}
if err := writeMessage(conn, authBatch); err != nil {
if err := msgHandler.WriteMessage(authBatch); err != nil {
conn.Close()
return nil, 0, fmt.Errorf("failed to send auth: %v", err)
}
// Read server response with timeout
reader := bufio.NewReader(conn)
// Set a read deadline for authentication
conn.SetReadDeadline(time.Now().Add(10 * time.Second))
lengthBuf := make([]byte, 4)
if _, err := io.ReadFull(reader, lengthBuf); err != nil {
// Read server response
response, err := msgHandler.ReadMessage()
if err != nil {
conn.Close()
return nil, 0, fmt.Errorf("failed to read auth response: %v", err)
}
messageLength := binary.BigEndian.Uint32(lengthBuf)
// Sanity check message size
if messageLength > 1024*1024 { // 1MB max message size
conn.Close()
return nil, 0, fmt.Errorf("authentication response too large: %d bytes", messageLength)
}
messageBuf := make([]byte, messageLength)
if _, err := io.ReadFull(reader, messageBuf); err != nil {
conn.Close()
return nil, 0, fmt.Errorf("failed to read auth response body: %v", err)
}
// Clear read deadline after authentication
conn.SetReadDeadline(time.Time{})
var response pb.ServerMessage
if err := proto.Unmarshal(messageBuf, &response); err != nil {
conn.Close()
return nil, 0, fmt.Errorf("failed to unmarshal auth response: %v", err)
}
if response.ProtocolVersion > protoVersion {
conn.Close()
return nil, 0, fmt.Errorf("server requires newer protocol version (server: %d, client: %d)",
@ -132,7 +246,8 @@ func ConnectToServer(username, password string, isRegistering bool) (net.Conn, i
}
func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Player, otherPlayers map[int32]*types.Player, quitChan <-chan struct{}) {
reader := bufio.NewReader(conn)
msgHandler := NewMessageHandler(conn)
defer func() {
if r := recover(); r != nil {
log.Printf("Recovered from panic in HandleServerCommunication: %v", r)
@ -176,7 +291,7 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play
PlayerId: playerID,
}},
}
writeMessage(conn, disconnectMsg)
msgHandler.WriteMessage(disconnectMsg)
done <- struct{}{}
return
case <-done:
@ -190,7 +305,7 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play
PlayerId: playerID,
LastSeenMessageTimestamp: lastSeenMessageTimestamp,
}
if err := writeMessage(conn, emptyBatch); err != nil {
if err := msgHandler.WriteMessage(emptyBatch); err != nil {
log.Printf("Failed to send heartbeat: %v", err)
errChan <- err
return
@ -211,7 +326,7 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play
player.ActionQueue = player.ActionQueue[:0]
player.Unlock()
if err := writeMessage(conn, batch); err != nil {
if err := msgHandler.WriteMessage(batch); err != nil {
errChan <- err
return
}
@ -237,101 +352,21 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play
case <-quitChan:
return
default:
lengthBuf := make([]byte, 4)
if _, err := io.ReadFull(reader, lengthBuf); err != nil {
if err != io.EOF {
serverMessage, err := msgHandler.ReadMessage()
if err != nil {
if err, ok := err.(net.Error); ok && err.Timeout() {
log.Printf("Network timeout: %v", err)
} else if err != io.EOF {
log.Printf("Network read error: %v", err)
errChan <- fmt.Errorf("failed to read message length: %v", err)
errChan <- err
} else {
log.Printf("Connection closed by server")
}
return
}
messageLength := binary.BigEndian.Uint32(lengthBuf)
// Sanity check message size to prevent potential memory issues
if messageLength > 1024*1024 { // 1MB max message size
log.Printf("Message size too large: %d bytes", messageLength)
errChan <- fmt.Errorf("message size too large: %d bytes", messageLength)
return
}
messageBuf := make([]byte, messageLength)
if _, err := io.ReadFull(reader, messageBuf); err != nil {
log.Printf("Failed to read message body: %v", err)
errChan <- fmt.Errorf("failed to read message body: %v", err)
return
}
var serverMessage pb.ServerMessage
if err := proto.Unmarshal(messageBuf, &serverMessage); err != nil {
log.Printf("Failed to unmarshal server message: %v", err)
continue // Skip this message but don't quit
}
player.Lock()
player.CurrentTick = serverMessage.CurrentTick
tickDiff := serverMessage.CurrentTick - player.CurrentTick
if tickDiff > types.MaxTickDesync {
for _, state := range serverMessage.Players {
if state.PlayerId == playerID {
player.ForceResync(state)
break
}
}
}
player.Unlock()
// Process player states
validPlayerIds := make(map[int32]bool)
for _, state := range serverMessage.Players {
validPlayerIds[state.PlayerId] = true
if state.PlayerId == playerID {
player.Lock()
// Update initial position if not set
if player.PosActual.X == 0 && player.PosActual.Z == 0 {
player.PosActual = rl.Vector3{
X: float32(state.X * types.TileSize),
Y: 0,
Z: float32(state.Y * types.TileSize),
}
player.PosTile = types.Tile{X: int(state.X), Y: int(state.Y)}
}
player.Unlock()
continue
}
// Update or create other players
if otherPlayer, exists := otherPlayers[state.PlayerId]; exists {
otherPlayer.UpdatePosition(state, types.ServerTickRate)
} else {
log.Printf("Creating new player with ID: %d", state.PlayerId)
otherPlayers[state.PlayerId] = types.NewPlayer(state)
}
}
// Remove players no longer in the server state
for id := range otherPlayers {
if id != playerID && !validPlayerIds[id] {
log.Printf("Removing player with ID: %d", id)
delete(otherPlayers, id)
}
}
// Handle chat messages
if handler, ok := player.UserData.(types.ChatMessageHandler); ok && len(serverMessage.ChatMessages) > 0 {
log.Printf("Received %d chat messages from server", len(serverMessage.ChatMessages))
handler.HandleServerMessages(serverMessage.ChatMessages)
// Update the last seen message timestamp to the most recent message
if len(serverMessage.ChatMessages) > 0 {
lastMsg := serverMessage.ChatMessages[len(serverMessage.ChatMessages)-1]
lastSeenMessageTimestamp = lastMsg.Timestamp
log.Printf("Updated last seen message timestamp to %d", lastSeenMessageTimestamp)
}
}
// Process the server message
UpdateGameState(serverMessage, player, otherPlayers)
}
}
}()
@ -348,7 +383,7 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play
PlayerId: playerID,
}},
}
writeMessage(conn, disconnectMsg)
msgHandler.WriteMessage(disconnectMsg)
close(done)
case err := <-errChan:
log.Printf("Network error: %v", err)
@ -358,21 +393,8 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play
// Helper function to write length-prefixed messages
func writeMessage(conn net.Conn, msg proto.Message) error {
data, err := proto.Marshal(msg)
if err != nil {
return err
}
// Write length prefix
lengthBuf := make([]byte, 4)
binary.BigEndian.PutUint32(lengthBuf, uint32(len(data)))
if _, err := conn.Write(lengthBuf); err != nil {
return err
}
// Write message body
_, err = conn.Write(data)
return err
msgHandler := NewMessageHandler(conn)
return msgHandler.WriteMessage(msg)
}
type Connection struct {