From 40223a5090bf8a502094927da39fc96a5cfd5eae Mon Sep 17 00:00:00 2001 From: Kae <80987908+Novaenia@users.noreply.github.com> Date: Wed, 12 Jul 2023 22:16:12 +1000 Subject: Initial work --- source/game/StarPlayer.cpp | 49 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) (limited to 'source/game/StarPlayer.cpp') diff --git a/source/game/StarPlayer.cpp b/source/game/StarPlayer.cpp index af8a2ed..c9b337f 100644 --- a/source/game/StarPlayer.cpp +++ b/source/game/StarPlayer.cpp @@ -2464,4 +2464,53 @@ Vec2F Player::cameraPosition() { return position(); } +NetworkedAnimatorPtr Player::effectsAnimator() { + return m_effectsAnimator; +} + +const String secretProprefix = strf("{:c}JsonProperty{:c}", 0, 0); + +Maybe Player::getSecretPropertyView(String const& name) const { + if (auto tag = m_effectsAnimator->globalTagPtr(secretProprefix + name)) { + auto& view = tag->utf8(); + DataStreamExternalBuffer buffer(view.data(), view.size()); + try { + uint8_t typeIndex = buffer.read() - 1; + if ((Json::Type)typeIndex == Json::Type::String) { + size_t len = buffer.readVlqU(); + size_t pos = buffer.pos(); + if (pos + len == buffer.size()) + return StringView(buffer.ptr() + pos, len); + } + } + catch (StarException const& e) {} + } + + return {}; +} + +Json Player::getSecretProperty(String const& name, Json defaultValue) const { + if (auto tag = m_effectsAnimator->globalTagPtr(secretProprefix + name)) { + DataStreamExternalBuffer buffer(tag->utf8Ptr(), tag->utf8Size()); + try + { return buffer.read(); } + catch (StarException const& e) + { Logger::error("Exception reading secret player property '{}': {}", name, e.what()); } + } + + return move(defaultValue); +} + +void Player::setSecretProperty(String const& name, Json const& value) { + if (value) { + DataStreamBuffer ds; + ds.write(value); + auto& data = ds.data(); + m_effectsAnimator->setGlobalTag(secretProprefix + name, String(data.ptr(), data.size())); + } + else + m_effectsAnimator->removeGlobalTag(secretProprefix + name); +} + + } -- cgit v1.2.3 From c3bf7a3c87e61c56d48dd932f295c99d64d14a38 Mon Sep 17 00:00:00 2001 From: Kae <80987908+Novaenia@users.noreply.github.com> Date: Thu, 13 Jul 2023 17:58:35 +1000 Subject: Add vanilla-compatible raw broadcasts --- source/core/StarConfig.hpp | 1 + source/game/StarPlayer.cpp | 2 +- source/game/StarWorldClient.cpp | 67 +++++++++++++++++++++++++++++++++++++++-- source/game/StarWorldClient.hpp | 6 ++++ 4 files changed, 73 insertions(+), 3 deletions(-) (limited to 'source/game/StarPlayer.cpp') diff --git a/source/core/StarConfig.hpp b/source/core/StarConfig.hpp index f070df4..5a95569 100644 --- a/source/core/StarConfig.hpp +++ b/source/core/StarConfig.hpp @@ -44,6 +44,7 @@ using std::mem_fn; using std::ref; using std::cref; using namespace std::placeholders; +using namespace std::string_literals; using std::prev; // using std::next; diff --git a/source/game/StarPlayer.cpp b/source/game/StarPlayer.cpp index c9b337f..d790d1c 100644 --- a/source/game/StarPlayer.cpp +++ b/source/game/StarPlayer.cpp @@ -2468,7 +2468,7 @@ NetworkedAnimatorPtr Player::effectsAnimator() { return m_effectsAnimator; } -const String secretProprefix = strf("{:c}JsonProperty{:c}", 0, 0); +const String secretProprefix = "\0JsonProperty\0"s; Maybe Player::getSecretPropertyView(String const& name) const { if (auto tag = m_effectsAnimator->globalTagPtr(secretProprefix + name)) { diff --git a/source/game/StarWorldClient.cpp b/source/game/StarWorldClient.cpp index 25b676c..2b4af00 100644 --- a/source/game/StarWorldClient.cpp +++ b/source/game/StarWorldClient.cpp @@ -20,11 +20,14 @@ #include "StarWorldTemplate.hpp" #include "StarStoredFunctions.hpp" #include "StarInspectableEntity.hpp" +#include "StarCurve25519.hpp" namespace Star { -const float WorldClient::DropDist = 6.0f; +const std::string SECRET_BROADCAST_PUBLIC_KEY = "SecretBroadcastPublicKey"; +const std::string SECRET_BROADCAST_PREFIX = "\0Broadcast\0"s; +const float WorldClient::DropDist = 6.0f; WorldClient::WorldClient(PlayerPtr mainPlayer) { auto& root = Root::singleton(); auto assets = root.assets(); @@ -792,7 +795,35 @@ void WorldClient::handleIncomingPackets(List const& packets) { m_damageManager->pushRemoteDamageRequest(damage->remoteDamageRequest); } else if (auto damage = as(packet)) { - m_damageManager->pushRemoteDamageNotification(damage->remoteDamageNotification); + auto& materialKind = damage->remoteDamageNotification.damageNotification.targetMaterialKind.utf8(); + const size_t prefixSize = SECRET_BROADCAST_PREFIX.size(); + const size_t signatureSize = Curve25519::SignatureSize; + const size_t dataSize = prefixSize + signatureSize; + + if (materialKind.size() >= dataSize && materialKind.rfind(SECRET_BROADCAST_PREFIX, 0) != NPos) { + // this is actually a secret broadcast!! + if (auto player = m_entityMap->get(damage->remoteDamageNotification.sourceEntityId)) { + if (auto publicKey = player->getSecretPropertyView(SECRET_BROADCAST_PUBLIC_KEY)) { + if (publicKey->utf8Size() == Curve25519::PublicKeySize) { + std::string_view broadcast(materialKind); + auto signature = broadcast.substr(prefixSize, signatureSize); + + auto rawBroadcast = broadcast.substr(dataSize); + if (Curve25519::verify( + (uint8_t const*)signature.data(), + (uint8_t const*)publicKey->utf8Ptr(), + (void*)rawBroadcast.data(), + rawBroadcast.size() + )) { + handleSecretBroadcast(player, rawBroadcast); + } + } + } + } + } + else { + m_damageManager->pushRemoteDamageNotification(damage->remoteDamageNotification); + } } else if (auto entityMessagePacket = as(packet)) { EntityPtr entity; @@ -917,6 +948,11 @@ void WorldClient::update() { } } + // Secret broadcasts are transmitted through DamageNotifications for vanilla server compatibility. + // Because DamageNotification packets are spoofable, we have to sign the data so other clients can validate that it is legitimate. + auto& publicKey = Curve25519::publicKey(); + m_mainPlayer->setSecretProperty(SECRET_BROADCAST_PUBLIC_KEY, String((const char*)publicKey.data(), publicKey.size())); + ++m_currentStep; //m_interpolationTracker.update(m_currentStep); m_interpolationTracker.update(Time::monotonicTime()); @@ -1847,6 +1883,33 @@ void WorldClient::connectWire(WireConnection const& output, WireConnection const m_outgoingPackets.append(make_shared(output, input)); } +bool WorldClient::sendSecretBroadcast(StringView broadcast, bool raw) { + if (!inWorld() || !m_mainPlayer || !m_mainPlayer->getSecretPropertyView(SECRET_BROADCAST_PUBLIC_KEY)) + return false; + + auto signature = Curve25519::sign((void*)broadcast.utf8Ptr(), broadcast.utf8Size()); + + auto damageNotification = make_shared(); + auto& remDmg = damageNotification->remoteDamageNotification; + auto& dmg = remDmg.damageNotification; + + dmg.targetEntityId = dmg.sourceEntityId = remDmg.sourceEntityId = m_mainPlayer->entityId(); + dmg.damageDealt = dmg.healthLost = 0.0f; + dmg.hitType = HitType::Hit; + dmg.damageSourceKind = "nodamage"; + dmg.targetMaterialKind = raw ? broadcast : strf("{}{}{}", SECRET_BROADCAST_PREFIX, StringView((char*)&signature, sizeof(signature)), broadcast); + dmg.position = m_mainPlayer->position(); + + m_outgoingPackets.emplace_back(move(damageNotification)); + return true; +} + +bool WorldClient::handleSecretBroadcast(PlayerPtr player, StringView broadcast) { + Logger::info("Received broadcast '{}'", broadcast); + return true; +} + + void WorldClient::ClientRenderCallback::addDrawable(Drawable drawable, EntityRenderLayer renderLayer) { drawables[renderLayer].append(move(drawable)); } diff --git a/source/game/StarWorldClient.hpp b/source/game/StarWorldClient.hpp index 85be6d9..0a48a31 100644 --- a/source/game/StarWorldClient.hpp +++ b/source/game/StarWorldClient.hpp @@ -150,6 +150,12 @@ public: void disconnectAllWires(Vec2I wireEntityPosition, WireNode const& node); void connectWire(WireConnection const& output, WireConnection const& input); + // Functions for sending broadcast messages to other players that can receive them, + // on completely vanilla servers by smuggling it through a DamageNotification. + // It's cursed as fuck, but it works. + bool sendSecretBroadcast(StringView broadcast, bool raw = false); + bool handleSecretBroadcast(PlayerPtr player, StringView broadcast); + List pullPendingChatActions(); WorldStructure const& centralStructure() const; -- cgit v1.2.3