mirror of
https://github.com/DarkflameUniverse/DarkflameServer.git
synced 2026-10-02 02:43:44 +00:00
feat: online players get grant changes at once
Adding or removing a grant sends the existing PLAYER_ACTION refresh through master to every world: REFRESH_ACCOUNT for an account's grants, REFRESH_CHARACTER for a character's. A world with that account's sessions (or the loaded character) drops the grants it kept, so the next command reads them again: no relog. The dashboard toasts whether the player was online. The dashboard's own open pages already get the new rights on their next request, and their WebSockets are checked again. Check: with a character in game, grant it /spawn on the dashboard (toast: "Applied in game at once") and use /spawn without relogging; remove the grant and /spawn is refused again at once. With the player offline the toast says it applies when they next play. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,8 @@
|
|||||||
#include "eHTTPMethod.h"
|
#include "eHTTPMethod.h"
|
||||||
#include "Game.h"
|
#include "Game.h"
|
||||||
#include "GeneralUtils.h"
|
#include "GeneralUtils.h"
|
||||||
|
#include "master/PlayerAction.h"
|
||||||
|
#include "PlayerActions.h"
|
||||||
#include "HTTPContext.h"
|
#include "HTTPContext.h"
|
||||||
#include "PermissionGrants.h"
|
#include "PermissionGrants.h"
|
||||||
#include "Permissions.h"
|
#include "Permissions.h"
|
||||||
@@ -168,10 +170,23 @@ namespace {
|
|||||||
std::map<uint32_t, bool> manageable;
|
std::map<uint32_t, bool> manageable;
|
||||||
};
|
};
|
||||||
|
|
||||||
// Grants apply at once: the dashboard's open pages get their account's rights again
|
/**
|
||||||
void Applied(const Target& target) {
|
* Grants apply at once: the dashboard's open pages get their account's rights again, and the world servers read the
|
||||||
|
* grants of the account's online sessions (or the character, if it is loaded) again, as for a GM level change.
|
||||||
|
* Returns the request ID of the refresh (the page shows its result).
|
||||||
|
*/
|
||||||
|
uint32_t Applied(const HTTPContext& context, const Target& target) {
|
||||||
Game::web.RecheckWebSockets(target.accountId);
|
Game::web.RecheckWebSockets(target.accountId);
|
||||||
BroadcastTableChanged("grants", std::to_string(target.accountId));
|
BroadcastTableChanged("grants", std::to_string(target.accountId));
|
||||||
|
PlayerActionRequest request;
|
||||||
|
const bool account = target.type == PermissionGrants::ACCOUNT;
|
||||||
|
request.action = account ? ePlayerAction::REFRESH_ACCOUNT : ePlayerAction::REFRESH_CHARACTER;
|
||||||
|
request.accountId = target.accountId;
|
||||||
|
request.characterId = account ? 0 : target.id;
|
||||||
|
return PlayerActions::Request(request, context.accountId, [account](const PlayerActionResult& result) {
|
||||||
|
if (result.affected == 0) return PlayerActions::Outcome{ true, account ? "The account isn't online; it applies when they next play" : "The character isn't online; it applies when they next play" };
|
||||||
|
return PlayerActions::Outcome{ true, "Applied in game at once" };
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -287,8 +302,8 @@ void GrantRoutes::RegisterRoutes() {
|
|||||||
const auto description = std::string(deny ? "Took away " : "Granted ") + what + (deny ? " from " : " to ") + target->Describe() +
|
const auto description = std::string(deny ? "Took away " : "Granted ") + what + (deny ? " from " : " to ") + target->Describe() +
|
||||||
(expiresAt ? " until " + UtcTime(expiresAt) : "") + (note.empty() ? "" : ": " + note) + OwnAccountNote(context.accountId, target->accountId);
|
(expiresAt ? " until " + UtcTime(expiresAt) : "") + (note.empty() ? "" : ": " + note) + OwnAccountNote(context.accountId, target->accountId);
|
||||||
Audit(context, deny ? "deny_permission" : "grant_permission", description, target->Audit());
|
Audit(context, deny ? "deny_permission" : "grant_permission", description, target->Audit());
|
||||||
Applied(*target);
|
const auto requestId = Applied(context, *target);
|
||||||
JsonSuccess(reply, { {"id", grant.id}, {"message", std::string(deny ? "Took away " : "Granted ") + what} });
|
JsonSuccess(reply, { {"id", grant.id}, {"requestId", requestId}, {"message", std::string(deny ? "Took away " : "Granted ") + what} });
|
||||||
});
|
});
|
||||||
|
|
||||||
Route(eHTTPMethod::POST, "/api/grants/:id/remove", Perm(MANAGE),
|
Route(eHTTPMethod::POST, "/api/grants/:id/remove", Perm(MANAGE),
|
||||||
@@ -311,7 +326,7 @@ void GrantRoutes::RegisterRoutes() {
|
|||||||
const auto what = PermissionGrants::Describe(*kind, grant->name);
|
const auto what = PermissionGrants::Describe(*kind, grant->name);
|
||||||
Audit(context, "remove_grant", std::string("Removed the ") + (grant->deny ? "deny of " : "grant of ") + what + (grant->deny ? " from " : " to ") +
|
Audit(context, "remove_grant", std::string("Removed the ") + (grant->deny ? "deny of " : "grant of ") + what + (grant->deny ? " from " : " to ") +
|
||||||
target->Describe() + " (given by " + grant->grantedBy + ")" + OwnAccountNote(context.accountId, target->accountId), target->Audit());
|
target->Describe() + " (given by " + grant->grantedBy + ")" + OwnAccountNote(context.accountId, target->accountId), target->Audit());
|
||||||
Applied(*target);
|
const auto requestId = Applied(context, *target);
|
||||||
JsonSuccess(reply, { {"message", std::string("Removed the ") + (grant->deny ? "deny of " : "grant of ") + what} });
|
JsonSuccess(reply, { {"requestId", requestId}, {"message", std::string("Removed the ") + (grant->deny ? "deny of " : "grant of ") + what} });
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,10 +48,12 @@ namespace {
|
|||||||
return static_cast<uint32_t>(users.size());
|
return static_cast<uint32_t>(users.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pick up a GM level change: lower the in-game level if it now exceeds the account's maximum
|
// Pick up a GM level change: lower the in-game level if it now exceeds the account's maximum. Also a change of the
|
||||||
|
// account's permission grants: they are read again the next time a command is used
|
||||||
uint32_t RefreshAccount(uint32_t accountId) {
|
uint32_t RefreshAccount(uint32_t accountId) {
|
||||||
const auto users = UserManager::Instance()->GetUsersForAccount(accountId);
|
const auto users = UserManager::Instance()->GetUsersForAccount(accountId);
|
||||||
if (users.empty()) return 0;
|
if (users.empty()) return 0;
|
||||||
|
for (auto* user : users) user->ForgetGrants();
|
||||||
|
|
||||||
const auto info = Database::Get()->GetAccountInfo(users.front()->GetUsername());
|
const auto info = Database::Get()->GetAccountInfo(users.front()->GetUsername());
|
||||||
if (!info) return 0;
|
if (!info) return 0;
|
||||||
@@ -83,10 +85,12 @@ namespace {
|
|||||||
return static_cast<uint32_t>(users.size());
|
return static_cast<uint32_t>(users.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Restrictions or permission grants of a character changed
|
||||||
uint32_t RefreshCharacter(LWOOBJID characterId) {
|
uint32_t RefreshCharacter(LWOOBJID characterId) {
|
||||||
auto* entity = PlayerManager::GetPlayer(characterId);
|
auto* entity = PlayerManager::GetPlayer(characterId);
|
||||||
auto* character = entity ? entity->GetCharacter() : nullptr;
|
auto* character = entity ? entity->GetCharacter() : nullptr;
|
||||||
if (!character) return 0;
|
if (!character) return 0;
|
||||||
|
if (auto* user = character->GetParentUser()) user->ForgetGrants();
|
||||||
|
|
||||||
const auto info = Database::Get()->GetCharacterInfo(characterId);
|
const auto info = Database::Get()->GetCharacterInfo(characterId);
|
||||||
if (!info) return 0;
|
if (!info) return 0;
|
||||||
|
|||||||
@@ -18,8 +18,8 @@
|
|||||||
*/
|
*/
|
||||||
enum class ePlayerAction : uint8_t {
|
enum class ePlayerAction : uint8_t {
|
||||||
KICK_ACCOUNT, // Disconnect every session of accountId
|
KICK_ACCOUNT, // Disconnect every session of accountId
|
||||||
REFRESH_ACCOUNT, // Reload account data (GM level) for online sessions of accountId
|
REFRESH_ACCOUNT, // Reload account data (GM level, permission grants) for online sessions of accountId
|
||||||
REFRESH_CHARACTER, // Reload character data (restrictions) for characterId if loaded
|
REFRESH_CHARACTER, // Reload character data (restrictions, permission grants) for characterId if loaded
|
||||||
RESCUE_CHARACTER, // Transfer characterId to zoneId if it is loaded in a world, landing on spawn point `text` (empty: the zone's default)
|
RESCUE_CHARACTER, // Transfer characterId to zoneId if it is loaded in a world, landing on spawn point `text` (empty: the zone's default)
|
||||||
// Tell a player a moderator decided on something they asked for (approved = yes/no, text = name or reason)
|
// Tell a player a moderator decided on something they asked for (approved = yes/no, text = name or reason)
|
||||||
NAME_MODERATED, // characterId's requested name; text is the name
|
NAME_MODERATED, // characterId's requested name; text is the name
|
||||||
|
|||||||
Reference in New Issue
Block a user