Methods!
|
|
Kali Dougall
Purple and Spikey
Join date: 5 Feb 2005
Posts: 98
|
03-24-2005 12:14
I'm going to attempt to make sense here, so bear with me. This has probably been brought up before, and is probably less a conscious decision on the Lindens' part than something they just haven't got around to yet. In any case, I long for real methods. Most scripters are aware that there are limited things you can do to objects which are apart from the object containing your script. My object-oriented background makes me very much want to be able to do things to objects that I have the key for, and a very large portion of LSL script I write seems like a workaround for lacking this. Currently, if you want to do anything to an unlinked object, the only way is to set up a listen and then scream at it. It's widely acknowledged that listens add to lag. Let's take an example. Let's say you want to build some 20-storey megastructure with a working elevator. Each floor needs a call button. This call button, when pressed, needs to scream like a banshee at the elevator whenever it's pressed, because the elevator is not linked and there's no other way to communicate with it. (We'll ignore the fact that an elevator on the top floor, for instance, may be out of shout range of the bottom call button. We won't even get into the workarounds you need for THAT.) So now we'll need a listen on the elevator and touch events on every call button which shout to the elevator. Every shout causes the server to look at everything in range and consider which objects might care it shouted. Any script emitting text around the elevator has to pause to consider the listen event and see whether it cares about the text. Now imagine that you want doors blocking your elevator shaft which open as the elevator arrives. If you don't use some implementation of llVolumeDetect, your best bet is to have the elevator say something to the call button as it arrives. This adds 20 more listens and makes a noticeable impact on sim FPS. Of course, most of us avoid doing anything like that because of the lag it causes. But what if there was another way? We have the key of the elevator, but we can't do anything with it. Without a sensor, we can't even get its current position. Well, let's pretend we could do the following with our call buttons: key elevator = "some literal key"; vector floor_position = <literal position where the elevator will be on this floor>;
state default { touch_start(integer i) { if (llVecDist(elevator.getPosition(), floor_position) < 1.0) { llWhisper(0, "The elevator has already arrived."); } else { llWhisper(0, "Calling elevator."); elevator.comeTo(floor_position); } } }
The significance of the above is it uses defined methods in your elevator script. Like this one: method vector getPosition() { return llGetPos(); }
Any script, using the elevator's key, can call that method on the elevator and get back its current position. No sensors. The call buttons have a line of communication to the elevator which can't be listened in on and doesn't involve shouting. No listens. This does two things that I see people on these forums long for: provides a reliable method of communication between two objects anywhere, and reduces the load that scripts, by necessity, place on the server. Of course, I'm sure this wouldn't be easy to implement, and perhaps the Lindens already considered it and found it undesirable. There are certain issues that would have to be considered: If the object is deleted or returned, does any referencing script receive a runtime error? Will this setup (or should it) work across sim lines? Does the calling script wait if the method delays? There are also security issues in a multi-user environment, like malicious folks sending bogus info to your elevator's comeTo method (if, of course, they get the elevator's key and also guess the name of your method). This can be solved either by some sort of builtin permissions system, or by yourself by passing some value to the comeTo method that allows the elevator to verify the sender itself. But these are implementation issues that are really separate to whether the basic idea is a good one, and I'm sure they could be overcome. In any case, for anyone used to an object-oriented environment, methods seem extremely useful and often very frustrating not to have. If we can have an object's key, we should be able to do something with it without resorting to laggy sensors and listens. Thoughts?
|
|
Racer Plisskin
Rezerator
Join date: 2 Jan 2005
Posts: 147
|
03-24-2005 14:33
Neat idea!
Sounds like it would make building something like a 'turbo lift' or 'Wonkavator' prety easy to implement.
I can think of so many other uses for it just off the top of my head...
Racer P.
|
|
Kali Dougall
Purple and Spikey
Join date: 5 Feb 2005
Posts: 98
|
03-24-2005 14:56
Thanks for your response, Racer. Indeed, the elevator was just one example.  I think there are few scripting projects of any complexity, using multiple objects anyway, where real methods wouldn't come in handy somewhere. Right now I'm part of a team working on a game with multiple balls in play, multiple targets, and one central server, all screaming at each other all the time... and I can't help but think that it should be possible to do things much more cleanly than this.
|
|
Olmy Seraph
Valued Member
Join date: 1 Nov 2004
Posts: 502
|
messages, not methods
03-24-2005 18:51
Hey Kali, how timely of you to post this, as I've been thinking a lot along these lines the last week. I've got a huge background in OOP, have build virtual machines for Smalltalk and Java, etc etc. I like how you're thinking here, so let me build on what you're suggesting.
The core of OOP is often thought to be classes, inheritance and methods. Alan Kay will tell you it's messages. Messages directly give you state encapsulation (object-based programming), and indirectly allow you to devise message resolution schemes that use inheritance and polymorphism. I consider those three (encapsulation, polymorphism and inheritance) to be the key elements of an OOP system.
Smalltalk was originally devised so that objects could be executed independently on separate processors. Using a message-passing paradigm for interactions between objects gives you many options to avoid the problems you were talking about. There have been many distributed variants of OOP languages with just the features you'd need, like weak pointers, distributed objects and asynchronous messaging.
I was thinking last week about what it would be like if every object was addressable and could receive messages. And if you could program them using inheritance. Since SL uses copying to create objects, a prototype-based language would be more appropriate than a class-based one. SELF, for example, would work pretty well. SELF is wonderful in that you get all the implementation efficiency of a class-based language but all the flexibility of a prototype-based one. In such a system, you could clone objects and they'd share the same script as their prototype, and if you then changed the prototype each clone's script would change too. Unless you explicity made changes in a clone's script, that is.
The event model gives LSL a lot of the power of message-passing. The problem is that developers can't define their own new events for objects.
I'd love to see a system that allowed asynchronous messaging between objects using UIDs as weak pointers, and synchronous messaging with return values between prims in a link set using link IDs. I would also want some better data structures in the language itself, as it would be nuts to be rezzing prims to put another record into a linked list, so there should also be insubstantial objects that are not bound to prims but live only in the linguistic space. That would so rock my world!
_____________________
Some people are like Slinkies... not really good for anything, but they sure bring a smile to your face when you push them down the stairs.
|
|
Kali Dougall
Purple and Spikey
Join date: 5 Feb 2005
Posts: 98
|
03-24-2005 21:28
I agree, Olmy. With an environment built entirely of unique objects, the lack of OOP is really quite stunning when you think about it. I just thought a complete rehauling of LSL was a bit much to suggest at once, though.  But yeah, I agree that the closer LSL gets to being truly object-oriented, the better off we'll be and the more we'll be able to do in SL. The prototype system you describe is what I'm most used to; I think that would work great. And real data structures at some point are, of course, a must.
|
|
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
|
03-24-2005 23:42
The trouble with this is that it would require a core rewrite. Everyone agrees that LSL isn't the best. They just can't agree what it should be. So i humbly propose a new comm protocal. It would be limited to prims in the sim (possibly extended to include prims with in a 10 meter buffer zone into boardering sims). A user defined event is marked with ":" as the first character but don't included that when you enable, disable, or trigger them. The event names are case sensative. You can only trigger user defined events with llTriggerEvent*(), events. User defined events are disabled by default. User defined events may not have the same name as built in events. All events (not just user defined ones) can be enabled or disabled via llEventEnable & llEventDisable. The generic event is what is called when llTriggerEvent*() is called with a null string; it is enabled by default if it is present in the state. The paramaters of a custom event are an integer, key and a list. The integer is the link number, if it was triggered by llTriggerEvent then link == -1. The key is the uuid of the prim that triggered the event. The list can contain anything and is passed by the triggering function. Functions: llEventEnable(string name) llEventDisable(string name) list llEventList(integer type) llTriggerEvent(key target, string event, list params) llTriggerEventLink(integer link, string event, list params)
list llEventList(integer type) type == 1 then return all user defined events in the active state type == 2 then return all disabled user defined events in the active state type == 3 then return all enabled user defined events in the active state type == 0 then return all events in the active state type == -1 then return all built-in events in the active state type == -2 then return all disabled built-in defined events in the active state type == -3 then return all enabled built-in defined events in the active state
Event syntax: default { state_entry() { llEventEnable("eventa"); } :eventa(integer link, string source, list params) { //your code here llEventEnable("eventb"); llEventDisable("eventa"); } :eventb(integer link, key source, list params) { //your code here } generic(integer link, key source, list params) { //your code here } }
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river. - Cyril Connolly
Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence. - James Nachtwey
|
|
Khamon Fate
fategardens.net
Join date: 21 Nov 2003
Posts: 4,177
|
03-25-2005 08:27
i think y'all are confusing the interface with the code. objects don't really shout at each other and listen to each other in the world because there is no world. channels are just common locs to which scripts establish a pointer if you tell them to listen. the overhead is that the script has to check constantly to see if the data has been updated. that's what causes listen lag; but that's the only way a virtual machine can operate a socket.
implementing methods will allow us to name the loc rather than using channel numbers. but i don't see the overhead descreasing at all.
|
|
Olmy Seraph
Valued Member
Join date: 1 Nov 2004
Posts: 502
|
03-25-2005 08:44
From: Khamon Fate i think y'all are confusing the interface with the code. objects don't really shout at each other and listen to each other in the world because there is no world. channels are just common locs to which scripts establish a pointer if you tell them to listen. the overhead is that the script has to check constantly to see if the data has been updated. that's what causes listen lag; but that's the only way a virtual machine can operate a socket.
implementing methods will allow us to name the loc rather than using channel numbers. but i don't see the overhead descreasing at all. No confusion here, Sprout. Unless I don't actually understand how listeners operate. The lag isn't that every listening object is constantly checking (polling) for messages. The lag is that every message much check for a match against every listening object (event-driven). The Linden explanation is that listeners don't constantly poll channels, rather speech (whisper/say/shout) events drive a match algorithm that results in listen event handlers in matched objects being invoked with the data. Allowing messages to be delivered directly to an object via UID reference can be done with a simple table (hash?) lookup, rather than a complex binding via channel/position/speaker/message matching. The big decrease in overhead is that you won't need to check every listener for a match, instead you can go directly to the intended object. What this provides is scalability, which is crucial for any significantly complex system.
_____________________
Some people are like Slinkies... not really good for anything, but they sure bring a smile to your face when you push them down the stairs.
|
|
Strife Onizuka
Moonchild
Join date: 3 Mar 2004
Posts: 5,887
|
03-25-2005 09:21
The major issue i have with the feature suggestion above is it lacks handling for errors. What if the object doesn't exist? Another issue is permissions and who is calling the command.
_____________________
Truth is a river that is always splitting up into arms that reunite. Islanded between the arms, the inhabitants argue for a lifetime as to which is the main river. - Cyril Connolly
Without the political will to find common ground, the continual friction of tactic and counter tactic, only creates suspicion and hatred and vengeance, and perpetuates the cycle of violence. - James Nachtwey
|
|
Kali Dougall
Purple and Spikey
Join date: 5 Feb 2005
Posts: 98
|
03-25-2005 09:53
From: Strife Onizuka The major issue i have with the feature suggestion above is it lacks handling for errors. What if the object doesn't exist? Another issue is permissions and who is calling the command. If this were implemented, I imagine we'd get something like llGetCaller() to return the owner of the calling script. Use it with llGetOwner() or llSameGroup(), or not, for whatever security the user sees fit. The object not existing is indeed an issue, since it couldn't be checked at compile time, and would probably force a runtime error on the script. A boolean llIsValid(key) could be used to determine whether a key is still in action and gracefully handle things when it isn't, and/or some kind of exception handling.
|
|
Olmy Seraph
Valued Member
Join date: 1 Nov 2004
Posts: 502
|
03-25-2005 10:37
From: Strife Onizuka The major issue i have with the feature suggestion above is it lacks handling for errors. What if the object doesn't exist? Another issue is permissions and who is calling the command. If you treat object messaging as an asynchronous call, it's not substantially different from shouts and listens. Currently, when you shout a message you have no idea if any other object is listening, unless that object explicitly shouts you an ack. I'd suggest that your event triggers would operate the same way - you don't know if anyone is on the other end. As I mentioned in my original response to this thread, there is a considerable body of research into distributed OOP systems, and techniques such as weak pointers, proxy objects, and asynchronous messages have been used since the 1980s to create robust systems. OMG/CORBA, .NET, and various Java middleware/distribution technologies all solve similar problems. By the way, your strawman proposal for an LSL extension for user-defined events isn't a bad start, but I'd like to see something that was integrated with the language with more syntactic sugar, rather than forcing programmers to use awkward llTriggerEvent() calls. It should not be difficult to extend the LSL compiler to recognize syntax like object.setTexture(). Integrating user-defined types and parameter lists is harder, but still a well-understood problem. It all depends on how serious LL is about creating a scalable programming system.
_____________________
Some people are like Slinkies... not really good for anything, but they sure bring a smile to your face when you push them down the stairs.
|
|
Khamon Fate
fategardens.net
Join date: 21 Nov 2003
Posts: 4,177
|
03-25-2005 11:03
i'm gonna cry now because olmy corrected me in public.
i see. i think so procedurally. it didn't occur to me that a sim could table script names per state and invoke them en masse when given parameters were met. i need to start programming again rather than working incessently on all this old fashioned networking code.
|