use std::collections::HashMap;
use std::sync::Arc;
use anyhow::bail;
use mcvm_core::auth_crate::mc::ClientId;
use mcvm_core::user::{User, UserManager};
use mcvm_core::util::versions::MinecraftVersionDeser;
use mcvm_plugin::plugin::PluginManifest;
use mcvm_shared::id::InstanceID;
use mcvm_shared::modifications::{ClientType, Modloader, ServerType};
use mcvm_shared::output::MCVMOutput;
use mcvm_shared::pkg::{PackageID, PackageStability};
use mcvm_shared::Side;
use crate::instance::Instance;
use crate::io::paths::Paths;
use crate::pkg::eval::EvalPermissions;
use crate::pkg::reg::PkgRegistry;
use crate::pkg::repo::PkgRepo;
use crate::plugin::PluginManager;
use super::instance::{read_instance_config, ClientWindowConfig, InstanceConfig, LaunchConfig};
use super::package::{FullPackageConfig, PackageConfigDeser};
use super::plugin::PluginConfig;
use super::preferences::ConfigPreferences;
use super::user::{UserConfig, UserVariant};
use super::Config;
pub struct ConfigBuilder {
users: UserManager,
instances: HashMap<InstanceID, Instance>,
instance_groups: HashMap<Arc<str>, Vec<InstanceID>>,
packages: PkgRegistry,
preferences: ConfigPreferences,
plugins: PluginManager,
default_user: Option<String>,
}
impl ConfigBuilder {
pub fn new(prefs: ConfigPreferences, repos: Vec<PkgRepo>) -> Self {
let packages = PkgRegistry::new(repos, prefs.package_caching_strategy.clone());
Self {
users: UserManager::new(ClientId::new("".into())),
instances: HashMap::new(),
instance_groups: HashMap::new(),
packages,
preferences: prefs,
plugins: PluginManager::new(),
default_user: None,
}
}
pub fn user(&mut self, id: String, kind: UserBuilderKind) -> UserBuilder {
UserBuilder::with_parent(id, kind, Some(self))
}
fn build_user(&mut self, user: User) {
self.users.add_user(user);
}
pub fn default_user(&mut self, user_id: String) -> &mut Self {
self.default_user = Some(user_id);
self
}
pub fn instance_group(&mut self, id: Arc<str>, contents: Vec<InstanceID>) -> &mut Self {
self.instance_groups.insert(id, contents);
self
}
pub fn add_plugin(
&mut self,
plugin: PluginConfig,
manifest: PluginManifest,
paths: &Paths,
o: &mut impl MCVMOutput,
) -> anyhow::Result<()> {
self.plugins.add_plugin(plugin, manifest, paths, None, o)
}
pub fn build(mut self) -> anyhow::Result<Config> {
if let Some(default_user_id) = &self.default_user {
if self.users.user_exists(default_user_id) {
self.users
.choose_user(default_user_id)
.expect("Default user should exist");
} else {
bail!("Provided default user '{default_user_id}' does not exist");
}
}
Ok(Config {
users: self.users,
instances: self.instances,
instance_groups: self.instance_groups,
packages: self.packages,
plugins: self.plugins,
prefs: self.preferences,
})
}
}
pub struct UserBuilder<'parent> {
id: String,
config: UserConfig,
parent: Option<&'parent mut ConfigBuilder>,
}
impl<'parent> UserBuilder<'parent> {
pub fn new(id: String, kind: UserBuilderKind) -> Self {
Self::with_parent(id, kind, None)
}
fn with_parent(
id: String,
kind: UserBuilderKind,
parent: Option<&'parent mut ConfigBuilder>,
) -> Self {
let variant = match kind {
UserBuilderKind::Microsoft => UserVariant::Microsoft {},
UserBuilderKind::Demo => UserVariant::Demo {},
};
Self {
id,
config: UserConfig { variant },
parent,
}
}
pub fn build(self) {
let (user, parent) = self.build_self();
if let Some(parent) = parent {
parent.build_user(user);
}
}
pub fn build_self(self) -> (User, Option<&'parent mut ConfigBuilder>) {
let built = self.config.to_user(&self.id);
(built, self.parent)
}
}
#[derive(Copy, Clone)]
pub enum UserBuilderKind {
Microsoft,
Demo,
}
pub struct InstanceBuilder<'parent> {
id: InstanceID,
config: InstanceConfig,
parent: Option<&'parent mut ConfigBuilder>,
}
impl<'parent> InstanceBuilder<'parent> {
pub fn new(id: InstanceID, side: Side) -> Self {
Self::with_parent(id, side, None)
}
fn with_parent(id: InstanceID, side: Side, parent: Option<&'parent mut ConfigBuilder>) -> Self {
let config = InstanceConfig {
side: Some(side),
name: None,
icon: None,
common: Default::default(),
window: Default::default(),
};
Self { id, config, parent }
}
pub fn name(&mut self, name: String) -> &mut Self {
self.config.name = Some(name);
self
}
pub fn icon(&mut self, icon: String) -> &mut Self {
self.config.icon = Some(icon);
self
}
pub fn version(&mut self, version: MinecraftVersionDeser) -> &mut Self {
self.config.common.version = Some(version);
self
}
pub fn modloader(&mut self, modloader: Modloader) -> &mut Self {
self.config.common.modloader = Some(modloader);
self
}
pub fn client_type(&mut self, client_type: ClientType) -> &mut Self {
self.config.common.client_type = Some(client_type);
self
}
pub fn server_type(&mut self, server_type: ServerType) -> &mut Self {
self.config.common.server_type = Some(server_type);
self
}
pub fn package_stability(&mut self, package_stability: PackageStability) -> &mut Self {
self.config.common.package_stability = Some(package_stability);
self
}
pub fn package<'this>(
&'this mut self,
data: InitialPackageData,
) -> PackageBuilder<PackageBuilderInstanceParent<'this, 'parent>> {
let parent = PackageBuilderInstanceParent(self);
PackageBuilder::with_parent(data, parent)
}
fn build_package(&mut self, package: FullPackageConfig) {
let config = PackageConfigDeser::Full(package);
self.config.common.packages.push(config);
}
pub fn launch_options(&mut self, launch_options: LaunchConfig) -> &mut Self {
self.config.common.launch = launch_options;
self
}
pub fn window_config(&mut self, window_config: ClientWindowConfig) -> &mut Self {
self.config.window = window_config;
self
}
pub fn datapack_folder(&mut self, folder: String) -> &mut Self {
self.config.common.datapack_folder = Some(folder);
self
}
pub fn build(self, paths: &Paths, o: &mut impl MCVMOutput) -> anyhow::Result<()> {
let (id, instance, parent) = self.build_self(paths, o)?;
if let Some(parent) = parent {
parent.instances.insert(id, instance);
}
Ok(())
}
pub fn build_self(
self,
paths: &Paths,
o: &mut impl MCVMOutput,
) -> anyhow::Result<(InstanceID, Instance, Option<&'parent mut ConfigBuilder>)> {
let default_plugins = PluginManager::new();
let plugins = if let Some(ref parent) = self.parent {
&parent.plugins
} else {
&default_plugins
};
let built = read_instance_config(
self.id.clone(),
self.config,
&HashMap::new(),
plugins,
paths,
o,
)?;
Ok((self.id, built, self.parent))
}
pub fn build_config(self) -> InstanceConfig {
self.config
}
}
pub struct PackageBuilder<Parent: PackageBuilderParent> {
config: FullPackageConfig,
parent: Parent,
}
impl<Parent> PackageBuilder<Parent>
where
Parent: PackageBuilderParent,
{
fn with_parent(data: InitialPackageData, parent: Parent) -> Self {
let config = FullPackageConfig {
id: data.id,
features: Default::default(),
use_default_features: true,
permissions: Default::default(),
stability: Default::default(),
worlds: Default::default(),
content_version: Default::default(),
};
Self { config, parent }
}
pub fn features(&mut self, features: Vec<String>) -> &mut Self {
self.config.features.extend(features);
self
}
pub fn use_default_features(&mut self, value: bool) -> &mut Self {
self.config.use_default_features = value;
self
}
pub fn permissions(&mut self, permissions: EvalPermissions) -> &mut Self {
self.config.permissions = permissions;
self
}
pub fn stability(&mut self, stability: PackageStability) -> &mut Self {
self.config.stability = Some(stability);
self
}
pub fn worlds(&mut self, worlds: Vec<String>) -> &mut Self {
self.config.worlds = worlds;
self
}
pub fn content_version(&mut self, version: String) -> &mut Self {
self.config.content_version = Some(version);
self
}
pub fn build(self) {
self.parent.build_package(self.config);
}
}
impl PackageBuilder<PackageBuilderOrphan> {
pub fn new(data: InitialPackageData) -> Self {
Self::with_parent(data, PackageBuilderOrphan)
}
}
pub struct InitialPackageData {
id: PackageID,
}
pub trait PackageBuilderParent {
fn build_package(self, package: FullPackageConfig);
}
pub struct PackageBuilderOrphan;
impl PackageBuilderParent for PackageBuilderOrphan {
fn build_package(self, _package: FullPackageConfig) {}
}
pub struct PackageBuilderInstanceParent<'instance, 'parent>(
&'instance mut InstanceBuilder<'parent>,
);
impl<'instance, 'parent> PackageBuilderParent for PackageBuilderInstanceParent<'instance, 'parent> {
fn build_package(self, package: FullPackageConfig) {
self.0.build_package(package)
}
}
#[cfg(test)]
mod tests {
}