The tricky bit in the homework is the dataserver bits. Let’s get started on that part.
Revised 2025-04-22 21:28
Let’s go ahead and put in one of the fields that needs to use the dataserver. We’ll follow the scheme that was in the homework assignment. It works like this:
We actually have two fields that we’ll ultimately need, the rez date and the payment info. So we’ll make two Lua tables, one for each. The tables will be in dictionary form, with the key being the key that comes back from the call to ll.RequestAgentData
, and the value being the Person instance for whom we are requesting the information.
We’ll request the data in the new
method, I think. No, on second thought, we’ll have a method for doing that and we’ll call it directly.
function Person:requestRezDate()
return ll.RequestAgentData(self._uuid, DATA_BORN)
end
We’ll call that method and enqueue the query, in the scanning code. That starts out like this:
function make_people_list()
People = {}
local peopleScan = ll.GetAgentList(AGENT_LIST_PARCEL, {})
for _i, uuid in ipairs(peopleScan) do
table.insert(People, Person:new(uuid))
end
end
We’ll extract a variable person
and use it, like this:
function make_people_list()
People = {}
RezDayQueries = {}
local peopleScan = ll.GetAgentList(AGENT_LIST_PARCEL, {})
for _i, uuid in ipairs(peopleScan) do
local person = Person:new(uuid) -- extract
table.insert(People, person) -- add to People
RezDayQueries[person:requestRezDate()] = person -- add to queries
end
end
I think I’ll add a dataserver event now, with a print in it:
function dataserver(queryId, data)
local person = RezDayQueries[queryId]
if person then
print(`RezDay for {person:display_name()} is {data}`)
end
RezDayQueries[queryId] = nil
end
We use the queryId to fetch the person from the table. If we get a non-nil person, we print. In any case, we remove the queryId from the list. Should we print something if we don’t recognize the Id? Perhaps, but so far I’ve done what I’ve done. Let’s see what it takes to make this work. Curiously enough, it works right off:
[10:08] OO Scanner: 22ba38c9-06e9-4d42-8a6d-495e35bc33b7, janet.rossini, Janet Rossini
Shape: 0, Language: not implemented, RezDate: dataserver
Position: <155.63109, 32.43759, 23.10590>
Hover: 0
[10:08] OO Scanner: RezDay for Marie is 2022-07-13
[10:08] OO Scanner: RezDay for SungAli is 2012-07-14
[10:08] OO Scanner: RezDay for Janet Rossini is 2007-02-24
Naturally, the dataserver events take place after we’ve done all the printing of the report, which we currently do in touch_start
:
function touch_start(total_number)
make_people_list()
create_report()
end
What we need to do now is to kick off the report when we are out of dataserver events. Since we remove them when we see them, we should do the report then. We’ll kick it off from dataserver, like this:
function touch_start(total_number)
make_people_list()
end
function dataserver(queryId, data)
local person = RezDayQueries[queryId]
if person then
print(`RezDay for {person:display_name()} is {data}`)
end
RezDayQueries[queryId] = nil
if next(RezDayQueries) == nil then
create_report()
end
end
Now we should see the dataserver prints before the reports instead of after. And we do:
[10:14] OO Scanner: RezDay for Marie is 2022-07-13
[10:14] OO Scanner: RezDay for SungAli is 2012-07-14
[10:14] OO Scanner: RezDay for Janet Rossini is 2007-02-24
[10:14] OO Scanner: a47bba4a-d54d-4632-8102-baa2ea9fa463, marieomani, Marie
Shape: 0, Language: not implemented, RezDate: dataserver
Position: <172.95132, 27.90528, 22.98849>
Hover: 0.125
[10:14] OO Scanner: ebff743c-5e47-400a-8ea6-a3bdb417b457, sungali, SungAli
Shape: 0, Language: not implemented, RezDate: dataserver
Position: <160.34364, 41.51177, 22.65000>
Hover: 0
[10:14] OO Scanner: 22ba38c9-06e9-4d42-8a6d-495e35bc33b7, janet.rossini, Janet Rossini
Shape: 0, Language: not implemented, RezDate: dataserver
Position: <155.63109, 32.43759, 23.10590>
Hover: 0
Now we just need to give the rez day to the person. We do that right inside the dataserver event, where we are currently doing the print. We remove the print and get this:
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
Now we should get the correct entry right in the report, because we set up to use it last time. And sure enough, we do! Here’s my report as of now:
[10:17] OO Scanner: 22ba38c9-06e9-4d42-8a6d-495e35bc33b7, janet.rossini, Janet Rossini
Shape: 0, Language: not implemented, RezDate: 2007-02-24
Position: <155.63109, 32.43759, 23.10590>
Hover: 0
This is a good spot to review and sum up. Let’s do.
We made a space in the Person object for the rez date, _rez_date
and initialized it to “dataserver”, right in Person:new()
:
function Person:new(uuid)
local obj = {_uuid=uuid, _rez_date="dataserver"}
setmetatable(obj, self)
self.__index = self
return obj
end
We used that field (member, attribute) in our report line, initially just showing “dataserver”, which showed us that we were referencing the right field:
function line_2(person)
return `Shape: {person:shape()}, Language: {'not implemented'}, RezDate: {person:rez_date()}`
end
We then built a query table, indexed by queryId, returning a Person, issuing the query with a direct call to the Person as we scan them in:
local RezDayQueries = {}
function make_people_list()
People = {}
RezDayQueries = {} -- <---
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 -- <---
end
end
Then,in the dataserver event, we look up the query in our table, find the person it pertains to, set that person’s _rez_date
, and remove the query from the query table. If there are no more queries, we have them all and we’re ready to kick off the report:
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
The cryptic next(RezDayQueries) == nil
is the Lua trick for asking “is this table empty”. I borrowed it from the homework, as I did the idea of having the query table point to the person. I think I’ll research whether there is a less cryptic way of testing for table empty.
With just a few lines, and very little digging around, we’ve installed a dataserver event for rez date. Next time we can do the same trick for the payment info, using a second query table for convenience, and fill in the rest of the fields by calling ll.GetObjectDetails()
. All of that will be pretty much by rote, because we’ve now got solutions to all the issues in the problem. The rest is just filling things in.
There are certainly things to improve. One is the access to the private member of Person that’s in dataserver. It’s certainly OK in principle. That is, it works. It’s not best practice, however, and a perfectionist would either remove the private-indicating underbar, or write a setter method on the object. Next time, if I remember, we’ll do that just to show how it’s done.
I’ll dump the whole program below, so you can see everything that’s there. What I hope you’ll notice is how everything is separated out into related functions, and how simple (although repetitive) the solution has become.
Until next time!
-- OO Scanner
-- JR 20250421
-- Global Collections
local RezDayQueries = {}
local People = {}
-- Person Class Definition
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:position()
return ll.GetObjectDetails(self._uuid, {OBJECT_POS})[1]
end
function Person:requestRezDate()
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
-- Reporting (should we make this an object also?)
function make_people_list()
People = {}
RezDayQueries = {}
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
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()}`
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
if next(RezDayQueries) == nil then
create_report()
end
end