mirror of
https://github.com/rive-app/rive-cpp.git
synced 2026-01-18 21:21:17 +01:00
* added setter methods for properties * made lists a reference. unsettle artboard in a bunch of places that needed it. * made lists references. Removed field notify value from lists. * added context menu entry for cleaning generated classes * started implementing clean function * added clean context menu entry for cleaning up duplicate blueprint types * attempting to make list manipulation more stable * re arranged how view models work * rebase error * now properly removes handle even if objects goes out of scope * uneeded header and clang format * more uneeded header * missing check * added unit test for getHandleForInstance * addresed pr comments * pr comments * missing semi colon * clang-format Co-authored-by: Jonathon Copeland <jcopela4@gmail.com>
206 lines
7.2 KiB
C++
206 lines
7.2 KiB
C++
/*
|
|
* Copyright 2025 Rive
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "rive/command_queue.hpp"
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <thread>
|
|
#include <unordered_map>
|
|
#include <type_traits>
|
|
|
|
namespace rive
|
|
{
|
|
|
|
// Server-side worker that executes commands from a CommandQueue.
|
|
class CommandServer
|
|
{
|
|
public:
|
|
CommandServer(rcp<CommandQueue>, Factory*);
|
|
virtual ~CommandServer();
|
|
|
|
Factory* factory() const { return m_factory; }
|
|
|
|
File* getFile(FileHandle) const;
|
|
bool getWasDisconnected() const { return m_wasDisconnectReceived; }
|
|
RenderImage* getImage(RenderImageHandle) const;
|
|
AudioSource* getAudioSource(AudioSourceHandle) const;
|
|
Font* getFont(FontHandle) const;
|
|
ArtboardInstance* getArtboardInstance(ArtboardHandle) const;
|
|
rcp<BindableArtboard> getBindableArtboard(ArtboardHandle) const;
|
|
StateMachineInstance* getStateMachineInstance(StateMachineHandle) const;
|
|
ViewModelInstanceRuntime* getViewModelInstance(
|
|
ViewModelInstanceHandle) const;
|
|
ViewModelInstanceHandle getHandleForInstance(
|
|
ViewModelInstanceRuntime*) const;
|
|
// Wait for queue to not be empty, then returns pollMessages.
|
|
bool waitCommands();
|
|
// Returns imidiatly after checking messages. If there are none just returns
|
|
// returns !m_wasDisconnectReceived.
|
|
bool processCommands();
|
|
// Blocks and runs waitMessages until disconnect is received.
|
|
void serveUntilDisconnect();
|
|
|
|
struct Subscription
|
|
{
|
|
// The request Id for sbuscribing to this particular property.
|
|
uint64_t requestId;
|
|
// Information about the property we want to "subsribe" to.
|
|
PropertyData data;
|
|
// The root view model of from the perspective of the path in data.name.
|
|
ViewModelInstanceHandle rootViewModel;
|
|
};
|
|
|
|
#ifdef TESTING
|
|
// Expose cursorPosForPointerEvent for testing.
|
|
Vec2D testing_cursorPosForPointerEvent(StateMachineInstance* instance,
|
|
CommandQueue::PointerEvent event)
|
|
{
|
|
return cursorPosForPointerEvent(instance, event);
|
|
}
|
|
|
|
const std::vector<Subscription>& testing_getSubsciptions() const
|
|
{
|
|
return m_propertySubscriptions;
|
|
}
|
|
|
|
RenderImageHandle testing_globalImageNamed(std::string name);
|
|
AudioSourceHandle testing_globalAudioNamed(std::string name);
|
|
FontHandle testing_globalFontNamed(std::string name);
|
|
|
|
bool testing_globalImageContains(std::string name);
|
|
bool testing_globalAudioContains(std::string name);
|
|
bool testing_globalFontContains(std::string name);
|
|
#endif
|
|
|
|
private:
|
|
friend class CommandQueue;
|
|
|
|
template <typename HandleType> class ErrorReporter
|
|
{
|
|
public:
|
|
ErrorReporter(CommandServer* server,
|
|
HandleType handle,
|
|
uint64_t requestId,
|
|
CommandQueue::Message message) :
|
|
m_server(server), m_lock(m_server->m_commandQueue->m_messageMutex)
|
|
{
|
|
assert(server);
|
|
assert(message >= CommandQueue::Message::fileError &&
|
|
message <= CommandQueue::Message::stateMachineError);
|
|
|
|
std::cerr << "ERROR : ";
|
|
m_server->m_commandQueue->m_messageStream << message;
|
|
m_server->m_commandQueue->m_messageStream << handle;
|
|
m_server->m_commandQueue->m_messageStream << requestId;
|
|
}
|
|
|
|
ErrorReporter& operator<<(std::vector<std::string> vector)
|
|
{
|
|
m_ostringstream << "{ ";
|
|
for (auto& s : vector)
|
|
m_ostringstream << s << ",";
|
|
m_ostringstream << "} ";
|
|
return *this;
|
|
}
|
|
|
|
template <typename T> ErrorReporter& operator<<(const T& t)
|
|
{
|
|
m_ostringstream << t;
|
|
return *this;
|
|
}
|
|
|
|
~ErrorReporter()
|
|
{
|
|
std::string str = m_ostringstream.str();
|
|
assert(!str.empty());
|
|
std::cerr << str << "\n";
|
|
m_server->m_commandQueue->m_messageNames << std::move(str);
|
|
}
|
|
|
|
CommandServer* m_server;
|
|
std::unique_lock<std::mutex> m_lock;
|
|
std::ostringstream m_ostringstream;
|
|
};
|
|
|
|
void checkPropertySubscriptions();
|
|
|
|
Vec2D cursorPosForPointerEvent(StateMachineInstance*,
|
|
const CommandQueue::PointerEvent&);
|
|
|
|
void cleanupArtboard(ArtboardHandle handle, uint64_t requestId)
|
|
{
|
|
auto itr = m_artboards.find(handle);
|
|
if (itr != m_artboards.end())
|
|
{
|
|
auto dependencyItr = m_artboardDependencies.find(handle);
|
|
if (dependencyItr != m_artboardDependencies.end())
|
|
{
|
|
auto& stateMachineVector = dependencyItr->second;
|
|
for (auto stateMachine : stateMachineVector)
|
|
{
|
|
if (m_stateMachines.erase(stateMachine) > 0)
|
|
{
|
|
std::unique_lock<std::mutex> lock(
|
|
m_commandQueue->m_messageMutex);
|
|
m_commandQueue->m_messageStream
|
|
<< CommandQueue::Message::stateMachineDeleted;
|
|
m_commandQueue->m_messageStream << stateMachine;
|
|
m_commandQueue->m_messageStream << requestId;
|
|
}
|
|
}
|
|
|
|
m_artboardDependencies.erase(dependencyItr);
|
|
}
|
|
m_artboards.erase(itr);
|
|
std::unique_lock<std::mutex> lock(m_commandQueue->m_messageMutex);
|
|
m_commandQueue->m_messageStream
|
|
<< CommandQueue::Message::artboardDeleted;
|
|
m_commandQueue->m_messageStream << handle;
|
|
m_commandQueue->m_messageStream << requestId;
|
|
}
|
|
}
|
|
|
|
bool m_wasDisconnectReceived = false;
|
|
const rcp<CommandQueue> m_commandQueue;
|
|
Factory* const m_factory;
|
|
#ifndef NDEBUG
|
|
const std::thread::id m_threadID;
|
|
#endif
|
|
|
|
// Vector to iterate on for subscriptions. This is a vector instead of a map
|
|
// because we iterate through the entire vector every frame anyway.
|
|
std::vector<Subscription> m_propertySubscriptions;
|
|
|
|
// Dependencies
|
|
// When a file gets deleted artboards and statemachine become invalid. Here
|
|
// we hold a reference to the artboard only because that artboard has a
|
|
// dependency to the State Machine.
|
|
std::unordered_map<FileHandle, std::vector<ArtboardHandle>>
|
|
m_fileDependencies;
|
|
// When an artboard gets deleted the statemachine assosiated with it is also
|
|
// now invalid.
|
|
std::unordered_map<ArtboardHandle, std::vector<StateMachineHandle>>
|
|
m_artboardDependencies;
|
|
|
|
// Handle Maps
|
|
std::unordered_map<FileHandle, rcp<File>> m_files;
|
|
std::unordered_map<FontHandle, rcp<Font>> m_fonts;
|
|
std::unordered_map<RenderImageHandle, rcp<RenderImage>> m_images;
|
|
std::unordered_map<AudioSourceHandle, rcp<AudioSource>> m_audioSources;
|
|
std::unordered_map<ArtboardHandle, rcp<BindableArtboard>> m_artboards;
|
|
std::unordered_map<ViewModelInstanceHandle, rcp<ViewModelInstanceRuntime>>
|
|
m_viewModels;
|
|
std::unordered_map<StateMachineHandle,
|
|
std::unique_ptr<StateMachineInstance>>
|
|
m_stateMachines;
|
|
|
|
std::unordered_map<DrawKey, CommandServerDrawCallback> m_uniqueDraws;
|
|
|
|
class CommandFileAssetLoader;
|
|
rcp<CommandFileAssetLoader> m_fileAssetLoader;
|
|
};
|
|
}; // namespace rive
|