mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-10 14:53:43 +00:00
fix(ugc): served player models keep the client's own collision
With ugc_manifest_models the world left made models out of the LXFML it sends when a property loads, so the client never built them and had no HKX: the UGC server makes no physics, and the HKX request got a 404, so served models had no collision. Now every model's LXFML is sent and the client builds each one (NIF and HKX). For the models whose mesh the UGC server made, once the client has loaded and 3 seconds after, the world sends it the served NIF's checksum and NotifyClientUGCModelReady: the client drops what it cached for the model and asks again, downloads the served mesh (its own NIF no longer matches) and keeps its own HKX (still matching). An HKX request is answered with the LXFML too, followed by the same switch, at most 3 times per client and model. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -23,7 +23,8 @@ namespace {
|
||||
|
||||
// What each request gets. A placed model's client asks for its NIF, HKX and LXFML and waits for every answer without a
|
||||
// timeout (LWOResMgr2Interface::RequestBlueprintManifestThenLoad, 0x0105a910), so model files are always answered:
|
||||
// from the UGC server when it made the mesh and models are served, else with the LXFML for the client to build.
|
||||
// from the UGC server when it made the mesh and models are served, else with the LXFML for the client to build. The HKX
|
||||
// always gets the LXFML: the UGC server makes no physics, the client builds its own (then gets the served mesh).
|
||||
TEST(UgcManifestTests, DecideCoversEveryType) {
|
||||
using T = eUgcResourceType;
|
||||
for (const auto type : { T::LXFML, T::NIF, T::HKX, T::DDS }) {
|
||||
@@ -34,7 +35,7 @@ TEST(UgcManifestTests, DecideCoversEveryType) {
|
||||
|
||||
EXPECT_EQ(UgcManifest::Decide(T::NIF, true, true, true), eAction::ANSWER);
|
||||
EXPECT_EQ(UgcManifest::Decide(T::LXFML, true, true, true), eAction::ANSWER);
|
||||
EXPECT_EQ(UgcManifest::Decide(T::HKX, true, true, true), eAction::ANSWER_UNKNOWN);
|
||||
EXPECT_EQ(UgcManifest::Decide(T::HKX, true, true, true), eAction::SEND_LXFML);
|
||||
|
||||
for (const auto type : { T::LXFML, T::NIF, T::HKX }) {
|
||||
EXPECT_EQ(UgcManifest::Decide(type, true, true, false), eAction::SEND_LXFML); // not made yet
|
||||
@@ -70,14 +71,16 @@ TEST_F(UgcManifestRequestTests, NothingWhenOff) {
|
||||
const auto sent = Capture([] { UgcManifest::OnRequest(Client(), 1234, eUgcResourceType::NIF); });
|
||||
EXPECT_TRUE(sent.empty());
|
||||
EXPECT_FALSE(UgcManifest::ServesModels());
|
||||
EXPECT_TRUE(UgcManifest::ClientBuildsModel(1234));
|
||||
EXPECT_FALSE(UgcManifest::ServesMesh(1234));
|
||||
EXPECT_FALSE(UgcManifest::ScheduleServedMesh(Client(), 1234));
|
||||
}
|
||||
|
||||
// A model file of a blueprint that isn't a player model: answered as not known (37 bytes, valid 0), never left waiting
|
||||
TEST_F(UgcManifestRequestTests, UnknownModelFilesAreAnsweredNotKnown) {
|
||||
Settings("1", "1");
|
||||
EXPECT_TRUE(UgcManifest::ServesModels());
|
||||
EXPECT_TRUE(UgcManifest::ClientBuildsModel(1234)); // its mesh isn't made
|
||||
EXPECT_FALSE(UgcManifest::ServesMesh(1234)); // its mesh isn't made
|
||||
EXPECT_FALSE(UgcManifest::ScheduleServedMesh(Client(), 1234));
|
||||
for (const auto type : { eUgcResourceType::NIF, eUgcResourceType::HKX, eUgcResourceType::LXFML }) {
|
||||
const auto sent = Capture([type] { UgcManifest::OnRequest(Client(), 0x0102030405060708, type); });
|
||||
ASSERT_EQ(sent.size(), 1);
|
||||
|
||||
Reference in New Issue
Block a user