osb/source/game/StarTeamClient.cpp
2023-06-20 14:33:09 +10:00

246 lines
7.3 KiB
C++

#include "StarTeamClient.hpp"
#include "StarJsonExtra.hpp"
#include "StarWorldTemplate.hpp"
#include "StarPlayer.hpp"
#include "StarPlayerLog.hpp"
#include "StarRoot.hpp"
#include "StarAssets.hpp"
#include "StarClientContext.hpp"
#include "StarWorldClient.hpp"
#include "StarJsonRpc.hpp"
namespace Star {
TeamClient::TeamClient(PlayerPtr mainPlayer, ClientContextPtr clientContext) {
m_mainPlayer = mainPlayer;
m_clientContext = clientContext;
m_hasPendingInvitation = false;
m_pollInvitationsTimer = 0;
m_fullUpdateRunning = false;
m_fullUpdateTimer = 0;
m_statusUpdateRunning = false;
m_statusUpdateTimer = 0;
}
bool TeamClient::isTeamLeader() {
if (!m_teamUuid)
return false;
return m_teamLeader == m_mainPlayer->uuid();
}
bool TeamClient::isTeamLeader(Uuid const& playerUuid) {
if (!m_teamUuid)
return false;
return m_teamLeader == playerUuid;
}
bool TeamClient::isMemberOfTeam() {
return (bool)m_teamUuid;
}
void TeamClient::invitePlayer(String const& playerName) {
if (playerName.empty())
return;
JsonObject request;
request["inviteeName"] = playerName;
request["inviterUuid"] = m_mainPlayer->uuid().hex();
request["inviterName"] = m_mainPlayer->name();
invokeRemote("team.invite", request, [](Json) {});
}
void TeamClient::acceptInvitation(Uuid const& inviterUuid) {
JsonObject request;
request["inviterUuid"] = inviterUuid.hex();
request["inviteeUuid"] = m_mainPlayer->uuid().hex();
invokeRemote("team.acceptInvitation", request, [this](Json) { forceUpdate(); });
}
Maybe<Uuid> TeamClient::currentTeam() const {
return m_teamUuid;
}
void TeamClient::makeLeader(Uuid const& playerUuid) {
if (!m_teamUuid)
return;
if (!isTeamLeader())
return;
JsonObject request;
request["teamUuid"] = m_teamUuid->hex();
request["playerUuid"] = playerUuid.hex();
invokeRemote("team.makeLeader", request, [this](Json) { forceUpdate(); });
}
void TeamClient::removeFromTeam(Uuid const& playerUuid) {
if (!m_teamUuid)
return;
if (!isTeamLeader() && playerUuid != m_mainPlayer->uuid())
return;
JsonObject request;
request["teamUuid"] = m_teamUuid->hex();
request["playerUuid"] = playerUuid.hex();
invokeRemote("team.removeFromTeam", request, [this](Json) { forceUpdate(); });
}
bool TeamClient::hasInvitationPending() {
return m_hasPendingInvitation;
}
std::pair<Uuid, String> TeamClient::pullInvitation() {
m_hasPendingInvitation = false;
return m_pendingInvitation;
}
void TeamClient::update() {
handleRpcResponses();
if (!m_hasPendingInvitation) {
if (Time::monotonicTime() - m_pollInvitationsTimer > Root::singleton().assets()->json("/interface.config:invitationPollInterval").toFloat()) {
m_pollInvitationsTimer = Time::monotonicTime();
JsonObject request;
request["playerUuid"] = m_mainPlayer->uuid().hex();
invokeRemote("team.pollInvitation", request, [this](Json response) {
if (response.isNull())
return;
if (m_hasPendingInvitation)
return;
m_pendingInvitation = {Uuid(response.getString("inviterUuid")), response.getString("inviterName")};
m_hasPendingInvitation = true;
});
}
}
if (!m_fullUpdateRunning) {
if (Time::monotonicTime() - m_fullUpdateTimer > Root::singleton().assets()->json("/interface.config:fullUpdateInterval").toFloat()) {
m_fullUpdateTimer = Time::monotonicTime();
pullFullUpdate();
}
}
if (!m_statusUpdateRunning) {
if (Time::monotonicTime() - m_statusUpdateTimer > Root::singleton().assets()->json("/interface.config:statusUpdateInterval").toFloat()) {
m_statusUpdateTimer = Time::monotonicTime();
statusUpdate();
}
}
}
void TeamClient::pullFullUpdate() {
if (m_fullUpdateRunning)
return;
m_fullUpdateRunning = true;
JsonObject request;
request["playerUuid"] = m_mainPlayer->uuid().hex();
invokeRemote("team.fetchTeamStatus", request, [this](Json response) {
m_fullUpdateRunning = false;
m_teamUuid = response.optString("teamUuid").apply(construct<Uuid>());
if (m_teamUuid) {
m_teamLeader = Uuid(response.getString("leader"));
m_members.clear();
for (auto m : response.getArray("members")) {
Member member;
member.name = m.getString("name");
member.uuid = Uuid(m.getString("uuid"));
member.entity = m.getInt("entity");
member.healthPercentage = m.getFloat("health");
member.energyPercentage = m.getFloat("energy");
member.position[0] = m.getFloat("x");
member.position[1] = m.getFloat("y");
member.world = parseWorldId(m.getString("world"));
member.warpMode = WarpModeNames.getLeft(m.getString("warpMode"));
member.portrait = jsonToList<Drawable>(m.get("portrait"));
m_members.push_back(member);
}
std::sort(m_members.begin(), m_members.end(), [](Member const& a, Member const& b) { return a.name < b.name; });
} else {
clearTeam();
}
});
}
void TeamClient::statusUpdate() {
if (m_statusUpdateRunning)
return;
if (!m_teamUuid)
return;
m_statusUpdateRunning = true;
JsonObject request;
auto player = m_mainPlayer;
// TODO: write full player data less often?
writePlayerData(request, player, true);
invokeRemote("team.updateStatus", request, [this](Json) {
m_statusUpdateRunning = false;
});
}
List<TeamClient::Member> TeamClient::members() {
return m_members;
}
void TeamClient::forceUpdate() {
m_statusUpdateTimer = 0;
m_fullUpdateTimer = 0;
m_pollInvitationsTimer = 0;
}
void TeamClient::invokeRemote(String const& method, Json const& args, function<void(Json const&)> responseFunction) {
auto promise = m_clientContext->rpcInterface()->invokeRemote(method, args);
m_pendingResponses.append({move(promise), move(responseFunction)});
}
void TeamClient::handleRpcResponses() {
List<RpcResponseHandler> stillPendingResponses;
while (m_pendingResponses.size() > 0) {
auto handler = m_pendingResponses.takeLast();
if (handler.first.finished()) {
if (auto const& res = handler.first.result()) {
if (handler.second)
handler.second(*res);
}
} else {
stillPendingResponses.append(move(handler));
}
}
m_pendingResponses = stillPendingResponses;
}
void TeamClient::writePlayerData(JsonObject& request, PlayerPtr player, bool fullWrite) const {
request["playerUuid"] = player->uuid().hex();
request["entity"] = player->entityId();
request["health"] = player->health() / player->maxHealth();
request["energy"] = player->energy() / player->maxEnergy();
request["x"] = player->position()[0];
request["y"] = player->position()[1];
request["world"] = printWorldId(m_clientContext->playerWorldId());
WarpMode mode = WarpMode::None;
if (player->log()->introComplete()) {
if (m_clientContext->playerWorldId().is<CelestialWorldId>())
mode = WarpMode::BeamOnly;
else
mode = player->isDeployed() ? WarpMode::DeployOnly : WarpMode::BeamOnly;
}
request["warpMode"] = WarpModeNames.getRight(mode);
if (fullWrite) {
request["name"] = player->name();
request["portrait"] = jsonFromList(player->portrait(PortraitMode::Head), mem_fn(&Drawable::toJson));
}
}
void TeamClient::clearTeam() {
m_teamLeader = Uuid();
m_teamUuid = {};
m_members.clear();
forceUpdate();
}
}