summaryrefslogtreecommitdiff
path: root/indra/newview/llpublishedobjectmgr.h
diff options
context:
space:
mode:
Diffstat (limited to 'indra/newview/llpublishedobjectmgr.h')
-rw-r--r--indra/newview/llpublishedobjectmgr.h349
1 files changed, 349 insertions, 0 deletions
diff --git a/indra/newview/llpublishedobjectmgr.h b/indra/newview/llpublishedobjectmgr.h
new file mode 100644
index 0000000000..8a6c8b705c
--- /dev/null
+++ b/indra/newview/llpublishedobjectmgr.h
@@ -0,0 +1,349 @@
+/**
+ * @file llpublishedobjectmgr.h
+ * @brief Published object state/logic manager extracted from llscripteditorws
+ *
+ * $LicenseInfo:firstyear=2026&license=viewerlgpl$
+ * Second Life Viewer Source Code
+ * Copyright (C) 2026, Linden Research, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation;
+ * version 2.1 of the License only.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ *
+ * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
+ * $/LicenseInfo$
+ */
+
+#pragma once
+
+#include "llsd.h"
+#include "lltimer.h"
+#include "lluuid.h"
+#include "lleventtimer.h"
+#include "stdtypes.h"
+
+#include <map>
+#include <functional>
+#include <memory>
+#include <optional>
+#include <set>
+#include <string>
+#include <vector>
+
+class LLScriptEditorWSServer;
+class LLChat;
+class LLPublishedPrimListener;
+class LLViewerObject;
+
+class LLPublishedObjectMgr
+{
+public:
+ struct PublishedPrimInfo
+ {
+ LLUUID mPrimID;
+ std::string mPrimName;
+ S32 mLinkNumber;
+ std::string mPrimDescription;
+ S16 mInventorySerial;
+ LLSD mPermissions;
+ };
+
+ struct PublishedObjectInfo
+ {
+ PublishedObjectInfo();
+ ~PublishedObjectInfo();
+ PublishedObjectInfo(PublishedObjectInfo&&) noexcept;
+ PublishedObjectInfo& operator=(PublishedObjectInfo&&) noexcept;
+ PublishedObjectInfo(const PublishedObjectInfo&) = delete;
+ PublishedObjectInfo& operator=(const PublishedObjectInfo&) = delete;
+
+ LLUUID mObjectID;
+ LLUUID mOwnerID;
+ std::string mObjectName;
+ std::string mObjectDescription;
+ std::string mRegionName;
+ bool mCanSaveBackToContents{ false };
+ LLUUID mSourceTaskID;
+ LLSD mPermissions;
+ std::vector<PublishedPrimInfo> mPrims;
+ std::vector<std::unique_ptr<LLPublishedPrimListener>> mListeners;
+ };
+
+ struct PendingPublish
+ {
+ PendingPublish();
+ ~PendingPublish();
+ PendingPublish(PendingPublish&&) noexcept;
+ PendingPublish& operator=(PendingPublish&&) noexcept;
+ PendingPublish(const PendingPublish&) = delete;
+ PendingPublish& operator=(const PendingPublish&) = delete;
+
+ LLUUID mObjectID;
+ std::set<LLUUID> mPendingPrims;
+ std::vector<std::unique_ptr<LLPublishedPrimListener>> mListeners;
+ bool mHasRootProperties{ false };
+ std::string mObjectName;
+ std::string mObjectDescription;
+ std::map<LLUUID, std::string> mPrimNames;
+ std::map<LLUUID, std::string> mPrimDescriptions;
+ };
+
+ enum class InventoryChangeKind
+ {
+ NOT_PUBLISHED,
+ CHILD_READY_WAIT,
+ CHILD_READY_FLUSH_NOW,
+ ROOT_INVENTORY_UPDATE,
+ CHILD_INVENTORY_UPDATE
+ };
+
+ struct InventoryChangeResult
+ {
+ InventoryChangeKind mKind{ InventoryChangeKind::NOT_PUBLISHED };
+ LLSD mUpdate;
+ };
+
+ struct PrimInventoryEventResult
+ {
+ InventoryChangeKind mKind{ InventoryChangeKind::NOT_PUBLISHED };
+ LLSD mUpdate;
+ bool mTimingConsumed{ false };
+ F64 mTimingElapsedSec{ 0.0 };
+ bool mHasPendingItemCreate{ false };
+ std::string mPendingItemCreatePump;
+ };
+
+ class RuntimeEventAggregator
+ {
+ public:
+ enum class Channel
+ {
+ DEBUG,
+ OWNER_SAY
+ };
+
+ enum class VM
+ {
+ LSL2,
+ LUAU
+ };
+
+ struct StackFrame
+ {
+ S32 mLine{ 0 };
+ std::string mFunction;
+ std::string mSource;
+ };
+
+ struct ParsedError
+ {
+ std::string mSource;
+ std::string mError;
+ S32 mLine{ 0 };
+ S32 mColumn{ 0 };
+ std::vector<StackFrame> mStack;
+ };
+
+ struct Fragment
+ {
+ LLUUID mFromID;
+ std::string mFromName;
+ std::string mText;
+ };
+
+ using fragments_t = std::vector<Fragment>;
+
+ struct RuntimeEvent
+ {
+ VM mVM;
+ Channel mChannel;
+ fragments_t mFragments;
+ ParsedError mError;
+ };
+
+ using FlushCallback = std::function<void(const RuntimeEvent&)>;
+
+ explicit RuntimeEventAggregator(FlushCallback flush_callback);
+
+ void ingest(const LLUUID& from_id,
+ const std::string& from_name,
+ const std::string& text,
+ VM vm,
+ Channel channel);
+ void flushExpired();
+ void flush();
+ ParsedError parseError(VM vm,
+ const fragments_t& fragments) const;
+ bool hasPending() const;
+
+ private:
+ struct PendingBurst
+ {
+ LLUUID mFromID;
+ std::string mFromName;
+ VM mVM{ VM::LSL2 };
+ Channel mChannel{ Channel::DEBUG };
+ fragments_t mFragments;
+ LLTimer mTimer;
+ };
+
+ static constexpr F32 FRAGMENT_TIMEOUT = 1.0f;
+
+ void flushPending();
+ bool isNewBurst(const LLUUID& from_id,
+ const std::string& from_name,
+ VM vm,
+ Channel channel) const;
+
+ FlushCallback mFlushCallback;
+ std::unique_ptr<PendingBurst> mPending;
+ };
+
+ struct RuntimeChatEvent
+ {
+ RuntimeEventAggregator::Channel mChannel;
+ RuntimeEventAggregator::VM mVM;
+ LLUUID mRootID;
+ LLUUID mPrimID;
+ LLUUID mItemID;
+ std::string mObjectName;
+ std::string mScriptName;
+ std::string mMessage;
+ std::string mError;
+ S32 mLine{ 0 };
+ S32 mColumn{ 0 };
+ std::vector<std::string> mStack;
+ bool mIsError{ false };
+ };
+
+ using RuntimeEventCallback =
+ std::function<void(const RuntimeChatEvent&)>;
+
+ explicit LLPublishedObjectMgr(
+ LLScriptEditorWSServer* server = nullptr,
+ RuntimeEventCallback runtime_event_callback = {});
+ ~LLPublishedObjectMgr();
+
+ void flushExpiredRuntimeFragments();
+ void ingestRuntimeChat(
+ const LLChat& chat_msg,
+ RuntimeEventAggregator::Channel channel);
+ std::optional<RuntimeChatEvent> buildRuntimeChatEvent(
+ const RuntimeEventAggregator::RuntimeEvent& event) const;
+
+ bool hasPublished(const LLUUID& object_id) const { return mPublishedObjects.find(object_id) != mPublishedObjects.end(); }
+ void erasePublished(const LLUUID& object_id) { mPublishedObjects.erase(object_id); }
+ PublishedObjectInfo* getPublished(const LLUUID& object_id);
+ const PublishedObjectInfo* getPublished(const LLUUID& object_id) const;
+
+ template <typename Fn>
+ void forEachPublished(Fn&& fn) const
+ {
+ for (const auto& [id, info] : mPublishedObjects)
+ {
+ fn(id, info);
+ }
+ }
+
+ LLSD buildPrimInventoryLLSD(LLViewerObject* object) const;
+ LLSD buildPublishedObjectLLSD(LLViewerObject* root) const;
+ LLSD buildObjectListLLSD() const;
+
+ // Both depend on a valid LLSelectNode, so they yield nothing until ObjectProperties arrives.
+ static LLSD getObjectPermissionsLLSD(LLViewerObject* object);
+ static bool computeCanSaveBack(LLViewerObject* root, LLUUID& source_task_id);
+ static LLSD makePermissionsLLSD(U32 owner_mask, U32 next_owner_mask);
+
+ bool buildLinksetUpdateLLSD(const LLUUID& root_id, LLSD& update) const;
+ bool reconcileLinksetChildAdded(const LLUUID& root_id, LLViewerObject* child, F64 request_start_sec);
+ bool reconcileLinksetChildRemoved(const LLUUID& root_id, const LLUUID& child_id);
+ bool handlePrimInventoryReadyEvent(const LLUUID& object_id, const LLUUID& prim_id);
+ PrimInventoryEventResult handlePrimInventoryChangedEvent(
+ const LLUUID& object_id,
+ const LLUUID& prim_id,
+ LLViewerObject* prim,
+ F64 now_sec);
+ InventoryChangeResult reconcileInventoryChanged(
+ const LLUUID& object_id,
+ const LLUUID& prim_id,
+ LLViewerObject* prim);
+ bool applyPropertyChange(
+ const LLUUID& root_id,
+ const LLUUID& prim_id,
+ const std::string& name,
+ const std::string& desc,
+ LLSD& update);
+ bool applyPermissionsChange(
+ const LLUUID& root_id,
+ const LLUUID& prim_id,
+ const LLUUID& owner_id,
+ U32 owner_mask,
+ U32 next_owner_mask,
+ LLSD& update);
+
+ void beginPendingPublish(const LLUUID& object_id, const std::vector<LLViewerObject*>& prims);
+ bool hasPendingPublish(const LLUUID& object_id) const;
+ bool markPendingPublishPrimReady(const LLUUID& object_id, const LLUUID& prim_id);
+ std::vector<std::unique_ptr<LLPublishedPrimListener>> takePendingPublishListeners(const LLUUID& object_id);
+ void recordPendingPropertyChange(
+ const LLUUID& root_id,
+ const LLUUID& prim_id,
+ const std::string& name,
+ const std::string& desc);
+ void cancelPendingPublish(const LLUUID& object_id);
+ PublishedObjectInfo& finalizePendingPublish(const LLUUID& object_id, PublishedObjectInfo&& info);
+ bool cleanupObjectStateForUnpublish(const LLUUID& object_id);
+ void clearAllStateWithListenerCleanup();
+
+ bool reservePendingItemCreate(const LLUUID& prim_id, std::string&& pump_name);
+ bool consumePendingItemCreate(const LLUUID& prim_id, std::string& pump_name);
+ void clearPendingItemCreate(const LLUUID& prim_id);
+
+ bool consumePendingNewChild(const LLUUID& root_id, const LLUUID& child_id, bool& root_empty_after_remove);
+ void clearPendingNewChildren(const LLUUID& root_id) { mNewChildPrims.erase(root_id); }
+
+ bool hasActiveLinksetFlushTimer(const LLUUID& root_id) const;
+ void setLinksetFlushTimer(const LLUUID& root_id, const std::weak_ptr<LLEventTimer>& timer);
+ bool cancelLinksetFlushTimer(const LLUUID& root_id);
+ void clearLinksetFlushTimer(const LLUUID& root_id);
+
+ void setInventoryRequestStart(const LLUUID& prim_id, F64 start_sec) { mInventoryRequestStartSec[prim_id] = start_sec; }
+ bool hasInventoryRequestStart(const LLUUID& prim_id) const { return mInventoryRequestStartSec.find(prim_id) != mInventoryRequestStartSec.end(); }
+ bool consumeInventoryRequestStart(const LLUUID& prim_id, F64& start_sec);
+ bool markPrimInventorySerialAndDetectChange(const LLUUID& root_id, const LLUUID& prim_id, S16 inventory_serial);
+
+private:
+ LLScriptEditorWSServer* mServer{ nullptr };
+ std::unique_ptr<RuntimeEventAggregator> mRuntimeEventAggregator;
+ RuntimeEventCallback mRuntimeEventCallback;
+ std::unique_ptr<LLEventTimer> mRuntimeFlushTimer;
+
+ static constexpr F32 RUNTIME_FLUSH_INTERVAL = 0.25f;
+
+ void cancelPendingPublishWithCleanup(const LLUUID& object_id);
+ void clearPublishedListeners(const LLUUID& object_id);
+
+ using published_map_t = std::map<LLUUID, PublishedObjectInfo>;
+ using pending_publish_map_t = std::map<LLUUID, PendingPublish>;
+ using pending_item_create_map_t = std::map<LLUUID, std::string>;
+ using new_child_prims_map_t = std::map<LLUUID, std::set<LLUUID>>;
+ using linkset_flush_timer_map_t = std::map<LLUUID, std::weak_ptr<LLEventTimer>>;
+ using inventory_request_start_map_t = std::map<LLUUID, F64>;
+
+ published_map_t mPublishedObjects;
+ pending_publish_map_t mPendingPublishes;
+ pending_item_create_map_t mPendingItemCreates;
+ new_child_prims_map_t mNewChildPrims;
+ linkset_flush_timer_map_t mLinksetFlushTimers;
+ inventory_request_start_map_t mInventoryRequestStartSec;
+};