Difference between revisions of "User:Void Singer/Red Tea"

From Second Life Wiki
Jump to navigation Jump to search
m (And now we haz content!)
m (updating form LSL tags to SOURCE tags)
 
(7 intermediate revisions by the same user not shown)
Line 8: Line 8:
|content=
|content=
=== What is it? ===
=== What is it? ===
Red Tea is an open source file system back end custom built for Teacup in support of LSL's HTTP-in [[Media On A Prim|MOAP]] functionality. It builds on the work of Several other people including but not limited to, [[User:Kelly_Linden|Kelly Linden]], [[User:Torley_Linden|Torley Linden]], [[User:Tali_Rosca|Tali Rosca]], [[User:Vegas_Silverweb|Vegas Silverweb]], and special mentions to [[User:Kate_Linden|Kate]] and/or [[User:Edelman_Linden|Edelman]] Linden (whoever wrote the original align for LSL). The idea is to make it easy to serve web content from within SL, with a minimum of work or understanding, in the most standards compliant and flexible way possible.
Red Tea is an open source File Service custom built for [[User:Void Singer/Teacup|Teacup/Saucer]]
=== How does it work? ===
=== How does it work? ===
When Red Tea starts, it looks for any notecards in the prim, reads them, converting and wrapping any notecards name ending with .tsp (Teacup Server Page) and storing them in memory, waiting for a request from the server. if the prim contents change, it waits 3 seconds then resets itself to start again. if the server requests a stored file, the file is sent with a 200 "success" code. if the file does not exist, a 404 "not found" code is sent.
When Red Tea starts, it looks for any notecards in the prim, reads them, converting and wrapping any notecards with names ending with {{HoverText|".tsp"|Teacup Server Page}}, then storing them in memory, waiting for a request from the Teacup server. If the number of notecards changes it waits 3 seconds then restarts.
=== Format Requirments? ===
=== Format Requirments? ===
Red Tea reads a modified html page from a notecard
Red Tea reads a modified html page from a notecard
* For html content, the notecard name should end with ".tsp" (Teacup server page)
* For html content, the notecard name should end with {{HoverText|".tsp"|Teacup Server Page}}
* For html content, the notecard contents are limited to what's valid ''inside'' a normal html "body" tag.
* For html content, the notecard contents are limited to what's valid ''inside'' a normal html "body" tag.
* no more than 255 bytes per line in the notecard (this is an LSL limitation)
* no more than 255 bytes per line in the notecard (this is an LSL limitation)
=== why all the tea jokes? ===
:[[User:Void Singer/Red_Tea#Return_to_Void_Singers_user_page|Return to top]]
So why all the tea jokes? Well I happen to like steam punk stuff, and it has a sort of pseudo-victorian-era feel to it, but in all truth it was sparked by a bit of geek humor. There was an old april fool's joke passed around as an official sounding memo about creating a protocol for controlling coffee pots with http (back before they actually started making net enabled appliances) and part of that proposal was that teapots should return an error response "418 I'm a teapot" if you tried to make it brew coffee... since these servers are small, and 418 is safe to use internally because it wouldn't normally be encountered, I figured hey, Teapot --> Teacup --> Victorian Imagery --> Steam Punk --> teacup that is it's own teapot --> [[Server In a Prim|SIP]] --> many different types of tea --> Success!
}}


... And thus was born the [[Server In a Prim|SIP]], Teapot server, and Red Tea file system
{{void-box
:[[User:Void Singer/Teacup#Return_to_Void_Singers_user_page|Return to top]]
|title=Quick Start
|content=
So you don't want to read a bunch of tech babble, you just want to make a website in a prim right? Well I can sympathize so here are some (mostly) easy steps to get you up and running fast...
# Rez a box and name it something creative, like "My website"
# Copy the text in the grey box below [[User:Void Singer/Teacup#teacup.js|Code: teacup.js]] into a notecard named "teacup.js", save it, and drop it in your rezzed box
#* You May want to Edit this file to your liking first... You can ''add'' most valid javascript functions that do not rely on body onload behavior.
# Copy the text in the grey box below [[User:Void Singer/Red_Tea#Sample_Index.tsp|Code: Sample Index.tsp]] into a notecard named "index.tsp", save it, and drop it in your rezzed box
#* You May want to Edit this file to your liking first... You can use any html that is valid inside of a body tag.
# Copy the text in the grey box below [[User:Void Singer/Red_Tea#Sample_Page1.tsp|Code: Sample Page1.js]] into a notecard named "page1.tsp", save it, and drop it in your rezzed box
#* You May want to Edit this file to your liking first... You can use any html that is valid inside of a body tag.
# Copy the text in the grey box below [[User:Void Singer/Red_Tea#Red_Tea|Code: Red Tea]] into a script named "Red Tea v0.3.1", save it, and drop it in your rezzed box
# Go [[User:Void Singer/Teacup#Quick_Start|here]] and start at step 2...
:[[User:Void Singer/Red_Tea#Return_to_Void_Singers_user_page|Return to top]]
}}
}}


Line 26: Line 38:
|title=Code
|title=Code
|content=
|content=
<lsl>/*( Red Tea v0.2 )*/
=== Red Tea ===
 
----
* Save in a [[Mono#How_to_use_Mono|MONO]] script named "'''Red Tea v0.5'''"
<source lang="lsl2">/*( Red Tea v0.5 )*/
//-- Master Handling lists
//-- Master Handling lists
list    gLstNom;
list    gLstNom;
list    gLstTxt;
list    gLstTxt;
 
//-- These track memory while loading
//-- These track memory while loading
integer gIntCap;
integer gIntCap;
integer gIntBfr;
integer gIntBfr;
 
//-- these handle the notecard loading
//-- these handle the notecard loading
key    gKeyQry;
key    gKeyQry;
Line 43: Line 58:


default{
default{
    state_entry(){
state_entry(){
        //-- grab all possible notecard names
//-- grab all possible notecard names
        if (gIntCap = llGetInventoryNumber( INVENTORY_NOTECARD )){
if (gIntCap = llGetInventoryNumber( INVENTORY_NOTECARD )){
            llWhisper( 0, "Attempting to load " + (string)gIntCap + " notecards" );
llWhisper( 0, "Attempting to load " + (string)gIntCap + " notecards" );
            do{
do{
                gLstNom += [llGetInventoryName( INVENTORY_NOTECARD, gIntNcd )];
gLstNom += [llGetInventoryName( INVENTORY_NOTECARD, gIntNcd )];
            }while (++gIntNcd < gIntCap);
}while (++gIntNcd < gIntCap);
            //-- work on the firs one.
//-- work on the firs one.
            gKeyQry = llGetNotecardLine( gStrNcd = llList2String( gLstNom, 0 ), gIntNcd = 0 );
gKeyQry = llGetNotecardLine( gStrNcd = llList2String( gLstNom, 0 ), gIntNcd = 0 );
            gIntCap = llGetFreeMemory();
gIntCap = llGetFreeMemory();
        }else{
}else{
            llWhisper( 0, "No content to load" );
llWhisper( 0, "No content to load" );
        }
}
    }
}
   
    changed( integer vBitChg ){
changed( integer vBitChg ){
        if (CHANGED_INVENTORY & vBitChg){
if (CHANGED_INVENTORY & vBitChg){
            //-- when inventory changes, wait a few seconds before trying to load notecards
//-- when inventory changes, only restart if the notecard count changed
            llSetTimerEvent( 3.0 );
if (llGetInventoryNumber( INVENTORY_NOTECARD ) != (gLstNom != [])){
        }
llSetTimerEvent( 5.0 );
    }
}
   
}
    timer(){
}
        llResetScript();
    }
timer(){
   
llResetScript();
    dataserver( key vKeyQID, string vStrDta ){
}
        if (gKeyQry == vKeyQID){ //-- is this our dataserver event?
            if (EOF == vStrDta){ //-- did we finish the notecard?
dataserver( key vKeyQID, string vStrDta ){
                if (~llSubStringIndex( gStrNcd, ".tsp" )){ //-- is this a html page?
if (gKeyQry == vKeyQID){ //-- is this our dataserver event?
                    gStrTmp = "vTea='" + gStrTmp + "';"; //-- wrap it
if (EOF == vStrDta){ //-- did we finish the notecard?
                }
if (~llSubStringIndex( gStrNcd, ".tsp" )){ //-- is this a html page?
                gLstTxt += [(gStrTmp = "") + gStrTmp]; //-- save it
gStrTmp = "v0='" + gStrTmp + "';"; //-- wrap it
                llWhisper( 0, gStrNcd + " successfully loaded");
}
                if ((integer)(vStrDta = (string)(gIntCap - llGetFreeMemory())) > gIntBfr){
gLstTxt += [(gStrTmp = "") + gStrTmp]; //-- save it
                    gIntBfr = (integer)vStrDta; //-- adjust buffer if needed
llWhisper( 0, gStrNcd + " successfully loaded");
                } //-- test if we have more cards to read and room to spare
llMessageLinked( LINK_SET, 601, gStrNcd, NULL_KEY ); //-- saucer compatibility
                if ((gIntNcd = -~llListFindList( gLstNom, [gStrNcd] )) < (gLstNom != []) && (gIntCap = llGetFreeMemory()) > gIntBfr){
if ((integer)(vStrDta = (string)(gIntCap - llGetFreeMemory())) > gIntBfr){
                    gKeyQry = llGetNotecardLine( gStrNcd = llList2String( gLstNom, gIntNcd ), gIntNcd = 0 ); //-- get next card
gIntBfr = (integer)vStrDta; //-- adjust buffer if needed
                }else{ //-- out of cards or space
} //-- test if we have more cards to read and room to spare
                    gStrTmp = gKeyQry = ""; //-- clear vars
if ((gIntNcd = -~llListFindList( gLstNom, [gStrNcd] )) < (gLstNom != []) && (gIntCap = llGetFreeMemory()) > gIntBfr){
                    //-- save totals and set conditional failure message
gKeyQry = llGetNotecardLine( gStrNcd = llList2String( gLstNom, gIntNcd ), gIntNcd = 0 ); //-- get next card
                    if ((gIntCap = (gLstNom != [])) > (gIntNcd = (gLstTxt != []))){
}else{ //-- out of cards or space
                        gStrNcd = "\nFailed to read " + gStrNcd;  
gStrTmp = gKeyQry = ""; //-- clear vars
                    }else{
//-- save totals and set conditional failure message
                        gStrNcd = gStrTmp;
if ((gIntCap = (gLstNom != [])) > (gIntNcd = (gLstTxt != []))){
                    }//-- report load
gStrNcd = "\nFailed to read " + gStrNcd;  
                    llWhisper( 0, "Loaded " + (string)gIntNcd + " of " + (string)gIntCap + " pages" + (gStrNcd = "") +
}else{
                                  gStrNcd + "\n~" + (string)(llGetFreeMemory() - gIntBfr) + " bytes free"  );
gStrNcd = gStrTmp;
                }
}//-- report load
            }else{ //-- notecard read
llWhisper( 0, "Loaded " + (string)gIntNcd + " of " + (string)gIntCap + " pages" + (gStrNcd = "") +
                if (~llSubStringIndex( gStrNcd, ".tsp" )){ //-- is this a html page?
              gStrNcd + "\n~" + (string)(llGetFreeMemory() - gIntBfr) + " bytes free"  );
                    //-- tweak for javascript wrapper compatibility
}
                    vStrDta = llDumpList2String( llParseStringKeepNulls( vStrDta, ["\\"], [] ), "\\\\" );
}else{ //-- notecard read
                    vStrDta = llDumpList2String( llParseStringKeepNulls( vStrDta, ["'"], [] ), "\\'" );
if (~llSubStringIndex( gStrNcd, ".tsp" )){ //-- is this a html page?
                }
//-- tweak for javascript wrapper compatibility
                gStrTmp += vStrDta + "\\n"; //-- accumulate to variable
vStrDta = llDumpList2String( llParseStringKeepNulls( vStrDta, ["\\"], [] ), "\\\\" ); //-- "
                if ((integer)(vStrDta = (string)(gIntCap - llGetFreeMemory())) > gIntBfr){
vStrDta = llDumpList2String( llParseStringKeepNulls( vStrDta, ["'"], [] ), "\\'" ) + "\\n";
                    gIntBfr = (integer)vStrDta; //-- adjust buffer if needed
}else{
                }
vStrDta += "\n";
                if ((gIntCap >> 1) > gIntBfr){ //-- test free space against buffer
}
                    gKeyQry = llGetNotecardLine( gStrNcd, ++gIntNcd ); //-- get next line
gStrTmp += vStrDta; //-- accumulate to variable
                }else{ //-- our saftey cap was exceeded, stop and report what we have
if ((integer)(vStrDta = (string)(gIntCap - llGetFreeMemory())) > gIntBfr){
                    gLstNom = llDeleteSubList( gLstNom, gIntNcd, -1 );
gIntBfr = (integer)vStrDta; //-- adjust buffer if needed
                    gIntCap = (gLstNom != []);
}
                    gIntNcd = (gLstTxt != []);
if ((gIntCap >> 1) > gIntBfr){ //-- test free space against buffer
                    gStrTmp = gKeyQry = "";
gKeyQry = llGetNotecardLine( gStrNcd, ++gIntNcd ); //-- get next line
                    llWhisper( 0, "Loaded " + (string)gIntNcd + " of " + (string)gIntCap + " page(s)\nFailed while reading " +
}else{ //-- our saftey cap was exceeded, stop and report what we have
                                  gStrNcd + "\n~" + (string)(llGetFreeMemory() - gIntBfr) + " bytes free"  );
gLstNom = llDeleteSubList( gLstNom, gIntNcd, -1 );
                }
gIntCap = (gLstNom != []);
            }
gIntNcd = (gLstTxt != []);
        }
gStrTmp = gKeyQry = "";
    }
llWhisper( 0, "Loaded " + (string)gIntNcd + " of " + (string)gIntCap + " page(s)\nFailed while reading " +
   
              gStrNcd + "\n~" + (string)(llGetFreeMemory() - gIntBfr) + " bytes free"  );
    link_message( integer vIntSrc, integer vIntDta, string vStrDta, key vKeyDta ){
}
        if (418 == vIntDta && !!llGetLinkNumber() == vIntSrc){ //-- server request?
}
            if (~vIntDta = llListFindList( gLstNom, [vStrDta] )){ //-- do we have that?
}
                llMessageLinked( vIntSrc, 200, llList2String( gLstTxt, vIntDta ), vKeyDta );
}
            }else{ //-- we don't have it
                llMessageLinked( vIntSrc, 404, "", vKeyDta );
link_message( integer vIntSrc, integer vIntDta, string vStrDta, key vKeyDta ){
            }
if (vKeyDta){ //-- valid key?
        }
if (418 == vIntDta){ //-- server request?
    }
if (~vIntDta = llListFindList( gLstNom, [llList2String( llParseStringKeepNulls( vStrDta, [], ["?", "#"] ), 0 )] )){ //-- do we have that?
llMessageLinked( vIntSrc, 200, llList2String( gLstTxt, vIntDta ), vKeyDta );
}
}
}//-- fast service, no need to add saucer start compatibility
}
}
}
/*//--                          License Text                          --//*/
/*//--                          License Text                          --//*/
Line 132: Line 152:
/*//  Void Singer [ https://wiki.secondlife.com/wiki/User:Void_Singer ]  //*/
/*//  Void Singer [ https://wiki.secondlife.com/wiki/User:Void_Singer ]  //*/
/*//  All usages must contain a plain text copy of the previous 2 lines.  //*/
/*//  All usages must contain a plain text copy of the previous 2 lines.  //*/
/*//--                                                                  --//*/</lsl>
/*//--                                                                  --//*/</source>
:[[User:Void Singer/Teacup#Return_to_Void_Singers_user_page|Return to top]]
^ [[User:Void Singer/Red_Tea#Return_to_Void_Singers_user_page|Return to top]]
 
=== Sample Index.tsp  ===
----
* Save in a notecard named "'''index.tsp'''"
<pre><h1>This is a sample Index Page</h1>
<p>would you like to <a href="page1.tsp">go to page 1</a>?<br>
Would you like to <a onClick="u1('Region Stats.tsp');" style="color:blue;text-decoration:underline;cursor:pointer;">view the Region Stats</a>?</p></pre>
^ [[User:Void Singer/Red_Tea#Return_to_Void_Singers_user_page|Return to top]]
 
=== Sample Page1.tsp ===
----
* Save in a notecard named "'''page1.tsp'''"
<pre><h1>This is a sample linked page</h1>
<p>Would you like to <a onClick="u1('Region Stats.tsp');" style="color:blue;text-decoration:underline;cursor:pointer;">view the Region Stats</a>?<br>
would you like to <a href="index.tsp">go to the index</a>?</p></pre>
:[[User:Void Singer/Red_Tea#Return_to_Void_Singers_user_page|Return to top]]
}}
}}


Line 140: Line 176:
|content=
|content=
=== Compatible Servers and Extensions ===
=== Compatible Servers and Extensions ===
[[User:Void_Singer/Teacup|Teacup]] - Server front end
----
* [[User:Void_Singer/Teacup|Teacup]] - [[Server_In_a_Prim|SIP]] front end
* [[User:Void_Singer/Saucer|Saucer]] - Optional Fast 404 Extension for preventing long timeouts for missing/bad pages
* [[User:Void_Singer/Tea_Strainer|Tea Strainer]] - Optional Troubleshooting Monitor for checking messages being sent and received.
 
=== Known Bugs ===
=== Known Bugs ===
* Extra line break inserted at the end of every file
----
* Extra line break inserted at the end of every {{HoverText|".tsp"|Teacup Server Page}} or text file
* If the server request a file that has a notecard, before that notecard is finished loading, a 200 "Success" response will be generated and an empty file will be served.
* If the server request a file that has a notecard, before that notecard is finished loading, a 200 "Success" response will be generated and an empty file will be served.
* Loading large files before small ones may lock them out if the buffer limit is reached. Try to read small files first. (they are read in alphabetic order)
* Loading large files before small ones may lock them out if the buffer limit is reached. Try to read small files first. (they are read in alphabetic order)
=== Planned Upgrades ===
=== Planned Upgrades ===
* None really, the intent is to be an example. other "flavors" can use differnt methods.
----
=== NOTE TO WIKI EDITORS ===
* None really, the intent is to be an example. other "flavors" can use different methods. but it may get minor updates as time passes
I have kept this page as a sub page of my user page to denote that it should NOT be edited except to correct errors, add confirmed bugs, or add related resources.<br>
 
The intent is to ensure executive control is in one person's hands to retain a cohesive vision of it's development.<br>
=== ChangeLog / Old Versions ===
If you have an improvement suggestion, or request, please use the [[User_Talk:Void Singer/Teacup|discussion]] page.
----
:[[User:Void Singer/Teacup#Return_to_Void_Singers_user_page|Return to top]]
* Code will be considered stable when it reaches v1.0
* [[User:Void_Singer/Red_Tea|Red Tea v0.5]]
** Compatibility update to match [[Teacup|Teacup v0.5]]
** Sample pages updated
* [http://wiki.secondlife.com/w/index.php?title=User:Void_Singer/Red_Tea&oldid=1141629 Red Tea v0.3.1]
** tweaked for changes to [[Saucer]]
* [http://wiki.secondlife.com/w/index.php?title=User:Void_Singer/Red_Tea&oldid=1141144 Red Tea v0.3]
** No longer sends 404 messages of it's own
** No longer restarts on every inventory change, only if the notecard count changes from what it has already read.
* [http://wiki.secondlife.com/w/index.php?title=User:Void_Singer/Red_Tea&oldid=1140507 v0.2]
** Initial Public Release
 
:[[User:Void Singer/Red_Tea#Return_to_Void_Singers_user_page|Return to top]]
}}
}}


Line 157: Line 211:
|title=Questions or Comments?
|title=Questions or Comments?
|content=
|content=
Feel free to leave me a note on the [[User_Talk:Void Singer/Teacup|discussion]] page.
Feel free to leave me a note on the [https://wiki.secondlife.com/w/index.php?title=User_talk:Void_Singer/Red_Tea&action=edit&section=new discussion] page.
}}
}}

Latest revision as of 08:55, 26 January 2015

Red Tea

What is it?

Red Tea is an open source File Service custom built for Teacup/Saucer

How does it work?

When Red Tea starts, it looks for any notecards in the prim, reads them, converting and wrapping any notecards with names ending with ".tsp", then storing them in memory, waiting for a request from the Teacup server. If the number of notecards changes it waits 3 seconds then restarts.

Format Requirments?

Red Tea reads a modified html page from a notecard

  • For html content, the notecard name should end with ".tsp"
  • For html content, the notecard contents are limited to what's valid inside a normal html "body" tag.
  • no more than 255 bytes per line in the notecard (this is an LSL limitation)
Return to top

Quick Start

So you don't want to read a bunch of tech babble, you just want to make a website in a prim right? Well I can sympathize so here are some (mostly) easy steps to get you up and running fast...

  1. Rez a box and name it something creative, like "My website"
  2. Copy the text in the grey box below Code: teacup.js into a notecard named "teacup.js", save it, and drop it in your rezzed box
    • You May want to Edit this file to your liking first... You can add most valid javascript functions that do not rely on body onload behavior.
  3. Copy the text in the grey box below Code: Sample Index.tsp into a notecard named "index.tsp", save it, and drop it in your rezzed box
    • You May want to Edit this file to your liking first... You can use any html that is valid inside of a body tag.
  4. Copy the text in the grey box below Code: Sample Page1.js into a notecard named "page1.tsp", save it, and drop it in your rezzed box
    • You May want to Edit this file to your liking first... You can use any html that is valid inside of a body tag.
  5. Copy the text in the grey box below Code: Red Tea into a script named "Red Tea v0.3.1", save it, and drop it in your rezzed box
  6. Go here and start at step 2...
Return to top

Code

Red Tea


  • Save in a MONO script named "Red Tea v0.5"
/*( Red Tea v0.5 )*/
 
//-- Master Handling lists
list    gLstNom;
list    gLstTxt;
 
//-- These track memory while loading
integer gIntCap;
integer gIntBfr;
 
//-- these handle the notecard loading
key     gKeyQry;
string  gStrNcd;
integer gIntNcd;
string  gStrTmp;

default{
	state_entry(){
		 //-- grab all possible notecard names
		if (gIntCap = llGetInventoryNumber( INVENTORY_NOTECARD )){
			llWhisper( 0, "Attempting to load " + (string)gIntCap + " notecards" );
			do{
				gLstNom += [llGetInventoryName( INVENTORY_NOTECARD, gIntNcd )];
			}while (++gIntNcd < gIntCap);
			 //-- work on the firs one.
			gKeyQry = llGetNotecardLine( gStrNcd = llList2String( gLstNom, 0 ), gIntNcd = 0 );
			gIntCap = llGetFreeMemory();
		}else{
			llWhisper( 0, "No content to load" );
		}
	}
	
	changed( integer vBitChg ){
		if (CHANGED_INVENTORY & vBitChg){
			 //-- when inventory changes, only restart if the notecard count changed
			if (llGetInventoryNumber( INVENTORY_NOTECARD ) != (gLstNom != [])){
				llSetTimerEvent( 5.0 );
			}
		}
	}
	
	timer(){
		llResetScript();
	}
	
	dataserver( key vKeyQID, string vStrDta ){
		if (gKeyQry == vKeyQID){ //-- is this our dataserver event?
			if (EOF == vStrDta){ //-- did we finish the notecard?
				if (~llSubStringIndex( gStrNcd, ".tsp" )){ //-- is this a html page?
					gStrTmp = "v0='" + gStrTmp + "';"; //-- wrap it
				}
				gLstTxt += [(gStrTmp = "") + gStrTmp]; //-- save it
				llWhisper( 0, gStrNcd + " successfully loaded");
				llMessageLinked( LINK_SET, 601, gStrNcd, NULL_KEY ); //-- saucer compatibility
				if ((integer)(vStrDta = (string)(gIntCap - llGetFreeMemory())) > gIntBfr){
					gIntBfr = (integer)vStrDta; //-- adjust buffer if needed
				} //-- test if we have more cards to read and room to spare
				if ((gIntNcd = -~llListFindList( gLstNom, [gStrNcd] )) < (gLstNom != []) && (gIntCap = llGetFreeMemory()) > gIntBfr){
					gKeyQry = llGetNotecardLine( gStrNcd = llList2String( gLstNom, gIntNcd ), gIntNcd = 0 ); //-- get next card
				}else{ //-- out of cards or space
					gStrTmp = gKeyQry = ""; //-- clear vars
					 //-- save totals and set conditional failure message
					if ((gIntCap = (gLstNom != [])) > (gIntNcd = (gLstTxt != []))){
						gStrNcd = "\nFailed to read " + gStrNcd; 
					}else{
						gStrNcd = gStrTmp;
					}//-- report load
					llWhisper( 0, "Loaded " + (string)gIntNcd + " of " + (string)gIntCap + " pages" + (gStrNcd = "") +
					              gStrNcd + "\n~" + (string)(llGetFreeMemory() - gIntBfr) + " bytes free"  );
				}
			}else{ //-- notecard read
				if (~llSubStringIndex( gStrNcd, ".tsp" )){ //-- is this a html page?
					 //-- tweak for javascript wrapper compatibility
					vStrDta = llDumpList2String( llParseStringKeepNulls( vStrDta, ["\\"], [] ), "\\\\" ); //-- "
					vStrDta = llDumpList2String( llParseStringKeepNulls( vStrDta, ["'"], [] ), "\\'" ) + "\\n";
				}else{
					vStrDta += "\n";
				}
				gStrTmp += vStrDta; //-- accumulate to variable
				if ((integer)(vStrDta = (string)(gIntCap - llGetFreeMemory())) > gIntBfr){
					gIntBfr = (integer)vStrDta; //-- adjust buffer if needed
				}
				if ((gIntCap >> 1) > gIntBfr){ //-- test free space against buffer
					gKeyQry = llGetNotecardLine( gStrNcd, ++gIntNcd ); //-- get next line
				}else{ //-- our saftey cap was exceeded, stop and report what we have
					gLstNom = llDeleteSubList( gLstNom, gIntNcd, -1 );
					gIntCap = (gLstNom != []);
					gIntNcd = (gLstTxt != []);
					gStrTmp = gKeyQry = "";
					llWhisper( 0, "Loaded " + (string)gIntNcd + " of " + (string)gIntCap + " page(s)\nFailed while reading " +
					              gStrNcd + "\n~" + (string)(llGetFreeMemory() - gIntBfr) + " bytes free"  );
				}
			}
		}
	}
	
	link_message( integer vIntSrc, integer vIntDta, string vStrDta, key vKeyDta ){
		if (vKeyDta){ //-- valid key?
			if (418 == vIntDta){ //-- server request?
				if (~vIntDta = llListFindList( gLstNom, [llList2String( llParseStringKeepNulls( vStrDta, [], ["?", "#"] ), 0 )] )){ //-- do we have that?
					llMessageLinked( vIntSrc, 200, llList2String( gLstTxt, vIntDta ), vKeyDta );
				}
			}
		}//-- fast service, no need to add saucer start compatibility
	}
}
/*//--                           License Text                           --//*/
/*//  Free to copy, use, modify, distribute, or sell, with attribution.   //*/
/*//    (C)2011 (CC-BY) [ http://creativecommons.org/licenses/by/3.0 ]    //*/
/*//   Void Singer [ https://wiki.secondlife.com/wiki/User:Void_Singer ]  //*/
/*//  All usages must contain a plain text copy of the previous 2 lines.  //*/
/*//--                                                                  --//*/

^ Return to top

Sample Index.tsp


  • Save in a notecard named "index.tsp"
<h1>This is a sample Index Page</h1>
<p>would you like to <a href="page1.tsp">go to page 1</a>?<br>
Would you like to <a onClick="u1('Region Stats.tsp');" style="color:blue;text-decoration:underline;cursor:pointer;">view the Region Stats</a>?</p>

^ Return to top

Sample Page1.tsp


  • Save in a notecard named "page1.tsp"
<h1>This is a sample linked page</h1>
<p>Would you like to <a onClick="u1('Region Stats.tsp');" style="color:blue;text-decoration:underline;cursor:pointer;">view the Region Stats</a>?<br>
would you like to <a href="index.tsp">go to the index</a>?</p>
Return to top

Notes

Compatible Servers and Extensions


  • Teacup - SIP front end
  • Saucer - Optional Fast 404 Extension for preventing long timeouts for missing/bad pages
  • Tea Strainer - Optional Troubleshooting Monitor for checking messages being sent and received.

Known Bugs


  • Extra line break inserted at the end of every ".tsp" or text file
  • If the server request a file that has a notecard, before that notecard is finished loading, a 200 "Success" response will be generated and an empty file will be served.
  • Loading large files before small ones may lock them out if the buffer limit is reached. Try to read small files first. (they are read in alphabetic order)

Planned Upgrades


  • None really, the intent is to be an example. other "flavors" can use different methods. but it may get minor updates as time passes

ChangeLog / Old Versions


  • Code will be considered stable when it reaches v1.0
  • Red Tea v0.5
    • Compatibility update to match Teacup v0.5
    • Sample pages updated
  • Red Tea v0.3.1
  • Red Tea v0.3
    • No longer sends 404 messages of it's own
    • No longer restarts on every inventory change, only if the notecard count changes from what it has already read.
  • v0.2
    • Initial Public Release
Return to top

Questions or Comments?

Feel free to leave me a note on the discussion page.