Let’s put in the second dataserver-requiring field, the pay info. It will work just like the one for rez date. We’ll proceed in tiny steps. Each step, we’ll try to put in the code that best deserves it.
We need a new query list, new handler for that event, and some new additions in the line that prints the information and support for that information in the Person class. It will all be just the same as what we’ve done before.
First, we’ll add another query list up top:
local RezDayQueries = {}
local PayInfoQueries = {}
local People = {}
local Person = {}
We’ll initialize it with the other items when we scan, and we’ll look at the resulting code and think about how to call it:
function make_people_list()
People = {}
RezDayQueries = {}
PayInfoQueries = {} -- <---
local peopleScan = ll.GetAgentList(AGENT_LIST_PARCEL, {})
for _i, uuid in ipairs(peopleScan) do
local person = Person:new(uuid)
table.insert(People, person)
RezDayQueries[person:requestRezDate()] = person
-- what should we do here?
end
end
Well, it seems we need a new method on person, requestPayInfo
. Let’s assume that we have such a method and call it, to finish up this function:
function make_people_list()
People = {}
RezDayQueries = {}
PayInfoQueries = {}
local peopleScan = ll.GetAgentList(AGENT_LIST_PARCEL, {})
for _i, uuid in ipairs(peopleScan) do
local person = Person:new(uuid)
table.insert(People, person)
RezDayQueries[person:requestRezDate()] = person
PayInfoQueries[person:requestPayInfo()] = person
end
end
Now, before we forget, we’ll do requestPayInfo
to be like requestRezDate
:
function requestPayInfo()
return ll.RequestAgentData(self._uuid, DATA_PAYINFO)
end
function Person:requestRezDate()
return ll.RequestAgentData(self._uuid, DATA_BORN)
end
At this point the program should run as before, so I’ll run it to be sure it does. I’m glad I did, because I got this message:
lua_script:63: attempt to call missing method 'requestPayInfo' of table
lua_script:63 function make_people_list
lua_script:100 function touch_start
As you can easily see, I forgot to put Person: on the new method. It should be:
function Person:requestPayInfo()
return ll.RequestAgentData(self._uuid, DATA_PAYINFO)
end
Well, it works and it doesn’t. It prints the report several times, once for every avatar present. Why? Because we fire the report from dataserver, when the one queue is empty:
function dataserver(queryId, data)
local person = RezDayQueries[queryId]
if person then
person._rez_date = data
end
RezDayQueries[queryId] = nil
if next(RezDayQueries) == nil then
create_report()
end
end
I did not see that coming. That’s why I test frequently: I don’t have to think so hard. The test run tells me what I have to do next. And what I’ll do now is replicate the RezDate code to deal similarly with PayInfo:
function dataserver(queryId, data)
local person = RezDayQueries[queryId]
if person then
person._rez_date = data
end
RezDayQueries[queryId] = nil
local person = PayInfoQueries[queryId]
if person then
person._pay_info = data
end
PayInfoQueries[queryId] = nil
if next(RezDayQueries) == nil and next(PayInfoQueries) == nil then
create_report()
end
end
Test again, thinking it should work now. I get the right number of paragraphs now. Let’s see where the pay info is supposed to be displayed and put it there. It goes right after the rez date, it turns out.
function line_2(person)
return `Shape: {person:shape()}, Language: {'not implemented'}, RezDate: {person:rez_date()} Pay Info: {person:pay_info()}`
end
Unlike Python, we can’t fold long strings with just enter, so let’s do this:
function line_2(person)
return `Shape: {person:shape()}, Language: {'not implemented'}, ` ..
`RezDate: {person:rez_date()} Pay Info: {person:pay_info()}`
end
That’s a bit more readable. The report looks good. Here’s me:
[07:08] OO Scanner: 22ba38c9-06e9-4d42-8a6d-495e35bc33b7, janet.rossini, Janet Rossini
Shape: 0, Language: not implemented, RezDate: 2007-02-24 Pay Info: 3
Position: <155.39104, 32.56980, 23.10590>
Hover: 0
I don’t know what Pay Info 3 means but there it is. Ah it is a two bit field and 3 means pay info is on file and has been used. Whee!.
Let’s review the morning’s work so far.
Because we have all the different kinds of functionality separated neatly, all our changes were just a couple of lines at a time. I was trained and have learned to work that way, and I find it very productive, because after only a couple of lines I can test and make sure I’m on track. Other people who are not used to working in tiny bites might prefer to write larger chunks of code, and if that works for you, well, do it. But I like the tiny steps. This time it was:
requestPayInfo
. (I just noticed that I used camelCase for those two methods and snake_case for everything else. I’ll change them right now. Done, tested.)_pay_info
that our request will set up.dataserver
to check for the new query coming back and pass the _pay_info1
to the right Person.After each of those steps the program was testable, and in every case it worked as advertised, except that before we checked for the query it repeated the output once for each new dataserver event, because we were not yet consuming them and not yet checking for both queues empty before printing. Because I was able to test after every tiny step, I caught that issue immediately.
I think this is a good place to stop. Let me sum up.
The basic shell of this program was created very early on, first just printing one item, then printing all four lines of the paragraph, mostly with no data yet. Everything we’ve done since was done in very tiny steps, always working.
Our design has a Person class, enabling us to create a person instance for each UUID we get from the scan, and that instance acts as a container for all the information we need to report. It happens to compute that information on demand, except for the dataserver information, which it requests and then our dataserver code fills in the details.
Describing this gives me what I think is a good idea for another session. What if we didn’t put code in dataserver that knows so much about Person, but instead let the Person handle the dataserver event?
Anyway, it should be easy to see that filling in the rest of the fields just comes down to some more person methods, each one calling ll.GetObjectDetails
for its bit of information.
Once again, I’ll put the whole program here at the bottom of the article in case you want to see it.
Bye for now!
-- OO Scanner
-- JR 20250423
local RezDayQueries = {}
local PayInfoQueries = {}
local People = {}
local Person = {}
function Person:new(uuid)
local obj = {_uuid=uuid, _rez_date="dataserver"}
setmetatable(obj, self)
self.__index = self
return obj
end
function Person:uuid()
return self._uuid
end
function Person:display_name()
return ll.GetDisplayName(self._uuid)
end
function Person:hover()
return ll.GetObjectDetails(self._uuid, {OBJECT_HOVER_HEIGHT})[1]
end
function Person:pay_info()
return self._pay_info
end
function Person:position()
return ll.GetObjectDetails(self._uuid, {OBJECT_POS})[1]
end
function Person:request_pay_info()
return ll.RequestAgentData(self._uuid, DATA_PAYINFO)
end
function Person:request_rez_date()
return ll.RequestAgentData(self._uuid, DATA_BORN)
end
function Person:rez_date()
return self._rez_date
end
function Person:shape()
return ll.GetObjectDetails(self._uuid, {OBJECT_BODY_SHAPE_TYPE})[1]
end
function Person:user_name()
return ll.GetUsername(self._uuid)
end
function make_people_list()
People = {}
RezDayQueries = {}
PayInfoQueries = {}
local peopleScan = ll.GetAgentList(AGENT_LIST_PARCEL, {})
for _i, uuid in ipairs(peopleScan) do
local person = Person:new(uuid)
table.insert(People, person)
RezDayQueries[person:request_rez_date()] = person
PayInfoQueries[person:request_pay_info()] = person
end
end
function create_report()
for i, person in ipairs(People) do
local paragraph = create_paragraph(person)
print(paragraph)
end
end
function create_paragraph(person)
local lines = {
line_1(person), line_2(person), line_3(person), line_4(person)
}
return table.concat(lines, "\n")
end
function line_1(person)
return `{person:uuid()}, {person:user_name()}, {person:display_name()}`
end
function line_2(person)
return `Shape: {person:shape()}, Language: {'not implemented'}, ` ..
`RezDate: {person:rez_date()} Pay Info: {person:pay_info()}`
end
function line_3(person)
return `Position: {person:position()}`
end
function line_4(person)
return `Hover: {person:hover()}`
end
-- Primary Events
function touch_start(total_number)
make_people_list()
end
function dataserver(queryId, data)
local person = RezDayQueries[queryId]
if person then
person._rez_date = data
end
RezDayQueries[queryId] = nil
local person = PayInfoQueries[queryId]
if person then
person._pay_info = data
end
PayInfoQueries[queryId] = nil
if next(RezDayQueries) == nil and next(PayInfoQueries) == nil then
create_report()
end
end