diff --git a/lib/main/adapter/gateway/installation_file_storage.dart b/lib/main/adapter/gateway/installation_file_storage.dart index 3f79ae6..6744403 100644 --- a/lib/main/adapter/gateway/installation_file_storage.dart +++ b/lib/main/adapter/gateway/installation_file_storage.dart @@ -2,4 +2,8 @@ import 'dart:typed_data'; abstract interface class InstallationFileStorage { Future saveFile(Uint8List fileBytes, String path); + + Future writeFile(String path, String content); + + Future grantFileExecutePermission(String path); } diff --git a/lib/main/adapter/gateway/installation_repository_impl.dart b/lib/main/adapter/gateway/installation_repository_impl.dart index 7d35089..41abe61 100644 --- a/lib/main/adapter/gateway/installation_repository_impl.dart +++ b/lib/main/adapter/gateway/installation_repository_impl.dart @@ -9,8 +9,16 @@ class InstallationRepositoryImpl implements InstallationRepository { InstallationRepositoryImpl(this._apiService, this._fileStorage); @override - Future downloadServerFile(Uri url, String path, {DownloadProgressCallback? onProgressChanged}) async { + Future downloadFile(Uri url, String path, {DownloadProgressCallback? onProgressChanged}) async { final fileBytes = await _apiService.fetchRemoteFile(url, onProgressChanged: onProgressChanged); await _fileStorage.saveFile(fileBytes, path); } + + @override + Future writeFile(String path, String content) => _fileStorage.writeFile(path, content); + + @override + Future grantFileExecutePermission(String path) { + return _fileStorage.grantFileExecutePermission(path); + } } diff --git a/lib/main/adapter/presentation/installation_bloc.dart b/lib/main/adapter/presentation/installation_bloc.dart index 316e529..3bf1abe 100644 --- a/lib/main/adapter/presentation/installation_bloc.dart +++ b/lib/main/adapter/presentation/installation_bloc.dart @@ -1,13 +1,21 @@ +import 'dart:io'; + import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:minecraft_server_installer/main/adapter/presentation/installation_state.dart'; import 'package:minecraft_server_installer/main/adapter/presentation/progress_view_model.dart'; import 'package:minecraft_server_installer/main/application/use_case/download_file_use_case.dart'; +import 'package:minecraft_server_installer/main/application/use_case/grant_file_permission_use_case.dart'; +import 'package:minecraft_server_installer/main/application/use_case/write_file_use_case.dart'; import 'package:minecraft_server_installer/main/constants.dart'; import 'package:minecraft_server_installer/vanilla/adapter/presentation/game_version_view_model.dart'; import 'package:path/path.dart' as path; class InstallationBloc extends Bloc { - InstallationBloc(DownloadFileUseCase downloadFileUseCase) : super(const InstallationState.empty()) { + InstallationBloc( + DownloadFileUseCase downloadFileUseCase, + WriteFileUseCase writeFileUseCase, + GrantFilePermissionUseCase grantFilePermissionUseCase, + ) : super(const InstallationState.empty()) { on((_, emit) async { if (!state.canStartToInstall) { return; @@ -24,6 +32,13 @@ class InstallationBloc extends Bloc { onProgressChanged: (progressValue) => add(_InstallationProgressValueChangedEvent(progressValue)), ); + final startScriptFilePath = path.join(savePath, Constants.startScriptFileName); + final startScriptContent = Platform.isWindows + ? 'java -jar .\\${Constants.serverFileName}\r\n' + : 'java -jar ./${Constants.serverFileName}\n'; + await writeFileUseCase(startScriptFilePath, startScriptContent); + await grantFilePermissionUseCase(startScriptFilePath); + emit(state.copyWith(isLocked: false, downloadProgress: const ProgressViewModel.complete())); }); diff --git a/lib/main/application/repository/installation_repository.dart b/lib/main/application/repository/installation_repository.dart index e155472..17b6ec0 100644 --- a/lib/main/application/repository/installation_repository.dart +++ b/lib/main/application/repository/installation_repository.dart @@ -1,5 +1,9 @@ typedef DownloadProgressCallback = void Function(double progress); abstract interface class InstallationRepository { - Future downloadServerFile(Uri url, String path, {DownloadProgressCallback? onProgressChanged}); + Future downloadFile(Uri url, String path, {DownloadProgressCallback? onProgressChanged}); + + Future writeFile(String path, String content); + + Future grantFileExecutePermission(String path); } diff --git a/lib/main/application/use_case/download_file_use_case.dart b/lib/main/application/use_case/download_file_use_case.dart index 2afdbc0..8d15978 100644 --- a/lib/main/application/use_case/download_file_use_case.dart +++ b/lib/main/application/use_case/download_file_use_case.dart @@ -6,5 +6,5 @@ class DownloadFileUseCase { DownloadFileUseCase(this._installationRepository); Future call(Uri url, String path, {DownloadProgressCallback? onProgressChanged}) => - _installationRepository.downloadServerFile(url, path, onProgressChanged: onProgressChanged); + _installationRepository.downloadFile(url, path, onProgressChanged: onProgressChanged); } diff --git a/lib/main/application/use_case/grant_file_permission_use_case.dart b/lib/main/application/use_case/grant_file_permission_use_case.dart new file mode 100644 index 0000000..771b41a --- /dev/null +++ b/lib/main/application/use_case/grant_file_permission_use_case.dart @@ -0,0 +1,9 @@ +import 'package:minecraft_server_installer/main/application/repository/installation_repository.dart'; + +class GrantFilePermissionUseCase { + final InstallationRepository _installationRepository; + + GrantFilePermissionUseCase(this._installationRepository); + + Future call(String path) => _installationRepository.grantFileExecutePermission(path); +} \ No newline at end of file diff --git a/lib/main/application/use_case/write_file_use_case.dart b/lib/main/application/use_case/write_file_use_case.dart new file mode 100644 index 0000000..fb7c7e5 --- /dev/null +++ b/lib/main/application/use_case/write_file_use_case.dart @@ -0,0 +1,9 @@ +import 'package:minecraft_server_installer/main/application/repository/installation_repository.dart'; + +class WriteFileUseCase { + final InstallationRepository _installationRepository; + + WriteFileUseCase(this._installationRepository); + + Future call(String path, String content) => _installationRepository.writeFile(path, content); +} diff --git a/lib/main/constants.dart b/lib/main/constants.dart index 1226125..958d835 100644 --- a/lib/main/constants.dart +++ b/lib/main/constants.dart @@ -1,4 +1,8 @@ +import 'dart:io'; + abstract class Constants { static const gameVersionListUrl = 'https://www.dropbox.com/s/mtz3moc9dpjtz7s/GameVersions.txt?dl=1'; static const serverFileName = 'server.jar'; + + static final startScriptFileName = Platform.isWindows ? 'start.bat' : 'start.sh'; } diff --git a/lib/main/framework/storage/installation_file_storage_impl.dart b/lib/main/framework/storage/installation_file_storage_impl.dart index 2089032..0cf0a02 100644 --- a/lib/main/framework/storage/installation_file_storage_impl.dart +++ b/lib/main/framework/storage/installation_file_storage_impl.dart @@ -7,12 +7,26 @@ class InstallationFileStorageImpl implements InstallationFileStorage { @override Future saveFile(Uint8List fileBytes, String path) async { final file = File(path); - - if (!await file.parent.exists()) { - await file.parent.create(recursive: true); - } - - await file.create(); + await file.create(recursive: true, exclusive: false); await file.writeAsBytes(fileBytes, flush: true); } + + @override + Future writeFile(String path, String content) async { + File file = File(path); + await file.create(recursive: true, exclusive: false); + await file.writeAsString(content, flush: true); + } + + @override + Future grantFileExecutePermission(String path) async { + File file = File(path); + await file.create(recursive: true, exclusive: false); + + if (Platform.isWindows) { + await Process.run('icacls', [path, '/grant', '%USERNAME%:(RX)']); + } else { + await Process.run('chmod', ['+x', path]); + } + } } diff --git a/lib/main/framework/ui/minecraft_server_installer.dart b/lib/main/framework/ui/minecraft_server_installer.dart index baa02c9..e8c3d17 100644 --- a/lib/main/framework/ui/minecraft_server_installer.dart +++ b/lib/main/framework/ui/minecraft_server_installer.dart @@ -4,6 +4,8 @@ import 'package:minecraft_server_installer/main/adapter/gateway/installation_rep import 'package:minecraft_server_installer/main/adapter/presentation/installation_bloc.dart'; import 'package:minecraft_server_installer/main/adapter/presentation/installation_state.dart'; import 'package:minecraft_server_installer/main/application/use_case/download_file_use_case.dart'; +import 'package:minecraft_server_installer/main/application/use_case/grant_file_permission_use_case.dart'; +import 'package:minecraft_server_installer/main/application/use_case/write_file_use_case.dart'; import 'package:minecraft_server_installer/main/framework/api/installation_api_service_impl.dart'; import 'package:minecraft_server_installer/main/framework/storage/installation_file_storage_impl.dart'; import 'package:minecraft_server_installer/main/framework/ui/basic_configuration_tab.dart'; @@ -28,6 +30,8 @@ class MinecraftServerInstaller extends StatelessWidget { final gameVersionRepository = VanillaRepositoryImpl(gameVersionApiService); final downloadFileUseCase = DownloadFileUseCase(installationRepository); + final writeFileUseCase = WriteFileUseCase(installationRepository); + final grantFilePermissionUseCase = GrantFilePermissionUseCase(installationRepository); final getGameVersionListUseCase = GetGameVersionListUseCase(gameVersionRepository); return MaterialApp( @@ -35,7 +39,13 @@ class MinecraftServerInstaller extends StatelessWidget { theme: ThemeData.light().copyWith(colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue.shade900)), home: MultiBlocProvider( providers: [ - BlocProvider(create: (_) => InstallationBloc(downloadFileUseCase)), + BlocProvider( + create: (_) => InstallationBloc( + downloadFileUseCase, + writeFileUseCase, + grantFilePermissionUseCase, + ), + ), BlocProvider( create: (_) => VanillaBloc(getGameVersionListUseCase)..add(VanillaGameVersionListLoadedEvent()), ),