Drop map-tool dependency (#375)
All checks were successful
continuous-integration/drone/push Build is passing

Paste the 80 lines of utility functions and drop the heavy dependency.

Reviewed-on: #375
Co-authored-by: Rhys Lloyd <krakow20@gmail.com>
Co-committed-by: Rhys Lloyd <krakow20@gmail.com>
This commit was merged in pull request #375.
This commit is contained in:
2026-07-10 20:30:00 +00:00
committed by Rhys Lloyd
parent 5aecf33d36
commit c6b20d3742
7 changed files with 104 additions and 37 deletions

31
Cargo.lock generated
View File

@@ -802,9 +802,9 @@ dependencies = [
[[package]]
name = "bytemuck_derive"
version = "1.10.2"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff"
checksum = "f65693059b6b9c588b9f62fed1cedbf0a8b805631457ea162d68f0de186f3de5"
dependencies = [
"proc-macro2",
"quote",
@@ -2139,29 +2139,6 @@ dependencies = [
"twox-hash",
]
[[package]]
name = "map-tool"
version = "5.0.0"
source = "sparse+https://git.itzana.me/api/packages/strafesnet/cargo/"
checksum = "cfc12859c2a0257bd824aec8be77c45456a411826bedef9923ad225bff0e1c66"
dependencies = [
"anyhow",
"flate2",
"image",
"image_dds",
"rbx_asset",
"rbx_binary",
"rbx_dom_weak",
"rbx_reflection_database",
"rbx_xml",
"rbxassetid",
"strafesnet_deferred_loader",
"strafesnet_rbx_loader",
"strafesnet_snf",
"thiserror 2.0.18",
"tokio",
]
[[package]]
name = "maps-combobulator"
version = "0.1.0"
@@ -2170,10 +2147,12 @@ dependencies = [
"aws-config",
"aws-sdk-s3",
"futures-util",
"map-tool",
"image",
"image_dds",
"rbx_asset",
"rbx_binary",
"rbx_dom_weak",
"rbx_xml",
"rbxassetid",
"serde",
"serde_json",

View File

@@ -12,6 +12,7 @@ futures-util = "0.3.31"
rbx_asset = { version = "0.7.1", features = ["cloud", "cookie", "gzip", "rustls-tls"], default-features = false, registry = "strafesnet" }
rbx_binary = "3.0.0"
rbx_dom_weak = "4.2.0"
rbx_xml = "3.0.0"
serde = { version = "1.0.215", features = ["derive"] }
serde_json = "1.0.133"
tokio = { version = "1.41.1", features = ["macros", "rt-multi-thread", "signal"] }

View File

@@ -8,10 +8,12 @@ async-nats.workspace = true
aws-config = { version = "1", features = ["behavior-version-latest"] }
aws-sdk-s3 = "1"
futures-util.workspace = true
map-tool = { version = "5.0.0", registry = "strafesnet", features = ["roblox"], default-features = false }
image = { version = "0.25.10", features = ["png", "jpeg"], default-features = false }
image_dds = { version = "0.7.2", features = ["ddsfile", "encode"], default-features = false }
rbx_asset.workspace = true
rbx_binary.workspace = true
rbx_dom_weak.workspace = true
rbx_xml.workspace = true
rbxassetid = { version = "0.1.0", registry = "strafesnet" }
serde.workspace = true
serde_json.workspace = true

View File

@@ -3,6 +3,7 @@ use tokio_stream::StreamExt;
mod nats_types;
mod process;
mod s3;
mod util;
const SUBJECT_MAPFIX_RELEASE:&str="maptest.mapfixes.release";
const SUBJECT_SUBMISSION_BATCHRELEASE:&str="maptest.submissions.batchrelease";

View File

@@ -2,6 +2,7 @@ use std::io::Cursor;
use crate::nats_types::ReleaseMapfixRequest;
use crate::s3::S3Cache;
use crate::util;
use futures_util::stream::iter as stream_iter;
use futures_util::{StreamExt,TryStreamExt};
@@ -16,9 +17,8 @@ const CONCURRENT_REQUESTS:usize=16;
#[derive(Debug)]
pub enum Error{
ArchivedModel,
LoadDom(map_tool::roblox::LoadDomError),
DownloadAsset(map_tool::roblox::DownloadAssetError),
IO(std::io::Error),
LoadDom(util::LoadDomError),
DownloadAsset(util::DownloadAssetError),
SNFMap(strafesnet_snf::map::Error),
S3Get(crate::s3::GetError),
S3Put(crate::s3::PutError),
@@ -49,9 +49,9 @@ impl Processor{
}
/// Download an asset, returning None if the asset is archived.
async fn download_asset(&self,asset_id:u64)->Result<Option<Vec<u8>>,Error>{
match map_tool::roblox::download_asset(&self.cloud_context,asset_id).await{
match util::download_asset(&self.cloud_context,asset_id).await{
Ok(data)=>Ok(Some(data)),
Err(map_tool::roblox::DownloadAssetError::NonFreeModel)=>{
Err(util::DownloadAssetError::NonFreeModel)=>{
println!("[combobulator] Asset {asset_id} is inaccessible");
Ok(None)
},
@@ -69,7 +69,7 @@ impl Processor{
// check raw cache, download if missing
let raw_key=S3Cache::texture_raw_key(asset_id);
let dds_result=if let Some(data)=self.s3.get(&raw_key).await.map_err(Error::S3Get)?{
map_tool::roblox::convert_texture_to_dds(&data)
util::convert_texture_to_dds(&data)
}else{
println!("[combobulator] Downloading texture {asset_id}");
let Some(data)=self.download_asset(asset_id).await? else{
@@ -77,7 +77,7 @@ impl Processor{
};
// decode while we have ownership
let dds_result=map_tool::roblox::convert_texture_to_dds(&data);
let dds_result=util::convert_texture_to_dds(&data);
self.s3.put(&raw_key,data).await.map_err(Error::S3Put)?;
dds_result
@@ -87,7 +87,7 @@ impl Processor{
let dds=match dds_result{
Ok(dds)=>dds,
Err(e)=>{
println!("[combobulator] Texture {asset_id} convert error: {e}");
println!("[combobulator] Texture {asset_id} convert error: {e:?}");
return Ok(None);
}
};
@@ -176,7 +176,7 @@ impl Processor{
.ok_or(Error::ArchivedModel)?;
// decode dom
let dom=map_tool::roblox::load_dom(&rbxl_bytes)
let dom=util::load_dom(&rbxl_bytes)
.map_err(Error::LoadDom)?;
// run scripts

84
combobulator/src/util.rs Normal file
View File

@@ -0,0 +1,84 @@
#[expect(dead_code)]
#[derive(Debug)]
pub enum LoadDomError{
Binary(rbx_binary::DecodeError),
Xml(rbx_xml::DecodeError),
UnknownFormat,
}
impl std::fmt::Display for LoadDomError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for LoadDomError{}
pub fn load_dom(data:&[u8])->Result<rbx_dom_weak::WeakDom,LoadDomError>{
match data.get(0..8){
Some(b"<roblox!")=>rbx_binary::from_reader(data).map_err(LoadDomError::Binary),
Some(b"<roblox ")=>rbx_xml::from_reader_default(data).map_err(LoadDomError::Xml),
_=>Err(LoadDomError::UnknownFormat),
}
}
#[expect(dead_code)]
#[derive(Debug)]
pub enum DownloadAssetError{
GetLocation(rbx_asset::cloud::GetError),
NonFreeModel,
GetAsset(rbx_asset::cloud::GetError),
IO(std::io::Error),
}
impl std::fmt::Display for DownloadAssetError{
fn fmt(&self,f:&mut std::fmt::Formatter<'_>)->std::fmt::Result{
write!(f,"{self:?}")
}
}
impl std::error::Error for DownloadAssetError{}
pub async fn download_asset(context:&rbx_asset::cloud::Context,asset_id:u64)->Result<Vec<u8>,DownloadAssetError>{
let asset_location_info=context.get_asset_location(rbx_asset::cloud::GetAssetLatestRequest{
asset_id,
}).await.map_err(DownloadAssetError::GetLocation)?;
let Some(asset_location)=asset_location_info.location else{
return Err(DownloadAssetError::NonFreeModel);
};
let data=context.get_asset(&asset_location).await.map_err(DownloadAssetError::GetAsset)?;
data.to_vec().map_err(DownloadAssetError::IO)
}
#[expect(dead_code)]
#[derive(Debug)]
pub enum ConvertTextureError{
Image(image::ImageError),
DdsEncode(image_dds::error::SurfaceError),
Dds(image_dds::CreateDdsError),
DdsWrite(image_dds::ddsfile::Error),
}
pub fn convert_texture_to_dds(data:&[u8])->Result<Vec<u8>,ConvertTextureError>{
let image=image::load_from_memory(data).map_err(ConvertTextureError::Image)?.to_rgba8();
let format=if image.width()%4!=0||image.height()%4!=0{
image_dds::ImageFormat::Rgba8UnormSrgb
}else{
image_dds::ImageFormat::BC7RgbaUnormSrgb
};
let surface=image_dds::SurfaceRgba8{
width:image.width(),
height:image.height(),
depth:1,
layers:1,
mipmaps:1,
data:image.as_raw(),
}.encode(
format,
image_dds::Quality::Slow,
image_dds::Mipmaps::GeneratedAutomatic
).map_err(ConvertTextureError::DdsEncode)?;
let dds=surface.to_dds().map_err(ConvertTextureError::Dds)?;
let mut buf=Vec::new();
dds.write(&mut buf).map_err(ConvertTextureError::DdsWrite)?;
Ok(buf)
}

View File

@@ -10,7 +10,7 @@ rbx_asset.workspace = true
rbx_binary.workspace = true
rbx_dom_weak.workspace = true
rbx_reflection_database = "3.0.0"
rbx_xml = "3.0.0"
rbx_xml.workspace = true
regex = { version = "1.11.3", default-features = false }
serde.workspace = true
serde_json.workspace = true