big ol refactor
This commit is contained in:
@ -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 {
|
||||
|
Reference in New Issue
Block a user