diff options
Diffstat (limited to 'source/game/StarTeamClient.cpp')
-rw-r--r-- | source/game/StarTeamClient.cpp | 245 |
1 files changed, 245 insertions, 0 deletions
diff --git a/source/game/StarTeamClient.cpp b/source/game/StarTeamClient.cpp new file mode 100644 index 0000000..f5cbfdc --- /dev/null +++ b/source/game/StarTeamClient.cpp @@ -0,0 +1,245 @@ +#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(); +} + +} |