Difference between revisions of "User:SuzannaLinn Resident/ScriptingClasses/Tipjars"

From Second Life Wiki
Jump to navigation Jump to search
 
(2 intermediate revisions by the same user not shown)
Line 6: Line 6:


-> Object Box for class on tipjars
-> Object Box for class on tipjars
First of all I want to thank Ayumi Cassini for her "ultimate guide to prim twisting" posted in her blog, here:
First of all I want to thank Ayumi Cassini for her "ultimate guide to prim twisting" posted in her blog, here:
* https://ayumicassini.blogspot.com/2009/07/ultimate-guide-to-prim-twisting.html
* https://ayumicassini.blogspot.com/2009/07/ultimate-guide-to-prim-twisting.html
Line 22: Line 23:
Now try to pay to it, and it will behave as a tipjar, showing default amounts and accepting payments. Pay it 1 linden and that will come to you. You are paying to yourself.
Now try to pay to it, and it will behave as a tipjar, showing default amounts and accepting payments. Pay it 1 linden and that will come to you. You are paying to yourself.


-> Script 1 Tipjar minimal"
-> Script 1 Tipjar minimal


The script is...  rather empty.
The script is...  rather empty.
Line 319: Line 320:
** So for vendors, always always check to be sure you got the right amount.  Best practice is to return wrong amounts with an appropriate notice.
** So for vendors, always always check to be sure you got the right amount.  Best practice is to return wrong amounts with an appropriate notice.
** We will study an example of this on Wednesday.
** We will study an example of this on Wednesday.
== Wednesday class ==
=== Groups of tipjars showing a total amount of tips. ===
We commented about two topics on tipjars:
* how to make different groups of tipjars to show the total amount tipped to the group, without counting tipjars of the other groups.
* how to avoid that other objects or people interfere with our process by sending message to our channel.
To make groups we use the description of the object. In this example we have two groups: the green tipjars and the red tipjars. They have "green" or "red" as its description.
To avoid interferences:
* we use a big random strange number as channel.
* we check that the messages come from an object with the same owner that the tipjar where the script is.
-> Tipjar grouping (xxx)
-> Script 5 Tipjar grouping
Please rezz the "Tipjar grouping (red)" and "Tipjar grouping (green)" that is in the Class Materials folder that you have received when touching the doggy Dufa. Rezz 2 or 3 of each one. Open the "Script 5 Tipjar grouping".
In line 18, we use a channel more difficult to guess. Use one more more difficult, some random big number.
We have added the global variable  myGroup  in line 24. It will store the description of this tipjar.
The function showText() and sayThanks() are unchanged.
In state_entry, line 69, we call llGetObjectDesc() to get the description of the object.
The main changes are in the event listen, lines 86-98.
We start checking the channel, as usual.
In line 88, we check is the owner of the object saying the message is the same than the owner of this object.
llGetOwnerKey() returns the uuid of the owner of the object id that we send as parameter.
id is the parameter in the event listen with the avatar or object that have said the message.
We check the ownership to avoid any other object or person sending messages in our channel and interfering with our process.
In line 90 we get the group of the tipjar saying the message.
llGetObjectDetails() returns information about the object or then avatar uuid used in the first parameter.
Its second parameter is a list with the information that we want. In this case only the object description.
llGetObjectDetails() returns a list with the information. We use llList2String() to get the object description from the element 0 (and only one) in the list.
We check that the sending tipjar is in the same group that this tipjar, in line 91.
If it is, we update the amount and show the new floating text.
=== The event money for a vendor. ===
Another possible problem that we commented on Monday is the case of a vendor.
When writing a vendor, always (ALWAYS!) check the amount paid in your money() event.
We will set the option for the paying dialog to show only one button, with the price of the object, and no textbox to change the amount.
But, anyway, we must check it. Let's see how.
-> Script 6 Tipjar vendor
We set the price in line 0.
In the list payButtons, line 1, that we will use for the paying dialog, we only have one button active with the price, the other buttons are hiddne.
payPrice, line 2, is also hidden, the paying dialog will not show a textbox to type the amount to pay.
Let's go to state_entry, lines 7-12.
We will only activate the vendor after asking for the permission to give money, in case that we need to return some amount.
In line 8, llSetPayPrice(), with all the parameters hidden, makes the object non-payable, even if it has an event money, and in line 9 we set the click action to touch, the usual one.
And we request for the permission, as we studied on Monday.
In the event run_time_permissions, lines 14-20, if we have granted the permission, we set the paying options and the click action ready for the vendor.
In the event money, lines 22-40, we check that the amount is ok.
Lines 23-29 is the case that we have been payed an amount below the price.
If this amount is not 0, we return it to the payer, line 27.
Lines 30-38 is when the payment is enough.
We check if the amount is too big, in lines 30-36, and return the difference, amount - price, in line 34.
In line 37, we would be giving the object to the buyer, but we haven't studied how to give objects. We will study it next week.
=== Answers to questions ===
Are all the tipjars on the same channel?
* Yes, all the tipjars have the same script and the same channel.
* All tipjars receive all the messages from all the other tipjars, but only use the messages from the tipjars with the same description.
So checking for the owner key keeps them from all acting together?
* No, checking the owner key keeps them from received messages from other objects or avatars that discover our channel and want to break our system.
* To exclude the tipjars in other groups we check for their description and compare it with the description of the tipjar of the script in lines 90-91.
Does all the tipjars being on the same channel show us the effectiveness of the myGroup attribution?
* Yes, we use the same channel to be able to use the same script in all of them, making the configuration more easy, and we use the description to group and filter them.
How could they break it?
* If we type, for instance    /-987654321 100    the tipjars will receive this message, but it will be ignored because it doesn't come from an object of the owner.
Even if the owner writes it?
* If the owner writes it, it will pass the filter in line 88, because an avatar is its own owner.
** llGetOwnerKey(id)  with the id of an avatar,  returns the same id.
* But avatars don't have descriptions, so it wouldn't pass the filter in line 91.
* We are checking channel, ownership, and description, lines 87, 88 and 91.
Could we have a "master tipjar" that shows the total amount of tips in all the tipjars?
* Yes, we could add a tipjar that doesn't filter the group and adds all the tips to the total as a "master tipjar".
What info is included in OBJECT_DESC?
* With the parameter OBJECT_DESC we ask for the description of the object.
** Editing the tipjar, in the tab "general", in the field "description", we have "green" or "red". This is the description that OBJECT_DESC returns.
What parameter would we use to get the name of the object?
* To get the name using llGetObjectDetails(), we use the parameter OBJECT_NAME.
** llGetObjectDetails() has many parameters to get information, we will be studying them in next classes.
Could we rezz more tipjars and give them the description "blue" to open a new group?
* Yes, this is the right way to do it.
Is the owner name that separates my tipjars from everybody else's?
* Yes, tipjars of other owners will be ignored by your tipjars.
When we grant permissions are they permanent?
* The granted permissions stay until the script is reset,.
* After a reset we will be requested again to grant the permissions.
Is the price set by integer price = 10 in the script and not  using the price set in the object?
* Yes, the script uses    price = 10  as set in the script, not the price of the object set in the edit window.
What is exactly debitPerms? What values does it hold?
* debitPerms is a global variable that we use as a boolean TRUE/FALSE to store the result of the request for permissions.
* If the permissions are granted debitPerms is TRUE, other wise FALSE.
** LSL doesnt have a boolean type like other languages, so we use the integer type.
** We have the constants TRUE and FALSE to make it look like a boolean. Their values are 1 and 0.
** In nearly all languages, Booleans are a subclass of an integer type anyway.  Things like changed or permission mask you see in the change and permissions events are bitwise Boolean values where each bit represents a true or false value for a given property.
What are the risks when money is involved?
* What you are protecting yourself against here is a hacked/hostile viewer.  It is the viewer that actually decides what to pay when interacting with a pay object pop-up, and it's a fundamental rule of security, specially when money is involved, that you never, ever, ever, trust software on the other guy's computer.
* If you set up a shop, you want to avoid allowing just anyone to rez objects near your vendors.  Alway have your sandbox, if you need  one, in a separate location.  One gimmick unethical people will use is to put an invisible prim over your vendor to let it take an incautious customer's money.
== Saturday class ==
=== Tipjar with login ===
-> Object Tipjar with login
-> Script 7 Tipjar with login
This script is based on the "Script 4 Tipjar share tips" from Monday.
Most of the script is the same that we saw in the class. The new lines that we are adding are commented.
Let's start with the new variables:
* loggedId (line 25)
* loggedName (line 27)
* sharedAmount( line 30)
We use loggedíd and loggedName to store the uuid and name of the host who has logged.
And sharedAmount is to store the amount given to the host.
Let's go to state_entry, line 108.
We have the option to give a percentage to the user (in the event money), in this example is set to 90%.
totalAmount is the total of tips given, 1.000 lindens for instance, and sharedAmount is the total of tips received by the host, 900 lindens in this example.
lastAmount is the amount of the last tip.
With each tip we add (in the event money) to totalAmount and sharedAmount, and we replace the value in lastAmount.
In line 112, llSetClickAction() sets the default action for the object when clicked.
We start without a tipjar host, and we set TOUCH as default, waiting someone to touch and log in.
And we set values to empty or 0, in lines 114-118.
NULL_KEY is a LSL constant, to set a variable of type key to empty.
Using      loggedID="";    also works, but using    loggedId=NULL_KEY;    is more clear, it shows that this is a variable a type key.
In the showTest() functions there is a change, in lines 35-39.
We have added an  if ... else.
If there is a host we show the name of the host, if not we show the name of the owner of the tipjar.
Now we go to the event touch_start, line 150
Here we have a bit more complicated if...else structure, with nested if's.
In lines 151-152, we get the uuid of the toucher, as usual.
First we check if the toucher is the same person that is logged, the host, in lines 153-154.
toucherId is the uuid of the person who has just touched, loggedId is the uuid of the host using the tipjar (if there is any).
If so, it means that the host want to logout. We call logOut(), we will see this function later.
If not, line 155, it means that a new host wants to log in.
But we only allow people with the same group than the tipjar to log in. They must be wearing the group tag.
The tipjar must be in the staff group for this check to work well. The owner of the tipjar can change the group in the edit window, "general" tab, "group" clicking on the tool at the right of it.
Only assigned to the group, not deeded to the group with the "deed" button.
First we check if the toucher is in the same group of the tipjar, in line 156.
llSameGroup() is the LSL function that does it. We give it the uuid of the person as parameter and the function returns a value of TRUE or FALSE that we use in the if condition.
If the toucher is in the group, so is allowed to log in, we are in lines 157-162.
In line 157, we check is there is already a host using the tipjar and if is still in the place, with our function checkLogged(), that we will see later.
if there is a host. but is not in the sim, checkLogged() will log out and the tipjar will be free.
After it, line 158, we check if there is a host logged and we send a message to the toucher saying that can't log, line 159.
Or, if the tipjar is free, we log the toucher in, calling logIn(), line 161.
We will see in a while that when someone logs in, we change the default click action to pay.
So, a person wearing the staff group tag, when someone else is using the tipjar, will get the tipping dialog when touching the tipjar, showing that the tipjar is taken.
Line 164 is the else of line 156, when the toucher is not in the same group we send a message about it.
Let's go to look at the new functions.
checkLogged(), lines 72-78.
In line 73 we check if there is someone logged in.
loggedId is a variable of type key.
When we do an  if  like this    if (loggedId)    with a variable of type key, the condition will be TRUE if the variable has a valid key, FALSE if the variable has an empty key or anything that is not in uuid format.
So no need to write    if ( loggedID != NULL_KEY )    that would do exactly the same.
In line 74 we check if the logged person is in the sim. There are several ways to do it.
One way is using the function llGetAgentSize(). This function returns a vector with the size of the avatar or ZERO_VECTOR if the avatar is not in the sim.
So if llGetAgentSize() returns ZERO_VECTOR we log out the user.
llGetAgentSize() is said to be the fasted and most reliable way to determine if a single agent is in the sim.
Next function, logIn(), lines 81-91.
In line 82 we send a message to the user logging in.
We assign the values to the variables and we show the floating text, line 88.
In line 89 we set a timer, to 180 seconds, 3 minutes.
The timer will call checkLogged() to log out the user if is gone.
And in line 90 we set the default click action to PAY.
Next, logOut(), lines 94-104.
We send a message to the leaving user, with the quantity of tips, that we have in the variable sharedAmount.
We will see later where we add to this variable.
sharedAmount is type integer, and we are making a string for the message, so we need to change the integer value to a string with    (string)sharedAmount.
We empty the variables and show the floating text in line 101.
We stop the timer in line 102 and in line 103 we set the default click action to TOUCH.
The event timer is in line 170.
It calls checkLogged(), every 3 minutes, to check that the host is still in the sim.
The last additions to look at are at the event money, lines 127-145.
We have changed the % to share the 90% in line 128.
So 90% for the host, and 10% for the club.
In line 130, we check if there is a host logged. If so, we send the lindens.
In line 134 we add the given amount to sharedAmount to be able to give the total of tips to the host when logging out.
And we give the lindens to the user logged in, in line 135.
We are giving to the host the share of each tip at the same moment when the tipjar is tipped.
And we have seen all the modifications to the previous script.
=== Next week ===
Next week, we will learn to list the contents in an object, to give them, to give them in a folder, to remove them and how to script a HUD to give things to the people around us.
=== Answers to questions ===
What is the range of llGetAgentSize()?
* llGetAgentSize() range is all the region.
How does  the owner set the group of the tipjar?
* The tipjar is rezzed with the group that the owner has active at that moment.
* To change the group in the edit window, go to the General Tab, click the Wrench Icon by the current group name, and select one of your groups from the list that pops up.
Could we write a function to set the group of the tipjar in the script ?
* No, there is no scripting function to do it.
Do we always need to use a group?
* No, the tipjar can allow anyone to login, without checking any group.
* In this script we need a group, because the script is checking for it, we need to be in the same group than the tipjar and the llSameGroup() function is checking the group tag
Using llSameGroup(), do the avatars have to be in the region?
* Yes, llSameGroup() only works on avatars who are present or have only recently left and are still known to the region.
if I have the permission to give money granted and someone copies my tipjar, would they then have access to my account?
* No, the money is only given from the account of the owner of the object.
*  If someone copies your tipjar immediately becomes the owner and any given money will come from their account.
What happens if the host is not present?
* In the script the timer is checking if the host is in the region every 3 minutes. If the host is not there, is automatically logged out. In meanwhile the host will be receiving the share of the tips.
What is the warning about access to my lindens?
* The script is asking for the permission to give lindens, it does that every time you reset the script.
* If you are the owner of the club, and you want to give the hosts their share, you will need to grant the permission to give money.
* That is why the sanity check to make sure the script never gives away more money than you were given in any given tip transaction. It's something to remember whenever you have a script giving money in your behalf.
* Another safety feature is to use an alt account to own your tipjars that only holds money received from tipping and then you occasionally transfer that money to your main.
How can we choose randomly in a list?
* We have the function  llFrand()  for a random choice.
* If we have a list of 13 people, so the list indexes go from 0 ro 12, we can choose one with    (integer)llFrand( 13 ).

Latest revision as of 03:45, 11 November 2024

Tipjars

Monday class

Tipjar prim

-> Object Box for class on tipjars

First of all I want to thank Ayumi Cassini for her "ultimate guide to prim twisting" posted in her blog, here:

The twisting for the object that we will be using as a tipjar come from there. Take a look at her blog, but after the class, please :)


Make an object to behave as a tipjar

Now your tipjar has no scripts, and you can't right click and pay to it.

We are going to make it "payable".

Look at your Class Materials folder and copy the "Script 1 Tipjar minimal" into the tipjar contents

Now try to pay to it, and it will behave as a tipjar, showing default amounts and accepting payments. Pay it 1 linden and that will come to you. You are paying to yourself.

-> Script 1 Tipjar minimal

The script is... rather empty.

There is only an empty event "money". And this is enough to make an object to behave as a tipjar.

All the management of the payment is done by SL.

SL opens the window with the suggested amounts. SL accepts the lindens from the payer. SL sends the lindens to the owner of the object. Always all the lindens, always to the owner of the object.

And there is no way to change this process in our script.

Of course this is not a nice tipjar, from now on we will see how to improve it.

The several scripts that we will see all have the same code as the previous one with some more lines added.


Add a text and a thanks message

Delete the script in your tipjar.

Look at your Class Materials folder and copy the "Script 2 Tipjar text and messages" into the tipjar contents.

-> Script 2 Tipjar text and messages

First we have a list of constants with color codes to use in the floating text of the tipjar, in the same way that we saw in the previous class on floating texts.

Let's move to line 18 in the script.

The payment options dialog box that SL shows to us is configurable. We can change the four default values, and also hide the option to write another amount.

We use two variables payButtons (for the default values) and payPrice (to show or hide the text option).

payButtons is a variable of type "list".

In our case, payButtons has to be a list of four values. And payPrice will be set to the LSL constant PAY_DEFAULT, which shows the text box to type a different amount.

This is the usual way for a tipjar. Commented on lines 18-19 there are the options for a vendor. It will show the payment dialog box with only one button (with 150 in this example) and no textbox. It will only allow you to pay 150. Try it later.

Now let's look at line 36 in the event state_entry.

llSetPayPrice() is the function that sets our parameters for the dialog box.

llSetClickAction() sets the default action when an object is clicked. In the previous script we had to right-click and choose "pay".

Which is too much work, we want people to tip us comfortably. Setting the default action to payment they will just need to click.

This is all about configuring the payment. Now we will add a floating text to the tipjar, so people know that it's a tipjar.

Still in the event state_entry, we set the variable myName to... your name. Since we will be using it in several places, it's better to have it in a variable. Less typing and for the script is faster to access variables than LSL functions.

We set the variable lastAmount (it will have the last tip amount) and tipjarAmount (with the total of tips) to 0, to be ready to count tips.

And we call our function showText() to show the tipjar floating text. We will call it from several places, so it's better to make a function for it. It also makes the script easier to read and the function easier to reuse in other scripts.

Let's take a look at showText() at line 24.

We add all the info to the variable "text", using "\n" to make a new line.

The operator += is a shorter way to write an addition to a variable, instead of writing:

  • text = text + "\n";

we write:

  • text += "\n";

At line 30 with add 3 new lines with one space. This makes the floating text to be displayed in a higher position above the tipjar. Otherwise it would show partially on the tipjar.

And we write the floating text with llSetText(), using the text, color white, and the third parameter is the transparency, 1 means fully opaque, so as much visible as possible, as we saw in the previous class.

Now let's move to the event "money" at line 43.

The event has two parameters.

key id is the UUID of the giver and integer amount is the quantity given.

We give thanks to the giver getting their name from the "id" with llGetDisplayName().

We update the counting variables, and we call showText() to show the updated text with the new values.

Try giving to your tipjar to see the thanks message and the floating text updating. If you are feeling generous you can give to the other students tipjars too :)


Synchronize several tipjars

Delete the script in your tipjar.

Look at your Class Materials folder and copy the "Script 3 Tipjar in sync" into the tipjar contents.

-> Script 3 Tipjar in sync

Now we will synchronize all our tipjars to show a total amount of tips given to them all.

To synchronize our tipjars we will make them to chat with one another.

We need to open a private channel of comunication. We will type our new text in this channel and our script will be listening in the same channel.

We can choose any number for our channel, up to 9 digits. In this script we will use channel 15.

Let's see how we will make our script to listen.

The function is llListen(). It has 4 parameters:

  • the channel we want to listen to (number 15, in our example).
  • the user name of the person we want to listen to.
  • the UUID of the person, we will use this parameter or the user name parameter, not both.
  • the message, if we are listening for a specific message.

We have to choose a channel to listen, the other parameters are optional, and we will use them to filter the message.

In this case we will listen to channel 15, and we want to listen to all the tipjars so we will not use the other parameters:

  • llListen( 15, "", "", "" );

And what happens when the script receive a message to the channel 15? It triggers the event listen, which has the same parameters than the llListen() function.

In the event listen is where we will update the variable totalAmount (with all the tips from all the tipjars) and show the updated floating text.

Let's look at our script to see what we have added to the previous script.

At the start, in line 18, there is:

  • integer CHANNEL_TIPJAR=15;

We could have used the number 15 in the listen function and event, but it's better to use a constant with a nice name or it would get very confusing if we listen to several channels.

In the event state_entry, at line 72, we have the function llListen().

In the event money, at line 81, we say on CHANNEL_TIPJAR the amount that our tipjar has been tipped.

We use llRegionSay() instead of llSay(), because it sends the message to all the region while llSay() sends the message only to 20 meters from the object.

Using llSay(), if the tipjars are not near, they will lose messages.

And at the end of the script, at line 84, we have added the event listen.

Inside the event listen we check that the channel is the our CHANNEL_TIPJAR. No need to do this here. We are only listening to a channel and we will not receive messages from any other channel. But it's better to do it, in case that we add more channels to our script in the future.

We update the totalAmount and we show the floating text.

We also update the totalAmount in the event money, at line 79, together with the other amounts.

Why? because objects doesn't receive the messages that they say. If we only listen to messages, the total amount will miss the tips to our tipjar.

In this script we are also making an improvement to our thanks message, that was:

  • llSay(0,"Thank you very much, "+llGetDisplayName(id)+", for your splendorous tip!");

It had two problems: perhaps we want the thanks message to be private, and if the giver is at more than 20 meters of the tipjar, the thanks message will be lost.

Now we have replaced it, in line 82, with the call to the function sayThanks(id), sending the UUID of the tipper as the function parameter.

We have done it a bit over-complicated, because we like to learn a lot :)

First, we have added the parameter PUBLIC_THANKS, if it's TRUE thanks will be public, if FALSE thanks will be private. You can change it to FALSE later to see it.

TRUE and FALSE are LSL constants. They are integers, TRUE is 1, FALSE is 0. We can use it in this way:

	if ( PUBLIC_THANKS ) {
		// execute this when PUBLIC_THANKS is different to 0
	} else {
		// execute this when PUBLIC_THANKS is 0
	}

Let's go to line 39 to look at the function.

The public option is the complicated one. We want to be sure that the giver receives the thanks message.

We will use llSay() if the giver is in 20 meters, if not we will use llShout() if the giver is in 100 meters.

No way to send public messages to more than 100 meters, so we will use llRegionSayTo(), to send a message to a person anywhere in the sim. This is a private message, but there isn't any other way. And since we want it public, we will also shout it with llShout().

By the way, I haven't tried to tip a tipjar from more than 100 meters away... I don't even know if it is possible.

But as scripters, we must make our scripts ready for anything :)

Now let's find out how to know the distance of the tipper to the tipjar.

To get the position of the tipper we will use llGetObjectDetails(). It has two parameters: the UUID of the person (or it could be an object) and a list with the information that we want the function to return.

There are lots of options, here we will use OBJECT_POS which gives us the position of the person or the object. The function expects a list, so, even if there is only one element, we must use a list in the parameter. We make a list with one element just adding [ and ]:

  • [OBJECT_POS].

llGetObjectDetails() also returns a list. If we ask for several informations, not only the position, it will return all the information in the list. In our script we get a list of one element, the person position, as return.

But we want a vector to assign to tipperPos. We will use llList2Vector() to get one element of the list to a vector, the element that we choose in the second parameter, in this case "0". List indexes start counting by 0, and a list of one element only has the element 0.

There are also functions llList2String(), llList2Integer(), llList2Float(), etc, to use depending on the type of our list elements.

With llGetPos() we get a vector with the position of the tipjar into our variable tipjarPos.

And to find the distance between tipperPos and tipjarPos we use the function llVecDist() that returns the distance in meters.

It always returns a positive value. If the distance from A to B is 10, the distance from B to A is also 10.

And the private option is easy: just use llRegionSayTo().

Try giving to your tipjar and look at the other tipjars to see if all of them updates the total tips.


Share tips

Delete the script in your tipjar.

Look at your Class Materials folder and open the "Script 4 Tipjar share tips" to look at it, don't copy it to the tipjar yet.

-> Script 4 Tipjar share tips

I said when we were looking at the first script that the tips always go to the owner of the object, at that it's not possible to change it. So how can we share the tips?

There is a function to give money. We will, as owners of the object, receive all the full tips, and we will give part of the tips to someone else.

The function is llGiveMoney(). It has two parameters: the UUID of the person whom we want to give the money, and the amount to give. Obviously the amount to give has to be in our lindens acount.

This is a dangerous function, we could become poor using it. So it need to be given a permission by the object owner.

Let's look at how to give the permission. It includes a function and an event. We also will use a variable to know if the permission has been granted or not.

The function, at line 67 in the state_entry event , is llRequestPermissions(). It has two parameters: our UUID that we get with llGetOwner() and the kind of permission that we are asking for. The LSL constant for the permission to give money is PERMISSION_DEBIT.

The event, at line 78, is run_time_permissions. It has the parameter perm which is used as a list of bits. We will use the operator & (bitwise AND) to check if the PERMISSION_DEBIT bit is at 1, meaning that the permission has been given. If so we will set our variable debitPerms to TRUE.

I'm always placing constants at the start of the script, and making functions as often as possible, and telling you to do it. But being llGiveMoney() a dangerous function I will tell you to place everything together inside the event money.

Let's look at the event money, at line 83.

SHARE_WITH_ID will be the UUID of the person that you want to share the tip with. Copy here the UUID of your alt, your best friend, or yourself to test the script. In this script, if you share with yourself, with a tip of, for instance, 10 lindens, you will get 10 lindens from yourself, and then 5 lindens from yourself.

SHARE_PER_CENT is the percentatge of sharing, 50% in this example.

With this parameters we calculate shareAmount rounding it with llRound().

llRound() rounds 0.5 to 1 so with a tip of 25 lindens, we will send 13 and keep 12.

Then it checks if we have added the UUID to share with.

In line 90 we are checking that the amount that we are going to share is less or equal than the amount that we have received, with:

  • if ( ShareAmount <= amount )

if ( ShareAmount <= amount ) is unnecessary here, it will always be true. We have added it to our script as a good scripting practice.

In case that a very complicated function or in case of a dangerous one, it's good to assert that the values are in the expected ranges.

In this example we make sure not to send more lindens that we have been tipped.

Remember, the function llGiveMoney() is not related to the event money and neither to tipjars. It doesn't try to know our intentions. It will happily, without any complain, give all our lindens to any avatar in all sl, including the ones being away for years.

Look at your Class Materials folder and copy the "Script 4 Tipjar share tips" into the tipjar contents. As soon as the script is there, it will ask you for the permission to give money.

Now give some tips to your tipjar and look at the transactions to see if it shares well.


Answers to questions

Could we use more than 4 pay buttons in llSetPayPrice()?

  • No, it is limited to 4 buttons by the dialog window that SL opens when we pay.
  • We can't change the format of the dialog window, only provide the parameters with the script.

Is PAY_HIDE to replace the other values if we don't have 4?

  • Yes, if we want to show less than 4 buttons, we need to use PAY_HIDE in the list for the empty options.

Can we change the default action manually, without using llSetClickAction()?

  • Yes ,we can change the click action with the object editor, in the "general" tab, in the drop down list "click to".

Is there a way to update the amount into tipjars across multiple regions?

  • There is a way, but more complicated, we will see it in a future class.

If we have the tipjars grouped in several sets, can we just use different channels?

  • Yes, to have different groups of tipjars showing totals of tips we can use a different channel for each group of tipjars.
  • Another way is setting the tipjar description, in the edit window, to the name of the group, and the scipt only acumulates amounts from tipjars with the same group in the description.

Could we save information, like the name of the tipper and the amount, to a file?

  • There is no way to write in a file. The only ways to export information are using http to connect to an external web, or sending an email.
  • We can save the information in the Linkset Data, that is more permanent than the script memory. We will study linkset data in a future class soon.

Does llRegionSayTo() gets to an avatar anywhere in the region?

  • Yes, the range of llRegionSayTo() is all the region.

Do messages send with llRegionSayTo() show in the public channel? Or do we have to use the event listen?

  • llRegionSayTo() sends the message only to the avatar which uuid we use in the first parameter.
  • It's not public but it appears in then local chat window, if we use channel 0 as the second parameter.
  • We can also use llRegionSayTo() to send a message to an object, using another channel, and the other object has to listen to that channel.

We could manipulate it like '/15 100'. Can we avoid it somehow?

  • Yes, we will study it on Wednesday.

What is "perm"?

  • "perm" is the parameter that we receive in the event run_time_permissions.
    • When the script is reset, it opens a dialog window requesting for the permission (to give money in this script).
    • It's a dialog window managed by SL called when we use llRequestPermissions(), as we do in state_entry.
    • When we accept or deny the permission in the dialog, it triggers the event run_time_permissions, and the parameter "perm" has the information about the permissions accepted.
  • "perm" is a bit mask with all the permissions, each bit is a different permission.
  • We check the bit of the permission to give money with the constant PERMISSION_DEBIT.

Does the tips to our tipjar go to our SL account as soon as they are tipped? Can we differentiate tipjar money from our total funds?

  • The tips go automatically to our lindens total amount. We must be very careful on what we give.
  • The only way to differentiate besides keeping our own records would be to make payments to an alt or have the alt own the tipjar to receive the payments directly.
  • Be very careful with script scripts that give money. Perhaps use an alt with a limited budget as payer so you don't accidentally bankrupt yourself if things go wrong.
  • A security point if you're using this as a vendor where you want a specific amount:
    • While the payment pop-up can be set to pay only a particular amount, there is nothing stopping someone from paying your object a different amount, including L$0 to trigger the money event.
    • So for vendors, always always check to be sure you got the right amount. Best practice is to return wrong amounts with an appropriate notice.
    • We will study an example of this on Wednesday.


Wednesday class

Groups of tipjars showing a total amount of tips.

We commented about two topics on tipjars:

  • how to make different groups of tipjars to show the total amount tipped to the group, without counting tipjars of the other groups.
  • how to avoid that other objects or people interfere with our process by sending message to our channel.

To make groups we use the description of the object. In this example we have two groups: the green tipjars and the red tipjars. They have "green" or "red" as its description.

To avoid interferences:

  • we use a big random strange number as channel.
  • we check that the messages come from an object with the same owner that the tipjar where the script is.

-> Tipjar grouping (xxx) -> Script 5 Tipjar grouping

Please rezz the "Tipjar grouping (red)" and "Tipjar grouping (green)" that is in the Class Materials folder that you have received when touching the doggy Dufa. Rezz 2 or 3 of each one. Open the "Script 5 Tipjar grouping".

In line 18, we use a channel more difficult to guess. Use one more more difficult, some random big number.

We have added the global variable myGroup in line 24. It will store the description of this tipjar.

The function showText() and sayThanks() are unchanged.

In state_entry, line 69, we call llGetObjectDesc() to get the description of the object.

The main changes are in the event listen, lines 86-98.

We start checking the channel, as usual.

In line 88, we check is the owner of the object saying the message is the same than the owner of this object.

llGetOwnerKey() returns the uuid of the owner of the object id that we send as parameter.

id is the parameter in the event listen with the avatar or object that have said the message.

We check the ownership to avoid any other object or person sending messages in our channel and interfering with our process.

In line 90 we get the group of the tipjar saying the message.

llGetObjectDetails() returns information about the object or then avatar uuid used in the first parameter.

Its second parameter is a list with the information that we want. In this case only the object description.

llGetObjectDetails() returns a list with the information. We use llList2String() to get the object description from the element 0 (and only one) in the list.

We check that the sending tipjar is in the same group that this tipjar, in line 91.

If it is, we update the amount and show the new floating text.


The event money for a vendor.

Another possible problem that we commented on Monday is the case of a vendor.

When writing a vendor, always (ALWAYS!) check the amount paid in your money() event.

We will set the option for the paying dialog to show only one button, with the price of the object, and no textbox to change the amount.

But, anyway, we must check it. Let's see how.

-> Script 6 Tipjar vendor

We set the price in line 0.

In the list payButtons, line 1, that we will use for the paying dialog, we only have one button active with the price, the other buttons are hiddne.

payPrice, line 2, is also hidden, the paying dialog will not show a textbox to type the amount to pay.

Let's go to state_entry, lines 7-12.

We will only activate the vendor after asking for the permission to give money, in case that we need to return some amount.

In line 8, llSetPayPrice(), with all the parameters hidden, makes the object non-payable, even if it has an event money, and in line 9 we set the click action to touch, the usual one.

And we request for the permission, as we studied on Monday.

In the event run_time_permissions, lines 14-20, if we have granted the permission, we set the paying options and the click action ready for the vendor.

In the event money, lines 22-40, we check that the amount is ok.

Lines 23-29 is the case that we have been payed an amount below the price.

If this amount is not 0, we return it to the payer, line 27.

Lines 30-38 is when the payment is enough.

We check if the amount is too big, in lines 30-36, and return the difference, amount - price, in line 34.

In line 37, we would be giving the object to the buyer, but we haven't studied how to give objects. We will study it next week.


Answers to questions

Are all the tipjars on the same channel?

  • Yes, all the tipjars have the same script and the same channel.
  • All tipjars receive all the messages from all the other tipjars, but only use the messages from the tipjars with the same description.

So checking for the owner key keeps them from all acting together?

  • No, checking the owner key keeps them from received messages from other objects or avatars that discover our channel and want to break our system.
  • To exclude the tipjars in other groups we check for their description and compare it with the description of the tipjar of the script in lines 90-91.

Does all the tipjars being on the same channel show us the effectiveness of the myGroup attribution?

  • Yes, we use the same channel to be able to use the same script in all of them, making the configuration more easy, and we use the description to group and filter them.

How could they break it?

  • If we type, for instance /-987654321 100 the tipjars will receive this message, but it will be ignored because it doesn't come from an object of the owner.

Even if the owner writes it?

  • If the owner writes it, it will pass the filter in line 88, because an avatar is its own owner.
    • llGetOwnerKey(id) with the id of an avatar, returns the same id.
  • But avatars don't have descriptions, so it wouldn't pass the filter in line 91.
  • We are checking channel, ownership, and description, lines 87, 88 and 91.

Could we have a "master tipjar" that shows the total amount of tips in all the tipjars?

  • Yes, we could add a tipjar that doesn't filter the group and adds all the tips to the total as a "master tipjar".

What info is included in OBJECT_DESC?

  • With the parameter OBJECT_DESC we ask for the description of the object.
    • Editing the tipjar, in the tab "general", in the field "description", we have "green" or "red". This is the description that OBJECT_DESC returns.

What parameter would we use to get the name of the object?

  • To get the name using llGetObjectDetails(), we use the parameter OBJECT_NAME.
    • llGetObjectDetails() has many parameters to get information, we will be studying them in next classes.

Could we rezz more tipjars and give them the description "blue" to open a new group?

  • Yes, this is the right way to do it.

Is the owner name that separates my tipjars from everybody else's?

  • Yes, tipjars of other owners will be ignored by your tipjars.

When we grant permissions are they permanent?

  • The granted permissions stay until the script is reset,.
  • After a reset we will be requested again to grant the permissions.

Is the price set by integer price = 10 in the script and not using the price set in the object?

  • Yes, the script uses price = 10 as set in the script, not the price of the object set in the edit window.

What is exactly debitPerms? What values does it hold?

  • debitPerms is a global variable that we use as a boolean TRUE/FALSE to store the result of the request for permissions.
  • If the permissions are granted debitPerms is TRUE, other wise FALSE.
    • LSL doesnt have a boolean type like other languages, so we use the integer type.
    • We have the constants TRUE and FALSE to make it look like a boolean. Their values are 1 and 0.
    • In nearly all languages, Booleans are a subclass of an integer type anyway. Things like changed or permission mask you see in the change and permissions events are bitwise Boolean values where each bit represents a true or false value for a given property.

What are the risks when money is involved?

  • What you are protecting yourself against here is a hacked/hostile viewer. It is the viewer that actually decides what to pay when interacting with a pay object pop-up, and it's a fundamental rule of security, specially when money is involved, that you never, ever, ever, trust software on the other guy's computer.
  • If you set up a shop, you want to avoid allowing just anyone to rez objects near your vendors. Alway have your sandbox, if you need one, in a separate location. One gimmick unethical people will use is to put an invisible prim over your vendor to let it take an incautious customer's money.


Saturday class

Tipjar with login

-> Object Tipjar with login -> Script 7 Tipjar with login

This script is based on the "Script 4 Tipjar share tips" from Monday.

Most of the script is the same that we saw in the class. The new lines that we are adding are commented.

Let's start with the new variables:

  • loggedId (line 25)
  • loggedName (line 27)
  • sharedAmount( line 30)

We use loggedíd and loggedName to store the uuid and name of the host who has logged.

And sharedAmount is to store the amount given to the host.

Let's go to state_entry, line 108.

We have the option to give a percentage to the user (in the event money), in this example is set to 90%.

totalAmount is the total of tips given, 1.000 lindens for instance, and sharedAmount is the total of tips received by the host, 900 lindens in this example.

lastAmount is the amount of the last tip.

With each tip we add (in the event money) to totalAmount and sharedAmount, and we replace the value in lastAmount.

In line 112, llSetClickAction() sets the default action for the object when clicked.

We start without a tipjar host, and we set TOUCH as default, waiting someone to touch and log in.

And we set values to empty or 0, in lines 114-118.

NULL_KEY is a LSL constant, to set a variable of type key to empty.

Using loggedID=""; also works, but using loggedId=NULL_KEY; is more clear, it shows that this is a variable a type key.

In the showTest() functions there is a change, in lines 35-39.

We have added an if ... else.

If there is a host we show the name of the host, if not we show the name of the owner of the tipjar.

Now we go to the event touch_start, line 150

Here we have a bit more complicated if...else structure, with nested if's.

In lines 151-152, we get the uuid of the toucher, as usual.

First we check if the toucher is the same person that is logged, the host, in lines 153-154.

toucherId is the uuid of the person who has just touched, loggedId is the uuid of the host using the tipjar (if there is any).

If so, it means that the host want to logout. We call logOut(), we will see this function later.

If not, line 155, it means that a new host wants to log in.

But we only allow people with the same group than the tipjar to log in. They must be wearing the group tag.

The tipjar must be in the staff group for this check to work well. The owner of the tipjar can change the group in the edit window, "general" tab, "group" clicking on the tool at the right of it.

Only assigned to the group, not deeded to the group with the "deed" button.

First we check if the toucher is in the same group of the tipjar, in line 156.

llSameGroup() is the LSL function that does it. We give it the uuid of the person as parameter and the function returns a value of TRUE or FALSE that we use in the if condition.

If the toucher is in the group, so is allowed to log in, we are in lines 157-162.

In line 157, we check is there is already a host using the tipjar and if is still in the place, with our function checkLogged(), that we will see later.

if there is a host. but is not in the sim, checkLogged() will log out and the tipjar will be free.

After it, line 158, we check if there is a host logged and we send a message to the toucher saying that can't log, line 159.

Or, if the tipjar is free, we log the toucher in, calling logIn(), line 161.

We will see in a while that when someone logs in, we change the default click action to pay.

So, a person wearing the staff group tag, when someone else is using the tipjar, will get the tipping dialog when touching the tipjar, showing that the tipjar is taken.

Line 164 is the else of line 156, when the toucher is not in the same group we send a message about it.

Let's go to look at the new functions.

checkLogged(), lines 72-78.

In line 73 we check if there is someone logged in.

loggedId is a variable of type key.

When we do an if like this if (loggedId) with a variable of type key, the condition will be TRUE if the variable has a valid key, FALSE if the variable has an empty key or anything that is not in uuid format.

So no need to write if ( loggedID != NULL_KEY ) that would do exactly the same.

In line 74 we check if the logged person is in the sim. There are several ways to do it.

One way is using the function llGetAgentSize(). This function returns a vector with the size of the avatar or ZERO_VECTOR if the avatar is not in the sim.

So if llGetAgentSize() returns ZERO_VECTOR we log out the user.

llGetAgentSize() is said to be the fasted and most reliable way to determine if a single agent is in the sim.

Next function, logIn(), lines 81-91.

In line 82 we send a message to the user logging in.

We assign the values to the variables and we show the floating text, line 88.

In line 89 we set a timer, to 180 seconds, 3 minutes.

The timer will call checkLogged() to log out the user if is gone.

And in line 90 we set the default click action to PAY.

Next, logOut(), lines 94-104.

We send a message to the leaving user, with the quantity of tips, that we have in the variable sharedAmount.

We will see later where we add to this variable.

sharedAmount is type integer, and we are making a string for the message, so we need to change the integer value to a string with (string)sharedAmount.

We empty the variables and show the floating text in line 101.

We stop the timer in line 102 and in line 103 we set the default click action to TOUCH.

The event timer is in line 170.

It calls checkLogged(), every 3 minutes, to check that the host is still in the sim.

The last additions to look at are at the event money, lines 127-145.

We have changed the % to share the 90% in line 128.

So 90% for the host, and 10% for the club.

In line 130, we check if there is a host logged. If so, we send the lindens.

In line 134 we add the given amount to sharedAmount to be able to give the total of tips to the host when logging out.

And we give the lindens to the user logged in, in line 135.

We are giving to the host the share of each tip at the same moment when the tipjar is tipped.

And we have seen all the modifications to the previous script.


Next week

Next week, we will learn to list the contents in an object, to give them, to give them in a folder, to remove them and how to script a HUD to give things to the people around us.


Answers to questions

What is the range of llGetAgentSize()?

  • llGetAgentSize() range is all the region.

How does the owner set the group of the tipjar?

  • The tipjar is rezzed with the group that the owner has active at that moment.
  • To change the group in the edit window, go to the General Tab, click the Wrench Icon by the current group name, and select one of your groups from the list that pops up.

Could we write a function to set the group of the tipjar in the script ?

  • No, there is no scripting function to do it.

Do we always need to use a group?

  • No, the tipjar can allow anyone to login, without checking any group.
  • In this script we need a group, because the script is checking for it, we need to be in the same group than the tipjar and the llSameGroup() function is checking the group tag

Using llSameGroup(), do the avatars have to be in the region?

  • Yes, llSameGroup() only works on avatars who are present or have only recently left and are still known to the region.

if I have the permission to give money granted and someone copies my tipjar, would they then have access to my account?

  • No, the money is only given from the account of the owner of the object.
  • If someone copies your tipjar immediately becomes the owner and any given money will come from their account.

What happens if the host is not present?

  • In the script the timer is checking if the host is in the region every 3 minutes. If the host is not there, is automatically logged out. In meanwhile the host will be receiving the share of the tips.

What is the warning about access to my lindens?

  • The script is asking for the permission to give lindens, it does that every time you reset the script.
  • If you are the owner of the club, and you want to give the hosts their share, you will need to grant the permission to give money.
  • That is why the sanity check to make sure the script never gives away more money than you were given in any given tip transaction. It's something to remember whenever you have a script giving money in your behalf.
  • Another safety feature is to use an alt account to own your tipjars that only holds money received from tipping and then you occasionally transfer that money to your main.

How can we choose randomly in a list?

  • We have the function llFrand() for a random choice.
  • If we have a list of 13 people, so the list indexes go from 0 ro 12, we can choose one with (integer)llFrand( 13 ).