package com.faforever.client.patch;
import com.faforever.client.game.GameType;
import com.faforever.client.i18n.I18n;
import com.faforever.client.notification.Action;
import com.faforever.client.notification.NotificationService;
import com.faforever.client.notification.PersistentNotification;
import com.faforever.client.notification.Severity;
import com.faforever.client.task.TaskService;
import com.google.common.annotations.VisibleForTesting;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.lang.invoke.MethodHandles;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionStage;
public class GitRepositoryGameUpdateService extends AbstractPatchService implements GameUpdateService {
@VisibleForTesting
static final String REPO_NAME = "binary-patch";
@VisibleForTesting
static final String STEAM_API_DLL = "steam_api.dll";
private static final Logger logger = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass());
@Resource
TaskService taskService;
@Resource
NotificationService notificationService;
@Resource
I18n i18n;
@Resource
GitWrapper gitWrapper;
@Resource
ApplicationContext applicationContext;
/**
* Path to the local binary-patch Git repository.
*/
private Path binaryPatchRepoDirectory;
@Override
protected boolean checkDirectories() {
return super.checkDirectories();
}
@PostConstruct
void postConstruct() {
binaryPatchRepoDirectory = preferencesService.getFafReposDirectory().resolve(REPO_NAME);
}
@Override
public CompletionStage<Void> updateInBackground(String gameType, Integer version, Map<String, Integer> modVersions, Set<String> simModUids) {
if (!checkDirectories()) {
logger.warn("Aborted patching since directories aren't initialized properly");
return CompletableFuture.completedFuture(null);
}
GitGameUpdateTask task = applicationContext.getBean(GitGameUpdateTask.class);
task.setBinaryPatchRepoDirectory(binaryPatchRepoDirectory);
task.setMigrationDataFile(getMigrationDataFile());
return taskService.submitTask(task).getFuture().thenAccept(aVoid -> notificationService.addNotification(
new PersistentNotification(
i18n.get("faUpdateSucceeded.notification"),
Severity.INFO
)
)).exceptionally(throwable -> {
notificationService.addNotification(
new PersistentNotification(
i18n.get("updateFailed.notification"),
Severity.WARN,
Collections.singletonList(
new Action(i18n.get("updateCheckFailed.retry"), event -> checkForUpdateInBackground())
)
)
);
return null;
});
}
@Override
public CompletionStage<Void> checkForUpdateInBackground() {
GitCheckGameUpdateTask task = applicationContext.getBean(GitCheckGameUpdateTask.class);
task.setBinaryPatchRepoDirectory(binaryPatchRepoDirectory);
task.setMigrationDataFile(getMigrationDataFile());
return taskService.submitTask(task).getFuture().thenAccept(needsPatching -> {
if (needsPatching) {
notificationService.addNotification(
new PersistentNotification(
i18n.get("faUpdateAvailable.notification"),
Severity.INFO,
Arrays.asList(
new Action(i18n.get("faUpdateAvailable.updateLater")),
new Action(i18n.get("faUpdateAvailable.updateNow"),
event -> updateInBackground(GameType.DEFAULT.getString(), null, null, null))
)
)
);
}
}).exceptionally(throwable -> {
notificationService.addNotification(
new PersistentNotification(
i18n.get("updateCheckFailed.notification"),
Severity.WARN,
Collections.singletonList(
new Action(i18n.get("updateCheckFailed.retry"), event -> checkForUpdateInBackground())
)
)
);
return null;
});
}
private Path getMigrationDataFile() {
String migrationDataFileName = guessInstallType().migrationDataFileName;
return binaryPatchRepoDirectory.resolve(migrationDataFileName);
}
@VisibleForTesting
InstallType guessInstallType() {
Path faBinDirectory = preferencesService.getPreferences().getForgedAlliance().getPath().resolve("bin");
if (Files.notExists(faBinDirectory)) {
throw new IllegalStateException("Directory does not exist: " + faBinDirectory);
}
if (Files.exists(faBinDirectory.resolve(STEAM_API_DLL))) {
return InstallType.STEAM;
} else {
return InstallType.RETAIL;
}
}
@VisibleForTesting
enum InstallType {
RETAIL("retail.json"),
STEAM("steam.json");
final String migrationDataFileName;
InstallType(String migrationDataFileName) {
this.migrationDataFileName = migrationDataFileName;
}
}
}