Compare commits

...
Sign in to create a new pull request.

4 commits

Author SHA1 Message Date
MetricExpansion
34a702abd2 Cleanup 2023-04-15 19:22:21 -07:00
MetricExpansion
9a8323be62 Fix Dragon Aspect better 2023-04-15 19:18:31 -07:00
MetricExpansion
d9fc1dd160 Fix Dragon Aspect 2023-04-15 19:10:31 -07:00
MetricExpansion
bf4ec42f10 Merge branch 'feature/web' 2023-04-15 18:27:16 -07:00
8 changed files with 68 additions and 66 deletions

18
package-lock.json generated
View file

@ -1,18 +0,0 @@
{
"name": "SkyrimOutfitSystemSE",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"devDependencies": {
"@types/lodash": "^4.14.191"
}
},
"node_modules/@types/lodash": {
"version": "4.14.191",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.191.tgz",
"integrity": "sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==",
"dev": true
}
}
}

View file

@ -1,5 +0,0 @@
{
"devDependencies": {
"@types/lodash": "^4.14.191"
}
}

View file

@ -18,6 +18,7 @@ use commonlibsse::*;
use log::*; use log::*;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use parking_lot::RwLock; use parking_lot::RwLock;
use settings::{IntragameCaches, INTRAGAME_CACHES};
#[no_mangle] #[no_mangle]
pub extern "C" fn plugin_main(skse: *const SKSE_LoadInterface) -> bool { pub extern "C" fn plugin_main(skse: *const SKSE_LoadInterface) -> bool {
@ -104,6 +105,7 @@ pub extern "C" fn messaging_callback(message: *mut SKSE_MessagingInterface_Messa
let mut service = OUTFIT_SERVICE_SINGLETON.write(); let mut service = OUTFIT_SERVICE_SINGLETON.write();
service.replace_with_new(); service.replace_with_new();
service.check_consistency(); service.check_consistency();
*(INTRAGAME_CACHES.write()) = IntragameCaches::new();
} }
message_type => { message_type => {
warn!("Got unknown message type {}", message_type); warn!("Got unknown message type {}", message_type);

View file

@ -2,7 +2,7 @@ use crate::{
armor::{ArmorExt, ArmorLocatorExt}, armor::{ArmorExt, ArmorLocatorExt},
interface::ffi::{OptionalStateType, StateType, TESObjectARMOPtr, WeatherFlags}, interface::ffi::{OptionalStateType, StateType, TESObjectARMOPtr, WeatherFlags},
policy::*, policy::*,
settings::SETTINGS, settings::{INTRAGAME_CACHES, SETTINGS},
stable_ptr::*, stable_ptr::*,
strings::UncasedString, strings::UncasedString,
}; };
@ -161,6 +161,7 @@ impl Outfit {
) -> Vec<*const RE_TESObjectARMO> { ) -> Vec<*const RE_TESObjectARMO> {
let start_time = Instant::now(); let start_time = Instant::now();
let policy_store = &*POLICY_STORE; let policy_store = &*POLICY_STORE;
let intragame_settings = INTRAGAME_CACHES.read();
let equipped = { let equipped = {
let mut slots = [std::ptr::null(); RE_BIPED_OBJECTS_BIPED_OBJECT_kEditorTotal as usize]; let mut slots = [std::ptr::null(); RE_BIPED_OBJECTS_BIPED_OBJECT_kEditorTotal as usize];
for armor in equipped { for armor in equipped {
@ -224,7 +225,7 @@ impl Outfit {
// First check if the equipped item is a "golden item". If so, it always wins. // First check if the equipped item is a "golden item". If so, it always wins.
if selected_armor.is_none() { if selected_armor.is_none() {
if let Some(equipped_item) = TESObjectARMOStablePtr::new(equipped[slot as usize]) { if let Some(equipped_item) = TESObjectARMOStablePtr::new(equipped[slot as usize]) {
if SETTINGS.golden_items().contains(&equipped_item) { if intragame_settings.golden_items.contains(&equipped_item) {
selected_armor = Some(equipped_item.as_ptr()); selected_armor = Some(equipped_item.as_ptr());
} }
}; };

View file

@ -1,4 +1,7 @@
use crate::OUTFIT_SERVICE_SINGLETON; use crate::{
settings::{IntragameCaches, INTRAGAME_CACHES},
OUTFIT_SERVICE_SINGLETON,
};
use commonlibsse::*; use commonlibsse::*;
use log::*; use log::*;
use std::ffi::c_void; use std::ffi::c_void;
@ -75,6 +78,7 @@ pub extern "C" fn serialization_load_callback(intfc: *mut SKSE_SerializationInte
error!("Did not read the correct amount of data for deserialization"); error!("Did not read the correct amount of data for deserialization");
break; break;
} }
*(INTRAGAME_CACHES.write()) = IntragameCaches::new();
let mut service = OUTFIT_SERVICE_SINGLETON.write(); let mut service = OUTFIT_SERVICE_SINGLETON.write();
if !service.replace_with_proto(&buffer, intfc) { if !service.replace_with_proto(&buffer, intfc) {
error!("Failed to use proto data to instantiate"); error!("Failed to use proto data to instantiate");

View file

@ -6,11 +6,11 @@ use configparser::ini::Ini;
use hash_hasher::HashedSet; use hash_hasher::HashedSet;
use log::*; use log::*;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use parking_lot::RwLock;
use std::{ffi::CString, path::PathBuf}; use std::{ffi::CString, path::PathBuf};
pub struct Settings { pub struct Settings {
ini: Ini, ini: Ini,
golden_items: HashedSet<TESObjectARMOStablePtr>,
} }
impl Settings { impl Settings {
@ -28,8 +28,7 @@ impl Settings {
} else { } else {
info!("Loaded INI file in {}", file.display()); info!("Loaded INI file in {}", file.display());
} }
let golden_items = Self::generate_golden_items(&ini); Settings { ini }
Settings { ini, golden_items }
} }
pub fn extra_logging_enabled(&self) -> bool { pub fn extra_logging_enabled(&self) -> bool {
@ -85,15 +84,25 @@ impl Settings {
.unwrap_or_else(|_| None) .unwrap_or_else(|_| None)
.unwrap_or_else(|| false) .unwrap_or_else(|| false)
} }
}
pub fn golden_items(&self) -> &HashedSet<TESObjectARMOStablePtr> { pub static SETTINGS: Lazy<Settings> = Lazy::new(|| Settings::new());
&self.golden_items
pub struct IntragameCaches {
pub golden_items: HashedSet<TESObjectARMOStablePtr>,
}
impl IntragameCaches {
pub fn new() -> Self {
let golden_items = Self::generate_golden_items(&SETTINGS.ini);
IntragameCaches { golden_items }
} }
fn generate_golden_items(ini: &Ini) -> HashedSet<TESObjectARMOStablePtr> { fn generate_golden_items(ini: &Ini) -> HashedSet<TESObjectARMOStablePtr> {
// Start with the set of built-in locators // Start with the set of built-in locators
let mut locators = vec![]; let mut locators = vec![
("Dragonborn.esm", 0x00021739), // Dragon Aspect Armor
];
// Load locators from INI file. // Load locators from INI file.
let ini_string = ini let ini_string = ini
.get("Compatibility", "GoldenItems") .get("Compatibility", "GoldenItems")
@ -111,7 +120,7 @@ impl Settings {
if data_handler.is_null() { if data_handler.is_null() {
return Default::default(); return Default::default();
} }
locators let items = locators
.into_iter() .into_iter()
.flat_map(|(mod_name, local_form_id)| { .flat_map(|(mod_name, local_form_id)| {
let Ok(mod_name) = CString::new(mod_name) else { return None }; let Ok(mod_name) = CString::new(mod_name) else { return None };
@ -126,8 +135,10 @@ impl Settings {
None None
} }
}) })
.collect() .collect();
items
} }
} }
pub static SETTINGS: Lazy<Settings> = Lazy::new(|| Settings::new()); pub static INTRAGAME_CACHES: Lazy<RwLock<IntragameCaches>> =
Lazy::new(|| RwLock::new(IntragameCaches::new()));

View file

@ -1,12 +1,15 @@
use std::{path::PathBuf, io::{Read, Cursor}};
use log::*; use log::*;
use std::{
io::{Cursor, Read},
path::PathBuf,
};
use warp_static_zip::ZipArchive; use crate::{interface::ffi::GetRuntimeDirectory, settings::SETTINGS};
use warp::Filter; use warp::Filter;
use warp::Reply; use warp::Reply;
use crate::{interface::ffi::GetRuntimeDirectory, settings::SETTINGS}; use warp_static_zip::ZipArchive;
pub async fn web_server() { pub async fn web_server() {
let get_state = warp::path!("state") let get_state = warp::path!("state")
.and(warp::get()) .and(warp::get())
.and_then(handlers::get_state); .and_then(handlers::get_state);
@ -37,7 +40,7 @@ pub async fn web_server() {
.or(policy_info); .or(policy_info);
let static_asset_routes = create_zip_route(); let static_asset_routes = create_zip_route();
let addr = if SETTINGS.server_remote_accessible() { let addr = if SETTINGS.server_remote_accessible() {
[0, 0, 0, 0] [0, 0, 0, 0]
} else { } else {
@ -48,7 +51,7 @@ pub async fn web_server() {
match binding { match binding {
Ok((addr, sock)) => { Ok((addr, sock)) => {
info!("Server bound to {}", addr); info!("Server bound to {}", addr);
sock.await; sock.await;
} }
Err(err) => { Err(err) => {
warn!("Could not bind address: {}", err); warn!("Could not bind address: {}", err);
@ -66,7 +69,7 @@ fn create_zip_route() -> warp::filters::BoxedFilter<(impl Reply,)> {
if let Err(error) = zip_data.read_to_end(&mut data) { if let Err(error) = zip_data.read_to_end(&mut data) {
error!("Failed to read web archive file: {}", error); error!("Failed to read web archive file: {}", error);
}; };
data data
} }
Err(error) => { Err(error) => {
error!("Failed to read zip archive file: {}", error); error!("Failed to read zip archive file: {}", error);
@ -74,7 +77,7 @@ fn create_zip_route() -> warp::filters::BoxedFilter<(impl Reply,)> {
} }
} }
}; };
let zip_archive = Box::leak(Box::new(ZipArchive::new(Cursor::new(&*zip_vector.leak())))); let zip_archive = Box::leak(Box::new(ZipArchive::new(Cursor::new(&*zip_vector.leak()))));
let static_asset_routes = { let static_asset_routes = {
match zip_archive { match zip_archive {
@ -96,9 +99,9 @@ fn create_zip_route() -> warp::filters::BoxedFilter<(impl Reply,)> {
mod handlers { mod handlers {
use crate::{ use crate::{
outfit::Outfit,
armor::{ArmorExt, ArmorLocatorExt}, armor::{ArmorExt, ArmorLocatorExt},
interface::ffi::{ArmorSearch, CreateRefreshAllArmorsTask}, interface::ffi::{ArmorSearch, CreateRefreshAllArmorsTask},
outfit::Outfit,
policy::POLICY_STORE, policy::POLICY_STORE,
settings::SETTINGS, settings::SETTINGS,
stable_ptr::TESObjectARMOStablePtr, stable_ptr::TESObjectARMOStablePtr,
@ -207,7 +210,7 @@ mod handlers {
let info = ArmorInfo { let info = ArmorInfo {
locator: locator_in, locator: locator_in,
name, name,
mask mask,
}; };
Ok(warp::reply::with_status( Ok(warp::reply::with_status(
warp::reply::json(&info), warp::reply::json(&info),
@ -241,9 +244,11 @@ mod handlers {
})?, })?,
name: unsafe { name: unsafe {
let cstr = CStr::from_ptr(armor.borrow()._base._base._base.GetName()); let cstr = CStr::from_ptr(armor.borrow()._base._base._base.GetName());
cstr.to_str().map(|s| s.to_owned()).unwrap_or_else(|_| "".to_owned()) cstr.to_str()
.map(|s| s.to_owned())
.unwrap_or_else(|_| "".to_owned())
}, },
mask: unsafe { armor.borrow()._base_10.GetSlotMask() } as u32 mask: unsafe { armor.borrow()._base_10.GetSlotMask() } as u32,
}) })
}) })
.collect(); .collect();

View file

@ -8,11 +8,8 @@ use headers::{
use http::{HeaderMap, StatusCode}; use http::{HeaderMap, StatusCode};
use hyper::Body; use hyper::Body;
use std::future::ready; use std::future::ready;
use std::{
path::{PathBuf},
time::SystemTime,
};
use std::io::{Read, Seek}; use std::io::{Read, Seek};
use std::{path::PathBuf, time::SystemTime};
use warp::{ use warp::{
filters::header::headers_cloned, filters::header::headers_cloned,
path::{tail, Tail}, path::{tail, Tail},
@ -29,8 +26,7 @@ pub use once_cell::sync::Lazy;
#[doc(hidden)] #[doc(hidden)]
pub fn failed() -> impl Filter<Extract = (Response,), Error = Rejection> + Clone + 'static { pub fn failed() -> impl Filter<Extract = (Response,), Error = Rejection> + Clone + 'static {
warp::get() warp::get().and_then(move || ready(Err(reject::not_found())))
.and_then(move || ready(Err(reject::not_found())))
} }
/// Creates a [Filter][warp::Filter] that serves an included directory. /// Creates a [Filter][warp::Filter] that serves an included directory.
@ -72,13 +68,20 @@ pub fn file<'a, R: Read + Seek + Clone + Send + Sync>(
.and_then(move |path, conds| ready(reply(dir, path, conds))) .and_then(move |path, conds| ready(reply(dir, path, conds)))
} }
fn parse_path<R: Read + Seek + Clone>(
fn parse_path<R: Read + Seek + Clone>(tail: &str, dir: &ZipArchive<R>) -> Result<String, Rejection> { tail: &str,
let mut path_str = urlencoding::decode(tail) dir: &ZipArchive<R>,
.map_err(|_| reject::not_found())?; ) -> Result<String, Rejection> {
let mut path_str = urlencoding::decode(tail).map_err(|_| reject::not_found())?;
let path: PathBuf = path_str.split('/').collect(); let path: PathBuf = path_str.split('/').collect();
if path.parent().is_none() || dir.clone().by_name(&path_str).map(|v| v.is_dir()).unwrap_or(false) { if path.parent().is_none()
|| dir
.clone()
.by_name(&path_str)
.map(|v| v.is_dir())
.unwrap_or(false)
{
log::debug!("appending index.html to {:?}", path); log::debug!("appending index.html to {:?}", path);
path_str += "/index.html"; path_str += "/index.html";
} }
@ -151,7 +154,11 @@ fn bytes_range(range: Range, max_len: u64) -> Option<(u64, u64)> {
} }
} }
fn reply<R: Read + Seek + Clone>(dir: &ZipArchive<R>, path: String, conds: Conditionals) -> Result<Response, Rejection> { fn reply<R: Read + Seek + Clone>(
dir: &ZipArchive<R>,
path: String,
conds: Conditionals,
) -> Result<Response, Rejection> {
let mut contents = Vec::new(); let mut contents = Vec::new();
if let Ok(mut file) = dir.clone().by_name(&path) { if let Ok(mut file) = dir.clone().by_name(&path) {
file.read_to_end(&mut contents).unwrap(); file.read_to_end(&mut contents).unwrap();
@ -194,12 +201,7 @@ fn reply_full(buf: Vec<u8>, path: &str, last_mod: LastModified) -> Response {
resp resp
} }
fn reply_range( fn reply_range(buf: Vec<u8>, (s, e): (u64, u64), path: &str, last_mod: LastModified) -> Response {
buf: Vec<u8>,
(s, e): (u64, u64),
path: &str,
last_mod: LastModified,
) -> Response {
let len = e - s; let len = e - s;
let buf = (&buf[s as usize..e as usize]).to_owned(); let buf = (&buf[s as usize..e as usize]).to_owned();