diff --git a/lib/main/adapter/presenter/installation_bloc.dart b/lib/main/adapter/presenter/installation_bloc.dart index b46d899..18b6514 100644 --- a/lib/main/adapter/presenter/installation_bloc.dart +++ b/lib/main/adapter/presenter/installation_bloc.dart @@ -2,44 +2,31 @@ import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:minecraft_server_installer/main/adapter/presenter/installation_state.dart'; import 'package:minecraft_server_installer/main/adapter/presenter/progress_view_model.dart'; import 'package:minecraft_server_installer/main/adapter/presenter/range_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/main/application/use_case/install_server_use_case.dart'; +import 'package:minecraft_server_installer/properties/adapter/presenter/server_properties_view_model.dart'; import 'package:minecraft_server_installer/vanilla/adapter/presenter/game_version_view_model.dart'; -import 'package:path/path.dart' as path; class InstallationBloc extends Bloc { InstallationBloc( - DownloadFileUseCase downloadFileUseCase, - WriteFileUseCase writeFileUseCase, - GrantFilePermissionUseCase grantFilePermissionUseCase, + InstallServerUseCase installServerUseCase, ) : super(const InstallationState.empty()) { - on((_, emit) async { + on((event, emit) async { if (!state.canStartToInstall) { return; } - final gameVersion = state.gameVersion!; - final savePath = state.savePath!; - emit(state.copyWith(isLocked: true, downloadProgress: const ProgressViewModel.start())); - await downloadFileUseCase( - gameVersion.url, - path.join(savePath, Constants.serverFileName), + await installServerUseCase( + gameVersion: state.gameVersion!.toEntity(), + savePath: state.savePath!, + maxRam: state.ramSize.max, + minRam: state.ramSize.min, + isGuiEnabled: state.isGuiEnabled, + serverProperties: event.serverProperties.toEntity(), onProgressChanged: (progressValue) => add(_InstallationProgressValueChangedEvent(progressValue)), ); - final startScriptFilePath = path.join(savePath, Constants.startScriptFileName); - final serverFilePath = path.join('.', Constants.serverFileName); - final startScriptContent = - 'java -Xmx${state.ramSize.max}M -Xms${state.ramSize.min}M -jar $serverFilePath ${state.isGuiEnabled ? '' : 'nogui'}'; - await writeFileUseCase(startScriptFilePath, startScriptContent); - await grantFilePermissionUseCase(startScriptFilePath); - - await writeFileUseCase(path.join(savePath, Constants.eulaFileName), Constants.eulaFileContent); - emit(state.copyWith(isLocked: false, downloadProgress: const ProgressViewModel.complete())); }); @@ -77,7 +64,11 @@ class InstallationBloc extends Bloc { sealed class InstallationEvent {} -class InstallationStartedEvent extends InstallationEvent {} +class InstallationStartedEvent extends InstallationEvent { + final ServerPropertiesViewModel serverProperties; + + InstallationStartedEvent(this.serverProperties); +} class _InstallationProgressValueChangedEvent extends InstallationEvent { final double progressValue; diff --git a/lib/main/application/use_case/install_server_use_case.dart b/lib/main/application/use_case/install_server_use_case.dart new file mode 100644 index 0000000..512ca4b --- /dev/null +++ b/lib/main/application/use_case/install_server_use_case.dart @@ -0,0 +1,53 @@ + +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/properties/application/use_case/write_server_properties_use_case.dart'; +import 'package:minecraft_server_installer/properties/domain/entity/server_properties.dart'; +import 'package:minecraft_server_installer/vanilla/domain/entity/game_version.dart'; +import 'package:path/path.dart' as path; + +class InstallServerUseCase { + final DownloadFileUseCase _downloadFileUseCase; + final WriteFileUseCase _writeFileUseCase; + final GrantFilePermissionUseCase _grantFilePermissionUseCase; + final WriteServerPropertiesUseCase _writeServerPropertiesUseCase; + + InstallServerUseCase( + this._downloadFileUseCase, + this._writeFileUseCase, + this._grantFilePermissionUseCase, + this._writeServerPropertiesUseCase, + ); + + Future call({ + required GameVersion gameVersion, + required String savePath, + required int maxRam, + required int minRam, + required bool isGuiEnabled, + required ServerProperties serverProperties, + required void Function(double) onProgressChanged, + }) async { + // 1. Download server file + await _downloadFileUseCase( + gameVersion.url, + path.join(savePath, Constants.serverFileName), + onProgressChanged: onProgressChanged, + ); + + // 2. Write start script + final startScriptFilePath = path.join(savePath, Constants.startScriptFileName); + final serverFilePath = path.join('.', Constants.serverFileName); + final startScriptContent = 'java -Xmx${maxRam}M -Xms${minRam}M -jar $serverFilePath ${isGuiEnabled ? '' : 'nogui'}'; + await _writeFileUseCase(startScriptFilePath, startScriptContent); + await _grantFilePermissionUseCase(startScriptFilePath); + + // 3. Write EULA file + await _writeFileUseCase(path.join(savePath, Constants.eulaFileName), Constants.eulaFileContent); + + // 4. Write server.properties file + await _writeServerPropertiesUseCase(serverProperties, savePath); + } +} diff --git a/lib/main/framework/ui/basic_configuration_tab.dart b/lib/main/framework/ui/basic_configuration_tab.dart index d820aa5..414b8cf 100644 --- a/lib/main/framework/ui/basic_configuration_tab.dart +++ b/lib/main/framework/ui/basic_configuration_tab.dart @@ -7,6 +7,7 @@ import 'package:minecraft_server_installer/main/adapter/presenter/installation_s import 'package:minecraft_server_installer/main/adapter/presenter/range_view_model.dart'; import 'package:minecraft_server_installer/main/constants.dart'; import 'package:minecraft_server_installer/main/framework/ui/strings.dart'; +import 'package:minecraft_server_installer/properties/adapter/presenter/server_properties_bloc.dart'; import 'package:minecraft_server_installer/vanilla/framework/ui/game_version_dropdown.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -209,6 +210,6 @@ class BasicConfigurationTab extends StatelessWidget { } void _downloadServerFile(BuildContext context) { - context.read().add((InstallationStartedEvent())); + context.read().add((InstallationStartedEvent(context.read().state))); } } diff --git a/lib/main/framework/ui/minecraft_server_installer.dart b/lib/main/framework/ui/minecraft_server_installer.dart index b9d90a7..e662388 100644 --- a/lib/main/framework/ui/minecraft_server_installer.dart +++ b/lib/main/framework/ui/minecraft_server_installer.dart @@ -5,6 +5,7 @@ import 'package:minecraft_server_installer/main/adapter/presenter/installation_b import 'package:minecraft_server_installer/main/adapter/presenter/navigation_bloc.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/install_server_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/main/framework/api/installation_api_service_impl.dart'; @@ -12,6 +13,10 @@ import 'package:minecraft_server_installer/main/framework/storage/installation_f import 'package:minecraft_server_installer/main/framework/ui/about_tab.dart'; import 'package:minecraft_server_installer/main/framework/ui/basic_configuration_tab.dart'; import 'package:minecraft_server_installer/main/framework/ui/side_navigation_bar.dart'; +import 'package:minecraft_server_installer/properties/adapter/gateway/server_properties_repository_impl.dart'; +import 'package:minecraft_server_installer/properties/adapter/presenter/server_properties_bloc.dart'; +import 'package:minecraft_server_installer/properties/application/use_case/write_server_properties_use_case.dart'; +import 'package:minecraft_server_installer/properties/framework/storage/server_properties_file_storage_impl.dart'; import 'package:minecraft_server_installer/vanilla/adapter/gateway/vanilla_repository_impl.dart'; import 'package:minecraft_server_installer/vanilla/adapter/presenter/vanilla_bloc.dart'; import 'package:minecraft_server_installer/vanilla/application/use_case/get_game_version_list_use_case.dart'; @@ -22,17 +27,27 @@ class MinecraftServerInstaller extends StatelessWidget { @override Widget build(BuildContext context) { + final gameVersionApiService = VanillaApiServiceImpl(); + final gameVersionRepository = VanillaRepositoryImpl(gameVersionApiService); + final getGameVersionListUseCase = GetGameVersionListUseCase(gameVersionRepository); + + final serverPropertiesFileStorage = ServerPropertiesFileStorageImpl(); + final serverPropertiesRepository = ServerPropertiesRepositoryImpl(serverPropertiesFileStorage); + final writeServerPropertiesUseCase = WriteServerPropertiesUseCase(serverPropertiesRepository); + final installationApiService = InstallationApiServiceImpl(); final installationFileStorage = InstallationFileStorageImpl(); final installationRepository = InstallationRepositoryImpl(installationApiService, installationFileStorage); - - final gameVersionApiService = VanillaApiServiceImpl(); - final gameVersionRepository = VanillaRepositoryImpl(gameVersionApiService); - final downloadFileUseCase = DownloadFileUseCase(installationRepository); final writeFileUseCase = WriteFileUseCase(installationRepository); final grantFilePermissionUseCase = GrantFilePermissionUseCase(installationRepository); - final getGameVersionListUseCase = GetGameVersionListUseCase(gameVersionRepository); + + final installServerUseCase = InstallServerUseCase( + downloadFileUseCase, + writeFileUseCase, + grantFilePermissionUseCase, + writeServerPropertiesUseCase, + ); return MaterialApp( title: Constants.appName, @@ -45,16 +60,13 @@ class MinecraftServerInstaller extends StatelessWidget { home: MultiBlocProvider( providers: [ BlocProvider(create: (_) => NavigationBloc()), - BlocProvider( - create: (_) => InstallationBloc( - downloadFileUseCase, - writeFileUseCase, - grantFilePermissionUseCase, - ), - ), + BlocProvider(create: (_) => ServerPropertiesBloc()), BlocProvider( create: (_) => VanillaBloc(getGameVersionListUseCase)..add(VanillaGameVersionListLoadedEvent()), ), + BlocProvider( + create: (_) => InstallationBloc(installServerUseCase), + ), ], child: Scaffold( body: Row( diff --git a/lib/properties/adapter/gateway/server_properties_dto.dart b/lib/properties/adapter/gateway/server_properties_dto.dart index aa774bf..52ef1a9 100644 --- a/lib/properties/adapter/gateway/server_properties_dto.dart +++ b/lib/properties/adapter/gateway/server_properties_dto.dart @@ -28,16 +28,18 @@ class ServerPropertiesDto { }); ServerPropertiesDto.fromEntity(ServerProperties serverProperties) - : serverPort = serverProperties.serverPort, - maxPlayers = serverProperties.maxPlayers, - spawnProtection = serverProperties.spawnProtection, - viewDistance = serverProperties.viewDistance, - pvp = serverProperties.pvp, - gameMode = serverProperties.gameMode.value, - difficulty = serverProperties.difficulty.value, - enableCommandBlock = serverProperties.enableCommandBlock, - onlineMode = serverProperties.onlineMode, - motd = serverProperties.motd; + : this( + serverPort: serverProperties.serverPort, + maxPlayers: serverProperties.maxPlayers, + spawnProtection: serverProperties.spawnProtection, + viewDistance: serverProperties.viewDistance, + pvp: serverProperties.pvp, + gameMode: serverProperties.gameMode.value, + difficulty: serverProperties.difficulty.value, + enableCommandBlock: serverProperties.enableCommandBlock, + onlineMode: serverProperties.onlineMode, + motd: serverProperties.motd, + ); Map toStringMap() => { 'server-port': serverPort.toString(), diff --git a/lib/properties/adapter/gateway/server_properties_file_storage.dart b/lib/properties/adapter/gateway/server_properties_file_storage.dart index 4960b70..a355084 100644 --- a/lib/properties/adapter/gateway/server_properties_file_storage.dart +++ b/lib/properties/adapter/gateway/server_properties_file_storage.dart @@ -1,5 +1,5 @@ import 'package:minecraft_server_installer/properties/adapter/gateway/server_properties_dto.dart'; abstract interface class ServerPropertiesFileStorage { - Future writeServerProperties(ServerPropertiesDto serverPropertiesDto, String directoryPath); + Future writeServerProperties(ServerPropertiesDto serverPropertiesDto, String savePath); } diff --git a/lib/properties/adapter/gateway/server_properties_repository_impl.dart b/lib/properties/adapter/gateway/server_properties_repository_impl.dart index a300837..a23893e 100644 --- a/lib/properties/adapter/gateway/server_properties_repository_impl.dart +++ b/lib/properties/adapter/gateway/server_properties_repository_impl.dart @@ -9,9 +9,9 @@ class ServerPropertiesRepositoryImpl implements ServerPropertiesRepository { ServerPropertiesRepositoryImpl(this._serverPropertiesFileStorage); @override - Future writeServerProperties(ServerProperties serverProperties, String directoryPath) => + Future writeServerProperties(ServerProperties serverProperties, String savePath) => _serverPropertiesFileStorage.writeServerProperties( ServerPropertiesDto.fromEntity(serverProperties), - directoryPath, + savePath, ); } diff --git a/lib/properties/adapter/presenter/server_properties_bloc.dart b/lib/properties/adapter/presenter/server_properties_bloc.dart new file mode 100644 index 0000000..1a3c799 --- /dev/null +++ b/lib/properties/adapter/presenter/server_properties_bloc.dart @@ -0,0 +1,51 @@ +import 'package:flutter_bloc/flutter_bloc.dart'; +import 'package:minecraft_server_installer/properties/adapter/presenter/server_properties_view_model.dart'; +import 'package:minecraft_server_installer/properties/domain/enum/difficulty.dart'; +import 'package:minecraft_server_installer/properties/domain/enum/game_mode.dart'; + +class ServerPropertiesBloc extends Bloc { + ServerPropertiesBloc() : super(const ServerPropertiesViewModel.defaultValue()) { + on((event, emit) => emit( + state.copyWith( + serverPort: event.serverPort ?? state.serverPort, + maxPlayers: event.maxPlayers ?? state.maxPlayers, + spawnProtection: event.spawnProtection ?? state.spawnProtection, + viewDistance: event.viewDistance ?? state.viewDistance, + pvp: event.pvp ?? state.pvp, + gameMode: event.gameMode ?? state.gameMode, + difficulty: event.difficulty ?? state.difficulty, + enableCommandBlock: event.enableCommandBlock ?? state.enableCommandBlock, + onlineMode: event.onlineMode ?? state.onlineMode, + motd: event.motd ?? state.motd, + ), + )); + } +} + +sealed class ServerPropertiesEvent {} + +class ServerPropertiesUpdatedEvent extends ServerPropertiesEvent { + final int? serverPort; + final int? maxPlayers; + final int? spawnProtection; + final int? viewDistance; + final bool? pvp; + final GameMode? gameMode; + final Difficulty? difficulty; + final bool? enableCommandBlock; + final bool? onlineMode; + final String? motd; + + ServerPropertiesUpdatedEvent({ + this.serverPort, + this.maxPlayers, + this.spawnProtection, + this.viewDistance, + this.pvp, + this.gameMode, + this.difficulty, + this.enableCommandBlock, + this.onlineMode, + this.motd, + }); +} diff --git a/lib/properties/adapter/presenter/server_properties_view_model.dart b/lib/properties/adapter/presenter/server_properties_view_model.dart new file mode 100644 index 0000000..bf129dc --- /dev/null +++ b/lib/properties/adapter/presenter/server_properties_view_model.dart @@ -0,0 +1,96 @@ +import 'package:equatable/equatable.dart'; +import 'package:minecraft_server_installer/properties/domain/entity/server_properties.dart'; +import 'package:minecraft_server_installer/properties/domain/enum/difficulty.dart'; +import 'package:minecraft_server_installer/properties/domain/enum/game_mode.dart'; + +class ServerPropertiesViewModel with EquatableMixin { + final int serverPort; + final int maxPlayers; + final int spawnProtection; + final int viewDistance; + final bool pvp; + final GameMode gameMode; + final Difficulty difficulty; + final bool enableCommandBlock; + final bool onlineMode; + final String motd; + + const ServerPropertiesViewModel({ + required this.serverPort, + required this.maxPlayers, + required this.spawnProtection, + required this.viewDistance, + required this.pvp, + required this.gameMode, + required this.difficulty, + required this.enableCommandBlock, + required this.onlineMode, + required this.motd, + }); + + const ServerPropertiesViewModel.defaultValue() + : this( + serverPort: 25565, + maxPlayers: 20, + spawnProtection: 16, + viewDistance: 10, + pvp: true, + gameMode: GameMode.survival, + difficulty: Difficulty.normal, + enableCommandBlock: false, + onlineMode: true, + motd: 'A Minecraft Server', + ); + + ServerProperties toEntity() => ServerProperties( + serverPort: serverPort, + maxPlayers: maxPlayers, + spawnProtection: spawnProtection, + viewDistance: viewDistance, + pvp: pvp, + gameMode: gameMode, + difficulty: difficulty, + enableCommandBlock: enableCommandBlock, + onlineMode: onlineMode, + motd: motd, + ); + + ServerPropertiesViewModel copyWith({ + int? serverPort, + int? maxPlayers, + int? spawnProtection, + int? viewDistance, + bool? pvp, + GameMode? gameMode, + Difficulty? difficulty, + bool? enableCommandBlock, + bool? onlineMode, + String? motd, + }) => + ServerPropertiesViewModel( + serverPort: serverPort ?? this.serverPort, + maxPlayers: maxPlayers ?? this.maxPlayers, + spawnProtection: spawnProtection ?? this.spawnProtection, + viewDistance: viewDistance ?? this.viewDistance, + pvp: pvp ?? this.pvp, + gameMode: gameMode ?? this.gameMode, + difficulty: difficulty ?? this.difficulty, + enableCommandBlock: enableCommandBlock ?? this.enableCommandBlock, + onlineMode: onlineMode ?? this.onlineMode, + motd: motd ?? this.motd, + ); + + @override + List get props => [ + serverPort, + maxPlayers, + spawnProtection, + viewDistance, + pvp, + gameMode, + difficulty, + enableCommandBlock, + onlineMode, + motd, + ]; +} diff --git a/lib/properties/application/repository/server_properties_repository.dart b/lib/properties/application/repository/server_properties_repository.dart index 77e07cd..10468f9 100644 --- a/lib/properties/application/repository/server_properties_repository.dart +++ b/lib/properties/application/repository/server_properties_repository.dart @@ -1,5 +1,5 @@ import 'package:minecraft_server_installer/properties/domain/entity/server_properties.dart'; abstract interface class ServerPropertiesRepository { - Future writeServerProperties(ServerProperties serverProperties, String directoryPath); + Future writeServerProperties(ServerProperties serverProperties, String savePath); } diff --git a/lib/properties/application/use_case/write_server_properties_use_case.dart b/lib/properties/application/use_case/write_server_properties_use_case.dart index ed8914c..38f0c90 100644 --- a/lib/properties/application/use_case/write_server_properties_use_case.dart +++ b/lib/properties/application/use_case/write_server_properties_use_case.dart @@ -6,6 +6,6 @@ class WriteServerPropertiesUseCase { WriteServerPropertiesUseCase(this._serverPropertiesRepository); - Future call(ServerProperties serverProperties, String directoryPath) => - _serverPropertiesRepository.writeServerProperties(serverProperties, directoryPath); + Future call(ServerProperties serverProperties, String savePath) => + _serverPropertiesRepository.writeServerProperties(serverProperties, savePath); } diff --git a/lib/properties/framework/storage/server_properties_file_storage_impl.dart b/lib/properties/framework/storage/server_properties_file_storage_impl.dart index 183db27..999677b 100644 --- a/lib/properties/framework/storage/server_properties_file_storage_impl.dart +++ b/lib/properties/framework/storage/server_properties_file_storage_impl.dart @@ -5,8 +5,8 @@ import 'package:minecraft_server_installer/properties/adapter/gateway/server_pro class ServerPropertiesFileStorageImpl implements ServerPropertiesFileStorage { @override - Future writeServerProperties(ServerPropertiesDto serverPropertiesDto, String directoryPath) async { - File file = File('$directoryPath/server.properties'); + Future writeServerProperties(ServerPropertiesDto serverPropertiesDto, String savePath) async { + File file = File('$savePath/server.properties'); await file.create(recursive: true); final propertiesMap = serverPropertiesDto.toStringMap();