summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLibravatar bigfoot547 <[email protected]>2025-02-18 18:02:51 -0600
committerLibravatar bigfoot547 <[email protected]>2025-02-18 18:02:51 -0600
commit7f7e6d7a7028536dbe3d9d6d6cc818d61307b12c (patch)
tree60b7f40e59b10309b70c25ff6c4083efb8dafa08
parentrandom changes (diff)
launch the game with an account
-rw-r--r--ozone-cli/src/main.rs20
-rw-r--r--ozone/src/auth/types.rs65
-rw-r--r--ozone/src/launcher.rs24
-rw-r--r--ozone/src/launcher/runner.rs29
4 files changed, 116 insertions, 22 deletions
diff --git a/ozone-cli/src/main.rs b/ozone-cli/src/main.rs
index 81a44f9..03f249a 100644
--- a/ozone-cli/src/main.rs
+++ b/ozone-cli/src/main.rs
@@ -429,12 +429,13 @@ async fn main_inner(cli: Cli) -> Result<ExitCode, Box<dyn Error>> {
let accounts_path = home.join(ACCOUNT_DB_PATH);
let mut accounts = AccountStorage::load(&accounts_path).await?;
- let Some(account) = accounts.get_selected_account_mut() else {
- eprintln!("No account selected.");
- return Ok(ExitCode::FAILURE);
- };
if !cli.offline {
+ let Some(account) = accounts.get_selected_account_mut() else {
+ eprintln!("No account selected.");
+ return Ok(ExitCode::FAILURE);
+ };
+
if let Account::MSA(msa_acct) = account {
let client = MsaAccount::create_client();
@@ -451,15 +452,20 @@ async fn main_inner(cli: Cli) -> Result<ExitCode, Box<dyn Error>> {
return Ok(ExitCode::FAILURE);
}
}
-
- accounts.save(&accounts_path).await?;
}
+
+ accounts.save(&accounts_path).await?;
}
+
+ let Some((_, account)) = accounts.get_selected_account() else {
+ eprintln!("No account selected.");
+ return Ok(ExitCode::FAILURE);
+ };
println!("Preparing the game files...");
let launcher = Launcher::new(&home, !cli.offline).await?;
- let launch = launcher.prepare_launch(inst, Settings::get_instance_path(selection), settings.client_id).await.map_err(|e| {
+ let launch = launcher.prepare_launch(inst, Settings::get_instance_path(selection), settings.client_id, account, false).await.map_err(|e| {
error!("error launching: {e}");
e
})?;
diff --git a/ozone/src/auth/types.rs b/ozone/src/auth/types.rs
index a2ac7da..3f75387 100644
--- a/ozone/src/auth/types.rs
+++ b/ozone/src/auth/types.rs
@@ -153,6 +153,13 @@ impl MsaAccount {
refresh_token: None
}
}
+
+ pub fn is_demo(&self) -> bool {
+ // player_profile is saved - player is permitted to play the game without demo mode as long
+ // as their profile exists (demo profiles don't seem to be used anymore - you must own the
+ // game to make a profile).
+ self.player_profile.is_none()
+ }
}
#[derive(Debug, Serialize, Deserialize)]
@@ -175,6 +182,64 @@ impl Account {
Account::MSA(account) => account.xuid.as_ref().map(|x| format!("msa:xuid:{x}"))
}
}
+
+ // stuff the game needs to know about this account
+ pub(crate) fn is_demo(&self) -> bool {
+ match *self {
+ Account::MSA(ref msa_acct) => msa_acct.is_demo(),
+ _ => false
+ }
+ }
+
+ pub(crate) fn get_access_token(&self) -> Option<&str> {
+ match *self {
+ Account::MSA(ref msa_acct) => msa_acct.mc_token.as_ref().map(|t| t.value.as_str()),
+ _ => None
+ }
+ }
+
+ pub(crate) fn get_player_name(&self) -> Option<&str> {
+ match *self {
+ Account::MSA(ref msa_acct) => msa_acct.player_profile.as_ref().map(|i| i.name.as_str()),
+ Account::Dummy(ref profile) => Some(profile.name.as_str())
+ }
+ }
+
+ pub(crate) fn get_session_token(&self) -> Option<String> {
+ match *self {
+ Account::MSA(ref msa_acct) => msa_acct.mc_token.as_ref().zip(msa_acct.player_info.as_ref())
+ .map(|(t, p)| format!("token:{}:{}", t.value, p.id.as_simple())),
+ _ => None
+ }
+ }
+
+ pub(crate) fn get_uuid(&self) -> Option<Uuid> {
+ match *self {
+ Account::MSA(ref msa_acct) => msa_acct.player_profile.as_ref().map(|p| p.id),
+ Account::Dummy(ref profile) => Some(profile.id)
+ }
+ }
+
+ pub(crate) fn get_xuid(&self) -> Option<&str> {
+ match *self {
+ Account::MSA(ref msa_acct) => msa_acct.xuid.as_deref(),
+ _ => None
+ }
+ }
+
+ pub(crate) fn get_profile_properties(&self) -> Option<&PropertyMap> {
+ match *self {
+ Account::MSA(ref msa_acct) => msa_acct.player_profile.as_ref().map(|p| &p.properties),
+ Account::Dummy(ref profile) => Some(&profile.properties)
+ }
+ }
+
+ pub(crate) fn get_user_type(&self) -> &'static str {
+ match *self {
+ Account::MSA(_) => "msa",
+ _ => "legacy"
+ }
+ }
}
#[derive(Debug, Default, Serialize, Deserialize)]
diff --git a/ozone/src/launcher.rs b/ozone/src/launcher.rs
index add9f36..f724e46 100644
--- a/ozone/src/launcher.rs
+++ b/ozone/src/launcher.rs
@@ -43,6 +43,7 @@ pub use runner::run_the_game;
pub use crate::util::{EnsureFileError, FileVerifyError, IntegrityError};
use crate::assets::AssetIndex;
use runner::ArgumentType;
+use crate::auth::Account;
use crate::launcher::download::FileDownload;
use crate::launcher::jre::{JavaRuntimeError, JavaRuntimeRepository};
use crate::launcher::version::VersionError;
@@ -210,7 +211,10 @@ struct LaunchInfo<'l> {
version_type: Option<VersionType>,
asset_index: Option<AssetIndex>,
- resolution: Option<Resolution>
+ resolution: Option<Resolution>,
+
+ account: &'l Account,
+ force_demo: bool
}
#[derive(Debug)]
@@ -224,14 +228,17 @@ pub struct Launch {
}
#[derive(Clone, Copy)]
-struct InstanceFeatureMatcher<'inst> {
- instance: &'inst Instance
+struct LaunchFeatureMatcher<'l> {
+ instance: &'l Instance,
+ account: &'l Account
}
-impl<'a> FeatureMatcher<'a> for InstanceFeatureMatcher<'_> {
+impl<'a> FeatureMatcher<'a> for LaunchFeatureMatcher<'_> {
fn matches(self, feature: &str) -> bool {
match feature {
"has_custom_resolution" => self.instance.resolution.is_some(),
+ "__ozone_win10_hack" => false, // todo
+ "is_demo_user" => self.account.is_demo(),
_ => false
}
}
@@ -319,9 +326,9 @@ impl Launcher {
/* TODO:
* - launch game using JNI
*/
- pub async fn prepare_launch(&self, instance: &Instance, inst_home: impl AsRef<Path>, client_id: Uuid) -> Result<Launch, LaunchError> {
+ pub async fn prepare_launch(&self, instance: &Instance, inst_home: impl AsRef<Path>, client_id: Uuid, account: &Account, force_demo: bool) -> Result<Launch, LaunchError> {
let start = Instant::now();
- let feature_matcher = InstanceFeatureMatcher { instance };
+ let feature_matcher = LaunchFeatureMatcher { instance, account };
let version_id = &instance.game_version;
let version_id = self.versions.get_instance_version_id(version_id);
@@ -513,7 +520,10 @@ impl Launcher {
version_type: ver.version_type.clone(),
asset_index: asset_idx,
- resolution: instance.resolution
+ resolution: instance.resolution,
+
+ account,
+ force_demo
};
let Some(ref main_class) = ver.main_class else {
diff --git a/ozone/src/launcher/runner.rs b/ozone/src/launcher/runner.rs
index 9e7c05e..23c0548 100644
--- a/ozone/src/launcher/runner.rs
+++ b/ozone/src/launcher/runner.rs
@@ -5,6 +5,7 @@ use std::path::{Path, PathBuf};
use std::process::Command;
use log::{debug, warn};
use tokio::{fs, io};
+use crate::auth::{property_map, PropertyMap};
use crate::util::{self, AsJavaPath};
use crate::version::{CompleteVersion, FeatureMatcher, OperatingSystem};
use super::rules::CompatCheck;
@@ -21,16 +22,28 @@ const PATH_SEP: &str = ";";
#[cfg(not(windows))]
const PATH_SEP: &str = ":";
+fn serialize_props(props: &PropertyMap, legacy: bool) -> String {
+ let mut out = Vec::<u8>::new();
+
+ if legacy {
+ let mut ser = serde_json::Serializer::new(&mut out);
+ property_map::legacy::serialize(props, &mut ser).expect("serializing property map should be infallible");
+ String::from_utf8(out).expect("WTF: serialized property map contained invalid UTF-8")
+ } else {
+ serde_json::to_string(props).expect("serializing property map should be infallible")
+ }
+}
+
impl<'key, 'rep> SubFunc<'key, 'rep> for LaunchArgSub<'rep, '_> {
fn substitute(self, key: &'key str) -> Option<Cow<'rep, str>> {
match key {
"assets_index_name" => self.0.asset_index_name.as_ref().map(|s| Cow::Borrowed(s.as_str())),
"assets_root" => Some(self.0.launcher.assets.get_home().as_java_path().to_string_lossy()),
- "auth_access_token" => Some(Cow::Borrowed("-")), // TODO
- "auth_player_name" => Some(Cow::Borrowed("Player")), // TODO
- "auth_session" => Some(Cow::Borrowed("-")), // TODO
- "auth_uuid" => Some(Cow::Borrowed("00000000-0000-0000-0000-000000000000")), // TODO
- "auth_xuid" => Some(Cow::Borrowed("0000000000000000")), // TODO
+ "auth_access_token" => Some(Cow::Borrowed(self.0.account.get_access_token().unwrap_or("-"))),
+ "auth_player_name" => Some(Cow::Borrowed(self.0.account.get_player_name().unwrap_or("Player"))),
+ "auth_session" => Some(self.0.account.get_session_token().map(Cow::Owned).unwrap_or(Cow::Borrowed("-"))),
+ "auth_uuid" => Some(Cow::Owned(self.0.account.get_uuid().unwrap_or_default().as_simple().to_string())),
+ "auth_xuid" => self.0.account.get_xuid().map(Cow::Borrowed),
"classpath" => Some(Cow::Borrowed(self.0.classpath.as_str())),
"classpath_separator" => Some(Cow::Borrowed(PATH_SEP)),
"clientid" | "client_id" => Some(Cow::Owned(self.0.client_id.to_string())),
@@ -49,9 +62,9 @@ impl<'key, 'rep> SubFunc<'key, 'rep> for LaunchArgSub<'rep, '_> {
"quickPlaySingleplayer" => None, // TODO
"resolution_height" => Some(self.0.resolution.map(|r| Cow::Owned(r.height.to_string())).unwrap_or(Cow::Borrowed(""))),
"resolution_width" => Some(self.0.resolution.map(|r| Cow::Owned(r.width.to_string())).unwrap_or(Cow::Borrowed(""))),
- "user_properties" => Some(Cow::Borrowed("{}")), // TODO
- "user_property_map" => Some(Cow::Borrowed("[]")), // TODO
- "user_type" => Some(Cow::Borrowed("legacy")), // TODO
+ "user_properties" => Some(self.0.account.get_profile_properties().map(|p| Cow::Owned(serialize_props(p, true))).unwrap_or(Cow::Borrowed("{}"))),
+ "user_property_map" => Some(self.0.account.get_profile_properties().map(|p| Cow::Owned(serialize_props(p, false))).unwrap_or(Cow::Borrowed("[]"))),
+ "user_type" => Some(Cow::Borrowed(self.0.account.get_user_type())),
"version_name" => Some(Cow::Borrowed(self.0.version_id.as_ref())),
"version_type" => self.0.version_type.as_ref().map(|s| Cow::Borrowed(s.as_ref())),
_ => {