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) { // Ticker for sending actions aligned with server ticks ticker := time.NewTicker(ServerTickRate) defer ticker.Stop() // Goroutine to handle sending player's actions to the server go func() { for range ticker.C { if len(player.ActionQueue) > 0 { // Bundle all actions that occurred during this tick actions := make([]*pb.Action, 0, len(player.ActionQueue)) for _, actionData := range player.ActionQueue { action := &pb.Action{ PlayerId: playerID, Type: pb.Action_MOVE, X: int32(actionData.X), Y: int32(actionData.Y), } actions = append(actions, action) } // Create a batch message batch := &pb.ActionBatch{ PlayerId: playerID, Actions: actions, Tick: player.CurrentTick, } // Serialize the batch data, err := proto.Marshal(batch) if err != nil { log.Printf("Failed to marshal action batch: %v", err) continue } // Send batch to server _, err = conn.Write(data) if err != nil { log.Printf("Failed to send actions to server: %v", err) return } // Clear the action queue after sending player.ActionQueue = player.ActionQueue[:0] } } }() // Main loop to handle receiving updates from the server for { buf := make([]byte, 4096) 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 } // Check for tick synchronization tickDiff := serverMessage.CurrentTick - player.CurrentTick if tickDiff > MaxTickDesync { log.Printf("Client too far behind (tick diff: %d), forcing resync", tickDiff) player.ForceResync(serverMessage.Players[playerID]) } // Update player's current tick player.CurrentTick = serverMessage.CurrentTick // Update other players' states for _, state := range serverMessage.Players { if state.PlayerId != playerID { if otherPlayer, exists := otherPlayers[state.PlayerId]; exists { // Calculate interpolation based on server tick otherPlayer.UpdatePosition(state, ServerTickRate) } else { // Initialize new player otherPlayers[state.PlayerId] = NewPlayer(state) } } } } }