Instant refresh and fix some web UI routing issues

This commit is contained in:
MetricExpansion 2023-03-07 18:38:56 -08:00
parent 998919dc47
commit 3faf0d4364
7 changed files with 52 additions and 13 deletions

View file

@ -16,3 +16,8 @@ ScriptHotReloading = off
Enable = off
Port = 3030
AllowRemoteAccess = off
; Allow refreshing actor appearance immediately when
; sending new settings from web UI. Experimental and
; could cause crashes of the game or hangs of the
; web server. Turn it off if you have web UI issues.
RefreshImmediately = on

View file

@ -139,6 +139,17 @@ namespace OutfitSystem {
RE::AIProcessAugments::UpdateEquipment(pm, actor);
}
}
void refreshArmorForAllConfiguredActors() {
rust::Vec<std::uint32_t> actors;
{
actors = outfit_service_get_singleton_ptr()->inner().list_actors();
}
for (auto& actor_form : actors) {
auto actor = RE::Actor::LookupByID<RE::Actor>(actor_form);
if (!actor) continue;
refreshArmorFor(actor);
}
}
void RefreshArmorFor(RE::BSScript::IVirtualMachine* registry,
std::uint32_t stackId,
RE::StaticFunctionTag*,
@ -151,15 +162,7 @@ namespace OutfitSystem {
std::uint32_t stackId,
RE::StaticFunctionTag*) {
LogExit exitPrint("RefreshArmorForAllConfiguredActors"sv);
rust::Vec<std::uint32_t> actors;
{
actors = outfit_service_get_singleton_ptr()->inner().list_actors();
}
for (auto& actor_form : actors) {
auto actor = RE::Actor::LookupByID<RE::Actor>(actor_form);
if (!actor) continue;
refreshArmorFor(actor);
}
refreshArmorForAllConfiguredActors();
}
std::vector<RE::Actor*> ActorsNearPC(RE::BSScript::IVirtualMachine* registry,

View file

@ -31,8 +31,16 @@ namespace OutfitSystem {
namespace ArmorFormSearchUtils {
std::unique_ptr<std::vector<TESObjectARMOPtr>> RustSearch(rust::Str query);
}
void refreshArmorForAllConfiguredActors();
}
std::unique_ptr<std::vector<TESObjectARMOPtr>> ArmorSearch(rust::Str query) {
return OutfitSystem::ArmorFormSearchUtils::RustSearch(query);
}
void CreateRefreshAllArmorsTask() {
auto task_intfc = SKSE::GetTaskInterface();
task_intfc->AddTask([]() {
OutfitSystem::refreshArmorForAllConfiguredActors();
});
}

View file

@ -30,6 +30,7 @@ std::unique_ptr<std::string> GetRuntimePath();
std::unique_ptr<std::string> GetRuntimeName();
std::unique_ptr<std::string> GetRuntimeDirectory();
std::unique_ptr<std::vector<TESObjectARMOPtr>> ArmorSearch(rust::Str query);
void CreateRefreshAllArmorsTask();
#endif

View file

@ -62,6 +62,7 @@ pub mod ffi {
// fn GetRuntimePath() -> UniquePtr<CxxString>;
// fn GetRuntimeName() -> UniquePtr<CxxString>;
fn GetRuntimeDirectory() -> UniquePtr<CxxString>;
fn CreateRefreshAllArmorsTask();
fn ArmorSearch(query: &str) -> UniquePtr<CxxVector<TESObjectARMOPtr>>;
}
extern "Rust" {

View file

@ -79,6 +79,13 @@ impl Settings {
.unwrap_or_else(|| false)
}
pub fn server_refresh_immediately(&self) -> bool {
self.ini
.getboolcoerce("Server", "RefreshImmediately")
.unwrap_or_else(|_| None)
.unwrap_or_else(|| false)
}
pub fn golden_items(&self) -> &HashedSet<TESObjectARMOStablePtr> {
&self.golden_items

View file

@ -10,6 +10,7 @@ pub async fn web_server() {
.and_then(handlers::get_state);
let set_state = warp::path!("state")
.and(warp::post())
.and(warp::query::<handlers::SetStateParams>())
.and(warp::body::bytes())
.and_then(handlers::set_state);
let armor_info = warp::path!("armors")
@ -23,7 +24,11 @@ pub async fn web_server() {
let mut file = PathBuf::from(GetRuntimeDirectory().to_string());
file.push("Data\\SKSE\\Plugins\\SkyrimOutfitSystemSE_Web");
let static_assets = warp::fs::dir(file);
let mut static_file = file.clone();
static_file.push("index.html");
let static_assets = warp::fs::dir(file) // Handle most static paths
.or(warp::fs::file(static_file)); // Fallback to index for SPA routes
let base = warp::path("api");
let routes = get_state
@ -46,10 +51,10 @@ pub async fn web_server() {
mod handlers {
use crate::{
armor::{ArmorExt, ArmorLocatorExt},
interface::ffi::ArmorSearch,
interface::ffi::{ArmorSearch, CreateRefreshAllArmorsTask},
policy::POLICY_STORE,
stable_ptr::TESObjectARMOStablePtr,
OUTFIT_SERVICE_SINGLETON,
OUTFIT_SERVICE_SINGLETON, settings::SETTINGS,
};
use commonlibsse::{
RE_LookupActorFormID, RE_TESDataHandler_GetSingleton, SKSE_GetSerializationInterface,
@ -66,7 +71,12 @@ mod handlers {
Ok(warp::reply::with_status(json, StatusCode::OK))
}
pub async fn set_state(body: bytes::Bytes) -> Result<impl warp::Reply, Infallible> {
#[derive(Deserialize)]
pub struct SetStateParams {
refresh_armors: Option<bool>
}
pub async fn set_state(params: SetStateParams, body: bytes::Bytes) -> Result<impl warp::Reply, Infallible> {
let Ok(body) = String::from_utf8(body.to_vec()) else {
return Ok(warp::reply::with_status("Failed", StatusCode::BAD_REQUEST));
};
@ -74,6 +84,10 @@ mod handlers {
let result = OUTFIT_SERVICE_SINGLETON
.write()
.replace_with_json(body.as_str(), unsafe { &*intfc });
// Try to set the refresh all armors task... unsure if this is a good idea.
if params.refresh_armors.unwrap_or(false) || SETTINGS.server_refresh_immediately() {
CreateRefreshAllArmorsTask();
}
if result {
Ok(warp::reply::with_status("OK", StatusCode::OK))
} else {