From 1a199151da72a277c71a88ff8e1f20eb101c92ab Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Tue, 21 Nov 2023 18:04:23 -0800 Subject: [PATCH 1/3] Remove std::couts littered throughout the base (#1313) --- dGame/dComponents/PhantomPhysicsComponent.cpp | 4 +-- dGame/dGameMessages/GameMessageHandler.cpp | 12 ------- dGame/dUtilities/SlashCommandHandler.cpp | 8 ++--- dPhysics/dpEntity.cpp | 8 +---- dPhysics/dpGrid.cpp | 2 -- dPhysics/dpShapeBase.cpp | 4 +-- dPhysics/dpShapeBox.cpp | 5 +-- dPhysics/dpShapeSphere.cpp | 4 ++- dPhysics/main.cpp | 35 ------------------- dZoneManager/Level.cpp | 16 ++++++--- dZoneManager/Level.h | 6 +--- dZoneManager/Zone.h | 2 ++ dZoneManager/dZMCommon.h | 4 +-- tests/dCommonTests/Amf3Tests.cpp | 10 ------ 14 files changed, 29 insertions(+), 91 deletions(-) delete mode 100644 dPhysics/main.cpp diff --git a/dGame/dComponents/PhantomPhysicsComponent.cpp b/dGame/dComponents/PhantomPhysicsComponent.cpp index e0a76f67c..6248bfe99 100644 --- a/dGame/dComponents/PhantomPhysicsComponent.cpp +++ b/dGame/dComponents/PhantomPhysicsComponent.cpp @@ -402,10 +402,10 @@ void PhantomPhysicsComponent::SetDirection(const NiPoint3& pos) { void PhantomPhysicsComponent::SpawnVertices() { if (!m_dpEntity) return; - std::cout << m_Parent->GetObjectID() << std::endl; + LOG("%llu", m_Parent->GetObjectID()); auto box = static_cast(m_dpEntity->GetShape()); for (auto vert : box->GetVertices()) { - std::cout << vert.x << ", " << vert.y << ", " << vert.z << std::endl; + LOG("%f, %f, %f", vert.x, vert.y, vert.z); EntityInfo info; info.lot = 33; diff --git a/dGame/dGameMessages/GameMessageHandler.cpp b/dGame/dGameMessages/GameMessageHandler.cpp index 9a50a746f..40293fbec 100644 --- a/dGame/dGameMessages/GameMessageHandler.cpp +++ b/dGame/dGameMessages/GameMessageHandler.cpp @@ -244,13 +244,6 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System case eGameMessageType::REQUEST_RESURRECT: { GameMessages::SendResurrect(entity); - /*auto* dest = static_cast(entity->GetComponent(eReplicaComponentType::DESTROYABLE)); - if (dest) { - dest->SetHealth(4); - dest->SetArmor(0); - dest->SetImagination(6); - Game::entityManager->SerializeEntity(entity); - }*/ break; } case eGameMessageType::GET_HOT_PROPERTY_DATA: { @@ -339,11 +332,8 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System RakNet::BitStream bitStreamLocal; BitStreamUtils::WriteHeader(bitStreamLocal, eConnectionType::CLIENT, eClientMessageType::GAME_MSG); bitStreamLocal.Write(entity->GetObjectID()); - //bitStreamLocal.Write((unsigned short)eGameMessageType::ECHO_SYNC_SKILL); - //bitStreamLocal.Write(inStream); SyncSkill sync = SyncSkill(inStream); // inStream replaced &bitStream - //sync.Serialize(&bitStreamLocal); ostringstream buffer; @@ -353,8 +343,6 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System buffer << setw(2) << hex << setfill('0') << (int)s << " "; } - //cout << buffer.str() << endl; - if (usr != nullptr) { RakNet::BitStream* bs = new RakNet::BitStream((unsigned char*)sync.sBitStream.c_str(), sync.sBitStream.size(), false); diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 71f0e38b6..f4a21cabf 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -1372,7 +1372,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit ChatPackets::SendSystemMessage(sysAddr, u"<" + (GeneralUtils::to_u16string(position.x)) + u", " + (GeneralUtils::to_u16string(position.y)) + u", " + (GeneralUtils::to_u16string(position.z)) + u">"); - std::cout << position.x << ", " << position.y << ", " << position.z << std::endl; + LOG("Position: %f, %f, %f", position.x, position.y, position.z); } if (chatCommand == "rot" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { @@ -1380,14 +1380,14 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit ChatPackets::SendSystemMessage(sysAddr, u"<" + (GeneralUtils::to_u16string(rotation.w)) + u", " + (GeneralUtils::to_u16string(rotation.x)) + u", " + (GeneralUtils::to_u16string(rotation.y)) + u", " + (GeneralUtils::to_u16string(rotation.z)) + u">"); - std::cout << rotation.w << ", " << rotation.x << ", " << rotation.y << ", " << rotation.z << std::endl; + LOG("Rotation: %f, %f, %f, %f", rotation.w, rotation.x, rotation.y, rotation.z); } if (chatCommand == "locrow" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { const auto position = entity->GetPosition(); const auto rotation = entity->GetRotation(); - std::cout << "" << std::endl; + LOG("", position.x, position.y, position.z, rotation.w, rotation.x, rotation.y, rotation.z); } if (chatCommand == "playlvlfx" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER) { @@ -1652,7 +1652,7 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit auto sphere = static_cast(prox.second->GetShape()); auto pos = prox.second->GetPosition(); - std::cout << prox.first << ", r: " << sphere->GetRadius() << ", pos: " << pos.x << "," << pos.y << "," << pos.z << std::endl; + LOG("Proximity: %s, r: %f, pos: %f, %f, %f", prox.first.c_str(), sphere->GetRadius(), pos.x, pos.y, pos.z); } } } diff --git a/dPhysics/dpEntity.cpp b/dPhysics/dpEntity.cpp index c7ed56f85..70bddb40a 100644 --- a/dPhysics/dpEntity.cpp +++ b/dPhysics/dpEntity.cpp @@ -22,7 +22,7 @@ dpEntity::dpEntity(const LWOOBJID& objectID, dpShapeType shapeType, bool isStati break; default: - std::cout << "No shape for shapeType: " << (int)shapeType << std::endl; + LOG("No shape for shapeType: %d", static_cast(shapeType)); } } @@ -83,15 +83,9 @@ void dpEntity::CheckCollision(dpEntity* other) { if (isColliding && !wasFound) { m_CurrentlyCollidingObjects.emplace(other->GetObjectID(), other); m_NewObjects.push_back(other); - - //if (m_CollisionShape->GetShapeType() == dpShapeType::Sphere && other->GetShape()->GetShapeType() == dpShapeType::Sphere) - //std::cout << "started sphere col at: " << other->GetPosition().x << ", " << other->GetPosition().y << ", " << other->GetPosition().z << std::endl; } else if (!isColliding && wasFound) { m_CurrentlyCollidingObjects.erase(other->GetObjectID()); m_RemovedObjects.push_back(other); - - //if (m_CollisionShape->GetShapeType() == dpShapeType::Sphere && other->GetShape()->GetShapeType() == dpShapeType::Sphere) - // std::cout << "stopped sphere col at: " << other->GetPosition().x << ", " << other->GetPosition().y << ", " << other->GetPosition().z << std::endl; } } diff --git a/dPhysics/dpGrid.cpp b/dPhysics/dpGrid.cpp index c3259b51e..1704e068e 100644 --- a/dPhysics/dpGrid.cpp +++ b/dPhysics/dpGrid.cpp @@ -8,8 +8,6 @@ dpGrid::dpGrid(int numCells, int cellSize) { CELL_SIZE = cellSize; m_DeleteGrid = true; - //dumb method but i can't be bothered - //fill x for (int i = 0; i < NUM_CELLS; i++) { m_Cells.push_back(std::vector>()); diff --git a/dPhysics/dpShapeBase.cpp b/dPhysics/dpShapeBase.cpp index 545751594..29f5d7890 100644 --- a/dPhysics/dpShapeBase.cpp +++ b/dPhysics/dpShapeBase.cpp @@ -10,7 +10,7 @@ dpShapeBase::~dpShapeBase() { } bool dpShapeBase::IsColliding(dpShapeBase* other) { - std::cout << "Base shapes do not have any *shape* to them, and thus cannot be overlapping." << std::endl; - std::cout << "You should be using a shape class inherited from this base class." << std::endl; + LOG("Base shapes do not have any *shape* to them, and thus cannot be overlapping."); + LOG("You should be using a shape class inherited from this base class."); return false; } diff --git a/dPhysics/dpShapeBox.cpp b/dPhysics/dpShapeBox.cpp index b40be6af2..bfd72deac 100644 --- a/dPhysics/dpShapeBox.cpp +++ b/dPhysics/dpShapeBox.cpp @@ -34,7 +34,7 @@ bool dpShapeBox::IsColliding(dpShapeBase* other) { return dpCollisionChecks::CheckBoxes(m_ParentEntity, other->GetParentEntity()); default: - std::cout << "No collision detection for: " << (int)m_ShapeType << "-to-" << (int)other->GetShapeType() << " collision!" << std::endl; + LOG("No collision detection for: %i-to-%i collision!", static_cast(m_ShapeType), static_cast(other->GetShapeType())); } return false; @@ -72,10 +72,7 @@ void dpShapeBox::SetScale(float scale) { m_Height *= scale; m_Depth *= scale; - //fuuuckkk yoouu InitVertices(); - - //SetRotation(m_ParentEntity->GetRotation()); } void dpShapeBox::SetRotation(const NiQuaternion& rotation) { diff --git a/dPhysics/dpShapeSphere.cpp b/dPhysics/dpShapeSphere.cpp index 168a3b214..545d58ed1 100644 --- a/dPhysics/dpShapeSphere.cpp +++ b/dPhysics/dpShapeSphere.cpp @@ -1,5 +1,7 @@ #include "dpShapeSphere.h" #include "dpCollisionChecks.h" +#include "Game.h" +#include "Logger.h" #include dpShapeSphere::dpShapeSphere(dpEntity* parentEntity, float radius) : @@ -22,7 +24,7 @@ bool dpShapeSphere::IsColliding(dpShapeBase* other) { return dpCollisionChecks::CheckSphereBox(m_ParentEntity, other->GetParentEntity()); default: - std::cout << "No collision detection for: " << (int)m_ShapeType << "-to-" << (int)other->GetShapeType() << " collision!" << std::endl; + LOG("No collision detection for: %i-to-%i collision!", static_cast(m_ShapeType), static_cast(other->GetShapeType())); } return false; diff --git a/dPhysics/main.cpp b/dPhysics/main.cpp deleted file mode 100644 index 7de1555a7..000000000 --- a/dPhysics/main.cpp +++ /dev/null @@ -1,35 +0,0 @@ -//This file included for reference only - -/*#include -#include -#include -#include "dpWorld.h" - -#include "NiQuaternion.hpp" -#include "NiPoint3.hpp" - -int main() { - std::cout << "dPhysics test engine" << std::endl; - - //Test rotation code: - NiPoint3 p(1.0f, 0.0f, 0.0f); - - float angle = 45.0f; - NiQuaternion q = NiQuaternion::CreateFromAxisAngle(NiPoint3(0.0f, 0.0f, 1.0f), angle); - - NiPoint3 rotated = p.RotateByQuaternion(q); - - std::cout << "OG: " << p.x << ", " << p.y << ", " << p.z << std::endl; - std::cout << "Quater: " << q.x << ", " << q.y << ", " << q.z << ", " << q.w << " angle: " << angle << std::endl; - std::cout << "Rotated: " << rotated.x << ", " << rotated.y << ", " << rotated.z << std::endl; - - //Test some collisions: - dpWorld::GetInstance().Initialize(1000); - - while (true) { - dpWorld::GetInstance().StepWorld(1.0f/60.0f); - std::this_thread::sleep_for(std::chrono::milliseconds(16)); - } - - return 0; -}*/ diff --git a/dZoneManager/Level.cpp b/dZoneManager/Level.cpp index 8202b7605..4c6ff93c6 100644 --- a/dZoneManager/Level.cpp +++ b/dZoneManager/Level.cpp @@ -16,6 +16,12 @@ #include "AssetManager.h" #include "dConfig.h" +void Level::SceneObjectDataChunk::PrintAllObjects() const { + for (const auto& [id, sceneObj] : objects) { + LOG("ID: %d LOT: %d", id, sceneObj.lot); + } +} + Level::Level(Zone* parentZone, const std::string& filepath) { m_ParentZone = parentZone; @@ -33,9 +39,9 @@ Level::Level(Zone* parentZone, const std::string& filepath) { } Level::~Level() { - for (std::map::iterator it = m_ChunkHeaders.begin(); it != m_ChunkHeaders.end(); ++it) { - if (it->second.id == Level::ChunkTypeID::FileInfo) delete it->second.fileInfo; - if (it->second.id == Level::ChunkTypeID::SceneObjectData) delete it->second.sceneObjects; + for (auto& [id, header] : m_ChunkHeaders) { + if (header.id == Level::ChunkTypeID::FileInfo) delete header.fileInfo; + if (header.id == Level::ChunkTypeID::SceneObjectData) delete header.sceneObjects; } } @@ -248,8 +254,8 @@ void Level::ReadSceneObjectDataChunk(std::istream& file, Header& header) { BinaryIO::BinaryRead(file, obj.id); BinaryIO::BinaryRead(file, obj.lot); - /*if (header.fileInfo->version >= 0x26)*/ BinaryIO::BinaryRead(file, obj.value1); - /*if (header.fileInfo->version >= 0x20)*/ BinaryIO::BinaryRead(file, obj.value2); + /*if (header.fileInfo->version >= 0x26)*/ BinaryIO::BinaryRead(file, obj.nodeType); + /*if (header.fileInfo->version >= 0x20)*/ BinaryIO::BinaryRead(file, obj.glomId); BinaryIO::BinaryRead(file, obj.position); BinaryIO::BinaryRead(file, obj.rotation); diff --git a/dZoneManager/Level.h b/dZoneManager/Level.h index a27326a21..6f56e0731 100644 --- a/dZoneManager/Level.h +++ b/dZoneManager/Level.h @@ -30,11 +30,7 @@ class Level { struct SceneObjectDataChunk { std::map objects; - const void PrintAllObjects() { - for (std::map::iterator it = objects.begin(); it != objects.end(); ++it) { - std::cout << "\t ID: " << it->first << " LOT: " << it->second.lot << std::endl; - } - } + void PrintAllObjects() const; uint32_t GetObjectCount() { return objects.size(); } }; diff --git a/dZoneManager/Zone.h b/dZoneManager/Zone.h index af9f6b554..ff9311545 100644 --- a/dZoneManager/Zone.h +++ b/dZoneManager/Zone.h @@ -148,6 +148,8 @@ struct PropertyPath { float repMultiplier; PropertyRentalPeriod rentalPeriod; PropertyAchievmentRequired achievementRequired; + + // Player respawn coordinates in the main zone (not the property zone) NiPoint3 playerZoneCoords; float maxBuildHeight; }; diff --git a/dZoneManager/dZMCommon.h b/dZoneManager/dZMCommon.h index 635faaaee..738fc283e 100644 --- a/dZoneManager/dZMCommon.h +++ b/dZoneManager/dZMCommon.h @@ -12,8 +12,8 @@ struct mapCompareLwoSceneIDs { struct SceneObject { LWOOBJID id; LOT lot; - uint32_t value1; - uint32_t value2; + uint32_t nodeType; + uint32_t glomId; NiPoint3 position; NiQuaternion rotation; float scale = 1.0f; diff --git a/tests/dCommonTests/Amf3Tests.cpp b/tests/dCommonTests/Amf3Tests.cpp index a51fe4ba4..5b52cf277 100644 --- a/tests/dCommonTests/Amf3Tests.cpp +++ b/tests/dCommonTests/Amf3Tests.cpp @@ -71,25 +71,15 @@ TEST(dCommonTests, AMF3InsertionAssociativeTest) { array.Insert>("Undefined", {}); array.Insert("Null", nullptr); - std::cout << "test" << std::endl; ASSERT_EQ(array.Get("CString")->GetValueType(), eAmf::String); - std::cout << "test" << std::endl; ASSERT_EQ(array.Get("String")->GetValueType(), eAmf::String); - std::cout << "test" << std::endl; ASSERT_EQ(array.Get("False")->GetValueType(), eAmf::False); - std::cout << "test" << std::endl; ASSERT_EQ(array.Get("True")->GetValueType(), eAmf::True); - std::cout << "test" << std::endl; ASSERT_EQ(array.Get("Integer")->GetValueType(), eAmf::Integer); - std::cout << "test" << std::endl; ASSERT_EQ(array.Get("Double")->GetValueType(), eAmf::Double); - std::cout << "test" << std::endl; ASSERT_EQ(array.GetArray("Array")->GetValueType(), eAmf::Array); - std::cout << "test" << std::endl; ASSERT_EQ(array.Get("Null")->GetValueType(), eAmf::Null); - std::cout << "test" << std::endl; ASSERT_EQ(array.Get>("Undefined")->GetValueType(), eAmf::Undefined); - std::cout << "test" << std::endl; } TEST(dCommonTests, AMF3InsertionDenseTest) { From 9c5388c70eb2fb86f752ddc8ad9951f3ba5794e6 Mon Sep 17 00:00:00 2001 From: David Markowitz <39972741+EmosewaMC@users.noreply.github.com> Date: Tue, 21 Nov 2023 18:04:44 -0800 Subject: [PATCH 2/3] feat: Add ability to toggle skipping of cinematics (#1312) * Cinematics: Add ability to toggle skipping them * Add docs * Move sections --- dCommon/dEnums/ePlayerFlag.h | 3 ++- dGame/dGameMessages/GameMessageHandler.cpp | 9 +++++++++ dGame/dGameMessages/GameMessages.cpp | 9 +++++++++ dGame/dUtilities/SlashCommandHandler.cpp | 16 ++++++++++++++++ docs/Commands.md | 3 ++- resources/worldconfig.ini | 4 ++++ 6 files changed, 42 insertions(+), 2 deletions(-) diff --git a/dCommon/dEnums/ePlayerFlag.h b/dCommon/dEnums/ePlayerFlag.h index fefdfb67c..01adcb0ea 100644 --- a/dCommon/dEnums/ePlayerFlag.h +++ b/dCommon/dEnums/ePlayerFlag.h @@ -166,7 +166,8 @@ enum ePlayerFlag : int32_t { NJ_LIGHTNING_SPINJITZU = 2031, NJ_ICE_SPINJITZU = 2032, NJ_FIRE_SPINJITZU = 2033, - NJ_WU_SHOW_DAILY_CHEST = 2099 + NJ_WU_SHOW_DAILY_CHEST = 2099, + DLU_SKIP_CINEMATICS = 1'000'000, }; #endif //!__EPLAYERFLAG__H__ diff --git a/dGame/dGameMessages/GameMessageHandler.cpp b/dGame/dGameMessages/GameMessageHandler.cpp index 40293fbec..551512429 100644 --- a/dGame/dGameMessages/GameMessageHandler.cpp +++ b/dGame/dGameMessages/GameMessageHandler.cpp @@ -34,6 +34,8 @@ #include "eMissionTaskType.h" #include "eReplicaComponentType.h" #include "eConnectionType.h" +#include "ePlayerFlag.h" +#include "dConfig.h" using namespace std; @@ -173,6 +175,13 @@ void GameMessageHandler::HandleMessage(RakNet::BitStream* inStream, const System GameMessages::SendPlayerReady(entity, sysAddr); GameMessages::SendPlayerReady(Game::zoneManager->GetZoneControlObject(), sysAddr); + if (Game::config->GetValue("allow_players_to_skip_cinematics") != "1" + || !entity->GetCharacter() + || !entity->GetCharacter()->GetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS)) return; + entity->AddCallbackTimer(0.5f, [entity, sysAddr]() { + if (!entity) return; + GameMessages::SendEndCinematic(entity->GetObjectID(), u"", sysAddr); + }); break; } diff --git a/dGame/dGameMessages/GameMessages.cpp b/dGame/dGameMessages/GameMessages.cpp index 7fb9c259f..4cddebffa 100644 --- a/dGame/dGameMessages/GameMessages.cpp +++ b/dGame/dGameMessages/GameMessages.cpp @@ -43,6 +43,7 @@ #include "eControlScheme.h" #include "eStateChangeType.h" #include "eConnectionType.h" +#include "ePlayerFlag.h" #include #include @@ -5161,6 +5162,14 @@ void GameMessages::HandleMissionDialogOK(RakNet::BitStream* inStream, Entity* en } else if (iMissionState == eMissionState::READY_TO_COMPLETE || iMissionState == eMissionState::COMPLETE_READY_TO_COMPLETE) { missionComponent->CompleteMission(missionID); } + + if (Game::config->GetValue("allow_players_to_skip_cinematics") != "1" + || !player->GetCharacter() + || !player->GetCharacter()->GetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS)) return; + player->AddCallbackTimer(0.5f, [player]() { + if (!player) return; + GameMessages::SendEndCinematic(player->GetObjectID(), u"", player->GetSystemAddress()); + }); } void GameMessages::HandleRequestLinkedMission(RakNet::BitStream* inStream, Entity* entity) { diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index f4a21cabf..8444bba7e 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -85,6 +85,7 @@ #include "CDObjectsTable.h" #include "CDZoneTableTable.h" +#include "ePlayerFlag.h" void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entity* entity, const SystemAddress& sysAddr) { auto commandCopy = command; @@ -171,6 +172,21 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit return; } + if (chatCommand == "toggleskipcinematics" && (Game::config->GetValue("allow_players_to_skip_cinematics") == "1" || entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER)) { + auto* character = entity->GetCharacter(); + if (!character) return; + bool current = character->GetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS); + character->SetPlayerFlag(ePlayerFlag::DLU_SKIP_CINEMATICS, !current); + if (!current) { + ChatPackets::SendSystemMessage(sysAddr, u"You have elected to skip cinematics. Note that not all cinematics can be skipped, but most will be skipped now."); + } else { + ChatPackets::SendSystemMessage(sysAddr, u"Cinematics will no longer be skipped."); + } + + return; + } + + //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! //HANDLE ALL NON GM SLASH COMMANDS RIGHT HERE! //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! diff --git a/docs/Commands.md b/docs/Commands.md index 71a3da432..162020563 100644 --- a/docs/Commands.md +++ b/docs/Commands.md @@ -14,6 +14,8 @@ |resurrect|`/resurrect`|Resurrects the player.|| |requestmailcount|`/requestmailcount`|Sends notification with number of unread messages in the player's mailbox.|| |who|`/who`|Displays in chat all players on the instance.|| +|togglenameplate|`/togglenameplate`|Turns the nameplate above your head that is visible to other players off and on.|8 or if `allow_nameplate_off` is set to exactly `1` in the settings| +|toggleskipcinematics|`/toggleskipcinematics`|Skips mission and world load related cinematics.|8 or if `allow_players_to_skip_cinematics` is set to exactly `1` in the settings then 0| ## Moderation Commands @@ -49,7 +51,6 @@ These commands are primarily for development and testing. The usage of many of t |Command|Usage|Description|Admin Level Requirement| |--- |--- |--- |--- | -|togglenameplate|`/togglenameplate`|Turns the nameplate above your head that is visible to other players off and on.|8 or if `allow_nameplate_off` is set to exactly `1` in the settings| |fix-stats|`/fix-stats`|Resets skills, buffs, and destroyables.|| |join|`/join `|Joins a private zone with given password.|| |leave-zone|`/leave-zone`|If you are in an instanced zone, transfers you to the closest main world. For example, if you are in an instance of Avant Gardens Survival or the Spider Queen Battle, you are sent to Avant Gardens. If you are in the Battle of Nimbus Station, you are sent to Nimbus Station.|| diff --git a/resources/worldconfig.ini b/resources/worldconfig.ini index 0b15973da..7f296afe6 100644 --- a/resources/worldconfig.ini +++ b/resources/worldconfig.ini @@ -61,6 +61,7 @@ allow_nameplate_off=0 # Turn logging of IP addresses for anti-cheat reporting on (1) or off(0) log_ip_addresses_for_anti_cheat=1 +# These are the 5 items items that are shown in the "Help" menu in-game along with their coresponding descriptions below. help_0_summary=Got an issue? help_1_summary=Stuck loading? help_2_summary=Missing features? @@ -72,3 +73,6 @@ help_1_description=Try switching networks, using a VPN, or using your phone's ho help_2_description=While DarkflameServer is a mostly complete emulator, there are still some features that aren't implemented. You can track these on the GitHub issues page.

help_3_description=Skill issue!

help_4_description=Visit Discussions on the DarkflameServer GitHub page
to ask questions and collaborate with other devs!

+ +# Toggleable quality of life feature to allow users to skip most cinematics. +allow_players_to_skip_cinematics=0 From df83f0d847ac8b15a046612a622a008515754a93 Mon Sep 17 00:00:00 2001 From: Aaron Kimbrell Date: Tue, 21 Nov 2023 20:05:15 -0600 Subject: [PATCH 3/3] feat: Reward codes (#1308) * feat: reward codes this is for giving rewards across characters as the did in live. Tested that the default config works Tested that all claim codes work Tested that saving and loading claim codes work Tested that mail sends correctly * newlines * include array * delete cascade * newline * address feedback --- dAuthServer/AuthServer.cpp | 2 + .../CDClientDatabase/CDClientManager.cpp | 2 + .../CDClientTables/CDRewardCodesTable.cpp | 47 ++++++++++++++++ .../CDClientTables/CDRewardCodesTable.h | 25 +++++++++ .../CDClientDatabase/CDClientTables/CDTable.h | 1 + .../CDClientTables/CMakeLists.txt | 1 + dDatabase/GameDatabase/GameDatabase.h | 3 +- .../ITables/IAccountsRewardCodes.h | 13 +++++ dDatabase/GameDatabase/MySQL/MySQLDatabase.h | 2 + .../MySQL/Tables/AccountsRewardCodes.cpp | 17 ++++++ .../GameDatabase/MySQL/Tables/CMakeLists.txt | 1 + dGame/dComponents/CharacterComponent.cpp | 56 +++++++++++++++++-- dGame/dComponents/CharacterComponent.h | 5 ++ dGame/dUtilities/SlashCommandHandler.cpp | 8 +++ dNet/AuthPackets.cpp | 20 +++++++ dNet/AuthPackets.h | 2 + dWorldServer/WorldServer.cpp | 5 +- docs/Commands.md | 1 + migrations/dlu/14_reward_codes.sql | 5 ++ resources/authconfig.ini | 8 +++ 20 files changed, 216 insertions(+), 8 deletions(-) create mode 100644 dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.cpp create mode 100644 dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.h create mode 100644 dDatabase/GameDatabase/ITables/IAccountsRewardCodes.h create mode 100644 dDatabase/GameDatabase/MySQL/Tables/AccountsRewardCodes.cpp create mode 100644 migrations/dlu/14_reward_codes.sql diff --git a/dAuthServer/AuthServer.cpp b/dAuthServer/AuthServer.cpp index c6d434b35..7cc28263e 100644 --- a/dAuthServer/AuthServer.cpp +++ b/dAuthServer/AuthServer.cpp @@ -94,6 +94,8 @@ int main(int argc, char** argv) { uint32_t framesSinceMasterDisconnect = 0; uint32_t framesSinceLastSQLPing = 0; + AuthPackets::LoadClaimCodes(); + while (!Game::shouldShutdown) { //Check if we're still connected to master: if (!Game::server->GetIsConnectedToMaster()) { diff --git a/dDatabase/CDClientDatabase/CDClientManager.cpp b/dDatabase/CDClientDatabase/CDClientManager.cpp index 465fd4e6f..7c2f39537 100644 --- a/dDatabase/CDClientDatabase/CDClientManager.cpp +++ b/dDatabase/CDClientDatabase/CDClientManager.cpp @@ -37,6 +37,7 @@ #include "CDPropertyTemplateTable.h" #include "CDFeatureGatingTable.h" #include "CDRailActivatorComponent.h" +#include "CDRewardCodesTable.h" // Uncomment this to cache the full cdclient database into memory. This will make the server load faster, but will use more memory. // A vanilla CDClient takes about 46MB of memory + the regular world data. @@ -82,6 +83,7 @@ CDClientManager::CDClientManager() { CDRailActivatorComponentTable::Instance().LoadValuesFromDatabase(); CDRarityTableTable::Instance().LoadValuesFromDatabase(); CDRebuildComponentTable::Instance().LoadValuesFromDatabase(); + CDRewardCodesTable::Instance().LoadValuesFromDatabase(); CDRewardsTable::Instance().LoadValuesFromDatabase(); CDScriptComponentTable::Instance().LoadValuesFromDatabase(); CDSkillBehaviorTable::Instance().LoadValuesFromDatabase(); diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.cpp b/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.cpp new file mode 100644 index 000000000..2bda73f41 --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.cpp @@ -0,0 +1,47 @@ +#include "CDRewardCodesTable.h" + +void CDRewardCodesTable::LoadValuesFromDatabase() { + + // First, get the size of the table + unsigned int size = 0; + auto tableSize = CDClientDatabase::ExecuteQuery("SELECT COUNT(*) FROM RewardCodes"); + while (!tableSize.eof()) { + size = tableSize.getIntField(0, 0); + + tableSize.nextRow(); + } + + tableSize.finalize(); + + // Reserve the size + this->entries.reserve(size); + + // Now get the data + auto tableData = CDClientDatabase::ExecuteQuery("SELECT * FROM RewardCodes"); + while (!tableData.eof()) { + CDRewardCode entry; + entry.id = tableData.getIntField("id", -1); + entry.code = tableData.getStringField("code", ""); + entry.attachmentLOT = tableData.getIntField("attachmentLOT", -1); + UNUSED_COLUMN(entry.locStatus = tableData.getIntField("locStatus", -1)); + UNUSED_COLUMN(entry.gate_version = tableData.getStringField("gate_version", "")); + + this->entries.push_back(entry); + tableData.nextRow(); + } +} + +LOT CDRewardCodesTable::GetAttachmentLOT(uint32_t rewardCodeId) const { + for (auto const &entry : this->entries){ + if (rewardCodeId == entry.id) return entry.attachmentLOT; + } + return LOT_NULL; +} + +uint32_t CDRewardCodesTable::GetCodeID(std::string code) const { + for (auto const &entry : this->entries){ + if (code == entry.code) return entry.id; + } + return -1; +} + diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.h new file mode 100644 index 000000000..1010a572e --- /dev/null +++ b/dDatabase/CDClientDatabase/CDClientTables/CDRewardCodesTable.h @@ -0,0 +1,25 @@ +#pragma once + +// Custom Classes +#include "CDTable.h" + + +struct CDRewardCode { + uint32_t id; + std::string code; + LOT attachmentLOT; + UNUSED(uint32_t locStatus); + UNUSED(std::string gate_version); +}; + + +class CDRewardCodesTable : public CDTable { +private: + std::vector entries; + +public: + void LoadValuesFromDatabase(); + const std::vector& GetEntries() const; + LOT GetAttachmentLOT(uint32_t rewardCodeId) const; + uint32_t GetCodeID(std::string code) const; +}; diff --git a/dDatabase/CDClientDatabase/CDClientTables/CDTable.h b/dDatabase/CDClientDatabase/CDClientTables/CDTable.h index e4c11fb9a..0a8f29adb 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CDTable.h +++ b/dDatabase/CDClientDatabase/CDClientTables/CDTable.h @@ -8,6 +8,7 @@ #include #include #include +#include // CPPLinq #ifdef _WIN32 diff --git a/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt b/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt index 43ff52b21..b2551efa3 100644 --- a/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt +++ b/dDatabase/CDClientDatabase/CDClientTables/CMakeLists.txt @@ -31,6 +31,7 @@ set(DDATABASE_CDCLIENTDATABASE_CDCLIENTTABLES_SOURCES "CDActivitiesTable.cpp" "CDRailActivatorComponent.cpp" "CDRarityTableTable.cpp" "CDRebuildComponentTable.cpp" + "CDRewardCodesTable.cpp" "CDRewardsTable.cpp" "CDScriptComponentTable.cpp" "CDSkillBehaviorTable.cpp" diff --git a/dDatabase/GameDatabase/GameDatabase.h b/dDatabase/GameDatabase/GameDatabase.h index 7d8c7de94..d874a4885 100644 --- a/dDatabase/GameDatabase/GameDatabase.h +++ b/dDatabase/GameDatabase/GameDatabase.h @@ -21,6 +21,7 @@ #include "ICharInfo.h" #include "IAccounts.h" #include "IActivityLog.h" +#include "IAccountsRewardCodes.h" namespace sql { class Statement; @@ -38,7 +39,7 @@ class GameDatabase : public IMail, public ICommandLog, public IPlayerCheatDetections, public IBugReports, public IPropertyContents, public IProperty, public IPetNames, public ICharXml, public IMigrationHistory, public IUgc, public IFriends, public ICharInfo, - public IAccounts, public IActivityLog { + public IAccounts, public IActivityLog, public IAccountsRewardCodes { public: virtual ~GameDatabase() = default; // TODO: These should be made private. diff --git a/dDatabase/GameDatabase/ITables/IAccountsRewardCodes.h b/dDatabase/GameDatabase/ITables/IAccountsRewardCodes.h new file mode 100644 index 000000000..2fcb9d2a8 --- /dev/null +++ b/dDatabase/GameDatabase/ITables/IAccountsRewardCodes.h @@ -0,0 +1,13 @@ +#ifndef __IACCOUNTSREWARDCODES__H__ +#define __IACCOUNTSREWARDCODES__H__ + +#include +#include + +class IAccountsRewardCodes { +public: + virtual void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) = 0; + virtual std::vector GetRewardCodesByAccountID(const uint32_t account_id) = 0; +}; + +#endif //!__IACCOUNTSREWARDCODES__H__ diff --git a/dDatabase/GameDatabase/MySQL/MySQLDatabase.h b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h index bed79bb78..7b4e21e49 100644 --- a/dDatabase/GameDatabase/MySQL/MySQLDatabase.h +++ b/dDatabase/GameDatabase/MySQL/MySQLDatabase.h @@ -103,6 +103,8 @@ class MySQLDatabase : public GameDatabase { std::optional GetDonationTotal(const uint32_t activityId) override; std::optional IsPlaykeyActive(const int32_t playkeyId) override; std::vector GetUgcModels(const LWOOBJID& propertyId) override; + void InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) override; + std::vector GetRewardCodesByAccountID(const uint32_t account_id) override; private: // Generic query functions that can be used for any query. diff --git a/dDatabase/GameDatabase/MySQL/Tables/AccountsRewardCodes.cpp b/dDatabase/GameDatabase/MySQL/Tables/AccountsRewardCodes.cpp new file mode 100644 index 000000000..7243ce3cc --- /dev/null +++ b/dDatabase/GameDatabase/MySQL/Tables/AccountsRewardCodes.cpp @@ -0,0 +1,17 @@ +#include "MySQLDatabase.h" + +void MySQLDatabase::InsertRewardCode(const uint32_t account_id, const uint32_t reward_code) { + ExecuteInsert("INSERT IGNORE INTO accounts_rewardcodes (account_id, rewardcode) VALUES (?, ?);", account_id, reward_code); +} + +std::vector MySQLDatabase::GetRewardCodesByAccountID(const uint32_t account_id) { + auto result = ExecuteSelect("SELECT rewardcode FROM accounts_rewardcodes WHERE account_id = ?;", account_id); + + std::vector toReturn; + toReturn.reserve(result->rowsCount()); + while (result->next()) { + toReturn.push_back(result->getUInt("rewardcode")); + } + + return toReturn; +} diff --git a/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt b/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt index e9593ba98..c45510b9b 100644 --- a/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt +++ b/dDatabase/GameDatabase/MySQL/Tables/CMakeLists.txt @@ -1,5 +1,6 @@ set(DDATABASES_DATABASES_MYSQL_TABLES_SOURCES "Accounts.cpp" + "AccountsRewardCodes.cpp" "ActivityLog.cpp" "BugReports.cpp" "CharInfo.cpp" diff --git a/dGame/dComponents/CharacterComponent.cpp b/dGame/dComponents/CharacterComponent.cpp index 8c59b28e9..d1b7ee21b 100644 --- a/dGame/dComponents/CharacterComponent.cpp +++ b/dGame/dComponents/CharacterComponent.cpp @@ -16,6 +16,10 @@ #include "Amf3.h" #include "eGameMasterLevel.h" #include "eGameActivity.h" +#include "User.h" +#include "Database.h" +#include "CDRewardCodesTable.h" +#include "Mail.h" #include CharacterComponent::CharacterComponent(Entity* parent, Character* character) : Component(parent) { @@ -74,10 +78,14 @@ CharacterComponent::~CharacterComponent() { void CharacterComponent::Serialize(RakNet::BitStream* outBitStream, bool bIsInitialUpdate) { if (bIsInitialUpdate) { - outBitStream->Write0(); - outBitStream->Write0(); - outBitStream->Write0(); - outBitStream->Write0(); + outBitStream->Write(m_ClaimCodes[0] != 0); + if (m_ClaimCodes[0] != 0) outBitStream->Write(m_ClaimCodes[0]); + outBitStream->Write(m_ClaimCodes[1] != 0); + if (m_ClaimCodes[1] != 0) outBitStream->Write(m_ClaimCodes[1]); + outBitStream->Write(m_ClaimCodes[2] != 0); + if (m_ClaimCodes[2] != 0) outBitStream->Write(m_ClaimCodes[2]); + outBitStream->Write(m_ClaimCodes[3] != 0); + if (m_ClaimCodes[3] != 0) outBitStream->Write(m_ClaimCodes[3]); outBitStream->Write(m_Character->GetHairColor()); outBitStream->Write(m_Character->GetHairStyle()); @@ -186,6 +194,13 @@ void CharacterComponent::LoadFromXml(tinyxml2::XMLDocument* doc) { SetReputation(0); } + character->QueryUnsigned64Attribute("co", &m_ClaimCodes[0]); + character->QueryUnsigned64Attribute("co1", &m_ClaimCodes[1]); + character->QueryUnsigned64Attribute("co2", &m_ClaimCodes[2]); + character->QueryUnsigned64Attribute("co3", &m_ClaimCodes[3]); + + AwardClaimCodes(); + character->QueryInt64Attribute("ls", &m_Uscore); // Load the statistics @@ -308,6 +323,11 @@ void CharacterComponent::UpdateXml(tinyxml2::XMLDocument* doc) { return; } + if (m_ClaimCodes[0] != 0) character->SetAttribute("co", m_ClaimCodes[0]); + if (m_ClaimCodes[1] != 0) character->SetAttribute("co1", m_ClaimCodes[1]); + if (m_ClaimCodes[2] != 0) character->SetAttribute("co2", m_ClaimCodes[2]); + if (m_ClaimCodes[3] != 0) character->SetAttribute("co3", m_ClaimCodes[3]); + character->SetAttribute("ls", m_Uscore); // Custom attribute to keep track of reputation. character->SetAttribute("rpt", GetReputation()); @@ -738,3 +758,31 @@ void CharacterComponent::UpdateClientMinimap(bool showFaction, std::string ventu arrayToSend.Insert(ventureVisionType, showFaction); GameMessages::SendUIMessageServerToSingleClient(m_Parent, m_Parent ? m_Parent->GetSystemAddress() : UNASSIGNED_SYSTEM_ADDRESS, "SetFactionVisibility", arrayToSend); } + +void CharacterComponent::AwardClaimCodes() { + if (!m_Parent) return; + auto* user = m_Parent->GetParentUser(); + if (!user) return; + + auto rewardCodes = Database::Get()->GetRewardCodesByAccountID(user->GetAccountID()); + if (rewardCodes.empty()) return; + + auto* cdrewardCodes = CDClientManager::Instance().GetTable(); + for (auto const rewardCode: rewardCodes){ + LOG_DEBUG("Processing RewardCode %i", rewardCode); + const uint32_t rewardCodeIndex = rewardCode >> 6; + const uint32_t bitIndex = rewardCode % 64; + if (GeneralUtils::CheckBit(m_ClaimCodes[rewardCodeIndex], bitIndex)) continue; + m_ClaimCodes[rewardCodeIndex] = GeneralUtils::SetBit(m_ClaimCodes[rewardCodeIndex], bitIndex); + + // Don't send it on this one since it's default and the mail doesn't make sense + if (rewardCode == 30) continue; + + auto attachmentLOT = cdrewardCodes->GetAttachmentLOT(rewardCode); + std::ostringstream subject; + subject << "%[RewardCodes_" << rewardCode << "_subjectText]"; + std::ostringstream body; + body << "%[RewardCodes_" << rewardCode << "_bodyText]"; + Mail::SendMail(LWOOBJID_EMPTY, "%[MAIL_SYSTEM_NOTIFICATION]", m_Parent, subject.str(), body.str(), attachmentLOT, 1); + } +} diff --git a/dGame/dComponents/CharacterComponent.h b/dGame/dComponents/CharacterComponent.h index 5bafb3dfc..4222ef4ac 100644 --- a/dGame/dComponents/CharacterComponent.h +++ b/dGame/dComponents/CharacterComponent.h @@ -10,6 +10,7 @@ #include "CDMissionsTable.h" #include "tinyxml2.h" #include "eReplicaComponentType.h" +#include enum class eGameActivity : uint32_t; @@ -566,6 +567,10 @@ class CharacterComponent : public Component { LWOOBJID m_LastRocketItemID = LWOOBJID_EMPTY; LWOOBJID m_CurrentInteracting = LWOOBJID_EMPTY; + + std::array m_ClaimCodes{}; + + void AwardClaimCodes(); }; #endif // CHARACTERCOMPONENT_H diff --git a/dGame/dUtilities/SlashCommandHandler.cpp b/dGame/dUtilities/SlashCommandHandler.cpp index 8444bba7e..13fc3ded1 100644 --- a/dGame/dUtilities/SlashCommandHandler.cpp +++ b/dGame/dUtilities/SlashCommandHandler.cpp @@ -83,6 +83,7 @@ #include "eChatInternalMessageType.h" #include "eMasterMessageType.h" +#include "CDRewardCodesTable.h" #include "CDObjectsTable.h" #include "CDZoneTableTable.h" #include "ePlayerFlag.h" @@ -1911,6 +1912,13 @@ void SlashCommandHandler::HandleChatCommand(const std::u16string& command, Entit } } + if (chatCommand == "setrewardcode" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() == 1) { + auto* cdrewardCodes = CDClientManager::Instance().GetTable(); + + auto id = cdrewardCodes->GetCodeID(args[0]); + if (id != -1) Database::Get()->InsertRewardCode(user->GetAccountID(), id); + } + if (chatCommand == "inspect" && entity->GetGMLevel() >= eGameMasterLevel::DEVELOPER && args.size() >= 1) { Entity* closest = nullptr; diff --git a/dNet/AuthPackets.cpp b/dNet/AuthPackets.cpp index f0b22b4eb..6ee38028e 100644 --- a/dNet/AuthPackets.cpp +++ b/dNet/AuthPackets.cpp @@ -29,6 +29,22 @@ #include "eMasterMessageType.h" #include "eGameMasterLevel.h" +namespace { + std::vector claimCodes; +} + +void AuthPackets::LoadClaimCodes() { + if(!claimCodes.empty()) return; + auto rcstring = Game::config->GetValue("rewardcodes"); + auto codestrings = GeneralUtils::SplitString(rcstring, ','); + for(auto const &codestring: codestrings){ + uint32_t code = -1; + if(GeneralUtils::TryParse(codestring, code) && code != -1){ + claimCodes.push_back(code); + } + } +} + void AuthPackets::HandleHandshake(dServer* server, Packet* packet) { RakNet::BitStream inStream(packet->data, packet->length, false); uint64_t header = inStream.Read(header); @@ -129,6 +145,10 @@ void AuthPackets::HandleLoginRequest(dServer* server, Packet* packet) { AuthPackets::SendLoginResponse(server, system, eLoginResponse::SUCCESS, "", zoneIP, zonePort, username); }); } + + for(auto const code: claimCodes){ + Database::Get()->InsertRewardCode(accountInfo->id, code); + } } void AuthPackets::SendLoginResponse(dServer* server, const SystemAddress& sysAddr, eLoginResponse responseCode, const std::string& errorMsg, const std::string& wServerIP, uint16_t wServerPort, std::string username) { diff --git a/dNet/AuthPackets.h b/dNet/AuthPackets.h index 0f004ca4b..eb275a463 100644 --- a/dNet/AuthPackets.h +++ b/dNet/AuthPackets.h @@ -15,6 +15,8 @@ namespace AuthPackets { void HandleLoginRequest(dServer* server, Packet* packet); void SendLoginResponse(dServer* server, const SystemAddress& sysAddr, eLoginResponse responseCode, const std::string& errorMsg, const std::string& wServerIP, uint16_t wServerPort, std::string username); + void LoadClaimCodes(); + } #endif // AUTHPACKETS_H diff --git a/dWorldServer/WorldServer.cpp b/dWorldServer/WorldServer.cpp index cfd7d1574..f916e40c7 100644 --- a/dWorldServer/WorldServer.cpp +++ b/dWorldServer/WorldServer.cpp @@ -1033,9 +1033,8 @@ void HandlePacket(Packet* packet) { Game::entityManager->ConstructAllEntities(packet->systemAddress); auto* characterComponent = player->GetComponent(); - if (characterComponent) { - player->GetComponent()->RocketUnEquip(player); - } + if (!characterComponent) return; + characterComponent->RocketUnEquip(player); // Do charxml fixes here auto* levelComponent = player->GetComponent(); diff --git a/docs/Commands.md b/docs/Commands.md index 162020563..0ba7d86ea 100644 --- a/docs/Commands.md +++ b/docs/Commands.md @@ -113,6 +113,7 @@ These commands are primarily for development and testing. The usage of many of t |setfaction|`/setfaction `|Clears the users current factions and sets it|8| |addfaction|`/addfaction `|Add the faction to the users list of factions|8| |getfactions|`/getfactions`|Shows the player's factions|8| +|setrewardcode|`/setrewardcode `|Sets the rewardcode for the account you are logged into if it's a valid rewardcode, See cdclient table `RewardCodes`|8| ## Detailed `/inspect` Usage `/inspect (-m | -a | -s | -p | -f (faction) | -t)` diff --git a/migrations/dlu/14_reward_codes.sql b/migrations/dlu/14_reward_codes.sql new file mode 100644 index 000000000..a439df2e3 --- /dev/null +++ b/migrations/dlu/14_reward_codes.sql @@ -0,0 +1,5 @@ +CREATE TABLE IF NOT EXISTS accounts_rewardcodes ( + account_id INT NOT NULL REFERENCES accounts(id) ON DELETE CASCADE, + rewardcode INT NOT NULL, + PRIMARY KEY (account_id, rewardcode) +); diff --git a/resources/authconfig.ini b/resources/authconfig.ini index ec414bc0f..62a5c6de3 100644 --- a/resources/authconfig.ini +++ b/resources/authconfig.ini @@ -4,3 +4,11 @@ port=1001 # 0 or 1, should ignore playkeys # If 1 everyone with an account will be able to login, regardless of if they have a key or not dont_use_keys=0 + +# list of rewardcodes to set on the accounts by default +# ex: 30,1,0,3 +# See RewardCodes in the CDclient for what codes exist +# Default 4,30 +# 4 allows LEGOClub access +# 30 makes the client not consume bricks when in bbb mode +rewardcodes=4,30