diff options
-rw-r--r-- | source/base/StarMixer.cpp | 10 | ||||
-rw-r--r-- | source/base/StarMixer.hpp | 3 | ||||
-rw-r--r-- | source/client/StarClientApplication.cpp | 12 | ||||
-rw-r--r-- | source/client/StarClientApplication.hpp | 5 | ||||
-rw-r--r-- | source/core/StarDataStream.cpp | 4 | ||||
-rw-r--r-- | source/core/StarDataStreamDevices.cpp | 7 | ||||
-rw-r--r-- | source/core/StarDataStreamDevices.hpp | 3 | ||||
-rw-r--r-- | source/frontend/StarMainMixer.cpp | 4 | ||||
-rw-r--r-- | source/frontend/StarMainMixer.hpp | 2 | ||||
-rw-r--r-- | source/game/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/game/StarNetworkedAnimator.cpp | 9 | ||||
-rw-r--r-- | source/game/StarNetworkedAnimator.hpp | 2 | ||||
-rw-r--r-- | source/game/StarPlayer.cpp | 49 | ||||
-rw-r--r-- | source/game/StarPlayer.hpp | 22 | ||||
-rw-r--r-- | source/game/StarVoice.cpp | 35 | ||||
-rw-r--r-- | source/game/StarVoice.hpp | 32 |
16 files changed, 189 insertions, 12 deletions
diff --git a/source/base/StarMixer.cpp b/source/base/StarMixer.cpp index 96705b0..7b8e338 100644 --- a/source/base/StarMixer.cpp +++ b/source/base/StarMixer.cpp @@ -221,7 +221,7 @@ void Mixer::stopAll(float rampTime) { p.first->stop(vel); } -void Mixer::read(int16_t* outBuffer, size_t frameCount) { +void Mixer::read(int16_t* outBuffer, size_t frameCount, ExtraMixFunction extraMixFunction) { // Make this method as least locky as possible by copying all the needed // member data before the expensive audio / effect stuff. unsigned sampleRate; @@ -326,7 +326,7 @@ void Mixer::read(int16_t* outBuffer, size_t frameCount) { m_mixBuffer[s * channels + c] = 0; } else { for (size_t c = 0; c < channels; ++c) - m_mixBuffer[s * channels + c] = m_mixBuffer[s * channels + c] * volume; + m_mixBuffer[s * channels + c] *= volume; } } } @@ -338,7 +338,8 @@ void Mixer::read(int16_t* outBuffer, size_t frameCount) { float vol = lerp((float)s / frameCount, beginVolume * groupVolume * audioStopVolBegin, endVolume * groupEndVolume * audioStopVolEnd); for (size_t c = 0; c < channels; ++c) { float sample = m_mixBuffer[s * channels + c] * vol * audioState.positionalChannelVolumes[c] * audioInstance->m_volume.value; - outBuffer[s * channels + c] = clamp(sample + outBuffer[s * channels + c], -32767.0f, 32767.0f); + int16_t& outSample = outBuffer[s * channels + c]; + outSample = clamp(sample + outSample, -32767.0f, 32767.0f); } } @@ -347,6 +348,9 @@ void Mixer::read(int16_t* outBuffer, size_t frameCount) { } } + if (extraMixFunction) + extraMixFunction(outBuffer, bufferSize, channels); + { MutexLocker locker(m_effectsMutex); // Apply all active effects diff --git a/source/base/StarMixer.hpp b/source/base/StarMixer.hpp index 079549f..d369cfb 100644 --- a/source/base/StarMixer.hpp +++ b/source/base/StarMixer.hpp @@ -95,6 +95,7 @@ private: // Thread safe mixer class with basic effects support. class Mixer { public: + typedef function<void(int16_t* buffer, size_t frames, unsigned channels)> ExtraMixFunction; typedef function<void(int16_t* buffer, size_t frames, unsigned channels)> EffectFunction; typedef function<float(unsigned, Vec2F, float)> PositionalAttenuationFunction; @@ -126,7 +127,7 @@ public: // Reads pending audio data. This is thread safe with the other Mixer // methods, but only one call to read may be active at a time. - void read(int16_t* samples, size_t frameCount); + void read(int16_t* samples, size_t frameCount, ExtraMixFunction extraMixFunction = {}); // Call within the main loop of the program using Mixer, calculates // positional attenuation of audio and does cleanup. diff --git a/source/client/StarClientApplication.cpp b/source/client/StarClientApplication.cpp index 356421b..e2a1515 100644 --- a/source/client/StarClientApplication.cpp +++ b/source/client/StarClientApplication.cpp @@ -14,6 +14,9 @@ #include "StarWorldTemplate.hpp" #include "StarWorldClient.hpp" #include "StarRootLoader.hpp" +#include "StarInput.hpp" +#include "StarVoice.hpp" + #include "StarInterfaceLuaBindings.hpp" #include "StarInputLuaBindings.hpp" @@ -171,6 +174,7 @@ void ClientApplication::applicationInit(ApplicationControllerPtr appController) m_guiContext = make_shared<GuiContext>(m_mainMixer->mixer(), appController); m_input = make_shared<Input>(); + m_voice = make_shared<Voice>(); auto configuration = m_root->configuration(); bool vsync = configuration->get("vsync").toBool(); @@ -417,8 +421,12 @@ void ClientApplication::render() { } void ClientApplication::getAudioData(int16_t* sampleData, size_t frameCount) { - if (m_mainMixer) - m_mainMixer->read(sampleData, frameCount); + if (m_mainMixer) { + m_mainMixer->read(sampleData, frameCount, [&](int16_t* buffer, size_t frames, unsigned channels) { + if (m_voice) + m_voice->mix(buffer, frames, channels); + }); + } } void ClientApplication::changeState(MainAppState newState) { diff --git a/source/client/StarClientApplication.hpp b/source/client/StarClientApplication.hpp index 186efaf..4ee837e 100644 --- a/source/client/StarClientApplication.hpp +++ b/source/client/StarClientApplication.hpp @@ -11,11 +11,13 @@ #include "StarErrorScreen.hpp" #include "StarCinematic.hpp" #include "StarKeyBindings.hpp" -#include "StarInput.hpp" #include "StarMainApplication.hpp" namespace Star { +STAR_CLASS(Input); +STAR_CLASS(Voice); + class ClientApplication : public Application { protected: virtual void startup(StringList const& cmdLineArgs) override; @@ -76,6 +78,7 @@ private: MainMixerPtr m_mainMixer; GuiContextPtr m_guiContext; InputPtr m_input; + VoicePtr m_voice; // Valid after renderInit is called the first time CinematicPtr m_cinematicOverlay; diff --git a/source/core/StarDataStream.cpp b/source/core/StarDataStream.cpp index c8d50a4..834f4de 100644 --- a/source/core/StarDataStream.cpp +++ b/source/core/StarDataStream.cpp @@ -205,7 +205,7 @@ size_t DataStream::readVlqU(uint64_t& i) { size_t bytesRead = Star::readVlqU(i, makeFunctionInputIterator([this]() { return this->read<uint8_t>(); })); if (bytesRead == NPos) - throw DataStreamException("Error reading VLQ encoded intenger!"); + throw DataStreamException("Error reading VLQ encoded integer!"); return bytesRead; } @@ -214,7 +214,7 @@ size_t DataStream::readVlqI(int64_t& i) { size_t bytesRead = Star::readVlqI(i, makeFunctionInputIterator([this]() { return this->read<uint8_t>(); })); if (bytesRead == NPos) - throw DataStreamException("Error reading VLQ encoded intenger!"); + throw DataStreamException("Error reading VLQ encoded integer!"); return bytesRead; } diff --git a/source/core/StarDataStreamDevices.cpp b/source/core/StarDataStreamDevices.cpp index 0c37d8d..85e6d3f 100644 --- a/source/core/StarDataStreamDevices.cpp +++ b/source/core/StarDataStreamDevices.cpp @@ -164,4 +164,11 @@ void DataStreamExternalBuffer::reset(char const* externalData, size_t len) { m_buffer.reset(externalData, len); } +void DataStreamExternalBuffer::readData(char* data, size_t len) { + m_buffer.readFull(data, len); +} +void DataStreamExternalBuffer::writeData(char const* data, size_t len) { + m_buffer.writeFull(data, len); +} + } diff --git a/source/core/StarDataStreamDevices.hpp b/source/core/StarDataStreamDevices.hpp index 88ee0e9..4a12d24 100644 --- a/source/core/StarDataStreamDevices.hpp +++ b/source/core/StarDataStreamDevices.hpp @@ -140,6 +140,9 @@ public: void reset(char const* externalData, size_t len); + void readData(char* data, size_t len) override; + void writeData(char const* data, size_t len) override; + private: ExternalBuffer m_buffer; }; diff --git a/source/frontend/StarMainMixer.cpp b/source/frontend/StarMainMixer.cpp index c49a0fc..59b6b52 100644 --- a/source/frontend/StarMainMixer.cpp +++ b/source/frontend/StarMainMixer.cpp @@ -127,8 +127,8 @@ void MainMixer::setVolume(float volume, float rampTime) { m_mixer->setVolume(volume, rampTime); } -void MainMixer::read(int16_t* sampleData, size_t frameCount) { - m_mixer->read(sampleData, frameCount); +void MainMixer::read(int16_t* sampleData, size_t frameCount, Mixer::ExtraMixFunction extraMixFunction) { + m_mixer->read(sampleData, frameCount, extraMixFunction); } } diff --git a/source/frontend/StarMainMixer.hpp b/source/frontend/StarMainMixer.hpp index ecf9443..2582a2f 100644 --- a/source/frontend/StarMainMixer.hpp +++ b/source/frontend/StarMainMixer.hpp @@ -22,7 +22,7 @@ public: MixerPtr mixer() const; void setVolume(float volume, float rampTime = 0.0f); - void read(int16_t* sampleData, size_t frameCount); + void read(int16_t* sampleData, size_t frameCount, Mixer::ExtraMixFunction = {}); private: UniverseClientPtr m_universeClient; diff --git a/source/game/CMakeLists.txt b/source/game/CMakeLists.txt index 120e9e8..fa55950 100644 --- a/source/game/CMakeLists.txt +++ b/source/game/CMakeLists.txt @@ -156,6 +156,7 @@ SET (star_game_HEADERS StarVehicle.hpp StarVehicleDatabase.hpp StarVersioningDatabase.hpp + StarVoice.hpp StarWarping.hpp StarWeather.hpp StarWeatherTypes.hpp @@ -414,6 +415,7 @@ SET (star_game_SOURCES StarVehicle.cpp StarVehicleDatabase.cpp StarVersioningDatabase.cpp + StarVoice.cpp StarWarping.cpp StarWeather.cpp StarWeatherTypes.cpp diff --git a/source/game/StarNetworkedAnimator.cpp b/source/game/StarNetworkedAnimator.cpp index f76882f..6f74f02 100644 --- a/source/game/StarNetworkedAnimator.cpp +++ b/source/game/StarNetworkedAnimator.cpp @@ -383,6 +383,15 @@ void NetworkedAnimator::setGlobalTag(String tagName, String tagValue) { m_globalTags.set(move(tagName), move(tagValue)); } +void NetworkedAnimator::removeGlobalTag(String const& tagName) { + m_globalTags.remove(tagName); +} + +String const* NetworkedAnimator::globalTagPtr(String const& tagName) const { + return m_globalTags.ptr(tagName); +} + + void NetworkedAnimator::setPartTag(String const& partType, String tagName, String tagValue) { m_partTags[partType].set(move(tagName), move(tagValue)); } diff --git a/source/game/StarNetworkedAnimator.hpp b/source/game/StarNetworkedAnimator.hpp index 563a21e..63f7774 100644 --- a/source/game/StarNetworkedAnimator.hpp +++ b/source/game/StarNetworkedAnimator.hpp @@ -115,6 +115,8 @@ public: // Drawables can also have a <frame> tag which will be set to whatever the // current state frame is (1 indexed, so the first frame is 1). void setGlobalTag(String tagName, String tagValue); + void removeGlobalTag(String const& tagName); + String const* globalTagPtr(String const& tagName) const; void setPartTag(String const& partType, String tagName, String tagValue); void setProcessingDirectives(Directives const& directives); 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<StringView> 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<uint8_t>() - 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<Json>(); } + 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); +} + + } diff --git a/source/game/StarPlayer.hpp b/source/game/StarPlayer.hpp index c63fe15..d534e03 100644 --- a/source/game/StarPlayer.hpp +++ b/source/game/StarPlayer.hpp @@ -447,6 +447,28 @@ public: using Entity::setTeam; + NetworkedAnimatorPtr effectsAnimator(); + + // We need to store ephemeral/large/always-changing networked properties that other clients can read. Candidates: + // genericProperties: + // Non-starter, is not networked. + // statusProperties: + // Nope! Changes to the status properties aren't networked efficiently - one change resends the whole map. + // We can't fix that because it would break compatibility with vanilla servers. + // effectsAnimator's globalTags: + // Cursed, but viable. + // Efficient networking due to using a NetElementMapWrapper. + // Unfortunately values are Strings, so to work with Json we need to serialize/deserialize. Whatever. + // Additionally, this is compatible with vanilla networking. + // I call this a 'secret property'. + + // If the secret property exists as a serialized Json string, returns a view to it without deserializing. + Maybe<StringView> getSecretPropertyView(String const& name) const; + // Gets a secret Json property. It will be de-serialized. + Json getSecretProperty(String const& name, Json defaultValue = Json()) const; + // Sets a secret Json property. It will be serialized. + void setSecretProperty(String const& name, Json const& value); + private: enum class State { Idle, diff --git a/source/game/StarVoice.cpp b/source/game/StarVoice.cpp new file mode 100644 index 0000000..1c949c1 --- /dev/null +++ b/source/game/StarVoice.cpp @@ -0,0 +1,35 @@ +#include "StarVoice.hpp" + +namespace Star { + +STAR_EXCEPTION(VoiceException, StarException); + +void Voice::mix(int16_t* buffer, size_t frames, unsigned channels) { + +} + +Voice* Voice::s_singleton; + +Voice* Voice::singletonPtr() { + return s_singleton; +} + +Voice& Voice::singleton() { + if (!s_singleton) + throw VoiceException("Voice::singleton() called with no Voice instance available"); + else + return *s_singleton; +} + +Voice::Voice() { + if (s_singleton) + throw VoiceException("Singleton Voice has been constructed twice"); + + s_singleton = this; +} + +Voice::~Voice() { + s_singleton = nullptr; +} + +}
\ No newline at end of file diff --git a/source/game/StarVoice.hpp b/source/game/StarVoice.hpp new file mode 100644 index 0000000..f102889 --- /dev/null +++ b/source/game/StarVoice.hpp @@ -0,0 +1,32 @@ +#ifndef STAR_VOICE_HPP +#define STAR_VOICE_HPP +#include "StarString.hpp" + +namespace Star { + + STAR_CLASS(Voice); + + class Voice { + public: + void mix(int16_t* buffer, size_t frames, unsigned channels); + + // Get pointer to the singleton Voice instance, if it exists. Otherwise, + // returns nullptr. + static Voice* singletonPtr(); + + // Gets reference to Voice singleton, throws VoiceException if root + // is not initialized. + static Voice& singleton(); + + Voice(); + ~Voice(); + + Voice(Voice const&) = delete; + Voice& operator=(Voice const&) = delete; + private: + static Voice* s_singleton; + }; + +} + +#endif
\ No newline at end of file |