workflow + commands

This commit is contained in:
luis
2026-08-06 07:32:03 -03:00
parent 57f920c969
commit 0181c4d1d9
3 changed files with 177 additions and 77 deletions
+11 -2
View File
@@ -86,13 +86,22 @@ jobs:
Copy-Item "${{ env.OPENSSL_ROOT_DIR }}/bin/libcrypto-3-x64.dll" . -ErrorAction SilentlyContinue
Copy-Item "${{ env.OPENSSL_ROOT_DIR }}/bin/libssl-3-x64.dll" . -ErrorAction SilentlyContinue
Copy-Item "${{ env.OPENSSL_ROOT_DIR }}/bin/legacy.dll" . -ErrorAction SilentlyContinue
foreach ($dll in @("libmysql.dll", "libssl-3-x64.dll", "libcrypto-3-x64.dll"))
{
if (-not (Test-Path $dll)) { Write-Error "Missing required DLL: $dll"; exit 1 }
}
Get-ChildItem *.dll | ForEach-Object { Write-Output " $($_.Name) ($($_.Length) bytes)" }
- name: Check binaries
shell: pwsh
run: |
cd build/bin/${{ env.CMAKE_BUILD_TYPE }}
./bnetserver --version
./worldserver --version
& .\bnetserver.exe --version
Write-Output "bnetserver --version exit code: $LASTEXITCODE"
if ($LASTEXITCODE -ne 0) { Write-Error "bnetserver --version failed"; exit $LASTEXITCODE }
& .\worldserver.exe --version
Write-Output "worldserver --version exit code: $LASTEXITCODE"
if ($LASTEXITCODE -ne 0) { Write-Error "worldserver --version failed"; exit $LASTEXITCODE }
- name: Create zip
shell: pwsh
+1 -1
View File
@@ -5,7 +5,7 @@ WowCommunity Project is a World of Warcraft blizzlike design based on TrinityCor
# CI Status:
[![Build status](https://github.com/Thordekk/WowCommunityProject/actions/workflows/build.yml/badge.svg)](https://github.com/Thordekk/WowCommunityProject/actions/workflows/build.yml)
[![Build status](https://github.com/Thordekk/WowCommunityProject/actions/workflows/build.yml/badge.svg?branch=main)](https://github.com/Thordekk/WowCommunityProject/actions/workflows/build.yml)
# Downloads:
+163 -72
View File
@@ -15,36 +15,68 @@
* with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* ScriptData
Name: garrison_commandscript
%Complete: 100
Comment: All garrison related commands
Category: commandscripts
EndScriptData */
/* ScriptData
Name: garrison_commandscript
%Complete: 100
Comment: GM/dev commands for the garrison (force site-level upgrade, bypassing the client blueprint/quest gate)
Category: commandscripts
EndScriptData */
#include "ScriptMgr.h"
#include "Chat.h"
#include "ChatCommand.h"
#include "DB2Stores.h"
#include "DB2Structure.h"
#include "Garrison.h"
#include "Player.h"
#include "RBAC.h"
#include "WorldSession.h"
#include <GarrisonMgr.h>
#include <iterator>
#include <unordered_map>
using namespace Trinity::ChatCommands;
// Per-class order-hall entry location for the .garrison enter dev command. Map + a teleport point near each hall's
// mission table. Coordinates verified from game_tele / hall-leader creature spawns; classes not yet filled decline
// gracefully. (Legion class halls are a mix of phased continent locations and separate instance maps.)
struct ClassHallLocation { uint32 Map; float X; float Y; float Z; char const* Name; };
static std::unordered_map<uint8 /*Classes*/, ClassHallLocation> const ClassHallLocations =
{
{ CLASS_WARRIOR, { 1479, 1028.47f, 7225.17f, 100.18f, "Skyhold" } },
{ CLASS_PALADIN, { 1220, -817.16f, 4391.80f, 739.24f, "Sanctum of Light" } },
{ CLASS_HUNTER, { 1220, 4632.03f, 5320.96f, 852.01f, "Trueshot Lodge" } },
{ CLASS_ROGUE, { 1220, -927.24f, 4501.06f, 700.75f, "Hall of Shadows" } },
{ CLASS_PRIEST, { 1512, 1333.91f, 1335.63f, 177.22f, "Netherlight Temple" } },
{ CLASS_DEATH_KNIGHT, { 1220, -1502.45f, 1060.68f, 260.42f, "Acherus" } },
{ CLASS_SHAMAN, { 730, 851.31f, 1067.76f, -10.02f, "The Maelstrom" } },
{ CLASS_MAGE, { 1513, -844.00f, 4759.00f, 918.00f, "Hall of the Guardian" } },
{ CLASS_WARLOCK, { 1107, 3094.91f, 1062.63f, 242.58f, "Dreadscar Rift" } },
{ CLASS_MONK, { 1514, 885.31f, 3605.37f, 192.23f, "Temple of Five Dawns" } },
{ CLASS_DRUID, { 1220, 4411.15f, 7164.21f, 350.00f, "The Dreamgrove" } },
{ CLASS_DEMON_HUNTER, { 1519, 1561.02f, 1399.94f, 237.11f, "The Fel Hammer" } },
};
// Dev commands for the WoD garrison. The retail upgrade path (Garrison Architect table) only unlocks the
// upgrade button once the client knows the "Garrison Blueprint: Level N" and the prerequisite quests are
// done; that client gate is not satisfied by completing the quests alone. This command drives the same
// server logic the architect handler uses (Garrison::Upgrade) directly, so a developer can level a garrison
// up on demand to test higher-level plots/buildings. Buildings are preserved by plot instance id.
class garrison_commandscript : public CommandScript
{
public:
garrison_commandscript() : CommandScript("garrison_commandscript") { }
garrison_commandscript() : CommandScript("garrison_commandscript") {}
std::span<ChatCommandBuilder const> GetCommands() const override
{
static ChatCommandTable garrisonCommandTable =
{
{ "create", HandleGarrisonCreateCommand, rbac::RBAC_PERM_COMMAND_GM, Console::No },
{ "info", HandleGarrisonInfoCommand, rbac::RBAC_PERM_COMMAND_GM, Console::No },
{ "upgrade", HandleGarrisonUpgradeCommand, rbac::RBAC_PERM_COMMAND_GM, Console::No },
{ "create", HandleGarrisonCreateCommand, rbac::RBAC_PERM_COMMAND_GM, Console::No },
{ "champions", HandleGarrisonChampionsCommand, rbac::RBAC_PERM_COMMAND_GM, Console::No },
{ "enter", HandleGarrisonEnterCommand, rbac::RBAC_PERM_COMMAND_GM, Console::No },
{ "exit", HandleGarrisonExitCommand, rbac::RBAC_PERM_COMMAND_GM, Console::No },
};
static ChatCommandTable commandTable =
{
{ "garrison", garrisonCommandTable },
@@ -52,97 +84,156 @@ public:
return commandTable;
}
static bool HandleGarrisonCreateCommand(ChatHandler* handler, Optional<uint32> siteId)
// .garrison upgrade - advance the selected player's WoD garrison one site level (repeatable to max).
static bool HandleGarrisonUpgradeCommand(ChatHandler* handler)
{
Player* player = handler->GetPlayer();
if (!player)
Player* target = handler->getSelectedPlayerOrSelf();
if (!target)
{
handler->SendSysMessage("You must be in-game to use this command.");
handler->SendSysMessage(LANG_PLAYER_NOT_FOUND);
handler->SetSentErrorMessage(true);
return false;
}
uint32 garrSiteId = siteId.value_or(2); // Default to Draenor garrison (site 2)
if (handler->HasLowerSecurity(target, ObjectGuid::Empty))
return false;
// Validate GarrSiteLevelEntry exists
GarrSiteLevelEntry const* siteLevel = sGarrisonMgr.GetGarrSiteLevelEntry(garrSiteId, 1);
if (!siteLevel)
Garrison* garrison = target->GetGarrison();
if (!garrison)
{
handler->PSendSysMessage("Garrison site %u does not exist.", garrSiteId);
handler->PSendSysMessage("{} has no WoD garrison.", target->GetName());
handler->SetSentErrorMessage(true);
return false;
}
// Check if player already has a garrison
if (player->GetGarrison())
GarrSiteLevelEntry const* before = garrison->GetSiteLevel();
uint32 beforeLevel = before ? before->GarrLevel : 0;
garrison->Upgrade();
GarrSiteLevelEntry const* after = garrison->GetSiteLevel();
uint32 afterLevel = after ? after->GarrLevel : 0;
if (afterLevel <= beforeLevel)
{
handler->PSendSysMessage("Player already has a garrison.");
handler->PSendSysMessage("Garrison is already at its maximum site level ({}).", beforeLevel);
handler->SetSentErrorMessage(true);
return false;
}
// Create the garrison
player->CreateGarrison(garrSiteId);
if (Garrison* garrison = player->GetGarrison())
{
// Save to database
CharacterDatabaseTransaction trans = CharacterDatabase.BeginTransaction();
garrison->SaveToDB(trans);
CharacterDatabase.CommitTransaction(trans);
handler->PSendSysMessage("Garrison created successfully! Site ID: %u", garrSiteId);
// Send garrison info to player
WorldPackets::Garrison::GetGarrisonInfoResult garrisonInfo;
garrisonInfo.FactionIndex = Garrison::GetFaction(player->GetTeam());
garrison->BuildInfoPacket(garrisonInfo.Garrisons.emplace_back());
garrisonInfo.FollowerSoftCaps = {
{ FOLLOWER_TYPE_GARRISON, 20 },
{ FOLLOWER_TYPE_SHIPYARD, 6 },
{ FOLLOWER_TYPE_CLASS_ORDER, 6 },
{ FOLLOWER_TYPE_WAR_CAMPAIGN, 30 },
{ FOLLOWER_TYPE_COVENANT, 100 }
};
player->GetSession()->SendPacket(garrisonInfo.Write());
return true;
}
handler->PSendSysMessage("Failed to create garrison.");
handler->SetSentErrorMessage(true);
return false;
handler->PSendSysMessage("Upgraded {}'s garrison: level {} -> {} (GarrSiteLevel {}). Buildings preserved.",
target->GetName(), beforeLevel, afterLevel, after ? after->ID : 0);
return true;
}
static bool HandleGarrisonInfoCommand(ChatHandler* handler)
// .garrison create <siteId> - create a garrison of the given GarrSite id for the selected player.
// Legion Hunter Order Hall (Trueshot Lodge) = 161; WoD Alliance = 2, WoD Horde = 71.
static bool HandleGarrisonCreateCommand(ChatHandler* handler, uint32 garrSiteId)
{
Player* player = handler->GetPlayer();
if (!player)
Player* target = handler->getSelectedPlayerOrSelf();
if (!target)
{
handler->SendSysMessage("You must be in-game to use this command.");
handler->SendSysMessage(LANG_PLAYER_NOT_FOUND);
handler->SetSentErrorMessage(true);
return false;
}
if (handler->HasLowerSecurity(target, ObjectGuid::Empty))
return false;
target->CreateGarrison(garrSiteId);
// Resolve which garrison (by type) the new site produced, so we can confirm it and report the map.
Garrison* created = nullptr;
for (GarrisonType t : { GARRISON_TYPE_GARRISON, GARRISON_TYPE_CLASS_ORDER, GARRISON_TYPE_WAR_CAMPAIGN, GARRISON_TYPE_COVENANT })
if (Garrison* g = target->GetGarrison(t))
if (g->GetSiteLevel() && g->GetSiteLevel()->GarrSiteID == garrSiteId)
created = g;
if (!created)
{
handler->PSendSysMessage("Failed to create a garrison for GarrSite {} (unknown/invalid site id).", garrSiteId);
handler->SetSentErrorMessage(true);
return false;
}
/* auto const& garrisons = player->GetGarrisons();
if (garrisons.empty())
handler->PSendSysMessage("Created garrison for {}: GarrSite {} -> type {}, map {}.",
target->GetName(), garrSiteId, uint32(created->GetType()), created->GetSiteLevel()->MapID);
return true;
}
// .garrison champions - grant the Hunter Order Hall starting champions to the class-order garrison and seed
// its mission board. Create the hall first with ".garrison create 161".
static bool HandleGarrisonChampionsCommand(ChatHandler* handler)
{
Player* target = handler->getSelectedPlayerOrSelf();
if (!target)
{
handler->SendSysMessage("You have no garrisons.");
return true;
}*/
handler->SendSysMessage(LANG_PLAYER_NOT_FOUND);
handler->SetSentErrorMessage(true);
return false;
}
if (handler->HasLowerSecurity(target, ObjectGuid::Empty))
return false;
/* handler->PSendSysMessage("You have %u garrison(s):", uint32(garrisons.size()));
for (auto const& [type, garrison] : garrisons)
Garrison* garrison = target->GetGarrison(GARRISON_TYPE_CLASS_ORDER);
if (!garrison)
{
handler->PSendSysMessage("- Type: %u, Site ID: %u, Site Level ID: %u",
type,
garrison->GetSiteLevel() ? garrison->GetSiteLevel()->GarrSiteID : 0,
garrison->GetSiteLevel() ? garrison->GetSiteLevel()->ID : 0);
handler->PSendSysMessage("{} has no Order Hall - create it first with '.garrison create 161'.", target->GetName());
handler->SetSentErrorMessage(true);
return false;
}
auto const& plots = garrison->GetPlots();
handler->PSendSysMessage(" Plots: %u", uint32(plots.size()));
}*/
// Hunter "Unseen Path" champions (GarrFollower ids, GarrType 3). Emmarel Shadewarden (593) is the hall leader.
static constexpr uint32 hunterChampions[] = { 593, 642, 742, 743, 744, 745, 746, 747, 748 };
for (uint32 followerId : hunterChampions)
garrison->AddFollower(followerId);
garrison->GenerateAvailableMissions();
handler->PSendSysMessage("Granted {} Hunter champions and seeded missions to {}'s Order Hall.",
uint32(std::size(hunterChampions)), target->GetName());
return true;
}
// .garrison enter - teleport the selected player to THEIR class's order hall (data-driven per class). Retail
// reaches the hall via the Dalaran Order Hall portal; this teleports straight there.
static bool HandleGarrisonEnterCommand(ChatHandler* handler)
{
Player* target = handler->getSelectedPlayerOrSelf();
if (!target)
{
handler->SendSysMessage(LANG_PLAYER_NOT_FOUND);
handler->SetSentErrorMessage(true);
return false;
}
auto itr = ClassHallLocations.find(target->GetClass());
if (itr == ClassHallLocations.end())
{
handler->PSendSysMessage("No order-hall entry location is configured for {}'s class yet.", target->GetName());
handler->SetSentErrorMessage(true);
return false;
}
ClassHallLocation const& loc = itr->second;
target->TeleportTo(loc.Map, loc.X, loc.Y, loc.Z, 0.0f);
handler->PSendSysMessage("Teleported {} to {} (order hall).", target->GetName(), loc.Name);
return true;
}
// .garrison exit - return from the Order Hall to Dalaran (Broken Isles).
static bool HandleGarrisonExitCommand(ChatHandler* handler)
{
Player* target = handler->getSelectedPlayerOrSelf();
if (!target)
{
handler->SendSysMessage(LANG_PLAYER_NOT_FOUND);
handler->SetSentErrorMessage(true);
return false;
}
target->TeleportTo(1220, -849.908f, 4461.17f, 735.661f, 0.0f);
handler->PSendSysMessage("Returned {} to Dalaran.", target->GetName());
return true;
}
};