Jump to content

Frionil Fang

Resident
  • Posts

    392
  • Joined

Everything posted by Frionil Fang

  1. Wow, that's surprising, I always assumed comma-separated statements were not available anywhere in LSL since you can't do multiple definitions (with or without assignments) like "integer a = 3, b = 4, c = 5". The for loop has unique privileges. Two other observations since I had to see for myself which other C-like features would be available: You can leave out the initializer and the per-loop statements, but you can't leave out the termination condition statement. "for(;TRUE;)" does the same as "while(TRUE)", but in C you could just do "for(;;)". Likewise, the termination condition can't be comma separated, it wants a single value, no more, no less. In C comma separation is legal anywhere and the rightmost value is what the entire comma-separated statement evaluates to. "for(a = 100, b = 0; a < 0, b < 100; ++a, ++b)" is valid but dumb C since even if "a < 0" obviously always evaluates to false, the entire statement gets its value from "b < 100".
  2. Had to check: the media URL is limited to 1024 bytes (including the data:<mimetype>, header), not great for images, but still might be useful for some esoteric ideas...
  3. It works with more than just text MIME types; you could use the url "data:image/png;base64" to specify a base64-encoded png. Of course, images of any usable size will balloon up fast to be terribly useful in LSL scripts/any prim property field, but something like .svg might be more feasible. Cheeky example: data:image/png;base64,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
  4. Oh right, my script is ancient from times before I realized I could simply just do that! I'll amend the example script. Thanks, no need to run a timer, even if it's lightweight.
  5. You can use a local texture but, it won't automatically update when used for a particle system. When a local texture is saved again it gets a new UUID, i.e. becomes an entirely new texture. If it's used on an object as a normal texture, the viewer will change that texture for you automatically, and that means you can use a proxy object set to that texture to simply read its UUID and reinsert that into the particle system. The tool I use myself is just a cube prim that I put my local texture on, with the following script: string last; default { changed(integer change) { if(change & CHANGED_TEXTURE) { string s = (string)llGetTexture(ALL_SIDES); if(s != last) llOwnerSay("texture change:\n"+s+"\n---"); last = s; } } } I.e. detects a texture change, gets the UUID on the object and compares to what it was on the last call and if it has changed, says it. This relies on re-pasting the UUID manually into the particle system, but you could just use it as a say over some channel which the particle script listens to, or even incorporate the on-object texture and UUID check into the particle system script directly.
  6. You can just move the unpacking code from the touch_start event to the on_rez event, like this: /* everything before state default was snipped */ default { on_rez(integer p) { OWNER = llGetOwner(); list items = []; integer noCopy = FALSE; string script = llGetScriptName(); integer i; for(i = 0; i < llGetInventoryNumber(INVENTORY_ALL); ++i) { string name = llGetInventoryName(INVENTORY_ALL, i); if(name != script) { if(~llGetInventoryPermMask(name, MASK_OWNER) & PERM_COPY) noCopy = TRUE; items = [name] + items; } } if(noCopy) { if(!llGetAttached()) { invite(); for(i = 0; i < llGetListLength(items); ++i) llGiveInventory(OWNER, llList2String(items, i)); } else llRegionSayTo(OWNER, 0, "Please rez before unpacking"); } else { invite(); llGiveInventoryList(OWNER, llGetObjectName(), items); } } } The lines before the default state aren't shown, so don't remove those. Remember that any changes you made to the unpacking code to filter items out won't be displayed here.
  7. Basically everything is a null reference exception, other than the basic llList2* functions, llDumpList2String and llGetListEntryType. Didn't check every single function, though.
  8. Out of curiosity I wanted to see more about this bug. Nothing majorly enlightening, useful or unexpected here considering the above discussions, but still mildly interesting. Test 1: different variable types. It's not a huge surprise that keys and lists, like strings, can be null if the declaration is skipped. Integers, floats, vectors and rotations are not references, so they just behave as all 0s, without any further problems. This means for rotations you do *not* get the ZERO_ROTATION value -- that's <0, 0, 0, 1>. Test 2: assignment. You can assign the null to another variable, not that it's any more useful. Assigning something to a variable in null state works normally. Test 3: comparisons. You can compare a null string normally, it's just equal to null but not other strings. You can coerce a null string directly into a TRUE value with "if(null_string)", suggesting it's equivalent to "if(null_string != "")". Trying to compare or coerce a null *list* throws the exception, much like llGetListLength(null_list) would. Test 4: nulls in lists. You can put a null string in a list without a problem and that "neutralizes" it somewhat: it can be read and otherwise handled, behaving like an empty string for llList2* functions except llList2ListSlice, though llGetListEntryType identifies it as an invalid entry, not a string. llDumpList2String considers it an empty string. Most other list functions like llList2CSV and llListSort refuse to deal with it with a null reference exception. llList2Json doesn't turn it into JSON_NULL and dies, what a shame. At least nothing crashed the server.
  9. Mostly just saying the same as Bleuhazenfurfle, but anyway... By access I basically just meant "when reading". The C behavior appears similar to LSL, except LSL really doesn't like it when the variable is in a truly uninitialized state. Normally they would be guaranteed to get a zero-like value before access, but the jump across prevents that part it seems. In C, the state of uninitialized memory is up to implementation but nothing stops you from using it, getting either garbage or zeroes. Strings being pointers, a null pointer is different from an empty string (a pointer to a zero byte), and dereferencing either a garbage or a null pointer is going to be a bad time.
  10. I saw your post and was "huh" and turned it into C to look at it again, this wasn't convergent evolution!
  11. Ah, yes, but I meant undeclared, not uninitialized. The compilers I checked just seem to keep better track of which variables are in scope than LSL, preventing the jump over declaration/access to undeclared variable at compile time. Doing that would almost certainly be unintended even if the actual memory structure for said variable was allocated regardless where the declaration is. Edit: maybe I was a little too drunk last night but I can actually make C happily jump over variable declaration and still compile when I look at it now, with the expected unitialized values on access. I.e. the below appears to work just like in LSL:
  12. Bound to agree with that, and from what I recall most languages would consider a possibility of an undeclared variable being accessed an error at compile time (at least C and Java). Side note, I fully approve of goto (jump) shenanigans, function calls are expensive in LSL so doing "tail calls" with jumps is cool.
  13. Does not, you need +mod on the object itself, script permissions don't matter for resetting them. You can open up a single unmodifiable script in a +mod object, too, and press the reset button even if you can't see or edit the contents; attempting to do that in a -mod object just says you're not allowed to open it.
  14. Assuming you just want the standard issue SL dialog box, this should work: integer last_face; // store the last clicked face integer last_prim; // last clicked prim integer listen_handle; integer CHANNEL = -3881; // channel for dialogs default { touch_start(integer total_number) { last_face = llDetectedTouchFace(0); // store the face number to be handled last_prim = llDetectedLinkNumber(0); // store the prim number llSetTimerEvent(10); // allow 10 seconds before timing out the listener llListenRemove(listen_handle); // release any open listen handle listen_handle = llListen(CHANNEL, "", llDetectedKey(0), ""); // listen to the toucher llDialog(llDetectedKey(0), "Random or default?", ["Random", "Default"], CHANNEL); // dialog } timer() { llSetTimerEvent(0); // stop timer llListenRemove(listen_handle); // close listen handle } listen(integer channel, string name, key id, string msg) { string texture_name = "default-1"; // default texture is, uh, default llListenRemove(listen_handle); // close listen handle if(msg == "Random") // random texture { integer textureindex = (integer)llFrand(llGetInventoryNumber(INVENTORY_TEXTURE)); texture_name = llGetInventoryName(INVENTORY_TEXTURE,textureindex); } llSetLinkPrimitiveParamsFast(last_prim,[PRIM_TEXTURE,last_face,texture_name,<1.0, 1.0, 0.0>, <0.0, 0.0, 0.0>, 0.0]); } } In more detail, instead of assigning the random texture on click, it will instead store the clicked prim and face, open a dialog with a listener for it and wait for the user to choose random or default. On choosing random, it uses the same code as in your example to assign the random texture from inventory (note: I changed it to retexture the prim that was clicked, not the prim the script is in; changing "last_prim" to "LINK_THIS" in the primitiveparams calls restores the original functionality). On choosing default, it will just use the texture named "default-1" from inventory instead. If no choice is made within 10 seconds, the listener is closed and the dialog will no longer do anything.
  15. Yeah, I dunno, I don't use C/++ a whole lot these days, popping return values off the stack but not storing them in memory sounds like what's happening. For instance printf() returns an integer with the number of characters written. I can't remember a single time I've checked the return value, though... but I was never a very Lawful coder or participated in super critical projects. In LSL's case there are some specific cases where the warning *would* be useful, namely anything that "modifies" its input like list or string replacements since they can't do it in-place, but those are just a subset of functions returning values.
  16. As far as I remember, allowing return values to be ignored is a very standard convention. Python, C, C++ in addition to LSL don't care with the compilers I tried, and if there's an option to warn for it, no idea what. E.g. const char* dummy() { return ""; } int main(int, char**) { dummy(); return 0; } is perfectly valid C/C++, without errors or warnings. IMO, if you want to warn for it in your system, it should be default off, and independent of the return value type.
  17. The LSL wiki might not be the best place for tutorials, but it's a good reference for what events and functions are available and what they do, and there certainly are examples scattered around categories. For your specific questions, LSL provides one random number generator: https://wiki.secondlife.com/wiki/LlFrand. Communication between objects is usually done via various forms of say, for example: https://wiki.secondlife.com/wiki/LlSay or https://wiki.secondlife.com/wiki/LlRegionSay. Within linksets, objects can communicate via linkmessages or linkset data, https://wiki.secondlife.com/wiki/LlMessageLinked and https://wiki.secondlife.com/wiki/Category:LSL_LinksetData. To secure the communications, you can choose a suitable obscure channel to make drive-by snooping harder and encrypt the messages; the wiki has documentation on the basic string XOR pad functions (usable on base64-encoded strings) https://wiki.secondlife.com/wiki/LlXorBase64 and has other articles and examples for stronger encryption available at https://wiki.secondlife.com/wiki/Category:LSL_Encryption. For storing data, the linkset data features mentioned above are probably the most spacious and versatile. Data display is a bit limited without getting involved with object-based displays or media on prim but generally forms of say on channel 0 (local chat) or https://wiki.secondlife.com/wiki/LlOwnerSay for owner-only display go pretty far. You can also set hovertext above objects with https://wiki.secondlife.com/wiki/LlSetText.
  18. I made another JIRA at https://jira.secondlife.com/browse/BUG-234261 with this specific discussion mentioned, seems to be the same bug though.
  19. This roused some interest and the problem indeed appears when a character whose UTF-16 representation is more than 2 bytes crosses a 256 byte boundary: simplifying the malfunctioning string to the following is enough to exhibit the problem: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx🗝 That's 127 x's (bytes \x00\x78 in UTF-16) for 254 bytes. The key emoji (bytes \xd8\xd3\xdd\xdd) gets its surrogate pair split apart and the script fails to deserialize. Adding or removing a single 2-byte character keeps the pair together, and everything is fine again... until presumably crossing another 256-byte boundary, which I didn't check. For this specific problem @NephilimSpark, it means reordering one of the 2-byte characters in the string is enough to fix this instance: DYS = " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~«»¿¡≡±≥≤ƒ÷√¢│║─■☢☣🕱℞🛠⚠⭍🖒🖓🗝🗢🕷❤🛇🍽█≈"; Appears to work (I moved the approximate equals sign to the end of the string; it looks like a character lookup table, so reordering might not be feasible though).
  20. The functions return a well-defined value instead of causing a script error (the same value as going out of bounds of what was detected) when called from an invalid event, they're listed on the wiki.
  21. Detected* get populated upon entry to an event, but not functions, so their values remain intact until the event is finished. Since LSL is single-threaded, there's no fear of them getting clobbered. At the end of an event (or upon entry to an event that doesn't support Detected*, no way to tell or difference in practice), they get blanked out. As for #2, nothing comes to mind off the bat.
  22. It's difficult to think of a setup for testing for performance differences, but nothing I can see points to big differences between the two. What I took a look at: calls to llMessageLinked and llLinksetDataWrite are about the same as far as performance goes, and both are quite capable of overrunning the event queue for link_message/linkset_data events. If you had the receiving script busy-looping linkset data reads instead of relying on the events, you could get higher throughput at the cost of script impact. Linkset data is an object property, but since it's not directly visible to viewers there doesn't seem to noticeable change in object updates when you change it rapidly, so there's no penalty over linkmessages there. You can choose which prims in the linkset receive link messages, unlike with linkset data, so you could save some event-processing performance by choosing the correct target prims. Of course, if scripts don't implement the linkset_data/link_message events in the first place, this advantage is moot, and you can make use of linkset data without the event.
  23. I got curious since my attempts to inline some performance-critical branches have had little effect in the past and had to do some barebones timing. Not super scientifically rigorous. 1 million ops per batch, 12 batches, drop best and worst batch, convert to operations per second. Script: Using value = 0: Inline math = 127 ko/s. If/else = 134 ko/s. Using value = (integer)llFrand(300)-100, which will obviously be slower than the actual bounds checking itself: Inline math = 45 ko/s. If/else = 45 ko/s. Conclusion: perfectly inconclusive and makes sense that I never really saw any tangible improvement, heh.
×
×
  • Create New...