summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2024-09-04 16:25:43 -0400
committerNat Goodspeed <nat@lindenlab.com>2024-09-04 16:25:43 -0400
commitd72ddddcaf07a1b9c7fb479e2432cf1bfa286a94 (patch)
treee86fe98bc9c46b4915d24684b07b7a8721465d6c
parenta980a8f30870d35a5a45cbf52169f7bf35cf95ce (diff)
parent487973d3f0ee9b8583b3d977ca6a405cba5fe518 (diff)
Merge branch 'release/luau-scripting' into lua-merge
-rw-r--r--indra/newview/llagentlistener.cpp75
-rw-r--r--indra/newview/llagentlistener.h8
-rw-r--r--indra/newview/llappviewer.cpp2
-rw-r--r--indra/newview/llfloaterluadebug.cpp2
-rw-r--r--indra/newview/llluamanager.cpp16
-rw-r--r--indra/newview/llluamanager.h5
-rw-r--r--indra/newview/llviewerstats.cpp5
-rw-r--r--indra/newview/scripts/lua/require/LLAgent.lua18
-rw-r--r--indra/newview/scripts/lua/test_animation.lua28
9 files changed, 152 insertions, 7 deletions
diff --git a/indra/newview/llagentlistener.cpp b/indra/newview/llagentlistener.cpp
index f3d6ad9a38..2ff4adcf5e 100644
--- a/indra/newview/llagentlistener.cpp
+++ b/indra/newview/llagentlistener.cpp
@@ -34,12 +34,14 @@
#include "llagentcamera.h"
#include "llvoavatar.h"
#include "llcommandhandler.h"
+#include "llinventorymodel.h"
#include "llslurl.h"
#include "llurldispatcher.h"
#include "llviewernetwork.h"
#include "llviewerobject.h"
#include "llviewerobjectlist.h"
#include "llviewerregion.h"
+#include "llvoavatarself.h"
#include "llsdutil.h"
#include "llsdutil_math.h"
#include "lltoolgrab.h"
@@ -47,9 +49,12 @@
#include "llagentcamera.h"
#include <functional>
+static const F64 PLAY_ANIM_THROTTLE_PERIOD = 1.f;
+
LLAgentListener::LLAgentListener(LLAgent &agent)
: LLEventAPI("LLAgent",
"LLAgent listener to (e.g.) teleport, sit, stand, etc."),
+ mPlayAnimThrottle("playAnimation", &LLAgentListener::playAnimation_, this, PLAY_ANIM_THROTTLE_PERIOD),
mAgent(agent)
{
add("requestTeleport",
@@ -157,6 +162,19 @@ LLAgentListener::LLAgentListener(LLAgent &agent)
add("removeCameraParams",
"Reset Follow camera params",
&LLAgentListener::removeFollowCamParams);
+
+ add("playAnimation",
+ "Play [\"item_id\"] animation locally (by default) or [\"inworld\"] (when set to true)",
+ &LLAgentListener::playAnimation,
+ llsd::map("item_id", LLSD(), "reply", LLSD()));
+ add("stopAnimation",
+ "Stop playing [\"item_id\"] animation",
+ &LLAgentListener::stopAnimation,
+ llsd::map("item_id", LLSD(), "reply", LLSD()));
+ add("getAnimationInfo",
+ "Return information about [\"item_id\"] animation",
+ &LLAgentListener::getAnimationInfo,
+ llsd::map("item_id", LLSD(), "reply", LLSD()));
}
void LLAgentListener::requestTeleport(LLSD const & event_data) const
@@ -591,3 +609,60 @@ void LLAgentListener::removeFollowCamParams(LLSD const & event) const
{
LLFollowCamMgr::getInstance()->removeFollowCamParams(gAgentID);
}
+
+LLViewerInventoryItem* get_anim_item(LLEventAPI::Response &response, const LLSD &event_data)
+{
+ LLViewerInventoryItem* item = gInventory.getItem(event_data["item_id"].asUUID());
+ if (!item || (item->getInventoryType() != LLInventoryType::IT_ANIMATION))
+ {
+ response.error(stringize("Animation item ", std::quoted(event_data["item_id"].asString()), " was not found"));
+ return NULL;
+ }
+ return item;
+}
+
+void LLAgentListener::playAnimation(LLSD const &event_data)
+{
+ Response response(LLSD(), event_data);
+ if (LLViewerInventoryItem* item = get_anim_item(response, event_data))
+ {
+ mPlayAnimThrottle(item->getAssetUUID(), event_data["inworld"].asBoolean());
+ }
+}
+
+void LLAgentListener::playAnimation_(const LLUUID& asset_id, const bool inworld)
+{
+ if (inworld)
+ {
+ mAgent.sendAnimationRequest(asset_id, ANIM_REQUEST_START);
+ }
+ else
+ {
+ gAgentAvatarp->startMotion(asset_id);
+ }
+}
+
+void LLAgentListener::stopAnimation(LLSD const &event_data)
+{
+ Response response(LLSD(), event_data);
+ if (LLViewerInventoryItem* item = get_anim_item(response, event_data))
+ {
+ gAgentAvatarp->stopMotion(item->getAssetUUID());
+ mAgent.sendAnimationRequest(item->getAssetUUID(), ANIM_REQUEST_STOP);
+ }
+}
+
+void LLAgentListener::getAnimationInfo(LLSD const &event_data)
+{
+ Response response(LLSD(), event_data);
+ if (LLViewerInventoryItem* item = get_anim_item(response, event_data))
+ {
+ // if motion exists, will return existing one
+ LLMotion* motion = gAgentAvatarp->createMotion(item->getAssetUUID());
+ response["anim_info"] = llsd::map("duration", motion->getDuration(),
+ "is_loop", motion->getLoop(),
+ "num_joints", motion->getNumJointMotions(),
+ "asset_id", item->getAssetUUID(),
+ "priority", motion->getPriority());
+ }
+}
diff --git a/indra/newview/llagentlistener.h b/indra/newview/llagentlistener.h
index 2a24de3f52..c77d1b3fc9 100644
--- a/indra/newview/llagentlistener.h
+++ b/indra/newview/llagentlistener.h
@@ -31,6 +31,7 @@
#define LL_LLAGENTLISTENER_H
#include "lleventapi.h"
+#include "throttle.h"
class LLAgent;
class LLSD;
@@ -60,12 +61,19 @@ private:
void setFollowCamParams(LLSD const & event_data) const;
void setFollowCamActive(LLSD const & event_data) const;
void removeFollowCamParams(LLSD const & event_data) const;
+
+ void playAnimation(LLSD const &event_data);
+ void playAnimation_(const LLUUID& asset_id, const bool inworld);
+ void stopAnimation(LLSD const &event_data);
+ void getAnimationInfo(LLSD const &event_data);
LLViewerObject * findObjectClosestTo( const LLVector3 & position ) const;
private:
LLAgent & mAgent;
LLUUID mFollowTarget;
+
+ LogThrottle<LLError::LEVEL_DEBUG, void(const LLUUID &, const bool)> mPlayAnimThrottle;
};
#endif // LL_LLAGENTLISTENER_H
diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp
index 23e06245f2..c0096c8d6d 100644
--- a/indra/newview/llappviewer.cpp
+++ b/indra/newview/llappviewer.cpp
@@ -1252,7 +1252,7 @@ bool LLAppViewer::init()
while (scripts.next(script))
{
LL_DEBUGS("InitInfo") << "LuaAutorunPath: " << absdir << ": " << script << LL_ENDL;
- LLLUAmanager::runScriptFile((abspath / script).string());
+ LLLUAmanager::runScriptFile((abspath / script).string(), true);
}
});
diff --git a/indra/newview/llfloaterluadebug.cpp b/indra/newview/llfloaterluadebug.cpp
index 2d0f8949e5..dc989fe15d 100644
--- a/indra/newview/llfloaterluadebug.cpp
+++ b/indra/newview/llfloaterluadebug.cpp
@@ -127,7 +127,7 @@ void LLFloaterLUADebug::runSelectedScript(const std::vector<std::string> &filena
if (!filepath.empty())
{
mScriptPath->setText(filepath);
- LLLUAmanager::runScriptFile(filepath, [this](int count, const LLSD &result)
+ LLLUAmanager::runScriptFile(filepath, false, [this](int count, const LLSD &result)
{
completion(count, result);
});
diff --git a/indra/newview/llluamanager.cpp b/indra/newview/llluamanager.cpp
index 41b1cd55b7..91a34345be 100644
--- a/indra/newview/llluamanager.cpp
+++ b/indra/newview/llluamanager.cpp
@@ -49,6 +49,8 @@
#include <string_view>
#include <vector>
+S32 LLLUAmanager::sAutorunScriptCount = 0;
+S32 LLLUAmanager::sScriptCount = 0;
std::map<std::string, std::string> LLLUAmanager::sScriptNames;
lua_function(sleep, "sleep(seconds): pause the running coroutine")
@@ -172,7 +174,7 @@ LLLUAmanager::startScriptFile(const std::string& filename)
// Despite returning from startScriptFile(), we need this Promise to
// remain alive until the callback has fired.
auto promise{ std::make_shared<LLCoros::Promise<script_result>>() };
- runScriptFile(filename,
+ runScriptFile(filename, false,
[promise](int count, LLSD result)
{ promise->set_value({ count, result }); });
return LLCoros::getFuture(*promise);
@@ -183,11 +185,11 @@ LLLUAmanager::script_result LLLUAmanager::waitScriptFile(const std::string& file
return startScriptFile(filename).get();
}
-void LLLUAmanager::runScriptFile(const std::string &filename, script_result_fn result_cb,
- script_finished_fn finished_cb)
+void LLLUAmanager::runScriptFile(const std::string &filename, bool autorun,
+ script_result_fn result_cb, script_finished_fn finished_cb)
{
// A script_result_fn will be called when LuaState::expr() completes.
- LLCoros::instance().launch(filename, [filename, result_cb, finished_cb]()
+ LLCoros::instance().launch(filename, [filename, autorun, result_cb, finished_cb]()
{
ScriptObserver observer(LLCoros::getName(), filename);
llifstream in_file;
@@ -195,6 +197,12 @@ void LLLUAmanager::runScriptFile(const std::string &filename, script_result_fn r
if (in_file.is_open())
{
+ if (autorun)
+ {
+ sAutorunScriptCount++;
+ }
+ sScriptCount++;
+
// A script_finished_fn is used to initialize the LuaState.
// It will be called when the LuaState is destroyed.
LuaState L(finished_cb);
diff --git a/indra/newview/llluamanager.h b/indra/newview/llluamanager.h
index 08997c29a5..df76ddd3e2 100644
--- a/indra/newview/llluamanager.h
+++ b/indra/newview/llluamanager.h
@@ -58,7 +58,7 @@ public:
// same semantics as script_result_fn parameters
typedef std::pair<int, LLSD> script_result;
- static void runScriptFile(const std::string &filename, script_result_fn result_cb = {},
+ static void runScriptFile(const std::string &filename, bool autorun = false, script_result_fn result_cb = {},
script_finished_fn finished_cb = {});
// Start running a Lua script file, returning an LLCoros::Future whose
// get() method will pause the calling coroutine until it can deliver the
@@ -84,6 +84,9 @@ public:
static const std::map<std::string, std::string> getScriptNames() { return sScriptNames; }
+ static S32 sAutorunScriptCount;
+ static S32 sScriptCount;
+
private:
static std::map<std::string, std::string> sScriptNames;
};
diff --git a/indra/newview/llviewerstats.cpp b/indra/newview/llviewerstats.cpp
index 3499c7eb7d..5218552314 100644
--- a/indra/newview/llviewerstats.cpp
+++ b/indra/newview/llviewerstats.cpp
@@ -66,6 +66,7 @@
#include "llinventorymodel.h"
#include "lluiusage.h"
#include "lltranslate.h"
+#include "llluamanager.h"
// "Minimal Vulkan" to get max API Version
@@ -673,6 +674,10 @@ void send_viewer_stats(bool include_preferences)
system["shader_level"] = shader_level;
+ LLSD &scripts = body["scripts"];
+ scripts["lua_scripts"] = LLLUAmanager::sScriptCount;
+ scripts["lua_auto_scripts"] = LLLUAmanager::sAutorunScriptCount;
+
LLSD &download = body["downloads"];
download["world_kbytes"] = F64Kilobytes(gTotalWorldData).value();
diff --git a/indra/newview/scripts/lua/require/LLAgent.lua b/indra/newview/scripts/lua/require/LLAgent.lua
index 5ee092f2f6..07ef1e0b0b 100644
--- a/indra/newview/scripts/lua/require/LLAgent.lua
+++ b/indra/newview/scripts/lua/require/LLAgent.lua
@@ -53,4 +53,22 @@ function LLAgent.removeCamParams()
leap.send('LLAgent', {op = 'removeCameraParams'})
end
+-- Play specified animation by "item_id" locally
+-- if "inworld" is specified as true, animation will be played inworld instead
+function LLAgent.playAnimation(...)
+ local args = mapargs('item_id,inworld', ...)
+ args.op = 'playAnimation'
+ return leap.request('LLAgent', args)
+end
+
+function LLAgent.stopAnimation(item_id)
+ return leap.request('LLAgent', {op = 'stopAnimation', item_id=item_id})
+end
+
+-- Get animation info by "item_id"
+-- reply contains "duration", "is_loop", "num_joints", "asset_id", "priority"
+function LLAgent.getAnimationInfo(item_id)
+ return leap.request('LLAgent', {op = 'getAnimationInfo', item_id=item_id}).anim_info
+end
+
return LLAgent
diff --git a/indra/newview/scripts/lua/test_animation.lua b/indra/newview/scripts/lua/test_animation.lua
new file mode 100644
index 0000000000..c16fef4918
--- /dev/null
+++ b/indra/newview/scripts/lua/test_animation.lua
@@ -0,0 +1,28 @@
+LLInventory = require 'LLInventory'
+LLAgent = require 'LLAgent'
+
+-- Get 'Animations' folder id (you can see all folder types via LLInventory.getFolderTypeNames())
+animations_id = LLInventory.getBasicFolderID('animatn')
+-- Get animations from the 'Animation' folder (you can see all folder types via LLInventory.getAssetTypeNames())
+anims = LLInventory.collectDescendentsIf{folder_id=animations_id, type="animatn"}.items
+
+local anim_ids = {}
+for key in pairs(anims) do
+ table.insert(anim_ids, key)
+end
+
+-- Start playing a random animation
+math.randomseed(os.time())
+local random_id = anim_ids[math.random(#anim_ids)]
+local anim_info = LLAgent.getAnimationInfo(random_id)
+
+print("Starting animation locally: " .. anims[random_id].name)
+print("Loop: " .. anim_info.is_loop .. " Joints: " .. anim_info.num_joints .. " Duration " .. tonumber(string.format("%.2f", anim_info.duration)))
+LLAgent.playAnimation{item_id=random_id}
+
+-- Stop animation after 3 sec if it's looped or longer than 3 sec
+if anim_info.is_loop == 1 or anim_info.duration > 3 then
+ LL.sleep(3)
+ print("Stop animation.")
+ LLAgent.stopAnimation(random_id)
+end