Skip to content

Commit

Permalink
Merge branch 'feat-unlimited-behavior-vars' into feat-modded-anim-compat
Browse files Browse the repository at this point in the history
  • Loading branch information
rfortier committed May 5, 2024
2 parents 14692f7 + 1dc6857 commit ffc958e
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 127 deletions.
13 changes: 6 additions & 7 deletions Code/client/Games/References.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -239,10 +239,9 @@ void TESObjectREFR::SaveAnimationVariables(AnimationVariables& aVariables) const
if (!pVariableSet)
return;

aVariables.Booleans = 0;

aVariables.Floats.resize(pDescriptor->FloatLookupTable.size());
aVariables.Integers.resize(pDescriptor->IntegerLookupTable.size());
aVariables.Booleans.assign(pDescriptor->BooleanLookUpTable.size(), false);
aVariables.Floats.assign(pDescriptor->FloatLookupTable.size(), 0.f);
aVariables.Integers.assign(pDescriptor->IntegerLookupTable.size(), 0);

#if TP_FALLOUT4
// TODO: maybe send a var with the variables indicating first or third person?
Expand All @@ -263,14 +262,14 @@ void TESObjectREFR::SaveAnimationVariables(AnimationVariables& aVariables) const
continue;

if (pFirstPersonVariables->data[*firstPersonIdx] != 0)
aVariables.Booleans |= (1ull << i);
aVariables.Booleans[i] = true;

continue;
}
#endif

if (pVariableSet->data[idx] != 0)
aVariables.Booleans |= (1ull << i);
aVariables.Booleans[i] = true;
}

for (size_t i = 0; i < pDescriptor->FloatLookupTable.size(); ++i)
Expand Down Expand Up @@ -379,7 +378,7 @@ void TESObjectREFR::LoadAnimationVariables(const AnimationVariables& aVariables)

if (pVariableSet->size > idx)
{
pVariableSet->data[idx] = (aVariables.Booleans & (1ull << i)) != 0;
pVariableSet->data[idx] = aVariables.Booleans[i];
}
}

Expand Down
214 changes: 98 additions & 116 deletions Code/encoding/Structs/AnimationVariables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,141 +12,123 @@ bool AnimationVariables::operator!=(const AnimationVariables& acRhs) const noexc
return !this->operator==(acRhs);
}

// std::vector<bool> implementation is unspecified, but often packed reasonably.
// The spec does not guarantee contiguous memory, though, so somewhat laborious
// translation needed. Should be better than winding down several layers to
// TiltedPhoques::Serialization::WriteBool, though.
//
void AnimationVariables::VectorBool_to_String(const Vector<bool>& bools, TiltedPhoques::String& chars) const
{
chars.assign((bools.size() + 7) >> 3, 0);

auto citer = chars.begin();
auto biter = bools.begin();
for (uint32_t mask = 1; biter < bools.end(); mask = 1, citer++)
for (; mask < 0x100 && biter < bools.end(); mask <<= 1)
*citer |= *biter++ ? mask : 0;
}

// The Vector<bool> must be the correct size when called.
//
void AnimationVariables::String_to_VectorBool(const TiltedPhoques::String& chars, Vector<bool>& bools)
{
bools.assign(bools.size(), false);

auto citer = chars.begin();
auto biter = bools.begin();
for (uint32_t mask = 1; biter < bools.end(); mask = 1, citer++)
for (; mask < 0x100 && biter < bools.end(); mask <<= 1)
*biter++ = (*citer & mask) ? true : false;
}


void AnimationVariables::Load(std::istream& aInput)
{
aInput.read(reinterpret_cast<char*>(&Booleans), sizeof(Booleans));
// Booleans are bitpacked and a bit different, not guaranteed contiguous.
TiltedPhoques::String chars((Booleans.size() + 7) >> 3, 0);

aInput.read(reinterpret_cast<char*>(chars.data()), chars.size());
String_to_VectorBool(chars, Booleans);
aInput.read(reinterpret_cast<char*>(Integers.data()), Integers.size() * sizeof(uint32_t));
aInput.read(reinterpret_cast<char*>(Floats.data()), Floats.size() * sizeof(float));
}

void AnimationVariables::Save(std::ostream& aOutput) const
{
aOutput.write(reinterpret_cast<const char*>(&Booleans), sizeof(Booleans));
// Booleans bitpacked and not guaranteed contiguous.
TiltedPhoques::String chars;
VectorBool_to_String(Booleans, chars);

aOutput.write(reinterpret_cast<const char*>(chars.data()), chars.size());
aOutput.write(reinterpret_cast<const char*>(Integers.data()), Integers.size() * sizeof(uint32_t));
aOutput.write(reinterpret_cast<const char*>(Floats.data()), Floats.size() * sizeof(float));
}

// Wire format description.
//
// Sends 3 VarInts, the count of Booleans, Integers and Floats, in that order. Then sends a bitstream of the
// sum of those counts. For the Booleans, these represent the bit values for the Booleans. For the Integers and
// Floats, it represents a truth table for whether the value has changed. If values HAVE changed, they follow on
// the stream.
//
//
void AnimationVariables::GenerateDiff(const AnimationVariables& aPrevious, TiltedPhoques::Buffer::Writer& aWriter) const
{
uint64_t changes = 0;
uint32_t idx = 0;

if (Booleans != aPrevious.Booleans)
{
changes |= (1ull << idx);
}
++idx;

auto integers = aPrevious.Integers;
if (integers.empty())
integers.assign(Integers.size(), 0);

for (auto i = 0u; i < Integers.size(); ++i)
{
if (Integers[i] != integers[i])
{
changes |= (1ull << idx);
}
++idx;
}

auto floats = aPrevious.Floats;
if (floats.empty())
floats.assign(Floats.size(), 0.f);

for (auto i = 0u; i < Floats.size(); ++i)
{
if (Floats[i] != floats[i])
{
changes |= (1ull << idx);
}
++idx;
}

const size_t sizeChangedVector = Booleans.size() + Integers.size() + Floats.size();
auto changedVector = Booleans;
changedVector.reserve(sizeChangedVector);

for (size_t i = 0; i < Integers.size(); i++)
changedVector.push_back(aPrevious.Integers.size() != Integers.size() || aPrevious.Integers[i] != Integers[i]);
for (size_t i = 0; i < Floats.size(); i++)
changedVector.push_back(aPrevious.Floats.size() != Floats.size() || aPrevious.Floats[i] != Floats[i]);

// Now serialize: VarInts Booleans.size(), Integers.size(), Floats.size(),
// then the change table bits, then changed Integers, then changed Floats.
TiltedPhoques::Serialization::WriteVarInt(aWriter, Booleans.size());
TiltedPhoques::Serialization::WriteVarInt(aWriter, Integers.size());
TiltedPhoques::Serialization::WriteVarInt(aWriter, Floats.size());

const auto cDiffBitCount = 1 + Integers.size() + Floats.size();

aWriter.WriteBits(changes, cDiffBitCount);

idx = 0;
if (changes & (1ull << idx))
{
aWriter.WriteBits(Booleans, 64);
}
++idx;

for (const auto value : Integers)
{
if (changes & (1ull << idx))
{
TiltedPhoques::Serialization::WriteVarInt(aWriter, value & 0xFFFFFFFF);
}
++idx;
}

for (const auto value : Floats)
{
if (changes & (1ull << idx))
{
aWriter.WriteBits(*reinterpret_cast<const uint32_t*>(&value), 32);
}
++idx;
}
TiltedPhoques::String chars;
VectorBool_to_String(changedVector, chars);
TiltedPhoques::Serialization::WriteString(aWriter, chars);

auto biter = changedVector.begin() + Booleans.size();
for (size_t i = 0; i < Integers.size(); i++)
if (*biter++)
TiltedPhoques::Serialization::WriteVarInt(aWriter, Integers[i] & 0xFFFFFFFF);
for (size_t i = 0; i < Floats.size(); i++)
if (*biter++)
TiltedPhoques::Serialization::WriteFloat(aWriter, Floats[i]);
}

// Reads 3 VarInts that represent the size of the Booleans, Integers and Floats.
// That's followed by a bitstream in a string of the Booleans values combined
// with a Changed? truth table for Integers and Floats.
// The Changed? table is scanned and for each true bit, the corresponsing Integer
// or Float is deserialized.
//
void AnimationVariables::ApplyDiff(TiltedPhoques::Buffer::Reader& aReader)
{
const auto cIntegersSize = TiltedPhoques::Serialization::ReadVarInt(aReader);
if (cIntegersSize > 0xFF)
throw std::runtime_error("Too many integers received !");

if (Integers.size() != cIntegersSize)
{
Integers.assign(cIntegersSize, 0);
}

const auto cFloatsSize = TiltedPhoques::Serialization::ReadVarInt(aReader);
if (cFloatsSize > 0xFF)
throw std::runtime_error("Too many floats received !");

if (Floats.size() != cFloatsSize)
{
Floats.assign(cFloatsSize, 0.f);
}

const auto cDiffBitCount = 1 + Integers.size() + Floats.size();

uint64_t changes = 0;
uint32_t idx = 0;

aReader.ReadBits(changes, cDiffBitCount);

if (changes & (1ull << idx))
{
aReader.ReadBits(Booleans, 64);
}
++idx;

for (auto& value : Integers)
{
if (changes & (1ull << idx))
{
value = TiltedPhoques::Serialization::ReadVarInt(aReader) & 0xFFFFFFFF;
}
++idx;
}

for (auto& value : Floats)
{
if (changes & (1ull << idx))
{
uint64_t tmp = 0;
aReader.ReadBits(tmp, 32);
uint32_t data = tmp & 0xFFFFFFFF;
value = *reinterpret_cast<float*>(&data);
}
++idx;
}
size_t booleansSize = TiltedPhoques::Serialization::ReadVarInt(aReader);
size_t integersSize = TiltedPhoques::Serialization::ReadVarInt(aReader);
size_t floatsSize = TiltedPhoques::Serialization::ReadVarInt(aReader);
if (Integers.size() != integersSize)
Integers.assign(integersSize, 0);
if (Floats.size() != floatsSize)
Floats.assign(floatsSize, 0.f);

TiltedPhoques::Vector<bool> changedVector(booleansSize + integersSize + floatsSize);
auto chars = TiltedPhoques::Serialization::ReadString(aReader);
String_to_VectorBool(chars, changedVector);

Booleans.assign(changedVector.begin(), changedVector.begin() + booleansSize);

auto biter = changedVector.begin() + booleansSize;
for (size_t i = 0; i < integersSize; i++)
if (*biter++)
Integers[i] = TiltedPhoques::Serialization::ReadVarInt(aReader);
for (size_t i = 0; i < floatsSize; i++)
if (*biter++)
Floats[i] = TiltedPhoques::Serialization::ReadFloat(aReader);
}
4 changes: 3 additions & 1 deletion Code/encoding/Structs/AnimationVariables.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ using TiltedPhoques::Vector;

struct AnimationVariables
{
uint64_t Booleans{0};
Vector<bool> Booleans{};
Vector<uint32_t> Integers{};
Vector<float> Floats{};

Expand All @@ -18,4 +18,6 @@ struct AnimationVariables

void GenerateDiff(const AnimationVariables& aPrevious, TiltedPhoques::Buffer::Writer& aWriter) const;
void ApplyDiff(TiltedPhoques::Buffer::Reader& aReader);
void VectorBool_to_String(const Vector<bool>& bools, TiltedPhoques::String& chars) const;
void String_to_VectorBool(const TiltedPhoques::String& chars, Vector<bool>& bools);
};
18 changes: 15 additions & 3 deletions Code/tests/encoding.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -276,7 +276,12 @@ TEST_CASE("Differential structures", "[encoding.differential]")
GIVEN("AnimationVariables")
{
AnimationVariables vars, recvVars;
vars.Booleans = 0x12345678ull;

vars.Booleans.resize(76);
String testString("\xDE\xAD\xBE\xEF"
"\xDE\xAD\xBE\xEF\x76\xB");
vars.String_to_VectorBool(testString, vars.Booleans);

vars.Floats.push_back(1.f);
vars.Floats.push_back(7.f);
vars.Floats.push_back(12.f);
Expand Down Expand Up @@ -305,7 +310,11 @@ TEST_CASE("Differential structures", "[encoding.differential]")
REQUIRE(vars.Integers == recvVars.Integers);
}

vars.Booleans = 0x9456123ull;
vars.Booleans.resize(33);
vars.Booleans[16] = false;
vars.Booleans[17] = false;
vars.Booleans[18] = false;
vars.Booleans[19] = false;
vars.Floats[3] = 42.f;
vars.Integers[0] = 18;
vars.Integers[3] = 0;
Expand Down Expand Up @@ -444,7 +453,10 @@ TEST_CASE("Packets", "[encoding.packets]")
auto& move = update.UpdatedMovement;

AnimationVariables vars;
vars.Booleans = 0x12345678ull;
vars.Booleans.resize(76);
String testString("\xDE\xAD\xBE\xEF\x76\xB");
vars.String_to_VectorBool(testString, vars.Booleans);

vars.Floats.push_back(1.f);
vars.Floats.push_back(7.f);
vars.Floats.push_back(12.f);
Expand Down

0 comments on commit ffc958e

Please sign in to comment.