Skip to content

Commit 38c684b

Browse files
Merge pull request #2805 from secondlife/maxim/lua-nearby-avatars
Lua api to get info about nearby avatars and objects
2 parents 43beabd + 3ed29a7 commit 38c684b

16 files changed

+306
-32
lines changed

indra/newview/app_settings/settings.xml

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4133,6 +4133,17 @@
41334133
<key>Value</key>
41344134
<array />
41354135
</map>
4136+
<key>LuaDebugShowSource</key>
4137+
<map>
4138+
<key>Comment</key>
4139+
<string>Show source info in Lua Debug Console output</string>
4140+
<key>Persist</key>
4141+
<integer>1</integer>
4142+
<key>Type</key>
4143+
<string>Boolean</string>
4144+
<key>Value</key>
4145+
<integer>0</integer>
4146+
</map>
41364147
<key>GridStatusRSS</key>
41374148
<map>
41384149
<key>Comment</key>

indra/newview/llagentlistener.cpp

Lines changed: 154 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,14 @@
3232

3333
#include "llagent.h"
3434
#include "llagentcamera.h"
35+
#include "llavatarname.h"
36+
#include "llavatarnamecache.h"
3537
#include "llvoavatar.h"
3638
#include "llcommandhandler.h"
3739
#include "llinventorymodel.h"
3840
#include "llslurl.h"
3941
#include "llurldispatcher.h"
42+
#include "llviewercontrol.h"
4043
#include "llviewernetwork.h"
4144
#include "llviewerobject.h"
4245
#include "llviewerobjectlist.h"
@@ -46,7 +49,8 @@
4649
#include "llsdutil_math.h"
4750
#include "lltoolgrab.h"
4851
#include "llhudeffectlookat.h"
49-
#include "llagentcamera.h"
52+
#include "llviewercamera.h"
53+
#include "resultset.h"
5054
#include <functional>
5155

5256
static const F64 PLAY_ANIM_THROTTLE_PERIOD = 1.f;
@@ -176,6 +180,32 @@ LLAgentListener::LLAgentListener(LLAgent &agent)
176180
"Return information about [\"item_id\"] animation",
177181
&LLAgentListener::getAnimationInfo,
178182
llsd::map("item_id", LLSD(), "reply", LLSD()));
183+
184+
add("getID",
185+
"Return your own avatar ID",
186+
&LLAgentListener::getID,
187+
llsd::map("reply", LLSD()));
188+
189+
add("getNearbyAvatarsList",
190+
"Return result set key [\"result\"] for nearby avatars in a range of [\"dist\"]\n"
191+
"if [\"dist\"] is not specified, 'RenderFarClip' setting is used\n"
192+
"reply contains \"result\" table with \"id\", \"name\", \"global_pos\", \"region_pos\", \"region_id\" fields",
193+
&LLAgentListener::getNearbyAvatarsList,
194+
llsd::map("reply", LLSD()));
195+
196+
add("getNearbyObjectsList",
197+
"Return result set key [\"result\"] for nearby objects in a range of [\"dist\"]\n"
198+
"if [\"dist\"] is not specified, 'RenderFarClip' setting is used\n"
199+
"reply contains \"result\" table with \"id\", \"global_pos\", \"region_pos\", \"region_id\" fields",
200+
&LLAgentListener::getNearbyObjectsList,
201+
llsd::map("reply", LLSD()));
202+
203+
add("getAgentScreenPos",
204+
"Return screen position of the [\"avatar_id\"] avatar or own avatar if not specified\n"
205+
"reply contains \"x\", \"y\" coordinates and \"onscreen\" flag to indicate if it's actually in within the current window\n"
206+
"avatar render position is used as the point",
207+
&LLAgentListener::getAgentScreenPos,
208+
llsd::map("reply", LLSD()));
179209
}
180210

181211
void LLAgentListener::requestTeleport(LLSD const & event_data) const
@@ -693,3 +723,126 @@ void LLAgentListener::getAnimationInfo(LLSD const &event_data)
693723
"priority", motion->getPriority());
694724
}
695725
}
726+
727+
void LLAgentListener::getID(LLSD const& event_data)
728+
{
729+
Response response(llsd::map("id", gAgentID), event_data);
730+
}
731+
732+
struct AvResultSet : public LL::ResultSet
733+
{
734+
AvResultSet() : LL::ResultSet("nearby_avatars") {}
735+
std::vector<LLVOAvatar*> mAvatars;
736+
737+
int getLength() const override { return narrow(mAvatars.size()); }
738+
LLSD getSingle(int index) const override
739+
{
740+
auto av = mAvatars[index];
741+
LLAvatarName av_name;
742+
LLAvatarNameCache::get(av->getID(), &av_name);
743+
LLVector3 region_pos = av->getCharacterPosition();
744+
return llsd::map("id", av->getID(),
745+
"global_pos", ll_sd_from_vector3d(av->getPosGlobalFromAgent(region_pos)),
746+
"region_pos", ll_sd_from_vector3(region_pos),
747+
"name", av_name.getUserName(),
748+
"region_id", av->getRegion()->getRegionID());
749+
}
750+
};
751+
752+
struct ObjResultSet : public LL::ResultSet
753+
{
754+
ObjResultSet() : LL::ResultSet("nearby_objects") {}
755+
std::vector<LLViewerObject*> mObjects;
756+
757+
int getLength() const override { return narrow(mObjects.size()); }
758+
LLSD getSingle(int index) const override
759+
{
760+
auto obj = mObjects[index];
761+
return llsd::map("id", obj->getID(),
762+
"global_pos", ll_sd_from_vector3d(obj->getPositionGlobal()),
763+
"region_pos", ll_sd_from_vector3(obj->getPositionRegion()),
764+
"region_id", obj->getRegion()->getRegionID());
765+
}
766+
};
767+
768+
769+
F32 get_search_radius(LLSD const& event_data)
770+
{
771+
static LLCachedControl<F32> render_far_clip(gSavedSettings, "RenderFarClip", 64);
772+
F32 dist = render_far_clip;
773+
if (event_data.has("dist"))
774+
{
775+
dist = llclamp((F32)event_data["dist"].asReal(), 1, 512);
776+
}
777+
return dist * dist;
778+
}
779+
780+
void LLAgentListener::getNearbyAvatarsList(LLSD const& event_data)
781+
{
782+
Response response(LLSD(), event_data);
783+
auto avresult = new AvResultSet;
784+
785+
F32 radius = get_search_radius(event_data);
786+
LLVector3d agent_pos = gAgent.getPositionGlobal();
787+
for (LLCharacter* character : LLCharacter::sInstances)
788+
{
789+
LLVOAvatar* avatar = (LLVOAvatar*)character;
790+
if (!avatar->isDead() && !avatar->isControlAvatar() && !avatar->isSelf())
791+
{
792+
if ((dist_vec_squared(avatar->getPositionGlobal(), agent_pos) <= radius))
793+
{
794+
avresult->mAvatars.push_back(avatar);
795+
}
796+
}
797+
}
798+
response["result"] = avresult->getKeyLength();
799+
}
800+
801+
void LLAgentListener::getNearbyObjectsList(LLSD const& event_data)
802+
{
803+
Response response(LLSD(), event_data);
804+
auto objresult = new ObjResultSet;
805+
806+
F32 radius = get_search_radius(event_data);
807+
S32 num_objects = gObjectList.getNumObjects();
808+
LLVector3d agent_pos = gAgent.getPositionGlobal();
809+
for (S32 i = 0; i < num_objects; ++i)
810+
{
811+
LLViewerObject* object = gObjectList.getObject(i);
812+
if (object && object->getVolume() && !object->isAttachment())
813+
{
814+
if ((dist_vec_squared(object->getPositionGlobal(), agent_pos) <= radius))
815+
{
816+
objresult->mObjects.push_back(object);
817+
}
818+
}
819+
}
820+
response["result"] = objresult->getKeyLength();
821+
}
822+
823+
void LLAgentListener::getAgentScreenPos(LLSD const& event_data)
824+
{
825+
Response response(LLSD(), event_data);
826+
LLVector3 render_pos;
827+
if (event_data.has("avatar_id") && (event_data["avatar_id"] != gAgentID))
828+
{
829+
LLUUID avatar_id(event_data["avatar_id"]);
830+
for (LLCharacter* character : LLCharacter::sInstances)
831+
{
832+
LLVOAvatar* avatar = (LLVOAvatar*)character;
833+
if (!avatar->isDead() && (avatar->getID() == avatar_id))
834+
{
835+
render_pos = avatar->getRenderPosition();
836+
break;
837+
}
838+
}
839+
}
840+
else if (gAgentAvatarp.notNull() && gAgentAvatarp->isValid())
841+
{
842+
render_pos = gAgentAvatarp->getRenderPosition();
843+
}
844+
LLCoordGL screen_pos;
845+
response["onscreen"] = LLViewerCamera::getInstance()->projectPosAgentToScreen(render_pos, screen_pos, false);
846+
response["x"] = screen_pos.mX;
847+
response["y"] = screen_pos.mY;
848+
}

indra/newview/llagentlistener.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ class LLAgentListener : public LLEventAPI
6767
void stopAnimation(LLSD const &event_data);
6868
void getAnimationInfo(LLSD const &event_data);
6969

70+
void getID(LLSD const& event_data);
71+
void getNearbyAvatarsList(LLSD const& event_data);
72+
void getNearbyObjectsList(LLSD const& event_data);
73+
void getAgentScreenPos(LLSD const& event_data);
74+
7075
LLViewerObject * findObjectClosestTo( const LLVector3 & position, bool sit_target = false ) const;
7176

7277
private:

indra/newview/llfloaterluadebug.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ bool LLFloaterLUADebug::postBuild()
5858
.listen("LLFloaterLUADebug",
5959
[mResultOutput=mResultOutput](const LLSD& data)
6060
{
61-
mResultOutput->pasteTextWithLinebreaks(data.asString());
61+
LLCachedControl<bool> show_source_info(gSavedSettings, "LuaDebugShowSource", false);
62+
std::string source_info = show_source_info ? data["source_info"].asString() : "";
63+
mResultOutput->pasteTextWithLinebreaks(stringize(data["level"].asString(), source_info, data["msg"].asString()));
6264
mResultOutput->addLineBreakChar(true);
6365
return false;
6466
});

indra/newview/llinventorylistener.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,8 @@ struct CatResultSet: public LL::ResultSet
116116
LLSD getSingle(int index) const override
117117
{
118118
auto cat = mCategories[index];
119-
return llsd::map("name", cat->getName(),
119+
return llsd::map("id", cat->getUUID(),
120+
"name", cat->getName(),
120121
"parent_id", cat->getParentUUID(),
121122
"type", LLFolderType::lookup(cat->getPreferredType()));
122123
}
@@ -133,7 +134,8 @@ struct ItemResultSet: public LL::ResultSet
133134
LLSD getSingle(int index) const override
134135
{
135136
auto item = mItems[index];
136-
return llsd::map("name", item->getName(),
137+
return llsd::map("id", item->getUUID(),
138+
"name", item->getName(),
137139
"parent_id", item->getParentUUID(),
138140
"desc", item->getDescription(),
139141
"inv_type", LLInventoryType::lookup(item->getInventoryType()),

indra/newview/llluamanager.cpp

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,10 @@ std::string lua_print_msg(lua_State* L, std::string_view level)
7272
lluau_checkstack(L, 2);
7373
luaL_where(L, 1);
7474
// start with the 'where' info at the top of the stack
75-
std::ostringstream out;
76-
out << lua_tostring(L, -1);
75+
std::string source_info{ lua_tostring(L, -1) };
7776
lua_pop(L, 1);
77+
78+
std::ostringstream out;
7879
const char* sep = ""; // 'where' info ends with ": "
7980
// now iterate over arbitrary args, calling Lua tostring() on each and
8081
// concatenating with separators
@@ -101,10 +102,10 @@ std::string lua_print_msg(lua_State* L, std::string_view level)
101102
// capture message string
102103
std::string msg{ out.str() };
103104
// put message out there for any interested party (*koff* LLFloaterLUADebug *koff*)
104-
LLEventPumps::instance().obtain("lua output").post(stringize(level, ": ", msg));
105+
LLEventPumps::instance().obtain("lua output").post(llsd::map("msg", msg, "level", stringize(level, ": "), "source_info", source_info));
105106

106107
llcoro::suspend();
107-
return msg;
108+
return source_info + msg;
108109
}
109110

110111
lua_function(print_debug, "print_debug(args...): DEBUG level logging")

indra/newview/llviewerwindowlistener.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
// std headers
3636
// external library headers
3737
// other Linden headers
38+
#include "llcallbacklist.h"
3839
#include "llviewerwindow.h"
3940

4041
LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow):
@@ -46,8 +47,7 @@ LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow):
4647
add("saveSnapshot",
4748
"Save screenshot: [\"filename\"] (extension may be specified: bmp, jpeg, png)\n"
4849
"[\"width\"], [\"height\"], [\"showui\"], [\"showhud\"], [\"rebuild\"], [\"type\"]\n"
49-
"type: \"COLOR\", \"DEPTH\"\n"
50-
"Post on [\"reply\"] an event containing [\"result\"]",
50+
"type: \"COLOR\", \"DEPTH\"\n",
5151
&LLViewerWindowListener::saveSnapshot,
5252
llsd::map("filename", LLSD::String(), "reply", LLSD()));
5353
add("requestReshape",
@@ -57,8 +57,6 @@ LLViewerWindowListener::LLViewerWindowListener(LLViewerWindow* llviewerwindow):
5757

5858
void LLViewerWindowListener::saveSnapshot(const LLSD& event) const
5959
{
60-
Response response(LLSD(), event);
61-
6260
typedef std::map<LLSD::String, LLSnapshotModel::ESnapshotLayerType> TypeMap;
6361
TypeMap types;
6462
#define tp(name) types[#name] = LLSnapshotModel::SNAPSHOT_TYPE_##name
@@ -88,15 +86,17 @@ void LLViewerWindowListener::saveSnapshot(const LLSD& event) const
8886
TypeMap::const_iterator found = types.find(event["type"]);
8987
if (found == types.end())
9088
{
91-
return response.error(stringize("Unrecognized type ", std::quoted(event["type"].asString()), " [\"COLOR\"] or [\"DEPTH\"] is expected."));
89+
sendReply(llsd::map("error", stringize("Unrecognized type ", std::quoted(event["type"].asString()), " [\"COLOR\"] or [\"DEPTH\"] is expected.")), event);
90+
return;
9291
}
9392
type = found->second;
9493
}
9594

9695
std::string filename(event["filename"]);
9796
if (filename.empty())
9897
{
99-
return response.error(stringize("File path is empty."));
98+
sendReply(llsd::map("error", stringize("File path is empty.")), event);
99+
return;
100100
}
101101

102102
LLSnapshotModel::ESnapshotFormat format(LLSnapshotModel::SNAPSHOT_FORMAT_BMP);
@@ -115,9 +115,13 @@ void LLViewerWindowListener::saveSnapshot(const LLSD& event) const
115115
}
116116
else if (ext != "bmp")
117117
{
118-
return response.error(stringize("Unrecognized format. [\"png\"], [\"jpeg\"] or [\"bmp\"] is expected."));
118+
sendReply(llsd::map("error", stringize("Unrecognized format. [\"png\"], [\"jpeg\"] or [\"bmp\"] is expected.")), event);
119+
return;
119120
}
120-
response["result"] = mViewerWindow->saveSnapshot(filename, width, height, showui, showhud, rebuild, type, format);
121+
// take snapshot on the main coro
122+
doOnIdleOneTime([this, event, filename, width, height, showui, showhud, rebuild, type, format]()
123+
{ sendReply(llsd::map("result", mViewerWindow->saveSnapshot(filename, width, height, showui, showhud, rebuild, type, format)), event); });
124+
121125
}
122126

123127
void LLViewerWindowListener::requestReshape(LLSD const & event_data) const

indra/newview/scripts/lua/require/LLAgent.lua

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,26 @@
11
local leap = require 'leap'
22
local mapargs = require 'mapargs'
3+
local result_view = require 'result_view'
4+
5+
local function result(keys)
6+
local result_table = {
7+
result=result_view(keys.result),
8+
-- call result_table:close() to release result sets before garbage
9+
-- collection or script completion
10+
close = function(self)
11+
result_view.close(keys.result[1])
12+
end
13+
}
14+
-- When the result_table is destroyed, close its result_views.
15+
return LL.setdtor('LLAgent result', result_table, result_table.close)
16+
end
317

418
local LLAgent = {}
519

20+
function LLAgent.getID()
21+
return leap.request('LLAgent', {op = 'getID'}).id
22+
end
23+
624
function LLAgent.getRegionPosition()
725
return leap.request('LLAgent', {op = 'getPosition'}).region
826
end
@@ -95,6 +113,33 @@ function LLAgent.requestStand()
95113
leap.send('LLAgent', {op = 'requestStand'})
96114
end
97115

116+
-- Get the nearby avatars in a range of provided "dist",
117+
-- if "dist" is not specified, "RenderFarClip" setting is used
118+
-- reply will contain "result" table with following fields:
119+
-- "id", "global_pos", "region_pos", "name", "region_id"
120+
function LLAgent.getNearbyAvatarsList(...)
121+
local args = mapargs('dist', ...)
122+
args.op = 'getNearbyAvatarsList'
123+
return result(leap.request('LLAgent', args))
124+
end
125+
126+
-- reply will contain "result" table with following fields:
127+
-- "id", "global_pos", "region_pos", "region_id"
128+
function LLAgent.getNearbyObjectsList(...)
129+
local args = mapargs('dist', ...)
130+
args.op = 'getNearbyObjectsList'
131+
return result(leap.request('LLAgent', args))
132+
end
133+
134+
-- Get screen position of your own avatar or any other (if "avatar_id" is specified)
135+
-- reply contains "x", "y" coordinates and "onscreen" flag to indicate if it's actually in within the current window
136+
-- avatar render position is used as the point
137+
function LLAgent.getAgentScreenPos(...)
138+
local args = mapargs('avatar_id', ...)
139+
args.op = 'getAgentScreenPos'
140+
return leap.request('LLAgent', args)
141+
end
142+
98143
-- ***************************************************************************
99144
-- Autopilot
100145
-- ***************************************************************************

0 commit comments

Comments
 (0)