mirror of
https://github.com/horizon-rust/horizon.git
synced 2026-11-10 17:18:25 -10:00
Launcher and horizon-server reach the engine through horizon-platform
Layer plan M6: horizon-platform gains feature-gated re-exports (version, no_ai, prepared_files always; world, protocol, prepare, server-files behind features) and the two Horizon-layer crates use them instead of depending on horizon-core, horizon-prepare, horizon-protocol, horizon-world and horizon-server-files. Seven layers.json exceptions go. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
1 parent
21a750533e
commit
92886cd964
37 files changed
+155
-118
No files matched your search
@@ -6,8 +6,24 @@ license.workspace = true
|
||||
rust-version.workspace = true
|
||||
description = "The platform's public API: what the Horizon layer and mods may use of the engine (docs/layers.md#platform)"
|
||||
|
||||
[features]
|
||||
# Each part of the engine a Horizon crate may reach, so a crate builds only the engine it uses.
|
||||
# level.dat, servers.dat and region files: NBT, compression, region read and write.
|
||||
world = ["dep:horizon-world"]
|
||||
# A server's address after its SRV record, and the offline-mode UUID of a name.
|
||||
protocol = ["world", "dep:horizon-protocol"]
|
||||
# Preparing the game's files from the player's own Minecraft install.
|
||||
prepare = ["dep:horizon-prepare"]
|
||||
# A server's own files: server.properties, the op, white and ban lists, the user cache.
|
||||
server-files = ["dep:horizon-server-files"]
|
||||
|
||||
[dependencies]
|
||||
horizon-core = { path = "../horizon-core" }
|
||||
horizon-system = { path = "../horizon-system" }
|
||||
horizon-world = { path = "../horizon-world", optional = true }
|
||||
horizon-protocol = { path = "../horizon-protocol", optional = true }
|
||||
horizon-prepare = { path = "../horizon-prepare", optional = true }
|
||||
horizon-server-files = { path = "../horizon-server-files", optional = true }
|
||||
|
||||
[lints]
|
||||
workspace = true
|
||||
@@ -4,8 +4,40 @@
|
||||
//! comes through here.
|
||||
//!
|
||||
//! Today this re-exports engine modules whole. Each one gets its own stable types as the split
|
||||
//! goes on (docs/layers.md#plan, M6).
|
||||
//! goes on (docs/layers.md#plan, M6). The parts that pull in large engine crates sit behind
|
||||
//! features (`world`, `protocol`, `prepare`, `server-files`), so a crate builds only what it uses.
|
||||
|
||||
/// The machine the game runs on (`horizon-system`): OS, CPU, memory, drive, power, GPU maker and
|
||||
/// memory.
|
||||
pub use horizon_system as system;
|
||||
|
||||
/// The pinned Minecraft version: its id, protocol and data version.
|
||||
pub use horizon_core::version;
|
||||
|
||||
/// The player's "no AI" switch, which the launcher, the game and their agent tools share.
|
||||
pub use horizon_core::no_ai;
|
||||
|
||||
/// The prepared folder's file of files (`prepared_files::FILE_NAME`).
|
||||
pub use horizon_core::prepared_files;
|
||||
|
||||
/// Vanilla's world files, as tools outside the game read and write them.
|
||||
#[cfg(feature = "world")]
|
||||
pub mod world {
|
||||
pub use horizon_core::position::ChunkPos;
|
||||
pub use horizon_world::{compression, nbt, region};
|
||||
}
|
||||
|
||||
/// What a server list and a profile head need of the network protocol.
|
||||
#[cfg(feature = "protocol")]
|
||||
pub mod protocol {
|
||||
pub use horizon_protocol::crypto::offline_uuid;
|
||||
pub use horizon_protocol::server_redirect;
|
||||
}
|
||||
|
||||
/// The preparer: the game's files from the player's own Minecraft install.
|
||||
#[cfg(feature = "prepare")]
|
||||
pub use horizon_prepare as prepare;
|
||||
|
||||
/// A server's own files: `server.properties`, the op, white and ban lists, the user cache.
|
||||
#[cfg(feature = "server-files")]
|
||||
pub use horizon_server_files as server_files;
|
||||
@@ -7,7 +7,10 @@ rust-version.workspace = true
|
||||
description = "Host a Horizon server: server.toml, the CLI and the supervisor that runs the game headless (docs/server.md)"
|
||||
|
||||
[dependencies]
|
||||
horizon-server-files = { path = "../horizon-server-files" }
|
||||
# The engine, through the platform's public API (docs/layers.md): server.properties and the user lists, the
|
||||
# version `server/status` reports, and region files for [prune_chunks] (prune.rs), read and rewritten as vanilla's
|
||||
# RegionFile does.
|
||||
horizon-platform = { path = "../horizon-platform", features = ["server-files", "world"] }
|
||||
# `horizon-server mcp`: the Model Context Protocol, shared with horizon-ctl and the launcher.
|
||||
horizon-mcp = { path = "../horizon-mcp" }
|
||||
# server.toml's [anticheat], which the game reads: checked here before every start.
|
||||
@@ -30,13 +33,9 @@ toml_edit = "0.25"
|
||||
# lockfile, ureq through horizon-server-files).
|
||||
horizon-release-sign = { path = "../horizon-release-sign" }
|
||||
sha2 = "0.10"
|
||||
# Vanilla's management protocol: the WebSocket handshake's SHA-1 and Base64 (both already in the lockfile), and
|
||||
# the version `server/status` reports.
|
||||
# Vanilla's management protocol: the WebSocket handshake's SHA-1 and Base64 (both already in the lockfile).
|
||||
sha1 = "0.10"
|
||||
base64 = "0.22"
|
||||
horizon-core = { path = "../horizon-core" }
|
||||
# [prune_chunks]: region files read and rewritten as vanilla's RegionFile does (prune.rs).
|
||||
horizon-world = { path = "../horizon-world" }
|
||||
# Its TLS (on by default, as vanilla's): rustls on ring, both already in the lockfile through ureq; the keystore
|
||||
# is a PKCS12 file, as vanilla's, and one is generated (self-signed) when the owner sets none.
|
||||
rustls = { version = "0.23", default-features = false, features = ["ring", "std", "tls12"] }
|
||||
|
||||
@@ -10,7 +10,7 @@ use crate::{
|
||||
engine,
|
||||
http::Response,
|
||||
};
|
||||
use horizon_server_files::user_lists::{
|
||||
use horizon_platform::server_files::user_lists::{
|
||||
BanList, IpBanList, OpList, UserList, WhiteList, format_date,
|
||||
};
|
||||
use serde_json::{Value, json};
|
||||
@@ -189,7 +189,7 @@ fn stopped_command(action: Action, body: &Value) -> Option<(&'static str, Vec<&s
|
||||
}
|
||||
|
||||
/// A list from its file in the server folder; missing or broken reads as empty, as the game would start.
|
||||
fn load<E: horizon_server_files::user_lists::Entry>(dir: &Path, file: &str) -> UserList<E> {
|
||||
fn load<E: horizon_platform::server_files::user_lists::Entry>(dir: &Path, file: &str) -> UserList<E> {
|
||||
let mut list = UserList::new(dir.join(file));
|
||||
let _ = list.load();
|
||||
list
|
||||
@@ -313,7 +313,7 @@ fn uuid(id: u128) -> String {
|
||||
fn whitelist_on(dir: &Path) -> bool {
|
||||
std::fs::read(dir.join(crate::config::PROPERTIES))
|
||||
.ok()
|
||||
.and_then(|b| horizon_server_files::java_properties::Properties::load_file(&b).ok())
|
||||
.and_then(|b| horizon_platform::server_files::java_properties::Properties::load_file(&b).ok())
|
||||
.and_then(|p| p.get("white-list").map(|v| v == "true"))
|
||||
.or_else(|| {
|
||||
crate::config::Config::load(dir)
|
||||
|
||||
@@ -583,8 +583,8 @@ pub fn same_token(a: &str, b: &str) -> bool {
|
||||
}
|
||||
|
||||
/// Writes a file only its owner can read: through a private temporary file renamed over it
|
||||
/// (`horizon_server_files::private_file`), so an older, wider-readable file never keeps the token.
|
||||
pub use horizon_server_files::private_file::write_private;
|
||||
/// (`horizon_platform::server_files::private_file`), so an older, wider-readable file never keeps the token.
|
||||
pub use horizon_platform::server_files::private_file::write_private;
|
||||
|
||||
/// Removes the endpoint file when the supervisor ends.
|
||||
pub struct Endpoint(PathBuf);
|
||||
|
||||
@@ -30,7 +30,7 @@ fn stamp(time: SystemTime) -> String {
|
||||
let secs = time
|
||||
.duration_since(SystemTime::UNIX_EPOCH)
|
||||
.map_or(0, |d| d.as_secs() as i64);
|
||||
let (y, m, d) = horizon_server_files::java_properties::civil_from_days(secs.div_euclid(86_400));
|
||||
let (y, m, d) = horizon_platform::server_files::java_properties::civil_from_days(secs.div_euclid(86_400));
|
||||
let s = secs.rem_euclid(86_400);
|
||||
format!(
|
||||
"{y:04}-{m:02}-{d:02}_{:02}-{:02}-{:02}",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
//! start the supervisor writes them into `server.properties`, which the game reads exactly as vanilla's
|
||||
//! server does; what the game changes there itself (`/whitelist on`) is copied back afterwards.
|
||||
|
||||
use horizon_server_files::{
|
||||
use horizon_platform::server_files::{
|
||||
java_properties::{Properties, date_string, now_millis},
|
||||
properties::{DIFFICULTIES, GAME_MODES, ServerProperties, SystemEntropy},
|
||||
};
|
||||
|
||||
@@ -115,7 +115,7 @@ fn start_failure(program: &Path, code: Option<i32>, stderr: &str) -> String {
|
||||
}
|
||||
|
||||
/// The Minecraft version this server is pinned to; prepared game files must be stamped with it.
|
||||
pub const GAME_VERSION: &str = horizon_core::version::VERSION_ID;
|
||||
pub const GAME_VERSION: &str = horizon_platform::version::VERSION_ID;
|
||||
|
||||
/// Where the prepared game files may be, in the order they are tried.
|
||||
pub fn resource_candidates(config: &Config, dir: &Path, program: Option<&Path>) -> Vec<PathBuf> {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
//! Every question has a flag, so it also runs unattended (Docker, scripts).
|
||||
|
||||
use crate::{config, config::Config, engine};
|
||||
use horizon_server_files::{
|
||||
use horizon_platform::server_files::{
|
||||
eula,
|
||||
java_properties::now_millis,
|
||||
user_lists::{OpEntry, OpList, WhiteEntry, WhiteList},
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
//! reloads it (`whitelist reload`), as it would be written while the server is stopped.
|
||||
|
||||
use crate::{config, supervisor};
|
||||
use horizon_server_files::user_lists::{
|
||||
use horizon_platform::server_files::user_lists::{
|
||||
BanEntry, BanList, IpBanList, NameAndId, OpEntry, OpList, WhiteEntry, WhiteList,
|
||||
};
|
||||
use std::path::Path;
|
||||
@@ -244,7 +244,7 @@ fn add_offline(dir: &Path, name: &str) -> Result<(), String> {
|
||||
/// The profile a running server would store for `name`: the offline one, or in online mode the account the
|
||||
/// services know by that name.
|
||||
pub fn account(name: &str, online: bool) -> Result<NameAndId, String> {
|
||||
horizon_server_files::profiles::resolve(name, online).ok_or_else(|| {
|
||||
horizon_platform::server_files::profiles::resolve(name, online).ok_or_else(|| {
|
||||
if online {
|
||||
format!("That player does not exist (no Minecraft account is named {name}, or the services didn't answer)")
|
||||
} else {
|
||||
@@ -259,9 +259,9 @@ fn listed<'a>(mut users: impl Iterator<Item = &'a NameAndId>, name: &str) -> Opt
|
||||
}
|
||||
|
||||
/// A list from its file; a missing file is an empty list, a broken one an error (vanilla would replace it).
|
||||
fn load<E: horizon_server_files::user_lists::Entry>(
|
||||
mut list: horizon_server_files::user_lists::UserList<E>,
|
||||
) -> Result<horizon_server_files::user_lists::UserList<E>, String> {
|
||||
fn load<E: horizon_platform::server_files::user_lists::Entry>(
|
||||
mut list: horizon_platform::server_files::user_lists::UserList<E>,
|
||||
) -> Result<horizon_platform::server_files::user_lists::UserList<E>, String> {
|
||||
// UserList::load deliberately tolerates I/O errors for the running game's vanilla behavior.
|
||||
// A stopped command must not edit an empty replacement for a file it could not read.
|
||||
let text = match std::fs::read_to_string(&list.file) {
|
||||
|
||||
@@ -11,7 +11,7 @@ use crate::{
|
||||
management_schema as schema, management_tls,
|
||||
websocket::{Link, Socket},
|
||||
};
|
||||
use horizon_server_files::properties::{ServerProperties, SystemEntropy, generate_secret_key};
|
||||
use horizon_platform::server_files::properties::{ServerProperties, SystemEntropy, generate_secret_key};
|
||||
use serde_json::{Value, json};
|
||||
use std::{
|
||||
net::{SocketAddr, TcpListener, TcpStream, ToSocketAddrs},
|
||||
|
||||
@@ -9,7 +9,7 @@ use crate::{
|
||||
engine,
|
||||
management::{self, Failure, INTERNAL_ERROR, invalid_params},
|
||||
};
|
||||
use horizon_server_files::properties::{ServerProperties, SystemEntropy};
|
||||
use horizon_platform::server_files::properties::{ServerProperties, SystemEntropy};
|
||||
use serde_json::{Value, json};
|
||||
use std::path::Path;
|
||||
|
||||
@@ -1007,8 +1007,8 @@ impl Game<'_> {
|
||||
/// `ServerStateService.status`.
|
||||
fn status(&self) -> Value {
|
||||
let version = json!({
|
||||
"name": horizon_core::version::VERSION_ID,
|
||||
"protocol": horizon_core::version::PROTOCOL_VERSION,
|
||||
"name": horizon_platform::version::VERSION_ID,
|
||||
"protocol": horizon_platform::version::PROTOCOL_VERSION,
|
||||
});
|
||||
let players = if self.running() {
|
||||
self.online().ok()
|
||||
@@ -1089,17 +1089,17 @@ impl Game<'_> {
|
||||
struct Lists<'a>(&'a Path);
|
||||
|
||||
impl Lists<'_> {
|
||||
fn load<E: horizon_server_files::user_lists::Entry>(
|
||||
fn load<E: horizon_platform::server_files::user_lists::Entry>(
|
||||
&self,
|
||||
file: &str,
|
||||
) -> horizon_server_files::user_lists::UserList<E> {
|
||||
let mut list = horizon_server_files::user_lists::UserList::new(self.0.join(file));
|
||||
) -> horizon_platform::server_files::user_lists::UserList<E> {
|
||||
let mut list = horizon_platform::server_files::user_lists::UserList::new(self.0.join(file));
|
||||
let _ = list.load();
|
||||
list
|
||||
}
|
||||
|
||||
fn allowlist(&self) -> Value {
|
||||
let list: horizon_server_files::user_lists::WhiteList = self.load("whitelist.json");
|
||||
let list: horizon_platform::server_files::user_lists::WhiteList = self.load("whitelist.json");
|
||||
list.entries()
|
||||
.iter()
|
||||
.map(|e| json!({ "id": uuid(e.user.id), "name": e.user.name }))
|
||||
@@ -1107,7 +1107,7 @@ impl Lists<'_> {
|
||||
}
|
||||
|
||||
fn operators(&self) -> Value {
|
||||
let list: horizon_server_files::user_lists::OpList = self.load("ops.json");
|
||||
let list: horizon_platform::server_files::user_lists::OpList = self.load("ops.json");
|
||||
list.entries()
|
||||
.iter()
|
||||
.map(|e| {
|
||||
@@ -1121,7 +1121,7 @@ impl Lists<'_> {
|
||||
}
|
||||
|
||||
fn bans(&self) -> Value {
|
||||
let list: horizon_server_files::user_lists::BanList = self.load("banned-players.json");
|
||||
let list: horizon_platform::server_files::user_lists::BanList = self.load("banned-players.json");
|
||||
list.entries()
|
||||
.iter()
|
||||
.map(|b| {
|
||||
@@ -1132,7 +1132,7 @@ impl Lists<'_> {
|
||||
}
|
||||
|
||||
fn ip_bans(&self) -> Value {
|
||||
let list: horizon_server_files::user_lists::IpBanList = self.load("banned-ips.json");
|
||||
let list: horizon_platform::server_files::user_lists::IpBanList = self.load("banned-ips.json");
|
||||
list.entries()
|
||||
.iter()
|
||||
.map(|b| ban(("ip", json!(b.user)), &b.reason, &b.source, b.expires))
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//! one instead (a self-signed certificate in `.horizon/`), so turning the protocol on is enough, and logs the
|
||||
//! certificate's fingerprint for clients to pin.
|
||||
|
||||
use horizon_server_files::properties::{SystemEntropy, generate_secret_key};
|
||||
use horizon_platform::server_files::properties::{SystemEntropy, generate_secret_key};
|
||||
use p12_keystore::{Certificate, KeyStore, KeyStoreEntry, PrivateKeyChain};
|
||||
use rustls::pki_types::{CertificateDer, PrivateKeyDer, PrivatePkcs8KeyDer};
|
||||
use sha2::{Digest, Sha256};
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
//! copied as they were) and a file left empty is deleted. Nothing else in the world is touched: not
|
||||
//! level.dat, player data, stats or advancements.
|
||||
|
||||
use horizon_world::{
|
||||
use horizon_platform::world::{
|
||||
nbt::{self, Limits, Tag},
|
||||
region::{ChunkRecord, RegionFile, RegionWriter, position_from_path},
|
||||
};
|
||||
@@ -344,7 +344,7 @@ struct Tally {
|
||||
fn spawn_chunk(world: &Path) -> Chunk {
|
||||
let read = || -> Option<Chunk> {
|
||||
let bytes = fs::read(world.join("level.dat")).ok()?;
|
||||
let bytes = horizon_world::compression::Compression::Gzip
|
||||
let bytes = horizon_platform::world::compression::Compression::Gzip
|
||||
.decode(&bytes, Limits::saved().max_bytes)
|
||||
.ok()?;
|
||||
let (root, _) = nbt::read_named(&bytes, Limits::saved()).ok()?;
|
||||
@@ -873,8 +873,8 @@ pub fn write_report(dir: &Path, outcome: &Outcome) -> io::Result<PathBuf> {
|
||||
#[cfg(test)]
|
||||
pub(crate) mod tests {
|
||||
use super::*;
|
||||
use horizon_core::position::ChunkPos;
|
||||
use horizon_world::{compression::Compression, nbt::NamedTag};
|
||||
use horizon_platform::world::ChunkPos;
|
||||
use horizon_platform::world::{compression::Compression, nbt::NamedTag};
|
||||
|
||||
fn temp(name: &str) -> PathBuf {
|
||||
let dir = std::env::temp_dir().join(format!("horizon-prune-{name}-{}", std::process::id()));
|
||||
|
||||
@@ -93,7 +93,7 @@ pub(super) fn recover_with(
|
||||
return clean(folder);
|
||||
}
|
||||
let journal: Journal = serde_json::from_slice(&fs::read(journal)?)?;
|
||||
let valid_name = horizon_world::region::position_from_path(Path::new(&journal.name)).is_ok()
|
||||
let valid_name = horizon_platform::world::region::position_from_path(Path::new(&journal.name)).is_ok()
|
||||
&& Path::new(&journal.name)
|
||||
.file_name()
|
||||
.is_some_and(|n| n == journal.name.as_str());
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::{
|
||||
engine,
|
||||
supervisor::note,
|
||||
};
|
||||
use horizon_server_files::properties::{ServerProperties, SystemEntropy};
|
||||
use horizon_platform::server_files::properties::{ServerProperties, SystemEntropy};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
io,
|
||||
@@ -308,7 +308,7 @@ fn read_info(shared: &Shared) -> Option<Info> {
|
||||
Some(Info {
|
||||
motd: p.motd,
|
||||
world: p.level_name,
|
||||
version: horizon_core::version::VERSION_ID.to_owned(),
|
||||
version: horizon_platform::version::VERSION_ID.to_owned(),
|
||||
players,
|
||||
max_players: status["max_players"]
|
||||
.as_i64()
|
||||
|
||||
@@ -15,7 +15,7 @@ use crate::{
|
||||
engine,
|
||||
supervisor::note,
|
||||
};
|
||||
use horizon_server_files::properties::{ServerProperties, SystemEntropy};
|
||||
use horizon_platform::server_files::properties::{ServerProperties, SystemEntropy};
|
||||
use serde_json::Value;
|
||||
use std::{
|
||||
io::{self, Read, Write},
|
||||
|
||||
+3
-6
@@ -40,12 +40,13 @@ sys-locale = "0.3"
|
||||
clap = { version = "4", features = ["derive"] }
|
||||
clap_complete = "4"
|
||||
horizon-theme = { path = "../crates/horizon-theme" }
|
||||
horizon-core = { path = "../crates/horizon-core" }
|
||||
# The engine, through the platform's public API (docs/layers.md): the pinned version, No AI, the preparer,
|
||||
# level.dat and servers.dat (the worlds and server lists of instances), and the official server's SRV record.
|
||||
horizon-platform = { path = "../crates/horizon-platform", features = ["prepare", "protocol", "world"] }
|
||||
horizon-cosmetics = { path = "../crates/horizon-cosmetics" }
|
||||
glam = "0.30"
|
||||
# `horizon-launcher mcp`: the Model Context Protocol, shared with horizon-ctl and horizon-server.
|
||||
horizon-mcp = { path = "../crates/horizon-mcp" }
|
||||
horizon-prepare = { path = "../crates/horizon-prepare" }
|
||||
# Join a friend: invite codes (the game does the joining).
|
||||
horizon-friends = { path = "../crates/horizon-friends" }
|
||||
horizon-bench = { path = "../crates/horizon-bench" }
|
||||
@@ -59,10 +60,6 @@ horizon-shader-api = { path = "../crates/horizon-shader-api" }
|
||||
horizon-release-sign = { path = "../crates/horizon-release-sign" }
|
||||
# Opt-in data collection: the consent, the outbox and its upload (docs/telemetry.md).
|
||||
horizon-telemetry = { path = "../crates/horizon-telemetry" }
|
||||
# The official server's count follows its SRV record, as the game does (server_redirect).
|
||||
horizon-protocol = { path = "../crates/horizon-protocol" }
|
||||
# level.dat and servers.dat (the worlds and server lists of instances)
|
||||
horizon-world = { path = "../crates/horizon-world" }
|
||||
# Deleting a world moves it to the system's trash
|
||||
trash = "5"
|
||||
# Backup names carry the local time, as vanilla's do
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
//! Settings' AI rows: the No AI switch (`horizon_core::no_ai`, shared with the game) and "Connect
|
||||
//! Settings' AI rows: the No AI switch (`horizon_platform::no_ai`, shared with the game) and "Connect
|
||||
//! an AI agent", the ready-made MCP config of each common client with Copy buttons, a one-click
|
||||
//! add where the client has a CLI (run only on that click, its command shown first), and a test
|
||||
//! that starts the MCP server and lists its tools. With No AI on, only the switch shows.
|
||||
|
||||
+39
-39
@@ -1388,7 +1388,7 @@ fn read_json(path: &Path) -> Option<serde_json::Value> {
|
||||
fn game_version_at(dir: &Path) -> String {
|
||||
read_json(&dir.join("research-lock.json"))
|
||||
.and_then(|v| v["minecraft"]["version"].as_str().map(String::from))
|
||||
.unwrap_or_else(|| horizon_core::VANILLA_VERSION.into())
|
||||
.unwrap_or_else(|| horizon_platform::version::VANILLA_VERSION.into())
|
||||
}
|
||||
|
||||
pub fn resources(tag: &str) -> PathBuf {
|
||||
@@ -1620,11 +1620,11 @@ const LEGACY_EXPORTERS: [&str; 18] = [
|
||||
];
|
||||
|
||||
/// A version folder's preparer as the launchers before `OUTPUT_VERSION` compared it: the digest
|
||||
/// of its sources (`horizon_prepare::sources_sha256`) and of the exporters it ran, line endings
|
||||
/// of its sources (`horizon_platform::prepare::sources_sha256`) and of the exporters it ran, line endings
|
||||
/// as checked out ignored.
|
||||
fn legacy_fingerprint(dir: &Path) -> Option<String> {
|
||||
use sha2::Digest;
|
||||
let sources = horizon_prepare::sources_sha256(&dir.join(PREPARER_CRATE))?;
|
||||
let sources = horizon_platform::prepare::sources_sha256(&dir.join(PREPARER_CRATE))?;
|
||||
let mut hash = sha2::Sha256::new();
|
||||
hash.update(sources.as_bytes());
|
||||
for name in LEGACY_EXPORTERS {
|
||||
@@ -1726,7 +1726,7 @@ pub(crate) fn copy_dir_except(from: &Path, to: &Path, skip: Option<&Path>) -> st
|
||||
if entry.file_type()?.is_dir() {
|
||||
copy_dir_except(&entry.path(), &target, skip)?;
|
||||
} else {
|
||||
horizon_prepare::clone_or_copy(&entry.path(), &target)?;
|
||||
horizon_platform::prepare::clone_or_copy(&entry.path(), &target)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
@@ -1762,7 +1762,7 @@ fn reuse_prepared(versions: &Path, tag: &str) -> Option<String> {
|
||||
let partial = to.with_file_name(format!(".{version}.partial-{}-{n}", std::process::id()));
|
||||
let _ = fs::remove_dir_all(&partial);
|
||||
// Without the data file: it names its own root, so the copy could not use it.
|
||||
let data_file = from.join(horizon_core::prepared_files::FILE_NAME);
|
||||
let data_file = from.join(horizon_platform::prepared_files::FILE_NAME);
|
||||
let copied = copy_dir_except(&from, &partial, Some(&data_file)).and_then(|_| {
|
||||
if to.join(RESOURCES_MANIFEST).exists() {
|
||||
return Ok(false); // Prepared meanwhile, by another copy or the player.
|
||||
@@ -1806,7 +1806,7 @@ pub(crate) fn reuse_prepared_for(tag: &str) {
|
||||
/// The game still writes it when this didn't finish. Only looks for the file, as
|
||||
/// `prepared_files::open` keeps it open for the whole run, which would stop a removal on Windows.
|
||||
fn write_data_file(resources: &Path) -> std::io::Result<()> {
|
||||
use horizon_core::prepared_files;
|
||||
use horizon_platform::prepared_files;
|
||||
if !prepared_at(resources) || resources.join(prepared_files::FILE_NAME).exists() {
|
||||
return Ok(());
|
||||
}
|
||||
@@ -1905,12 +1905,12 @@ fn complete_reports(versions: &Path, tag: &str) -> bool {
|
||||
|
||||
/// What a prepared version may lack that the launcher adds without preparing again, each copied
|
||||
/// from the player's own Minecraft asset store as `horizon-prepare <kind>` does: what the
|
||||
/// player misses without it, its kind (`horizon_prepare::missing_extras`, the same check as
|
||||
/// player misses without it, its kind (`horizon_platform::prepare::missing_extras`, the same check as
|
||||
/// `horizon-prepare --check`), and the copy.
|
||||
type Completion = (
|
||||
&'static str,
|
||||
&'static str,
|
||||
fn(&Path, Option<&Path>, horizon_prepare::Log) -> Result<PathBuf, String>,
|
||||
fn(&Path, Option<&Path>, horizon_platform::prepare::Log) -> Result<PathBuf, String>,
|
||||
);
|
||||
|
||||
const COMPLETIONS: [Completion; 5] = [
|
||||
@@ -1918,15 +1918,15 @@ const COMPLETIONS: [Completion; 5] = [
|
||||
(
|
||||
"Programmer Art and High Contrast",
|
||||
"packs",
|
||||
horizon_prepare::packs,
|
||||
horizon_platform::prepare::packs,
|
||||
),
|
||||
// A preparation that could not read the asset store keeps the client jar's 1x1 grey
|
||||
// placeholders, its English alone, and no sounds (#1867: Prism Launcher installs prepared
|
||||
// since 0.2.48).
|
||||
("The title panorama", "panorama", horizon_prepare::panorama),
|
||||
("The Unifont glyphs", "fonts", horizon_prepare::fonts),
|
||||
("The languages", "languages", horizon_prepare::languages),
|
||||
("The sounds", "sounds", horizon_prepare::sounds),
|
||||
("The title panorama", "panorama", horizon_platform::prepare::panorama),
|
||||
("The Unifont glyphs", "fonts", horizon_platform::prepare::fonts),
|
||||
("The languages", "languages", horizon_platform::prepare::languages),
|
||||
("The sounds", "sounds", horizon_platform::prepare::sounds),
|
||||
];
|
||||
|
||||
/// Adds what a prepared version lacks of `COMPLETIONS`: a new version reuses the resources of the
|
||||
@@ -1948,7 +1948,7 @@ fn refill_own_store(tag: &str) {
|
||||
let own = data_dir().join("minecraft");
|
||||
let resources = resources_at(&versions_dir().join(tag));
|
||||
if !own.join("assets/indexes").is_dir()
|
||||
|| !horizon_prepare::missing_extras(&resources)
|
||||
|| !horizon_platform::prepare::missing_extras(&resources)
|
||||
.iter()
|
||||
.any(|(kind, _)| matches!(*kind, "languages" | "sounds"))
|
||||
|| ASKED.swap(true, std::sync::atomic::Ordering::Relaxed)
|
||||
@@ -1989,7 +1989,7 @@ fn complete_assets_in(
|
||||
if !resources.join(RESOURCES_MANIFEST).exists() {
|
||||
return Vec::new();
|
||||
}
|
||||
let missing = horizon_prepare::missing_extras(&resources);
|
||||
let missing = horizon_platform::prepare::missing_extras(&resources);
|
||||
COMPLETIONS
|
||||
.iter()
|
||||
.filter(|(_, kind, _)| missing.iter().any(|(m, _)| m == kind))
|
||||
@@ -2025,7 +2025,7 @@ fn complete_assets_in(
|
||||
// The data file answers "missing" for what it lacks, so one from before these copies would
|
||||
// hide them. Only when something was added: a version that stays lacking keeps its file.
|
||||
if lacking().len() < lacked {
|
||||
let data_file = resources.join(horizon_core::prepared_files::FILE_NAME);
|
||||
let data_file = resources.join(horizon_platform::prepared_files::FILE_NAME);
|
||||
if let Err(e) = fs::remove_file(&data_file) {
|
||||
if e.kind() != std::io::ErrorKind::NotFound {
|
||||
log(format!("{}: not removed: {e}", data_file.display()));
|
||||
@@ -2041,7 +2041,7 @@ fn complete_assets_in(
|
||||
fn minecraft_folders() -> Vec<PathBuf> {
|
||||
let mut folders: Vec<PathBuf> = Settings::load().minecraft_dir.into_iter().collect();
|
||||
folders.extend([
|
||||
horizon_prepare::artifacts::minecraft_dir(None),
|
||||
horizon_platform::prepare::artifacts::minecraft_dir(None),
|
||||
data_dir().join("linked-minecraft"),
|
||||
data_dir().join("minecraft"),
|
||||
]);
|
||||
@@ -2092,7 +2092,7 @@ fn python() -> Option<&'static str> {
|
||||
/// How a version's resources are prepared.
|
||||
pub enum Preparer {
|
||||
/// horizon-prepare, linked into the launcher: the version ships the same preparer sources.
|
||||
InProcess(horizon_prepare::Options),
|
||||
InProcess(horizon_platform::prepare::Options),
|
||||
/// The version's own preparer: its horizon-prepare binary when its sources differ from the
|
||||
/// launcher's (a newer or older release), or the Python script of versions made before it.
|
||||
Process(Command),
|
||||
@@ -2308,9 +2308,9 @@ fn java(
|
||||
log: &dyn Fn(String),
|
||||
) -> Result<PathBuf, String> {
|
||||
let find = if compiler {
|
||||
horizon_prepare::java::find
|
||||
horizon_platform::prepare::java::find
|
||||
} else {
|
||||
horizon_prepare::java::find_runtime
|
||||
horizon_platform::prepare::java::find_runtime
|
||||
};
|
||||
if let Ok(jdk) = find(major, &[]) {
|
||||
return Ok(jdk.home());
|
||||
@@ -2318,7 +2318,7 @@ fn java(
|
||||
let mut runtimes: Vec<PathBuf> = Vec::new();
|
||||
for home in games
|
||||
.iter()
|
||||
.flat_map(|game| horizon_prepare::java::minecraft_runtimes(&game.join("runtime")))
|
||||
.flat_map(|game| horizon_platform::prepare::java::minecraft_runtimes(&game.join("runtime")))
|
||||
{
|
||||
if !runtimes.contains(&home) {
|
||||
runtimes.push(home);
|
||||
@@ -2459,7 +2459,7 @@ pub fn preparer(
|
||||
set_prepare_step(PrepareStep::Files);
|
||||
let version = game_version(tag);
|
||||
let dir = version_dir(tag);
|
||||
let reused = horizon_prepare::artifacts::reuse_imported(&dir, &|line| log(line.to_string()))?;
|
||||
let reused = horizon_platform::prepare::artifacts::reuse_imported(&dir, &|line| log(line.to_string()))?;
|
||||
let chosen = minecraft.to_path_buf();
|
||||
// Resolved even when the game files are reused: the assets still come from the install.
|
||||
let minecraft = {
|
||||
@@ -2531,7 +2531,7 @@ pub fn preparer(
|
||||
set_prepare_step(PrepareStep::Java);
|
||||
let java_home = java(
|
||||
major,
|
||||
!horizon_prepare::exporters::available(&dir),
|
||||
!horizon_platform::prepare::exporters::available(&dir),
|
||||
&games,
|
||||
&manifest,
|
||||
log,
|
||||
@@ -2559,10 +2559,10 @@ pub fn preparer(
|
||||
.map_err(|e| e.to_string())?;
|
||||
}
|
||||
if let Some(binary) = binary {
|
||||
if horizon_prepare::sources_sha256(&dir.join(PREPARER_CRATE)).as_deref()
|
||||
== Some(horizon_prepare::SOURCES_SHA256)
|
||||
if horizon_platform::prepare::sources_sha256(&dir.join(PREPARER_CRATE)).as_deref()
|
||||
== Some(horizon_platform::prepare::SOURCES_SHA256)
|
||||
{
|
||||
let options = horizon_prepare::Options {
|
||||
let options = horizon_platform::prepare::Options {
|
||||
root: dir,
|
||||
import: !reused,
|
||||
minecraft_dir: Some(minecraft.clone()),
|
||||
@@ -2600,8 +2600,8 @@ pub fn preparer(
|
||||
/// the sounds where the launcher's preparer puts them.
|
||||
pub fn sounds(tag: &str, minecraft: &Path, log: &dyn Fn(String)) {
|
||||
let dir = version_dir(tag);
|
||||
let own = horizon_prepare::sources_sha256(&dir.join(PREPARER_CRATE)).as_deref()
|
||||
== Some(horizon_prepare::SOURCES_SHA256);
|
||||
let own = horizon_platform::prepare::sources_sha256(&dir.join(PREPARER_CRATE)).as_deref()
|
||||
== Some(horizon_platform::prepare::SOURCES_SHA256);
|
||||
let binary = preparer_binary(&dir).filter(|binary| {
|
||||
!own && crate::process::command(binary)
|
||||
.arg("--help")
|
||||
@@ -2631,7 +2631,7 @@ pub fn sounds(tag: &str, minecraft: &Path, log: &dyn Fn(String)) {
|
||||
}
|
||||
}),
|
||||
None => std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
||||
horizon_prepare::sounds(&dir, Some(minecraft), &|line| log(line.to_string()))
|
||||
horizon_platform::prepare::sounds(&dir, Some(minecraft), &|line| log(line.to_string()))
|
||||
}))
|
||||
.unwrap_or_else(|_| Err("The sound copy stopped unexpectedly".into()))
|
||||
.map(|_| ()),
|
||||
@@ -2644,11 +2644,11 @@ pub fn sounds(tag: &str, minecraft: &Path, log: &dyn Fn(String)) {
|
||||
/// Runs `Preparer::InProcess` on this thread, each progress line to `log`. A panic is an
|
||||
/// error like any other, so the window never waits on a preparation that died.
|
||||
pub fn prepare_in_process(
|
||||
options: &horizon_prepare::Options,
|
||||
options: &horizon_platform::prepare::Options,
|
||||
log: &dyn Fn(String),
|
||||
) -> Result<(), String> {
|
||||
std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
||||
horizon_prepare::prepare(options, &|line| log(line.to_string()))
|
||||
horizon_platform::prepare::prepare(options, &|line| log(line.to_string()))
|
||||
}))
|
||||
.unwrap_or_else(|_| Err("The preparation stopped unexpectedly".into()))
|
||||
.map(|_| ())
|
||||
@@ -2671,15 +2671,15 @@ pub fn game_home() -> PathBuf {
|
||||
.join("horizon")
|
||||
}
|
||||
|
||||
/// Whether No AI is on: every AI feature hidden and off (`horizon_core::no_ai`, kept in the
|
||||
/// Whether No AI is on: every AI feature hidden and off (`horizon_platform::no_ai`, kept in the
|
||||
/// game's `horizon-extras.txt` so the game reads the same switch).
|
||||
pub fn no_ai() -> bool {
|
||||
horizon_core::no_ai::is_on(&game_home())
|
||||
horizon_platform::no_ai::is_on(&game_home())
|
||||
}
|
||||
|
||||
/// Turns No AI on or off for the launcher and the game.
|
||||
pub fn set_no_ai(on: bool) -> Result<(), String> {
|
||||
horizon_core::no_ai::set(&game_home(), on).map_err(|e| format!("Could not save No AI: {e}"))
|
||||
horizon_platform::no_ai::set(&game_home(), on).map_err(|e| format!("Could not save No AI: {e}"))
|
||||
}
|
||||
|
||||
// ----- Logs and crash reports ---------------------------------------------------------------------
|
||||
@@ -3140,7 +3140,7 @@ pub fn minecraft_dir(requested: Option<PathBuf>) -> Result<PathBuf, String> {
|
||||
Ok(requested
|
||||
.or(Settings::load().minecraft_dir)
|
||||
.or_else(default_minecraft_dir)
|
||||
.unwrap_or_else(|| horizon_prepare::artifacts::minecraft_dir(None)))
|
||||
.unwrap_or_else(|| horizon_platform::prepare::artifacts::minecraft_dir(None)))
|
||||
}
|
||||
|
||||
/// The server folder: the one given, the one chosen in the launcher, or `server/` in the data folder.
|
||||
@@ -6431,7 +6431,7 @@ pub fn {f}("
|
||||
|
||||
#[test]
|
||||
fn a_prepared_version_gets_a_data_file_that_holds_its_files() {
|
||||
use horizon_core::prepared_files::FILE_NAME;
|
||||
use horizon_platform::prepared_files::FILE_NAME;
|
||||
let resources =
|
||||
std::env::temp_dir().join(format!("horizon-launcher-data-file-{}", std::process::id()));
|
||||
let _ = fs::remove_dir_all(&resources);
|
||||
@@ -6500,7 +6500,7 @@ pub fn {f}("
|
||||
release("v2026.10.12", "26.2");
|
||||
prepared("v2026.10.12", "26.2");
|
||||
// A data file names its own root: the new version writes its own.
|
||||
let data_file = horizon_core::prepared_files::FILE_NAME;
|
||||
let data_file = horizon_platform::prepared_files::FILE_NAME;
|
||||
write(
|
||||
"v2026.10.13",
|
||||
&format!(".local/horizon-client/26.3/{data_file}"),
|
||||
@@ -6747,7 +6747,7 @@ pub fn {f}("
|
||||
data.push(n);
|
||||
data
|
||||
};
|
||||
let mut assets: Vec<(String, Vec<u8>)> = horizon_prepare::BUILT_IN_PACKS
|
||||
let mut assets: Vec<(String, Vec<u8>)> = horizon_platform::prepare::BUILT_IN_PACKS
|
||||
.iter()
|
||||
.map(|name| (name.to_string(), name.as_bytes().to_vec()))
|
||||
.collect();
|
||||
@@ -6822,7 +6822,7 @@ pub fn {f}("
|
||||
|
||||
// Resources prepared without any of them: a folder without an asset store is skipped.
|
||||
release("v0.2.1", true);
|
||||
let data_file = horizon_core::prepared_files::FILE_NAME;
|
||||
let data_file = horizon_platform::prepared_files::FILE_NAME;
|
||||
write(
|
||||
resources_at(&versions.join("v0.2.1")).join(data_file),
|
||||
b"older",
|
||||
|
||||
@@ -23,7 +23,7 @@ use serde::{Deserialize, Serialize};
|
||||
|
||||
/// The Minecraft version Horizon plays: the pinned version, so a port's regenerated
|
||||
/// `crates/horizon-core/src/version.rs` moves it with everything else (docs/version-ports.md).
|
||||
pub const GAME: &str = horizon_core::version::VERSION_ID;
|
||||
pub const GAME: &str = horizon_platform::version::VERSION_ID;
|
||||
|
||||
/// What a project is, as far as Horizon is concerned.
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
|
||||
+1
-1
@@ -1173,7 +1173,7 @@ pub fn run() -> Option<i32> {
|
||||
let cli = Cli::from_arg_matches(&definition.get_matches()).unwrap_or_else(|e| e.exit());
|
||||
let command = cli.command?;
|
||||
let result = match command {
|
||||
Cmd::Mcp | Cmd::AgentConfig { .. } if no_ai => Err(horizon_core::no_ai::REFUSAL.into()),
|
||||
Cmd::Mcp | Cmd::AgentConfig { .. } if no_ai => Err(horizon_platform::no_ai::REFUSAL.into()),
|
||||
Cmd::Mcp => crate::mcp::serve(),
|
||||
command => {
|
||||
let result = execute(command, cli.json);
|
||||
|
||||
@@ -93,7 +93,7 @@ pub fn launcher_entry(exe: &Path) -> String {
|
||||
StartupWMClass=horizon-launcher\n",
|
||||
quote(exe),
|
||||
exe.display(),
|
||||
minecraft = horizon_core::VANILLA_VERSION,
|
||||
minecraft = horizon_platform::version::VANILLA_VERSION,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
//! rest goes under `overrides/`, which a launcher copies into the game folder.
|
||||
use crate::browser::installed::{Entry, Installed};
|
||||
use crate::browser::{FileRef, Kind, SourceId, GAME};
|
||||
use horizon_world::nbt::{self, Limits, Tag};
|
||||
use horizon_platform::world::nbt::{self, Limits, Tag};
|
||||
use sha2::Digest;
|
||||
use std::fs;
|
||||
use std::io::{Read, Write};
|
||||
|
||||
@@ -704,9 +704,9 @@ fn follow_player(game_home: &Path, home: &Path) {
|
||||
let _ = fs::create_dir_all(to.parent().unwrap());
|
||||
let _ = fs::copy(consent, to);
|
||||
}
|
||||
let no_ai = horizon_core::no_ai::is_on(game_home);
|
||||
if horizon_core::no_ai::is_on(home) != no_ai {
|
||||
let _ = horizon_core::no_ai::set(home, no_ai);
|
||||
let no_ai = horizon_platform::no_ai::is_on(game_home);
|
||||
if horizon_platform::no_ai::is_on(home) != no_ai {
|
||||
let _ = horizon_platform::no_ai::set(home, no_ai);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -205,7 +205,7 @@ fn an_instance_follows_the_players_answers() {
|
||||
put(&main.join("options.txt"), "fov:90\n");
|
||||
put(&main.join("horizon-player.txt"), "name:Player123\n");
|
||||
put(&main.join("telemetry/consent.json"), r#"{"version":1}"#);
|
||||
horizon_core::no_ai::set(&main, true).unwrap();
|
||||
horizon_platform::no_ai::set(&main, true).unwrap();
|
||||
put(&own.join("options.txt"), "fov:70\n");
|
||||
follow_player(&main, &own);
|
||||
assert_eq!(
|
||||
@@ -220,10 +220,10 @@ fn an_instance_follows_the_players_answers() {
|
||||
fs::read_to_string(own.join("telemetry/consent.json")).unwrap(),
|
||||
r#"{"version":1}"#
|
||||
);
|
||||
assert!(horizon_core::no_ai::is_on(&own));
|
||||
horizon_core::no_ai::set(&main, false).unwrap();
|
||||
assert!(horizon_platform::no_ai::is_on(&own));
|
||||
horizon_platform::no_ai::set(&main, false).unwrap();
|
||||
follow_player(&main, &own);
|
||||
assert!(!horizon_core::no_ai::is_on(&own));
|
||||
assert!(!horizon_platform::no_ai::is_on(&own));
|
||||
fs::remove_dir_all(&root).unwrap();
|
||||
}
|
||||
|
||||
|
||||
@@ -140,7 +140,7 @@ pub fn runtimes_dir() -> PathBuf {
|
||||
|
||||
/// The runtimes in a Mojang `runtime` folder.
|
||||
pub fn mojang_runtimes(dir: &Path) -> Vec<Runtime> {
|
||||
horizon_prepare::java::minecraft_runtimes(dir)
|
||||
horizon_platform::prepare::java::minecraft_runtimes(dir)
|
||||
.into_iter()
|
||||
.filter_map(|home| runtime(home, Source::Mojang))
|
||||
.collect()
|
||||
|
||||
@@ -1783,7 +1783,7 @@ impl Launcher {
|
||||
.settings
|
||||
.minecraft_dir
|
||||
.clone()
|
||||
.unwrap_or_else(|| horizon_prepare::artifacts::minecraft_dir(None));
|
||||
.unwrap_or_else(|| horizon_platform::prepare::artifacts::minecraft_dir(None));
|
||||
self.prepare_log.clear();
|
||||
self.prepare_error = None;
|
||||
self.prepare_log
|
||||
|
||||
+1
-1
@@ -278,7 +278,7 @@ impl Session {
|
||||
}
|
||||
// No AI turned on during the session: nothing more runs.
|
||||
"tools/call" if backend::no_ai() => {
|
||||
Err((-32000, horizon_core::no_ai::REFUSAL.to_string()))
|
||||
Err((-32000, horizon_platform::no_ai::REFUSAL.to_string()))
|
||||
}
|
||||
"tools/call" => {
|
||||
let name = params
|
||||
|
||||
@@ -351,7 +351,7 @@ fn java_runtime_lays_out_mojangs_runtime_with_its_executables_and_links() {
|
||||
LICENSE
|
||||
);
|
||||
// horizon-prepare finds it where the official launcher would have put it.
|
||||
assert_eq!(horizon_prepare::java::minecraft_runtimes(&dir), [home]);
|
||||
assert_eq!(horizon_platform::prepare::java::minecraft_runtimes(&dir), [home]);
|
||||
// A second call keeps the files: only the index and manifest are asked again.
|
||||
server.asked();
|
||||
java_runtime(
|
||||
|
||||
@@ -2907,7 +2907,7 @@ fn copy_runtime(from: &Path, to: &Path) -> std::io::Result<()> {
|
||||
if entry.file_type()?.is_dir() {
|
||||
copy_runtime(&entry.path(), &destination)?;
|
||||
} else {
|
||||
horizon_prepare::clone_or_copy(&entry.path(), &destination)?;
|
||||
horizon_platform::prepare::clone_or_copy(&entry.path(), &destination)?;
|
||||
fs::set_permissions(&destination, entry.metadata()?.permissions())?;
|
||||
}
|
||||
}
|
||||
@@ -2967,7 +2967,7 @@ pub(crate) fn copy_base(from: &Path, to: &Path, target: Target) -> Result<(), St
|
||||
backend::copy_dir(&entry.path(), &to)
|
||||
} else {
|
||||
// A clone is a new file: horizon-ctl and horizon-server keep their permissions.
|
||||
horizon_prepare::clone_or_copy(&entry.path(), &to)
|
||||
horizon_platform::prepare::clone_or_copy(&entry.path(), &to)
|
||||
.and_then(|()| fs::set_permissions(&to, entry.metadata()?.permissions()))
|
||||
};
|
||||
copied.map_err(|e| format!("Could not copy {}: {e}", entry.path().display()))?;
|
||||
|
||||
@@ -26,7 +26,7 @@ struct Head {
|
||||
/// The key an offline name's head is kept under: its offline UUID, as the game makes it, whose
|
||||
/// default skin it wears without one of its own.
|
||||
pub fn offline_key(name: &str) -> String {
|
||||
format!("{:032x}", horizon_protocol::crypto::offline_uuid(name))
|
||||
format!("{:032x}", horizon_platform::protocol::offline_uuid(name))
|
||||
}
|
||||
|
||||
impl Heads {
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
//! The launcher only adds and removes entries: every other field of every entry is written back
|
||||
//! as it was read.
|
||||
use base64::Engine;
|
||||
use horizon_world::nbt::{self, Compound, JavaString, Limits, NamedTag, Tag};
|
||||
use horizon_platform::world::nbt::{self, Compound, JavaString, Limits, NamedTag, Tag};
|
||||
use std::fs;
|
||||
use std::path::Path;
|
||||
|
||||
|
||||
@@ -224,7 +224,7 @@ fn prism_native_jars_are_found_through_the_linked_folder() {
|
||||
);
|
||||
let official = Path::new("com/mojang/jtracy/1.14.38/jtracy-1.14.38-natives-windows.jar");
|
||||
assert!(!linked.join("libraries").join(official).exists());
|
||||
let found = horizon_prepare::artifacts::launcher_library(
|
||||
let found = horizon_platform::prepare::artifacts::launcher_library(
|
||||
&linked,
|
||||
"com.mojang:jtracy:1.14.38:natives-windows",
|
||||
official,
|
||||
|
||||
@@ -95,7 +95,7 @@ fn official_server() -> String {
|
||||
/// (through the address's SRV record), with a 3-second timeout. None when it doesn't answer.
|
||||
pub fn server_players(address: &str) -> Option<horizon_telemetry::server_status::Players> {
|
||||
let (host, port) = horizon_telemetry::server_status::split_address(address)?;
|
||||
let (host, port) = horizon_protocol::server_redirect(&host, port);
|
||||
let (host, port) = horizon_platform::protocol::server_redirect(&host, port);
|
||||
horizon_telemetry::server_status::players_at(
|
||||
address,
|
||||
(&host, port),
|
||||
@@ -418,7 +418,7 @@ mod tests {
|
||||
#[ignore = "asks the live official server"]
|
||||
fn official_count_follows_the_srv_record() {
|
||||
let (host, port) =
|
||||
horizon_protocol::server_redirect(crate::server_list::OFFICIAL_ADDRESS, 25565);
|
||||
horizon_platform::protocol::server_redirect(crate::server_list::OFFICIAL_ADDRESS, 25565);
|
||||
assert_eq!(
|
||||
(host.trim_end_matches('.'), port),
|
||||
("play.playhorizon.dev", 24454)
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
//! up, copying and deleting them. Backups are made as vanilla's Make Backup makes them
|
||||
//! (`LevelStorageAccess.makeWorldBackup`): `backups/<yyyy-MM-dd_HH-mm-ss>_<folder>.zip` in the
|
||||
//! game folder, every file under `<folder>/` but the world's `session.lock`.
|
||||
use horizon_world::nbt::{self, Limits, Tag};
|
||||
use horizon_platform::world::nbt::{self, Limits, Tag};
|
||||
use std::fs;
|
||||
use std::io::{Read, Write};
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
+1
-8
@@ -113,13 +113,6 @@
|
||||
"horizon-client-render -> horizon-polytone",
|
||||
"horizon-client-render -> horizon-shader-api",
|
||||
"horizon-client-render -> horizon-shaders",
|
||||
"horizon-client-render -> horizon-theme",
|
||||
"horizon-launcher -> horizon-core",
|
||||
"horizon-launcher -> horizon-prepare",
|
||||
"horizon-launcher -> horizon-protocol",
|
||||
"horizon-launcher -> horizon-world",
|
||||
"horizon-server -> horizon-core",
|
||||
"horizon-server -> horizon-server-files",
|
||||
"horizon-server -> horizon-world"
|
||||
"horizon-client-render -> horizon-theme"
|
||||
]
|
||||
}
|
||||
Reference in new issue
Block a user