package main import ( "log" "net" "time" pb "gitea.boner.be/bdnugget/goonserver/actions" "google.golang.org/protobuf/proto" ) func ConnectToServer() (net.Conn, int32, error) { // Attempt to connect to the server conn, err := net.Dial("tcp", serverAddr) if err != nil { log.Printf("Failed to dial server: %v", err) return nil, 0, err } log.Println("Connected to server. Waiting for player ID...") // Buffer for incoming server message buf := make([]byte, 1024) n, err := conn.Read(buf) if err != nil { log.Printf("Error reading player ID from server: %v", err) return nil, 0, err } log.Printf("Received data: %x", buf[:n]) // Unmarshal server message to extract the player ID var response pb.ServerMessage if err := proto.Unmarshal(buf[:n], &response); err != nil { log.Printf("Failed to unmarshal server response: %v", err) return nil, 0, err } playerID := response.GetPlayerId() log.Printf("Successfully connected with player ID: %d", playerID) return conn, playerID, nil } func HandleServerCommunication(conn net.Conn, playerID int32, player *Player, otherPlayers map[int32]*Player) { buf := make([]byte, 4096) // Ticker for sending actions actionTicker := time.NewTicker(ClientTickRate) defer actionTicker.Stop() go func() { for range actionTicker.C { player.Lock() if len(player.ActionQueue) > 0 { // Bundle all actions that occurred during this tick actions := make([]*pb.Action, len(player.ActionQueue)) copy(actions, player.ActionQueue) // Copy to avoid holding lock while sending // Create a batch message batch := &pb.ActionBatch{ PlayerId: playerID, Actions: actions, Tick: player.CurrentTick, } // Clear the action queue after copying player.ActionQueue = player.ActionQueue[:0] player.Unlock() // Serialize and send the batch data, err := proto.Marshal(batch) if err != nil { log.Printf("Failed to marshal action batch: %v", err) continue } if _, err = conn.Write(data); err != nil { log.Printf("Failed to send actions to server: %v", err) return } } else { player.Unlock() } } }() // Main loop to handle receiving updates from the server for { n, err := conn.Read(buf) if err != nil { log.Printf("Failed to read from server: %v", err) return } var serverMessage pb.ServerMessage if err := proto.Unmarshal(buf[:n], &serverMessage); err != nil { log.Printf("Failed to unmarshal server message: %v", err) continue } // Update game state with received data player.Lock() player.CurrentTick = serverMessage.CurrentTick // Check for desync tickDiff := serverMessage.CurrentTick - player.CurrentTick if tickDiff > MaxTickDesync { // Force resync if we're too far behind for _, state := range serverMessage.Players { if state.PlayerId == playerID { player.ForceResync(state) break } } } player.Unlock() // Update other players for _, state := range serverMessage.Players { if state.PlayerId == playerID { continue // Skip self } if otherPlayer, exists := otherPlayers[state.PlayerId]; exists { otherPlayer.UpdatePosition(state, ServerTickRate) } else { otherPlayers[state.PlayerId] = NewPlayer(state) } } } }