use std::hash::Hash;
use std::{cmp::Ordering, fmt::Debug};
use bevy_app::App;
use bevy_config_system::{ConfigKey, ConfigValue};
use bevy_ecs::{
system::Commands,
world::{CommandQueue, World},
};
use bevy_reflect::Reflect;
use bevy_utils::{HashMap, HashSet};
#[derive(Debug, Clone, PartialEq, Eq, Reflect)]
pub enum ModDependency {
Required {
name: String,
version: ModVersionRequirement,
},
Optional {
name: String,
version: ModVersionRequirement,
},
Conflict(String),
}
#[derive(Debug, Clone, PartialEq, Eq, Reflect, Copy, Default)]
pub struct ModVersion {
pub major: u16,
pub minor: u16,
pub patch: u16,
}
impl ModVersion {
pub fn from_str(version: &str) -> Self {
let parts: Vec<&str> = version.split('.').collect();
let major = parts
.get(0)
.and_then(|s| s.parse::<u16>().ok())
.unwrap_or(0);
let minor = parts
.get(1)
.and_then(|s| s.parse::<u16>().ok())
.unwrap_or(0);
let patch = parts
.get(2)
.and_then(|s| s.parse::<u16>().ok())
.unwrap_or(0);
Self {
major,
minor,
patch,
}
}
fn from_str_end(version: &str) -> Self {
let parts: Vec<&str> = version.split('.').collect();
let major = parts
.get(0)
.and_then(|s| s.parse::<u16>().ok())
.unwrap_or(u16::MAX);
let minor = parts
.get(1)
.and_then(|s| s.parse::<u16>().ok())
.unwrap_or(u16::MAX);
let patch = parts
.get(2)
.and_then(|s| s.parse::<u16>().ok())
.unwrap_or(u16::MAX);
Self {
major,
minor,
patch,
}
}
}
impl From<&str> for ModVersion {
fn from(version: &str) -> Self {
Self::from_str(version)
}
}
impl PartialOrd for ModVersion {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ModVersion {
fn cmp(&self, other: &Self) -> Ordering {
if self.major != other.major {
return self.major.cmp(&other.major);
}
if self.minor != other.minor {
return self.minor.cmp(&other.minor);
}
self.patch.cmp(&other.patch)
}
}
impl ToString for ModVersion {
fn to_string(&self) -> String {
format!("{}.{}.{}", self.major, self.minor, self.patch)
}
}
impl Hash for ModVersion {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.major.hash(state);
self.minor.hash(state);
self.patch.hash(state);
}
}
#[derive(Debug, Clone, PartialEq, Eq, Reflect, Copy)]
pub enum ModVersionRequirement {
GreaterThan(ModVersion),
Range(ModVersion, ModVersion),
Exact(ModVersion),
}
impl ModVersionRequirement {
pub fn from_str(version: &str) -> Self {
match version {
v if v.starts_with(">=") => {
let version = v[2..].trim();
ModVersionRequirement::GreaterThan(ModVersion::from_str(version))
}
v if v.contains("-") => {
let versions: Vec<&str> = v.split('-').collect();
if versions.len() == 2 {
let start = ModVersion::from_str(versions[0]);
let end = ModVersion::from_str(versions[1]);
assert!(
start <= end,
"Invalid version range: {} - {}",
start.to_string(),
end.to_string()
);
ModVersionRequirement::Range(start, end)
} else {
panic!("Invalid version range: {}", v);
}
}
v => {
if version.split('.').count() == 3 {
ModVersionRequirement::Exact(ModVersion::from_str(v))
} else {
ModVersionRequirement::Range(
ModVersion::from_str(v),
ModVersion::from_str_end(v),
)
}
}
}
}
pub fn matches(&self, version: &ModVersion) -> bool {
match &self {
ModVersionRequirement::GreaterThan(v) => version > v,
ModVersionRequirement::Range(start, end) => version >= start && version <= end,
ModVersionRequirement::Exact(v) => version == v,
}
}
}
impl ToString for ModVersionRequirement {
fn to_string(&self) -> String {
match self {
ModVersionRequirement::GreaterThan(v) => format!(">={}", v.to_string()),
ModVersionRequirement::Range(start, end) => {
format!("{}-{}", start.to_string(), end.to_string())
}
ModVersionRequirement::Exact(v) => v.to_string(),
}
}
}
impl Hash for ModVersionRequirement {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.to_string().hash(state);
}
}
impl Hash for ModDependency {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
match self {
ModDependency::Required { name, version } => {
name.hash(state);
version.hash(state);
}
ModDependency::Optional { name, version } => {
name.hash(state);
version.hash(state);
}
ModDependency::Conflict(name) => {
name.hash(state);
}
};
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Copy)]
#[repr(u8)]
pub enum ModType {
Reserved,
Modification,
Library,
}
impl TryFrom<&str> for ModType {
type Error = String;
fn try_from(value: &str) -> Result<Self, Self::Error> {
match value {
"modification" => Ok(ModType::Modification),
"library" => Ok(ModType::Library),
_ => Err(format!("Invalid mod type: {}", value)),
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Reflect, Default, Copy)]
#[repr(u8)]
pub enum ModState {
#[default]
Unloaded,
Prototype,
Data,
Modify,
Loaded,
}
impl ModState {
pub fn consecutive(self) -> Self {
match self {
ModState::Unloaded => ModState::Prototype,
ModState::Prototype => ModState::Data,
ModState::Data => ModState::Modify,
ModState::Modify => ModState::Loaded,
_ => ModState::Loaded,
}
}
}
impl PartialOrd for ModState {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for ModState {
fn cmp(&self, other: &Self) -> Ordering {
(*self as u8).cmp(&(*other as u8))
}
}
pub type ModConfig = HashMap<ConfigKey, ConfigValue>;
pub type GlobalModConfig = HashMap<String, ModConfig>;
pub trait ModLoaderInterface: Send + Sync {
fn exclusive(&self) -> bool;
fn unloadable(&self) -> bool {
false
}
fn get_config_keys(&self) -> HashSet<ConfigKey>;
fn load(
&mut self,
mod_state: ModState,
config: &ModConfig,
commands: Commands,
world: &World,
) -> Result<(), String>;
fn load_exclusive(
&mut self,
mod_state: ModState,
config: &ModConfig,
world: &mut World,
) -> Result<(), String> {
let mut queue = CommandQueue::default();
let commands = Commands::new(&mut queue, world);
let res = self.load(mod_state, config, commands, world);
queue.apply(world);
res
}
#[allow(unused_variables)]
fn unload(
&mut self,
mod_state: ModState,
config: &ModConfig,
commands: Commands,
world: &World,
) -> Result<(), String> {
unimplemented!("unload not supported");
}
fn unload_exclusive(
&mut self,
mod_state: ModState,
config: &ModConfig,
world: &mut World,
) -> Result<(), String> {
let mut queue = CommandQueue::default();
let commands = Commands::new(&mut queue, world);
let res = self.unload(mod_state, config, commands, world);
queue.apply(world);
res
}
fn requires_build(&self) -> bool {
false
}
#[allow(unused_variables)]
fn build(&mut self, app: &mut App) {}
#[allow(unused_variables)]
fn build_cleanup(&mut self, app: &mut App) {}
}
#[derive(Debug, Clone, PartialEq, Eq, Reflect)]
pub struct ModInfo {
pub name: String,
pub version: ModVersion,
pub description: String,
pub author: String,
pub dependencies: HashSet<ModDependency>,
pub mod_type: ModType,
}
impl Default for ModInfo {
fn default() -> Self {
Self {
name: "mod".into(),
version: ModVersion::default(),
description: "".into(),
mod_type: ModType::Modification,
author: "unknown".into(),
dependencies: HashSet::default(),
}
}
}
impl Hash for ModInfo {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.name.hash(state);
self.version.hash(state);
}
}
impl Debug for Box<dyn ModLoaderInterface> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(format!("Box<dyn ModLoaderInterface @{:p}>", self.as_ref()).as_str())
}
}
impl Clone for Mod {
fn clone(&self) -> Self {
unimplemented!()
}
}
#[allow(unused)]
const _: () = {
#[allow(unused_mut)]
impl bevy_reflect::GetTypeRegistration for Mod
where
Mod: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
{
fn get_type_registration() -> bevy_reflect::TypeRegistration {
let mut registration = bevy_reflect::TypeRegistration::of::<Self>();
registration.insert:: <bevy_reflect::ReflectFromPtr>(bevy_reflect::FromType:: <Self> ::from_type());
registration.insert::<bevy_reflect::ReflectFromReflect>(
bevy_reflect::FromType::<Self>::from_type(),
);
registration
}
}
const _: () = {
mod private_scope {
type AssertIsPrimitive = crate::mod_object::Mod;
}
};
impl bevy_reflect::TypePath for Mod
where
Mod: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
{
fn type_path() -> &'static str {
"Mod"
}
fn short_type_path() -> &'static str {
"Mod"
}
fn type_ident() -> Option<&'static str> {
::core::option::Option::Some("Mod")
}
fn crate_name() -> Option<&'static str> {
::core::option::Option::None
}
fn module_path() -> Option<&'static str> {
::core::option::Option::None
}
}
impl bevy_reflect::Typed for Mod
where
Mod: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
{
#[inline]
fn type_info() -> &'static bevy_reflect::TypeInfo {
static CELL: bevy_reflect::utility::NonGenericTypeInfoCell =
bevy_reflect::utility::NonGenericTypeInfoCell::new();
CELL.get_or_set(|| {
let info = bevy_reflect::OpaqueInfo::new::<Self>();
bevy_reflect::TypeInfo::Opaque(info)
})
}
}
impl bevy_reflect::Reflect for Mod
where
Mod: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
{
#[inline]
fn into_any(self: ::std::boxed::Box<Self>) -> ::std::boxed::Box<dyn ::core::any::Any> {
self
}
#[inline]
fn as_any(&self) -> &dyn ::core::any::Any {
self
}
#[inline]
fn as_any_mut(&mut self) -> &mut dyn ::core::any::Any {
self
}
#[inline]
fn into_reflect(
self: ::std::boxed::Box<Self>,
) -> ::std::boxed::Box<dyn bevy_reflect::Reflect> {
self
}
#[inline]
fn as_reflect(&self) -> &dyn bevy_reflect::Reflect {
self
}
#[inline]
fn as_reflect_mut(&mut self) -> &mut dyn bevy_reflect::Reflect {
self
}
#[inline]
fn set(
&mut self,
value: ::std::boxed::Box<dyn bevy_reflect::Reflect>,
) -> ::core::result::Result<(), ::std::boxed::Box<dyn bevy_reflect::Reflect>> {
*self = <dyn bevy_reflect::Reflect>::take(value)?;
::core::result::Result::Ok(())
}
}
impl bevy_reflect::PartialReflect for Mod
where
Mod: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
{
#[inline]
fn get_represented_type_info(
&self,
) -> ::core::option::Option<&'static bevy_reflect::TypeInfo> {
::core::option::Option::Some(<Self as bevy_reflect::Typed>::type_info())
}
#[inline]
fn clone_value(&self) -> ::std::boxed::Box<dyn bevy_reflect::PartialReflect> {
::std::boxed::Box::new(::core::clone::Clone::clone(self))
}
#[inline]
fn try_apply(
&mut self,
value: &dyn bevy_reflect::PartialReflect,
) -> ::core::result::Result<(), bevy_reflect::ApplyError> {
if let ::core::option::Option::Some(value) =
<dyn bevy_reflect::PartialReflect>::try_downcast_ref::<Self>(value)
{
*self = ::core::clone::Clone::clone(value);
return ::core::result::Result::Ok(());
}
::core::result::Result::Err(bevy_reflect::ApplyError::MismatchedTypes {
from_type: ::core::convert::Into::into(
bevy_reflect::DynamicTypePath::reflect_type_path(value),
),
to_type: ::core::convert::Into::into(<Self as bevy_reflect::TypePath>::type_path()),
})
}
#[inline]
fn reflect_kind(&self) -> bevy_reflect::ReflectKind {
bevy_reflect::ReflectKind::Opaque
}
#[inline]
fn reflect_ref(&self) -> bevy_reflect::ReflectRef {
bevy_reflect::ReflectRef::Opaque(self)
}
#[inline]
fn reflect_mut(&mut self) -> bevy_reflect::ReflectMut {
bevy_reflect::ReflectMut::Opaque(self)
}
#[inline]
fn reflect_owned(self: ::std::boxed::Box<Self>) -> bevy_reflect::ReflectOwned {
bevy_reflect::ReflectOwned::Opaque(self)
}
#[inline]
fn try_into_reflect(
self: ::std::boxed::Box<Self>,
) -> ::core::result::Result<
::std::boxed::Box<dyn bevy_reflect::Reflect>,
::std::boxed::Box<dyn bevy_reflect::PartialReflect>,
> {
::core::result::Result::Ok(self)
}
#[inline]
fn try_as_reflect(&self) -> ::core::option::Option<&dyn bevy_reflect::Reflect> {
::core::option::Option::Some(self)
}
#[inline]
fn try_as_reflect_mut(&mut self) -> ::core::option::Option<&mut dyn bevy_reflect::Reflect> {
::core::option::Option::Some(self)
}
#[inline]
fn into_partial_reflect(
self: ::std::boxed::Box<Self>,
) -> ::std::boxed::Box<dyn bevy_reflect::PartialReflect> {
self
}
#[inline]
fn as_partial_reflect(&self) -> &dyn bevy_reflect::PartialReflect {
self
}
#[inline]
fn as_partial_reflect_mut(&mut self) -> &mut dyn bevy_reflect::PartialReflect {
self
}
}
impl bevy_reflect::FromReflect for Mod
where
Mod: ::core::any::Any + ::core::marker::Send + ::core::marker::Sync,
{
fn from_reflect(
reflect: &dyn bevy_reflect::PartialReflect,
) -> ::core::option::Option<Self> {
::core::option::Option::Some(::core::clone::Clone::clone(
<dyn bevy_reflect::PartialReflect>::try_downcast_ref::<Mod>(reflect)?,
))
}
}
};
#[derive(Debug)]
pub struct Mod {
pub info: ModInfo,
pub mod_state: ModState,
pub mod_loader: Box<dyn ModLoaderInterface>,
}
impl Hash for Mod {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.info.hash(state);
}
}
impl PartialEq for Mod {
fn eq(&self, other: &Self) -> bool {
self.info == other.info
}
}
impl Eq for Mod {}
impl Ord for Mod {
fn cmp(&self, other: &Self) -> Ordering {
self.info.name.cmp(&other.info.name)
}
}
impl PartialOrd for Mod {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}