mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2025-12-18 03:37:23 -06:00
Compare commits
4 Commits
reset-enti
...
Restart-be
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
edda646856 | ||
|
|
55d181ea4b | ||
|
|
ecbb465020 | ||
|
|
ec9927acbb |
@@ -3,6 +3,7 @@
|
|||||||
// C++
|
// C++
|
||||||
#include <charconv>
|
#include <charconv>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <cmath>
|
||||||
#include <ctime>
|
#include <ctime>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@@ -145,7 +146,7 @@ namespace GeneralUtils {
|
|||||||
template <typename... Bases>
|
template <typename... Bases>
|
||||||
struct overload : Bases... {
|
struct overload : Bases... {
|
||||||
using is_transparent = void;
|
using is_transparent = void;
|
||||||
using Bases::operator() ... ;
|
using Bases::operator() ...;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct char_pointer_hash {
|
struct char_pointer_hash {
|
||||||
@@ -202,7 +203,7 @@ namespace GeneralUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
template<typename T>
|
template<typename T>
|
||||||
requires(!Numeric<T>)
|
requires(!Numeric<T>)
|
||||||
[[nodiscard]] std::optional<T> TryParse(std::string_view str);
|
[[nodiscard]] std::optional<T> TryParse(std::string_view str);
|
||||||
|
|
||||||
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
|
#if !(__GNUC__ >= 11 || _MSC_VER >= 1924)
|
||||||
@@ -221,7 +222,7 @@ namespace GeneralUtils {
|
|||||||
*/
|
*/
|
||||||
template <std::floating_point T>
|
template <std::floating_point T>
|
||||||
[[nodiscard]] std::optional<T> TryParse(std::string_view str) noexcept
|
[[nodiscard]] std::optional<T> TryParse(std::string_view str) noexcept
|
||||||
try {
|
try {
|
||||||
while (!str.empty() && std::isspace(str.front())) str.remove_prefix(1);
|
while (!str.empty() && std::isspace(str.front())) str.remove_prefix(1);
|
||||||
|
|
||||||
size_t parseNum;
|
size_t parseNum;
|
||||||
@@ -323,4 +324,28 @@ namespace GeneralUtils {
|
|||||||
|
|
||||||
return GenerateRandomNumber<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
|
return GenerateRandomNumber<T>(std::numeric_limits<T>::min(), std::numeric_limits<T>::max());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://www.quora.com/How-do-you-round-to-specific-increments-like-0-5-in-C
|
||||||
|
// Rounds to the nearest floating point value specified.
|
||||||
|
template <typename T, std::enable_if_t<std::is_floating_point_v<T>, int> = 0>
|
||||||
|
T RountToNearestEven(const T value, const T modulus) {
|
||||||
|
const auto modulo = std::fmod(value, modulus);
|
||||||
|
const auto abs_modulo_2 = std::abs(modulo * 2);
|
||||||
|
const auto abs_modulus = std::abs(modulus);
|
||||||
|
|
||||||
|
bool round_away_from_zero = false;
|
||||||
|
if (abs_modulo_2 > abs_modulus) {
|
||||||
|
round_away_from_zero = true;
|
||||||
|
} else if (abs_modulo_2 == abs_modulus) {
|
||||||
|
const auto trunc_quot = std::floor(std::abs(value / modulus));
|
||||||
|
const auto odd = std::fmod(trunc_quot, T{ 2 }) != 0;
|
||||||
|
round_away_from_zero = odd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (round_away_from_zero) {
|
||||||
|
return value + (std::copysign(modulus, value) - modulo);
|
||||||
|
} else {
|
||||||
|
return value - modulo;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
|
|
||||||
Lxfml::Result Lxfml::NormalizePosition(const std::string_view data) {
|
Lxfml::Result Lxfml::NormalizePosition(const std::string_view data, const NiPoint3& curPosition) {
|
||||||
Result toReturn;
|
Result toReturn;
|
||||||
tinyxml2::XMLDocument doc;
|
tinyxml2::XMLDocument doc;
|
||||||
const auto err = doc.Parse(data.data());
|
const auto err = doc.Parse(data.data());
|
||||||
@@ -44,29 +44,42 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data) {
|
|||||||
NiPoint3 lowest{ 10'000.0f, 10'000.0f, 10'000.0f };
|
NiPoint3 lowest{ 10'000.0f, 10'000.0f, 10'000.0f };
|
||||||
NiPoint3 highest{ -10'000.0f, -10'000.0f, -10'000.0f };
|
NiPoint3 highest{ -10'000.0f, -10'000.0f, -10'000.0f };
|
||||||
|
|
||||||
// Calculate the lowest and highest points on the entire model
|
NiPoint3 delta = NiPoint3Constant::ZERO;
|
||||||
for (const auto& transformation : transformations | std::views::values) {
|
if (curPosition == NiPoint3Constant::ZERO) {
|
||||||
auto split = GeneralUtils::SplitString(transformation, ',');
|
// Calculate the lowest and highest points on the entire model
|
||||||
if (split.size() < 12) {
|
for (const auto& transformation : transformations | std::views::values) {
|
||||||
LOG("Not enough in the split?");
|
auto split = GeneralUtils::SplitString(transformation, ',');
|
||||||
continue;
|
if (split.size() < 12) {
|
||||||
|
LOG("Not enough in the split?");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto x = GeneralUtils::TryParse<float>(split[9]).value();
|
||||||
|
auto y = GeneralUtils::TryParse<float>(split[10]).value();
|
||||||
|
auto z = GeneralUtils::TryParse<float>(split[11]).value();
|
||||||
|
if (x < lowest.x) lowest.x = x;
|
||||||
|
if (y < lowest.y) lowest.y = y;
|
||||||
|
if (z < lowest.z) lowest.z = z;
|
||||||
|
|
||||||
|
if (highest.x < x) highest.x = x;
|
||||||
|
if (highest.y < y) highest.y = y;
|
||||||
|
if (highest.z < z) highest.z = z;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto x = GeneralUtils::TryParse<float>(split[9]).value();
|
delta = (highest - lowest) / 2.0f;
|
||||||
auto y = GeneralUtils::TryParse<float>(split[10]).value();
|
} else {
|
||||||
auto z = GeneralUtils::TryParse<float>(split[11]).value();
|
lowest = curPosition;
|
||||||
if (x < lowest.x) lowest.x = x;
|
highest = curPosition;
|
||||||
if (y < lowest.y) lowest.y = y;
|
delta = NiPoint3Constant::ZERO;
|
||||||
if (z < lowest.z) lowest.z = z;
|
|
||||||
|
|
||||||
if (highest.x < x) highest.x = x;
|
|
||||||
if (highest.y < y) highest.y = y;
|
|
||||||
if (highest.z < z) highest.z = z;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto delta = (highest - lowest) / 2.0f;
|
|
||||||
auto newRootPos = lowest + delta;
|
auto newRootPos = lowest + delta;
|
||||||
|
|
||||||
|
// Need to snap this chosen position to the nearest valid spot
|
||||||
|
// on the LEGO grid
|
||||||
|
newRootPos.x = GeneralUtils::RountToNearestEven(newRootPos.x, 0.8f);
|
||||||
|
newRootPos.z = GeneralUtils::RountToNearestEven(newRootPos.z, 0.8f);
|
||||||
|
|
||||||
// Clamp the Y to the lowest point on the model
|
// Clamp the Y to the lowest point on the model
|
||||||
newRootPos.y = lowest.y;
|
newRootPos.y = lowest.y;
|
||||||
|
|
||||||
@@ -78,9 +91,9 @@ Lxfml::Result Lxfml::NormalizePosition(const std::string_view data) {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto x = GeneralUtils::TryParse<float>(split[9]).value() - newRootPos.x;
|
auto x = GeneralUtils::TryParse<float>(split[9]).value() - newRootPos.x + curPosition.x;
|
||||||
auto y = GeneralUtils::TryParse<float>(split[10]).value() - newRootPos.y;
|
auto y = GeneralUtils::TryParse<float>(split[10]).value() - newRootPos.y + curPosition.y;
|
||||||
auto z = GeneralUtils::TryParse<float>(split[11]).value() - newRootPos.z;
|
auto z = GeneralUtils::TryParse<float>(split[11]).value() - newRootPos.z + curPosition.z;
|
||||||
std::stringstream stream;
|
std::stringstream stream;
|
||||||
for (int i = 0; i < 9; i++) {
|
for (int i = 0; i < 9; i++) {
|
||||||
stream << split[i];
|
stream << split[i];
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ namespace Lxfml {
|
|||||||
|
|
||||||
// Normalizes a LXFML model to be positioned relative to its local 0, 0, 0 rather than a game worlds 0, 0, 0.
|
// Normalizes a LXFML model to be positioned relative to its local 0, 0, 0 rather than a game worlds 0, 0, 0.
|
||||||
// Returns a struct of its new center and the updated LXFML containing these edits.
|
// Returns a struct of its new center and the updated LXFML containing these edits.
|
||||||
[[nodiscard]] Result NormalizePosition(const std::string_view data);
|
[[nodiscard]] Result NormalizePosition(const std::string_view data, const NiPoint3& curPosition = NiPoint3Constant::ZERO);
|
||||||
|
|
||||||
// these are only for the migrations due to a bug in one of the implementations.
|
// these are only for the migrations due to a bug in one of the implementations.
|
||||||
[[nodiscard]] Result NormalizePositionOnlyFirstPart(const std::string_view data);
|
[[nodiscard]] Result NormalizePositionOnlyFirstPart(const std::string_view data);
|
||||||
|
|||||||
@@ -48,6 +48,7 @@ void MigrationRunner::RunMigrations() {
|
|||||||
bool runSd0Migrations = false;
|
bool runSd0Migrations = false;
|
||||||
bool runNormalizeMigrations = false;
|
bool runNormalizeMigrations = false;
|
||||||
bool runNormalizeAfterFirstPartMigrations = false;
|
bool runNormalizeAfterFirstPartMigrations = false;
|
||||||
|
bool runBrickBuildsNotOnGrid = false;
|
||||||
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/" / migrationFolder).string())) {
|
for (const auto& entry : GeneralUtils::GetSqlFileNamesFromFolder((BinaryPathFinder::GetBinaryDir() / "./migrations/dlu/" / migrationFolder).string())) {
|
||||||
auto migration = LoadMigration("dlu/" + migrationFolder + "/", entry);
|
auto migration = LoadMigration("dlu/" + migrationFolder + "/", entry);
|
||||||
|
|
||||||
@@ -64,6 +65,8 @@ void MigrationRunner::RunMigrations() {
|
|||||||
runNormalizeMigrations = true;
|
runNormalizeMigrations = true;
|
||||||
} else if (migration.name.ends_with("_normalize_model_positions_after_first_part.sql")) {
|
} else if (migration.name.ends_with("_normalize_model_positions_after_first_part.sql")) {
|
||||||
runNormalizeAfterFirstPartMigrations = true;
|
runNormalizeAfterFirstPartMigrations = true;
|
||||||
|
} else if (migration.name.ends_with("_brickbuilds_not_on_grid.sql")) {
|
||||||
|
runBrickBuildsNotOnGrid = true;
|
||||||
} else {
|
} else {
|
||||||
finalSQL.append(migration.data.c_str());
|
finalSQL.append(migration.data.c_str());
|
||||||
}
|
}
|
||||||
@@ -71,7 +74,7 @@ void MigrationRunner::RunMigrations() {
|
|||||||
Database::Get()->InsertMigration(migration.name);
|
Database::Get()->InsertMigration(migration.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (finalSQL.empty() && !runSd0Migrations && !runNormalizeMigrations && !runNormalizeAfterFirstPartMigrations) {
|
if (finalSQL.empty() && !runSd0Migrations && !runNormalizeMigrations && !runNormalizeAfterFirstPartMigrations && !runBrickBuildsNotOnGrid) {
|
||||||
LOG("Server database is up to date.");
|
LOG("Server database is up to date.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -103,6 +106,10 @@ void MigrationRunner::RunMigrations() {
|
|||||||
if (runNormalizeAfterFirstPartMigrations) {
|
if (runNormalizeAfterFirstPartMigrations) {
|
||||||
ModelNormalizeMigration::RunAfterFirstPart();
|
ModelNormalizeMigration::RunAfterFirstPart();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (runBrickBuildsNotOnGrid) {
|
||||||
|
ModelNormalizeMigration::RunBrickBuildGrid();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void MigrationRunner::RunSQLiteMigrations() {
|
void MigrationRunner::RunSQLiteMigrations() {
|
||||||
|
|||||||
@@ -48,3 +48,24 @@ void ModelNormalizeMigration::RunAfterFirstPart() {
|
|||||||
}
|
}
|
||||||
Database::Get()->SetAutoCommit(oldCommit);
|
Database::Get()->SetAutoCommit(oldCommit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ModelNormalizeMigration::RunBrickBuildGrid() {
|
||||||
|
const auto oldCommit = Database::Get()->GetAutoCommit();
|
||||||
|
Database::Get()->SetAutoCommit(false);
|
||||||
|
for (auto& [lxfmlData, id, modelID] : Database::Get()->GetAllUgcModels()) {
|
||||||
|
const auto model = Database::Get()->GetModel(modelID);
|
||||||
|
// only BBB models (lot 14) need to have their position fixed from the above blunder
|
||||||
|
if (model.lot != 14) continue;
|
||||||
|
|
||||||
|
Sd0 sd0(lxfmlData);
|
||||||
|
const auto asStr = sd0.GetAsStringUncompressed();
|
||||||
|
const auto [newLxfml, newCenter] = Lxfml::NormalizePosition(asStr, model.position);
|
||||||
|
|
||||||
|
sd0.FromData(reinterpret_cast<const uint8_t*>(newLxfml.data()), newLxfml.size());
|
||||||
|
LOG("Updated model %llu to have a center of %f %f %f", modelID, newCenter.x, newCenter.y, newCenter.z);
|
||||||
|
auto asStream = sd0.GetAsStream();
|
||||||
|
Database::Get()->UpdateModel(model.id, newCenter, model.rotation, model.behaviors);
|
||||||
|
Database::Get()->UpdateUgcModelData(id, asStream);
|
||||||
|
}
|
||||||
|
Database::Get()->SetAutoCommit(oldCommit);
|
||||||
|
}
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
namespace ModelNormalizeMigration {
|
namespace ModelNormalizeMigration {
|
||||||
void Run();
|
void Run();
|
||||||
void RunAfterFirstPart();
|
void RunAfterFirstPart();
|
||||||
|
void RunBrickBuildGrid();
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //!MODELNORMALIZEMIGRATION_H
|
#endif //!MODELNORMALIZEMIGRATION_H
|
||||||
|
|||||||
@@ -167,6 +167,7 @@ Entity::~Entity() {
|
|||||||
|
|
||||||
if (m_Character) {
|
if (m_Character) {
|
||||||
m_Character->SaveXMLToDatabase();
|
m_Character->SaveXMLToDatabase();
|
||||||
|
m_Character->SetEntity(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
CancelAllTimers();
|
CancelAllTimers();
|
||||||
|
|||||||
@@ -526,7 +526,7 @@ void UserManager::LoginCharacter(const SystemAddress& sysAddr, uint32_t playerID
|
|||||||
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, zoneID, character->GetZoneClone(), false, [=](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
|
ZoneInstanceManager::Instance()->RequestZoneTransfer(Game::server, zoneID, character->GetZoneClone(), false, [=](bool mythranShift, uint32_t zoneID, uint32_t zoneInstance, uint32_t zoneClone, std::string serverIP, uint16_t serverPort) {
|
||||||
LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", character->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
|
LOG("Transferring %s to Zone %i (Instance %i | Clone %i | Mythran Shift: %s) with IP %s and Port %i", character->GetName().c_str(), zoneID, zoneInstance, zoneClone, mythranShift == true ? "true" : "false", serverIP.c_str(), serverPort);
|
||||||
if (character) {
|
if (character) {
|
||||||
auto* entity = character->GetEntity();
|
auto* entity = Game::entityManager->GetEntity(character->GetObjectID());
|
||||||
if (entity) {
|
if (entity) {
|
||||||
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
auto* characterComponent = entity->GetComponent<CharacterComponent>();
|
||||||
if (characterComponent) {
|
if (characterComponent) {
|
||||||
|
|||||||
@@ -63,6 +63,12 @@ void ModelComponent::Update(float deltaTime) {
|
|||||||
for (auto& behavior : m_Behaviors) {
|
for (auto& behavior : m_Behaviors) {
|
||||||
behavior.Update(deltaTime, *this);
|
behavior.Update(deltaTime, *this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!m_RestartAtEndOfFrame) return;
|
||||||
|
|
||||||
|
GameMessages::ResetModelToDefaults reset{};
|
||||||
|
OnResetModelToDefaults(reset);
|
||||||
|
m_RestartAtEndOfFrame = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelComponent::LoadBehaviors() {
|
void ModelComponent::LoadBehaviors() {
|
||||||
@@ -265,7 +271,7 @@ bool ModelComponent::TrySetVelocity(const NiPoint3& velocity) const {
|
|||||||
|
|
||||||
// If we're currently moving on an axis, prevent the move so only 1 behavior can have control over an axis
|
// If we're currently moving on an axis, prevent the move so only 1 behavior can have control over an axis
|
||||||
if (velocity != NiPoint3Constant::ZERO) {
|
if (velocity != NiPoint3Constant::ZERO) {
|
||||||
const auto [x, y, z] = velocity;
|
const auto [x, y, z] = velocity * m_Speed;
|
||||||
if (x != 0.0f) {
|
if (x != 0.0f) {
|
||||||
if (currentVelocity.x != 0.0f) return false;
|
if (currentVelocity.x != 0.0f) return false;
|
||||||
currentVelocity.x = x;
|
currentVelocity.x = x;
|
||||||
@@ -280,7 +286,6 @@ bool ModelComponent::TrySetVelocity(const NiPoint3& velocity) const {
|
|||||||
currentVelocity = velocity;
|
currentVelocity = velocity;
|
||||||
}
|
}
|
||||||
|
|
||||||
currentVelocity *= m_Speed;
|
|
||||||
m_Parent->SetVelocity(currentVelocity);
|
m_Parent->SetVelocity(currentVelocity);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,7 +146,11 @@ public:
|
|||||||
|
|
||||||
void OnChatMessageReceived(const std::string& sMessage);
|
void OnChatMessageReceived(const std::string& sMessage);
|
||||||
|
|
||||||
|
// Sets the speed of the model
|
||||||
void SetSpeed(const float newSpeed) { m_Speed = newSpeed; }
|
void SetSpeed(const float newSpeed) { m_Speed = newSpeed; }
|
||||||
|
|
||||||
|
// Whether or not to restart at the end of the frame
|
||||||
|
void RestartAtEndOfFrame() { m_RestartAtEndOfFrame = true; }
|
||||||
private:
|
private:
|
||||||
|
|
||||||
// Loads a behavior from the database.
|
// Loads a behavior from the database.
|
||||||
@@ -190,4 +194,7 @@ private:
|
|||||||
|
|
||||||
// The speed at which this model moves
|
// The speed at which this model moves
|
||||||
float m_Speed{ 3.0f };
|
float m_Speed{ 3.0f };
|
||||||
|
|
||||||
|
// Whether or not to restart at the end of the frame.
|
||||||
|
bool m_RestartAtEndOfFrame{ false };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -221,6 +221,8 @@ void Strip::ProcNormalAction(float deltaTime, ModelComponent& modelComponent) {
|
|||||||
sound.target = modelComponent.GetParent()->GetObjectID();
|
sound.target = modelComponent.GetParent()->GetObjectID();
|
||||||
sound.soundID = numberAsInt;
|
sound.soundID = numberAsInt;
|
||||||
sound.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
sound.Send(UNASSIGNED_SYSTEM_ADDRESS);
|
||||||
|
} else if (nextActionType == "Restart") {
|
||||||
|
modelComponent.RestartAtEndOfFrame();
|
||||||
}
|
}
|
||||||
/* END Action */
|
/* END Action */
|
||||||
/* BEGIN Gameplay */
|
/* BEGIN Gameplay */
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ private:
|
|||||||
|
|
||||||
// The position of the parent model on the previous frame
|
// The position of the parent model on the previous frame
|
||||||
NiPoint3 m_PreviousFramePosition{};
|
NiPoint3 m_PreviousFramePosition{};
|
||||||
|
|
||||||
|
NiPoint3 m_SavedVelocity{};
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif //!__STRIP__H__
|
#endif //!__STRIP__H__
|
||||||
|
|||||||
1
migrations/dlu/mysql/22_brickbuilds_not_on_grid.sql
Normal file
1
migrations/dlu/mysql/22_brickbuilds_not_on_grid.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/* See ModelNormalizeMigration.cpp for details */
|
||||||
1
migrations/dlu/sqlite/5_brickbuilds_not_on_grid.sql
Normal file
1
migrations/dlu/sqlite/5_brickbuilds_not_on_grid.sql
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/* See ModelNormalizeMigration.cpp for details */
|
||||||
Reference in New Issue
Block a user