refactor/less-complexity #5
@ -1 +1 @@
|
||||
Subproject commit f9ec811b10bbab54e843199eb68156e9e7c143cc
|
||||
Subproject commit c720b668180d46b8eb37747f9c24d2fa1f49de72
|
@ -19,6 +19,7 @@ import (
|
||||
const protoVersion = 1
|
||||
|
||||
var serverAddr = "boner.be:6969" // Default server address
|
||||
var lastSeenMessageTimestamp int64 = 0 // Track the last message timestamp seen by this client
|
||||
|
||||
func SetServerAddr(addr string) {
|
||||
serverAddr = addr
|
||||
@ -26,10 +27,32 @@ func SetServerAddr(addr string) {
|
||||
}
|
||||
|
||||
func ConnectToServer(username, password string, isRegistering bool) (net.Conn, int32, error) {
|
||||
conn, err := net.Dial("tcp", serverAddr)
|
||||
if err != nil {
|
||||
log.Printf("Failed to dial server: %v", err)
|
||||
return nil, 0, err
|
||||
log.Printf("Connecting to server at %s...", serverAddr)
|
||||
|
||||
var err error
|
||||
var conn net.Conn
|
||||
|
||||
// Try connecting with a timeout
|
||||
connChan := make(chan net.Conn, 1)
|
||||
errChan := make(chan error, 1)
|
||||
|
||||
go func() {
|
||||
c, e := net.Dial("tcp", serverAddr)
|
||||
if e != nil {
|
||||
errChan <- e
|
||||
return
|
||||
}
|
||||
connChan <- c
|
||||
}()
|
||||
|
||||
// Wait for connection with timeout
|
||||
select {
|
||||
case conn = <-connChan:
|
||||
// Connection successful, continue
|
||||
case err = <-errChan:
|
||||
return nil, 0, fmt.Errorf("failed to dial server: %v", err)
|
||||
case <-time.After(5 * time.Second):
|
||||
return nil, 0, fmt.Errorf("connection timeout after 5 seconds")
|
||||
}
|
||||
|
||||
log.Println("Connected to server. Authenticating...")
|
||||
@ -54,8 +77,12 @@ func ConnectToServer(username, password string, isRegistering bool) (net.Conn, i
|
||||
return nil, 0, fmt.Errorf("failed to send auth: %v", err)
|
||||
}
|
||||
|
||||
// Read server response
|
||||
// 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 {
|
||||
conn.Close()
|
||||
@ -63,12 +90,21 @@ func ConnectToServer(username, password string, isRegistering bool) (net.Conn, i
|
||||
}
|
||||
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()
|
||||
@ -88,6 +124,10 @@ func ConnectToServer(username, password string, isRegistering bool) (net.Conn, i
|
||||
|
||||
playerID := response.GetPlayerId()
|
||||
log.Printf("Successfully authenticated with player ID: %d", playerID)
|
||||
|
||||
// Reset the lastSeenMessageTimestamp when reconnecting
|
||||
lastSeenMessageTimestamp = 0
|
||||
|
||||
return conn, playerID, nil
|
||||
}
|
||||
|
||||
@ -110,6 +150,12 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play
|
||||
errChan := make(chan error, 1)
|
||||
done := make(chan struct{})
|
||||
|
||||
// Add a heartbeat ticker to detect connection issues
|
||||
heartbeatTicker := time.NewTicker(5 * time.Second)
|
||||
defer heartbeatTicker.Stop()
|
||||
|
||||
lastMessageTime := time.Now()
|
||||
|
||||
// Start message sending goroutine
|
||||
go func() {
|
||||
defer func() {
|
||||
@ -135,6 +181,22 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play
|
||||
return
|
||||
case <-done:
|
||||
return
|
||||
case <-heartbeatTicker.C:
|
||||
// If no message has been sent for a while, send a heartbeat
|
||||
timeSinceLastMessage := time.Since(lastMessageTime)
|
||||
if timeSinceLastMessage > 5*time.Second {
|
||||
// Send an empty batch as a heartbeat
|
||||
emptyBatch := &pb.ActionBatch{
|
||||
PlayerId: playerID,
|
||||
LastSeenMessageTimestamp: lastSeenMessageTimestamp,
|
||||
}
|
||||
if err := writeMessage(conn, emptyBatch); err != nil {
|
||||
log.Printf("Failed to send heartbeat: %v", err)
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
lastMessageTime = time.Now()
|
||||
}
|
||||
case <-actionTicker.C:
|
||||
player.Lock()
|
||||
if len(player.ActionQueue) > 0 {
|
||||
@ -144,6 +206,7 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play
|
||||
PlayerId: playerID,
|
||||
Actions: actions,
|
||||
Tick: player.CurrentTick,
|
||||
LastSeenMessageTimestamp: lastSeenMessageTimestamp,
|
||||
}
|
||||
player.ActionQueue = player.ActionQueue[:0]
|
||||
player.Unlock()
|
||||
@ -152,6 +215,7 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play
|
||||
errChan <- err
|
||||
return
|
||||
}
|
||||
lastMessageTime = time.Now()
|
||||
} else {
|
||||
player.Unlock()
|
||||
}
|
||||
@ -260,6 +324,13 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -268,6 +339,7 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play
|
||||
// Wait for error or quit signal
|
||||
select {
|
||||
case <-quitChan:
|
||||
log.Printf("Received quit signal, sending disconnect message")
|
||||
// Send disconnect message
|
||||
disconnectMsg := &pb.ActionBatch{
|
||||
PlayerId: playerID,
|
||||
@ -277,8 +349,10 @@ func HandleServerCommunication(conn net.Conn, playerID int32, player *types.Play
|
||||
}},
|
||||
}
|
||||
writeMessage(conn, disconnectMsg)
|
||||
close(done)
|
||||
case err := <-errChan:
|
||||
log.Printf("Network error: %v", err)
|
||||
close(done)
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user