launcher: the game's made-up name can be rolled for a new one
The game's own name (horizon-player.txt) isn't a kept offline name, so Forget never applied to it and there was no way to be rid of it. Settings > Account shows it with "New name", which writes a fresh PlayerNNNNN to every existing game home — the default and each instance's — so the player stays the same (new) player in its worlds, as PLAYER_FILES' copies do. Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
+2
-1
@@ -126,7 +126,8 @@ The opening, then Play ([clip](docs/opening.webm)):
|
||||
each signed-in Microsoft account, the offline names the player keeps, and the name the game
|
||||
made up (`horizon-player.txt`). Picking one plays as it from the next Play. Settings > Account
|
||||
adds offline names (3 to 16 letters, digits or `_`; the game gets `--name`) and forgets them,
|
||||
skin and cape files included; the Console's Share log takes the names out of the log too.
|
||||
skin and cape files included, and rolls the game's made-up name for a new one; the Console's
|
||||
Share log takes the names out of the log too.
|
||||
`horizon-launcher account offline
|
||||
[NAME]`, `account select <name>` (an account or an offline name), `account forget-offline
|
||||
<name>`.
|
||||
|
||||
@@ -491,6 +491,34 @@ fn follow_player(game_home: &Path, home: &Path) {
|
||||
}
|
||||
}
|
||||
|
||||
/// A fresh made-up name everywhere the game keeps one: `horizon-player.txt` is per game
|
||||
/// folder (`PLAYER_FILES`), so the same new name goes to the default home and every
|
||||
/// instance's — the player stays one player in its worlds. The file's `name:` line and the
|
||||
/// `Player` + five digits scheme are horizon-client's (`offline_name.rs`).
|
||||
pub fn new_player_name() -> Result<String, String> {
|
||||
new_player_name_in(&backend::game_home(), &backend::data_dir())
|
||||
}
|
||||
|
||||
fn new_player_name_in(game_home: &Path, data: &Path) -> Result<String, String> {
|
||||
let nanos = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map_or(0, |d| d.as_nanos() as u64);
|
||||
let name = format!(
|
||||
"Player{:05}",
|
||||
(nanos ^ u64::from(std::process::id()).rotate_left(32)) % 100_000
|
||||
);
|
||||
for instance in list_in(game_home, data) {
|
||||
if instance.home.is_dir() {
|
||||
fs::write(
|
||||
instance.home.join("horizon-player.txt"),
|
||||
format!("name:{name}\n"),
|
||||
)
|
||||
.map_err(|e| format!("{}: {e}", instance.home.display()))?;
|
||||
}
|
||||
}
|
||||
Ok(name)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
#[path = "instances/tests.rs"]
|
||||
mod tests;
|
||||
|
||||
@@ -67,6 +67,33 @@ fn player_home(home: &Path) {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn a_new_player_name_replaces_the_kept_one_everywhere() {
|
||||
let root = scratch("newname");
|
||||
let (home, data) = (root.join("horizon"), root.join("launcher"));
|
||||
player_home(&home);
|
||||
// An own instance that has the old name, and one with no file yet.
|
||||
put(
|
||||
&home.join("instances/one/horizon-player.txt"),
|
||||
"name:Player123\n",
|
||||
);
|
||||
fs::create_dir_all(home.join("instances/two")).unwrap();
|
||||
let name = new_player_name_in(&home, &data).unwrap();
|
||||
assert_ne!(name, "Player123");
|
||||
assert!(name.starts_with("Player") && name.len() == 11);
|
||||
let kept = |home: &Path| fs::read_to_string(home.join("horizon-player.txt")).unwrap();
|
||||
for h in [
|
||||
&home,
|
||||
&home.join("instances/one"),
|
||||
&home.join("instances/two"),
|
||||
&home.join("ports/small-pack/home"),
|
||||
&home.join("horizon-packs/cozy/home"),
|
||||
] {
|
||||
assert_eq!(kept(h), format!("name:{name}\n"), "{}", h.display());
|
||||
}
|
||||
let _ = fs::remove_dir_all(&root);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn existing_worlds_stay_where_they_are_and_every_folder_becomes_an_instance() {
|
||||
let root = scratch("migrate");
|
||||
|
||||
@@ -212,6 +212,37 @@ impl Launcher {
|
||||
));
|
||||
}
|
||||
rows = rows.children(self.skin_panel(cx));
|
||||
// The name the game made up can't be forgotten — it's what an offline player is —
|
||||
// only rolled for a new one.
|
||||
if let Some(made_up) = self.offline_name.clone() {
|
||||
let plays = now == Player::GameName;
|
||||
n += 1;
|
||||
rows = rows.child(index_row(
|
||||
n,
|
||||
made_up,
|
||||
Some(
|
||||
div()
|
||||
.child(tr(if plays {
|
||||
"Offline name · the game's own · plays"
|
||||
} else {
|
||||
"Offline name · the game's own"
|
||||
}))
|
||||
.into_any_element(),
|
||||
),
|
||||
Some(
|
||||
button("game-name-new", "New name", Kind::Ghost, &t)
|
||||
.on_click(cx.listener(|this, _: &ClickEvent, _, cx| {
|
||||
match instances::new_player_name() {
|
||||
Ok(name) => this.offline_name = Some(name),
|
||||
Err(e) => this.sign_in_state = Some(SignInState::Done(Err(e))),
|
||||
}
|
||||
cx.notify();
|
||||
}))
|
||||
.into_any_element(),
|
||||
),
|
||||
&t,
|
||||
));
|
||||
}
|
||||
n += 1;
|
||||
let add = div()
|
||||
.flex()
|
||||
|
||||
Reference in New Issue
Block a user