phoenix-firestorm/indra/newview/fslslbridge.cpp

1190 lines
34 KiB
C++

/**
* @fslslbridge.cpp
* @FSLSLBridge implementation
*
* $LicenseInfo:firstyear=2011&license=fsviewerlgpl$
* Phoenix Firestorm Viewer Source Code
* Copyright (C) 2011-2012, The Phoenix Firestorm Project, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation;
* version 2.1 of the License only.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*
* The Phoenix Firestorm Project, Inc., 1831 Oakwood Drive, Fairmont, Minnesota 56031-3225 USA
* http://www.firestormviewer.org
* $/LicenseInfo$
*/
#include "llviewerprecompiledheaders.h"
#include "fslslbridge.h"
#include "fslslbridgerequest.h"
#include "imageids.h"
#include "llxmlnode.h"
#include "llbufferstream.h"
#include "llsdserialize.h"
#include "llviewerinventory.h"
#include "llagent.h"
#include "llvoavatar.h"
#include "llattachmentsmgr.h"
#include "llinventorymodel.h"
#include "llinventoryfunctions.h"
#include "llviewerassettype.h"
#include "llfloaterreg.h"
#include "llinventorybridge.h"
#include "llpreviewscript.h"
#include "llselectmgr.h"
#include "llinventorydefines.h"
#include "llviewerregion.h"
#include "llfoldertype.h"
#include "llhttpclient.h"
#include "llassetuploadresponders.h"
#include "llnotificationmanager.h"
#include "llviewerobject.h"
#include "llappearancemgr.h"
#include "lltrans.h"
#include "aoengine.h"
#include "fscommon.h"
#include <boost/regex.hpp>
#include <string>
#include <fstream>
#include <streambuf>
#define FS_BRIDGE_FOLDER "#LSL Bridge"
#define FS_BRIDGE_CONTAINER_FOLDER "Landscaping"
#define FS_BRIDGE_MAJOR_VERSION 2
#define FS_BRIDGE_MINOR_VERSION 3
#define FS_MAX_MINOR_VERSION 99
//current script version is 2.3
const std::string UPLOAD_SCRIPT_CURRENT = "EBEDD1D2-A320-43f5-88CF-DD47BBCA5DFB.lsltxt";
//
//-TT Client LSL Bridge File
//
class NameCollectFunctor : public LLInventoryCollectFunctor
{
public:
NameCollectFunctor(std::string name)
{
sName = name;
}
virtual ~NameCollectFunctor() {}
virtual bool operator()(LLInventoryCategory* cat,
LLInventoryItem* item)
{
if(item)
{
return (item->getName() == sName);
}
return false;
}
private:
std::string sName;
};
//
//
// Bridge functionality
//
FSLSLBridge :: FSLSLBridge():
mBridgeCreating(false),
mpBridge(NULL),
mIsFirstCallDone(false)
{
llinfos << "Initializing FSLSLBridge" << llendl;
std::stringstream sstr;
sstr << FS_BRIDGE_NAME;
sstr << FS_BRIDGE_MAJOR_VERSION;
sstr << ".";
sstr << FS_BRIDGE_MINOR_VERSION;
mCurrentFullName = sstr.str();
//mBridgeCreating = false;
//mpBridge = NULL;
}
FSLSLBridge :: ~FSLSLBridge()
{
}
bool FSLSLBridge :: lslToViewer(std::string message, LLUUID fromID, LLUUID ownerID)
{
// <FS:PP> Attempt to speed up things a little
// if (!gSavedSettings.getBOOL("UseLSLBridge"))
static LLCachedControl<bool> UseLSLBridge(gSavedSettings, "UseLSLBridge");
if (!UseLSLBridge)
// </FS:PP>
return false;
lldebugs << message << llendl;
//<FS:TS> FIRE-962: Script controls for built-in AO
if ((message[0]) != '<')
return false; // quick exit if no leading <
S32 closebracket = message.find('>');
S32 firstblank = message.find(' ');
S32 tagend;
if (closebracket == std::string::npos)
tagend = firstblank;
else if (firstblank == std::string::npos)
tagend = closebracket;
else tagend = (closebracket < firstblank) ? closebracket : firstblank;
if (tagend == std::string::npos)
return false;
std::string tag = message.substr(0,tagend+1);
std::string ourBridge = gSavedPerAccountSettings.getString("FSLSLBridgeUUID");
//</FS:TS> FIRE-962
bool status = false;
if (tag == "<bridgeURL>")
{
// brutish parsing
S32 urlStart = message.find("<bridgeURL>") + 11;
S32 urlEnd = message.find("</bridgeURL>");
S32 authStart = message.find("<bridgeAuth>") + 12;
S32 authEnd = message.find("</bridgeAuth>");
S32 verStart = message.find("<bridgeVer>") + 11;
S32 verEnd = message.find("</bridgeVer>");
std::string bURL = message.substr(urlStart,urlEnd - urlStart);
std::string bAuth = message.substr(authStart,authEnd - authStart);
std::string bVer = message.substr(verStart,verEnd - verStart);
// Verify Version
// todo
// Verify Authorization
if (ourBridge != bAuth)
{
llwarns << "BridgeURL message received from ("<< bAuth <<") , but not from our registered bridge ("<< ourBridge <<"). Ignoring." << llendl;
// Failing bridge authorization automatically kicks off a bridge rebuild. This correctly handles the
// case where a user logs in from multiple computers which cannot have the bridgeAuth ID locally
// synchronized.
// If something that looks like our current bridge is attached but failed auth, detach and recreate.
LLUUID catID = findFSCategory();
LLViewerInventoryItem* fsBridge = findInvObject(mCurrentFullName, catID, LLAssetType::AT_OBJECT);
if (fsBridge != NULL)
{
if (get_is_item_worn(fsBridge->getUUID()))
{
LLVOAvatarSelf::detachAttachmentIntoInventory(fsBridge->getUUID());
//This may have been an unfinished bridge. If so - stop the process before recreating.
if (mBridgeCreating)
mBridgeCreating = false;
recreateBridge();
return true;
}
}
// If something that didn't look like our current bridge failed auth, don't recreate, it might interfere with a bridge creation in progress
// or normal bridge startup. Bridge creation isn't threadsafe yet.
return true;
}
// Save the inworld UUID of this attached bridge for later checking
mBridgeUUID = fromID;
// Get URL
mCurrentURL = bURL;
llinfos << "New Bridge URL is: " << mCurrentURL << llendl;
if (mpBridge == NULL)
{
LLUUID catID = findFSCategory();
LLViewerInventoryItem* fsBridge = findInvObject(mCurrentFullName, catID, LLAssetType::AT_OBJECT);
mpBridge = fsBridge;
}
status = viewerToLSL("URL Confirmed", new FSLSLBridgeRequestResponder());
//updateBoolSettingValue("UseLSLFlightAssist");
if (!mIsFirstCallDone)
{
//on first call from bridge, confirm that we are here
//then check options use
updateBoolSettingValue("UseLSLFlightAssist");
updateBoolSettingValue("FSPublishRadarTag");
mIsFirstCallDone = true;
}
return true;
}
//<FS:TS> FIRE-962: Script controls for built-in AO
if (fromID != mBridgeUUID)
return false; // ignore if not from the bridge
if (tag == "<clientAO ")
{
status = true;
S32 valuepos = message.find("state=")+6;
if (valuepos != std::string::npos)
{
if (message.substr(valuepos,2) == "on")
{
gSavedPerAccountSettings.setBOOL("UseAO",TRUE);
}
else if (message.substr(valuepos,3) == "off")
{
gSavedPerAccountSettings.setBOOL("UseAO",FALSE);
}
}
}
//</FS:TS> FIRE-962
return status;
}
bool FSLSLBridge :: viewerToLSL(std::string message, FSLSLBridgeRequestResponder *responder)
{
// <FS:PP> Attempt to speed up things a little
// if (!gSavedSettings.getBOOL("UseLSLBridge"))
static LLCachedControl<bool> UseLSLBridge(gSavedSettings, "UseLSLBridge");
if (!UseLSLBridge)
// </FS:PP>
return false;
if (responder == NULL)
responder = new FSLSLBridgeRequestResponder();
LLHTTPClient::post(mCurrentURL, LLSD(message), responder);
return true;
}
bool FSLSLBridge :: updateBoolSettingValue(std::string msgVal)
{
std::string boolVal = "0";
if (gSavedSettings.getBOOL(msgVal))
boolVal = "1";
return viewerToLSL( msgVal+"|"+boolVal, new FSLSLBridgeRequestResponder());
}
bool FSLSLBridge :: updateBoolSettingValue(std::string msgVal, bool contentVal)
{
std::string boolVal = "0";
if (contentVal)
boolVal = "1";
return viewerToLSL( msgVal+"|"+boolVal, new FSLSLBridgeRequestResponder());
}
//
//Bridge initialization
//
void FSLSLBridge :: recreateBridge()
{
// <FS:PP> Attempt to speed up things a little
// if (!gSavedSettings.getBOOL("UseLSLBridge"))
static LLCachedControl<bool> UseLSLBridge(gSavedSettings, "UseLSLBridge");
if (!UseLSLBridge)
// </FS:PP>
return;
// <FS:PP> Attempt to speed up things a little
// if (gSavedSettings.getBOOL("NoInventoryLibrary"))
static LLCachedControl<bool> NoInventoryLibrary(gSavedSettings, "NoInventoryLibrary");
if (NoInventoryLibrary)
// </FS:PP>
{
llwarns << "Asked to create bridge, but we don't have a library. Aborting." << llendl;
reportToNearbyChat(LLTrans::getString("fsbridge_no_library"));
mBridgeCreating = false;
return;
}
if (mBridgeCreating)
{
llwarns << "Bridge creation already in progress, aborting new attempt." << llendl;
reportToNearbyChat(LLTrans::getString("fsbridge_already_creating"));
return;
}
LLUUID catID = findFSCategory();
LLViewerInventoryItem* fsBridge = findInvObject(mCurrentFullName, catID, LLAssetType::AT_OBJECT);
if (fsBridge != NULL)
{
if (get_is_item_worn(fsBridge->getUUID()))
{
LLVOAvatarSelf::detachAttachmentIntoInventory(fsBridge->getUUID());
}
}
// clear the stored bridge ID - we are starting over.
mpBridge = 0; //the object itself will get cleaned up when new one is created.
initCreationStep();
}
void FSLSLBridge :: initBridge()
{
// <FS:PP> Attempt to speed up things a little
// if (!gSavedSettings.getBOOL("UseLSLBridge"))
static LLCachedControl<bool> UseLSLBridge(gSavedSettings, "UseLSLBridge");
if (!UseLSLBridge)
// </FS:PP>
return;
// <FS:PP> Attempt to speed up things a little
// if (gSavedSettings.getBOOL("NoInventoryLibrary"))
static LLCachedControl<bool> NoInventoryLibrary(gSavedSettings, "NoInventoryLibrary");
if (NoInventoryLibrary)
// </FS:PP>
{
llwarns << "Asked to create bridge, but we don't have a library. Aborting." << llendl;
reportToNearbyChat(LLTrans::getString("fsbridge_no_library"));
mBridgeCreating = false;
return;
}
LLUUID catID = findFSCategory();
LLUUID libCatID = findFSBridgeContainerCategory();
//check for inventory load
// AH: Use overloaded LLInventoryFetchDescendentsObserver to check for load of
// bridge and bridge rock category before doing anything!
uuid_vec_t cats;
cats.push_back(catID);
cats.push_back(libCatID);
FSLSLBridgeInventoryObserver *bridgeInventoryObserver = new FSLSLBridgeInventoryObserver(cats);
gInventory.addObserver(bridgeInventoryObserver);
bridgeInventoryObserver->startFetch();
}
// Gets called by the Init, when inventory loaded.
void FSLSLBridge :: startCreation()
{
if( !isAgentAvatarValid() )
{
llwarns << "AgentAvatar is not valid" << llendl;
return;
}
llinfos << "startCreation called. gInventory.isInventoryUsable=" << gInventory.isInventoryUsable() << llendl;
//if bridge object doesn't exist - create and attach it, update script.
LLUUID catID = findFSCategory();
LLViewerInventoryItem* fsBridge = findInvObject(mCurrentFullName, catID, LLAssetType::AT_OBJECT);
//detach everything else
detachOtherBridges();
if (fsBridge == NULL)
{
llinfos << "Bridge not found in inventory, creating new one..." << llendl;
initCreationStep();
}
else
{
//TODO need versioning - see isOldBridgeVersion()
mpBridge = fsBridge;
if (!isItemAttached(mpBridge->getUUID()))
{
if (!LLAppearanceMgr::instance().getIsInCOF(mpBridge->getUUID()))
{
//Is this a valid bridge - wear it.
LLAttachmentsMgr::instance().addAttachment(mpBridge->getUUID(), BRIDGE_POINT, FALSE, TRUE);
llinfos << "Bridge not attached but found in inventory, reattaching..." << llendl;
//from here, the attach shoould report to ProcessAttach and make sure bridge is valid.
}
else
{
llinfos << "Bridge not found but in CoF. Waiting for automatic attach..." << llendl;
}
}
}
}
void FSLSLBridge :: initCreationStep()
{
mBridgeCreating = true;
//announce yourself
reportToNearbyChat(LLTrans::getString("fsbridge_creating"));
createNewBridge();
}
void FSLSLBridge :: createNewBridge()
{
//check if user has a bridge
LLUUID catID = findFSCategory();
//attach the Linden rock from the library (will resize as soon as attached)
LLUUID libID = gInventory.getLibraryRootFolderID();
LLViewerInventoryItem* libRock = findInvObject(LIB_ROCK_NAME, libID, LLAssetType::AT_OBJECT);
//shouldn't happen but just in case
if (libRock != NULL)
{
//copy the library item to inventory and put it on
LLPointer<LLInventoryCallback> cb = new FSLSLBridgeRezCallback();
llinfos << "Cloning a new Bridge container from the Library..." << llendl;
copy_inventory_item(gAgent.getID(),libRock->getPermissions().getOwner(),libRock->getUUID(),catID,mCurrentFullName,cb);
}
else
{
llwarns << "Bridge container not found in the Library!" << llendl;
// AH: Set to false or we won't be able to start another bridge creation
// process in this session!
mBridgeCreating = false;
}
}
void FSLSLBridge :: processAttach(LLViewerObject *object, const LLViewerJointAttachment *attachment)
{
llinfos << "Entering processAttach, checking the bridge container - gInventory.isInventoryUsable=" << gInventory.isInventoryUsable()<< llendl;
if ((!gAgentAvatarp->isSelf()) || (attachment->getName() != "Bridge"))
{
llwarns << "Bridge not created. Our bridge container attachment isn't named correctly." << llendl;
if (mBridgeCreating)
{
reportToNearbyChat(LLTrans::getString("fsbridge_failure_creation_bad_name"));
mBridgeCreating = false; //in case we interrupted the creation
}
return;
}
LLViewerInventoryItem *fsObject = gInventory.getItem(object->getAttachmentItemID());
if (fsObject == NULL) //just in case
{
llwarns << "Bridge container is still NULL in inventory. Aborting." << llendl;
if (mBridgeCreating)
{
reportToNearbyChat(LLTrans::getString("fsbridge_failure_creation_null"));
mBridgeCreating = false; //in case we interrupted the creation
}
return;
}
if (mpBridge == NULL) //user is attaching an existing bridge?
{
//is it the right version?
if (fsObject->getName() != mCurrentFullName)
{
LLVOAvatarSelf::detachAttachmentIntoInventory(fsObject->getUUID());
llwarns << "Attempt to attach to bridge point an object other than current bridge" << llendl;
reportToNearbyChat(LLTrans::getString("fsbridge_failure_attach_wrong_object"));
if (mBridgeCreating)
{
mBridgeCreating = false; //in case we interrupted the creation
}
return;
}
//is it in the right place?
LLUUID catID = findFSCategory();
if (catID != fsObject->getParentUUID())
{
//the object is not where we think it is. Kick it off.
LLVOAvatarSelf::detachAttachmentIntoInventory(fsObject->getUUID());
llwarns << "Bridge container isn't in the correct inventory location. Detaching it and aborting." << llendl;
if (mBridgeCreating)
{
reportToNearbyChat(LLTrans::getString("fs_bridge_failure_attach_wrong_location"));
mBridgeCreating = false; //in case we interrupted the creation
}
return;
}
mpBridge = fsObject;
}
lldebugs << "Bridge container is attached, mpBridge not NULL, avatar is self, point is bridge, all is good." << llendl;
if (fsObject->getUUID() != mpBridge->getUUID())
{
//something odd just got attached to bridge?
llwarns << "Something unknown just got attached to bridge point, detaching and aborting." << llendl;
if (mBridgeCreating)
{
reportToNearbyChat(LLTrans::getString("fsbridge_failure_attach_point_in_use"));
mBridgeCreating = false; //in case we interrupted the creation
}
LLVOAvatarSelf::detachAttachmentIntoInventory(mpBridge->getUUID());
return;
}
lldebugs << "Bridge container found is the same bridge we saved, id matched." << llendl;
if (!mBridgeCreating) //just an attach. See what it is
{
// AH: We need to request objects inventory first before we can
// do anything with it!
llinfos << "Requesting bridge inventory contents..." << llendl;
object->registerInventoryListener(this, NULL);
object->requestInventory();
}
else
{
configureBridgePrim(object);
}
}
void FSLSLBridge::inventoryChanged(LLViewerObject* object,
LLInventoryObject::object_list_t* inventory,
S32 serial_num,
void* user_data)
{
object->removeInventoryListener(this);
llinfos << "Received object inventory for existing bridge prim. Checking contents..." << llendl;
//are we attaching the right thing? Check size and script
LLInventoryObject::object_list_t inventory_objects;
object->getInventoryContents(inventory_objects);
if (object->flagInventoryEmpty())
{
llinfos << "Empty bridge detected- re-enter creation process" << llendl;
mBridgeCreating = true;
}
else if (inventory_objects.size() > 0)
{
S32 count(0);
LLInventoryObject::object_list_t::iterator it = inventory_objects.begin();
LLInventoryObject::object_list_t::iterator end = inventory_objects.end();
bool isOurScript = false;
for ( ; it != end; ++it)
{
LLInventoryItem* item = ((LLInventoryItem*)((LLInventoryObject*)(*it)));
// AH: Somehow always contains a wonky object item with creator
// UUID = NULL UUID and asset type AT_NONE - don't count it
if (item->getType() != LLAssetType::AT_NONE)
{
count++;
}
if (item->getType() == LLAssetType::AT_LSL_TEXT)
{
if (item->getCreatorUUID() == gAgent.getID())
isOurScript = true;
else //problem, not our script
llwarns << "The bridge inventory contains a script not created by user." << llendl;
}
}
if (count == 1 && isOurScript) //We attached a valid bridge. Run along.
return;
else
{
reportToNearbyChat(LLTrans::getString("fsbridge_warning_unexpected_items"));
llwarns << "The bridge inventory contains items other than bridge script." << llendl;
if (!isOurScript) //some junk but no valid script? Unlikely to happen, but lets add script anyway.
mBridgeCreating = true;
else //Let the script disable competitors
{
return;
}
}
}
else
llwarns << "Bridge not empty, but we're unable to retrieve contents." << llendl;
//modify the rock size and texture
if (mBridgeCreating)
{
configureBridgePrim(object);
}
}
void FSLSLBridge::configureBridgePrim(LLViewerObject* object)
{
if( !isBridgeValid() )
{
llwarns << "Bridge not valid" << llendl;
return;
}
//modify the rock size and texture
llinfos << "Bridge container found after second attachment, resizing..." << llendl;
setupBridgePrim(object);
mpBridge->setDescription(mCurrentFullName);
mpBridge->setComplete(TRUE);
mpBridge->updateServer(FALSE);
gInventory.updateItem(mpBridge);
gInventory.notifyObservers();
//add bridge script to object
llinfos << "Creating bridge script..." << llendl;
create_script_inner(object);
}
void FSLSLBridge :: processDetach(LLViewerObject *object, const LLViewerJointAttachment *attachment)
{
llinfos << "Entering processDetach" << llendl;
if (gAgentAvatarp.isNull() || (!gAgentAvatarp->isSelf()) || (attachment == NULL) || (attachment->getName() != "Bridge"))
{
llwarns << "Couldn't detach bridge, object has wrong name or avatar wasn't self." << llendl;
return;
}
LLViewerInventoryItem *fsObject = gInventory.getItem(object->getAttachmentItemID());
if (fsObject == NULL) //just in case
{
llwarns << "Couldn't detach bridge. inventory object was NULL." << llendl;
return;
}
//is it in the right place?
LLUUID catID = findFSCategory();
if (catID != fsObject->getParentUUID())
{
//that was in the wrong place. It's not ours.
llwarns << "Bridge seems to be the wrong inventory category. Aborting detachment." << llendl;
return;
}
if (mpBridge != NULL && mpBridge->getUUID() == fsObject->getUUID())
{
mpBridge = NULL;
reportToNearbyChat(LLTrans::getString("fsbridge_detached"));
mIsFirstCallDone = false;
if (mBridgeCreating)
{
reportToNearbyChat(LLTrans::getString("fsbridge_warning_not_finished"));
mBridgeCreating = false; //in case we interrupted the creation
}
}
llinfos << "processDetach Finished" << llendl;
}
void FSLSLBridge :: setupBridgePrim(LLViewerObject *object)
{
lldebugs << "Entering bridge container setup..." << llendl;
LLProfileParams profParams(LL_PCODE_PROFILE_CIRCLE, F32(0.230), F32(0.250), F32(0.95));
LLPathParams pathParams(LL_PCODE_PATH_CIRCLE, F32(0.2), F32(0.22),
F32(0.0), F32(350.0), //scale
F32(0.0), F32(0.0), //shear
F32(0), F32(0), //twist
F32(0), //offset
F32(0), F32(0.0), //taper
F32(0.05), F32(0.05)); //revolutions, skew
pathParams.setRevolutions(F32(1.0));
object->setVolume(LLVolumeParams(profParams, pathParams), 0);
object->setScale(LLVector3(10.0f, 10.0f, 10.0f), TRUE);
for (int i = 0; i < object->getNumTEs(); i++)
{
LLViewerTexture* image = LLViewerTextureManager::getFetchedTexture( IMG_INVISIBLE );
object->setTEImage(i, image); //transparent texture
}
object->setChanged(LLXform::MOVED | LLXform::SILHOUETTE | LLXform::TEXTURE);
//object->setTETexture(0, LLUUID("29de489d-0491-fb00-7dab-f9e686d31e83")); //another test texture
object->sendShapeUpdate();
object->markForUpdate(TRUE);
//object->setFlags(FLAGS_TEMPORARY_ON_REZ, true);
object->addFlags(FLAGS_TEMPORARY_ON_REZ);
object->updateFlags();
lldebugs << "End bridge container setup." << llendl;
}
void FSLSLBridge :: create_script_inner(LLViewerObject* object)
{
if( !isBridgeValid() )
{
llwarns << "Bridge no valid" << llendl;
return;
}
LLUUID catID = findFSCategory();
LLPointer<LLInventoryCallback> cb = new FSLSLBridgeScriptCallback();
create_inventory_item(gAgent.getID(),
gAgent.getSessionID(),
catID, //LLUUID::null,
LLTransactionID::tnull,
mCurrentFullName,
mCurrentFullName,
LLAssetType::AT_LSL_TEXT,
LLInventoryType::IT_LSL,
NOT_WEARABLE,
mpBridge->getPermissions().getMaskNextOwner(),
cb);
}
//
// Bridge rez callback
//
FSLSLBridgeRezCallback :: FSLSLBridgeRezCallback()
{
}
FSLSLBridgeRezCallback :: ~FSLSLBridgeRezCallback()
{
}
void FSLSLBridgeRezCallback :: fire(const LLUUID& inv_item)
{
// this is the first attach - librock got copied and worn on hand - but the ID is now real.
if ((FSLSLBridge::instance().getBridge() != NULL) || inv_item.isNull() || !FSLSLBridge::instance().getBridgeCreating())
{
llinfos << "Ignoring bridgerezcallback, already have bridge, or new bridge is null, or we're not flagged as creating a bridge." << llendl;
return;
}
if( gAgentAvatarp.isNull() )
{
llwarns << "Agent is 0, bailing out" << llendl;
return;
}
llinfos << "Bridge attach callback fired, looking for object..." << llendl;
LLViewerObject* obj = gAgentAvatarp->getWornAttachment(inv_item);
if (obj != NULL)
{
llinfos << "Bridge object found, resizing..." << llendl;
FSLSLBridge::instance().setupBridgePrim(obj);
}
else
llinfos << "Bridge object not found yet, keep going" << llendl;
//detach from default and put on the right point
LLVOAvatarSelf::detachAttachmentIntoInventory(inv_item);
LLViewerInventoryItem *item = gInventory.getItem(inv_item);
//from this point on, this is our bridge - accept no substitutes!
FSLSLBridge::instance().setBridge(item);
llinfos << "Attaching bridge to the 'bridge' attachmnent point..." << llendl;
LLAttachmentsMgr::instance().addAttachment(inv_item, FSLSLBridge::BRIDGE_POINT, TRUE, TRUE);
}
//
// Bridge script creation callback
//
FSLSLBridgeScriptCallback :: FSLSLBridgeScriptCallback()
{
}
FSLSLBridgeScriptCallback :: ~FSLSLBridgeScriptCallback()
{
}
void FSLSLBridgeScriptCallback::fire(const LLUUID& inv_item)
{
if (inv_item.isNull() || !FSLSLBridge::instance().getBridgeCreating())
{
llwarns << "BridgeScriptCallback fired, but target item was null or bridge isn't marked as under creation. Ignoring." << llendl;
return;
}
LLViewerInventoryItem* item = gInventory.getItem(inv_item);
if (!item)
{
llwarns << "BridgeScriptCallback Can't find target item in inventory. Ignoring." << llendl;
return;
}
if( gAgentAvatarp.isNull() )
{
llwarns << "Agent is 0, bailing out" << llendl;
return;
}
gInventory.updateItem(item);
gInventory.notifyObservers();
LLViewerObject* obj( 0 );
if( FSLSLBridge::instance().isBridgeValid() )
obj = gAgentAvatarp->getWornAttachment(FSLSLBridge::instance().getBridge()->getUUID());
else
llwarns << "Bridge non valid" << llendl;
//caps import
std::string url;
if( gAgent.getRegion() )
url = gAgent.getRegion()->getCapability("UpdateScriptAgent");
std::string isMono = "mono"; //could also be "lsl2"
if (!url.empty() && obj != NULL)
{
const std::string fName = prepUploadFile();
LLLiveLSLEditor::uploadAssetViaCapsStatic(url, fName,
obj->getID(), inv_item, isMono, true);
llinfos << "updating script ID for bridge" << llendl;
FSLSLBridge::instance().mScriptItemID = inv_item;
}
else
{
//can't complete bridge creation - detach and remove object, remove script
//try to clean up and go away. Fail.
if( FSLSLBridge::instance().isBridgeValid() )
LLVOAvatarSelf::detachAttachmentIntoInventory(FSLSLBridge::instance().getBridge()->getUUID());
FSLSLBridge::instance().cleanUpBridge();
//also clean up script remains
gInventory.purgeObject(item->getUUID());
gInventory.notifyObservers();
llwarns << "Can't update bridge script. Purging bridge." << llendl;
return;
}
}
std::string FSLSLBridgeScriptCallback::prepUploadFile()
{
std::string fName = gDirUtilp->getExpandedFilename(LL_PATH_FS_RESOURCES, UPLOAD_SCRIPT_CURRENT);
std::string fNew = gDirUtilp->getExpandedFilename(LL_PATH_CACHE,UPLOAD_SCRIPT_CURRENT);
LLFILE *fpIn = LLFile::fopen( fName, "rt" );
fseek( fpIn, 0, SEEK_END );
long lSize = ftell( fpIn );
rewind( fpIn );
std::vector< char > vctData;
vctData.resize( lSize+1, 0 );
if( lSize != fread( &vctData[0], 1, lSize, fpIn ) )
llwarns << "Size mismatch during read" << llendl;
LLFile::close( fpIn );
std::string bridgeScript( (char const*)&vctData[0] );
std::string bridgekey = "BRIDGEKEY";
std::string newauth = LLUUID::generateNewID().asString();
bridgeScript.replace(bridgeScript.find(bridgekey),bridgekey.length(),newauth);
gSavedPerAccountSettings.setString("FSLSLBridgeUUID",newauth);
LLFILE *fpOut = LLFile::fopen( fNew, "wt" );
if( bridgeScript.size() != fwrite( bridgeScript.c_str(), 1, bridgeScript.size(), fpOut ) )
llwarns << "Size mismatch during write" << llendl;
LLFile::close( fpOut );
return fNew;
}
void FSLSLBridge :: checkBridgeScriptName(std::string fileName)
{
if ((fileName.length() == 0) || !mBridgeCreating)
{
llwarns << "Bridge script length was zero, or bridge was not marked as under creation. Aborting." << llendl;
return;
}
if( !isBridgeValid() )
{
llwarns << "Bridge not valid (anymore)" << llendl;
cleanUpBridge();
return;
}
//need to parse out the last length of a GUID and compare to saved possible names.
std::string fileOnly = fileName.substr(fileName.length()-UPLOAD_SCRIPT_CURRENT.length(), UPLOAD_SCRIPT_CURRENT.length());
if (fileOnly == UPLOAD_SCRIPT_CURRENT)
{
//this is our script upload
LLViewerObject* obj = gAgentAvatarp->getWornAttachment(mpBridge->getUUID());
if (obj == NULL)
{
//something happened to our object. Try to fail gracefully.
cleanUpBridge();
return;
}
//registerVOInventoryListener(obj, NULL);
obj->saveScript(gInventory.getItem(mScriptItemID), TRUE, false);
FSLSLBridgeCleanupTimer *objTimer = new FSLSLBridgeCleanupTimer((F32)1.0);
objTimer->startTimer();
//obj->doInventoryCallback();
//requestVOInventory();
}
}
BOOL FSLSLBridgeCleanupTimer::tick()
{
FSLSLBridge::instance().finishBridge();
stopTimer();
return TRUE;
}
void FSLSLBridge :: cleanUpBridge()
{
//something unexpected went wrong. Try to clean up and not crash.
llwarns << "Bridge object not found. Can't proceed with creation, exiting." << llendl;
reportToNearbyChat(LLTrans::getString("fsbridge_failure_not_found"));
if( isBridgeValid() )
gInventory.purgeObject(mpBridge->getUUID());
gInventory.notifyObservers();
mpBridge = NULL;
mBridgeCreating = false;
}
void FSLSLBridge :: finishBridge()
{
//announce yourself
llinfos << "Bridge created." << llendl;
reportToNearbyChat(LLTrans::getString("fsbridge_created"));
mBridgeCreating = false;
mIsFirstCallDone = false;
//removeVOInventoryListener();
cleanUpOldVersions();
cleanUpBridgeFolder();
}
//
// Helper functions
///
bool FSLSLBridge :: isItemAttached(LLUUID iID)
{
return (isAgentAvatarValid() && gAgentAvatarp->isWearingAttachment(iID));
}
LLUUID FSLSLBridge :: findFSCategory()
{
if (!mBridgeFolderID.isNull())
return mBridgeFolderID;
LLUUID fsCatID;
LLUUID bridgeCatID;
fsCatID = gInventory.findCategoryByName(ROOT_FIRESTORM_FOLDER);
if(!fsCatID.isNull())
{
LLInventoryModel::item_array_t* items;
LLInventoryModel::cat_array_t* cats;
gInventory.getDirectDescendentsOf(fsCatID, cats, items);
if(cats)
{
S32 count = cats->count();
for(S32 i = 0; i < count; ++i)
{
if(cats->get(i)->getName() == FS_BRIDGE_FOLDER)
{
bridgeCatID = cats->get(i)->getUUID();
}
}
}
}
else
{
fsCatID = gInventory.createNewCategory(gInventory.getRootFolderID(), LLFolderType::FT_NONE, ROOT_FIRESTORM_FOLDER);
}
if (bridgeCatID.isNull())
{
bridgeCatID = gInventory.createNewCategory(fsCatID, LLFolderType::FT_NONE, FS_BRIDGE_FOLDER);
}
mBridgeFolderID = bridgeCatID;
return mBridgeFolderID;
}
LLUUID FSLSLBridge::findFSBridgeContainerCategory()
{
llinfos << "Retrieving FSBridge container category (" << FS_BRIDGE_CONTAINER_FOLDER << ")" << llendl;
if (mBridgeContainerFolderID.notNull())
{
llinfos << "Returning FSBridge container category UUID from instance: " << mBridgeContainerFolderID << llendl;
return mBridgeContainerFolderID;
}
LLUUID LibRootID = gInventory.getLibraryRootFolderID();
if (LibRootID.notNull())
{
LLInventoryModel::item_array_t* items;
LLInventoryModel::cat_array_t* cats;
gInventory.getDirectDescendentsOf(LibRootID, cats, items);
if (cats)
{
S32 count = cats->count();
for(S32 i = 0; i < count; ++i)
{
if (cats->get(i)->getName() == "Objects")
{
LLUUID LibObjectsCatID = cats->get(i)->getUUID();
if (LibObjectsCatID.notNull())
{
LLInventoryModel::item_array_t* objects_items;
LLInventoryModel::cat_array_t* objects_cats;
gInventory.getDirectDescendentsOf(LibObjectsCatID, objects_cats, objects_items);
if (objects_cats)
{
S32 objects_count = objects_cats->count();
for (S32 j = 0; j < objects_count; ++j)
{
if (objects_cats->get(j)->getName() == FS_BRIDGE_CONTAINER_FOLDER)
{
mBridgeContainerFolderID = objects_cats->get(j)->getUUID();
llinfos << "FSBridge container category found in library. UUID: " << mBridgeContainerFolderID << llendl;
gInventory.fetchDescendentsOf(mBridgeContainerFolderID);
return mBridgeContainerFolderID;
}
}
}
}
}
}
}
}
llwarns << "FSBridge container category not found in library!" << llendl;
return LLUUID();
}
LLViewerInventoryItem* FSLSLBridge :: findInvObject(std::string obj_name, LLUUID catID, LLAssetType::EType type)
{
LLViewerInventoryCategory::cat_array_t cats;
LLViewerInventoryItem::item_array_t items;
//gInventory.findCategoryByName
LLUUID itemID;
NameCollectFunctor namefunctor(obj_name);
gInventory.collectDescendentsIf(catID,cats,items,FALSE,namefunctor);
for (S32 iIndex = 0; iIndex < items.count(); iIndex++)
{
const LLViewerInventoryItem* itemp = items.get(iIndex);
if (!itemp->getIsLinkType() && (itemp->getType() == LLAssetType::AT_OBJECT))
{
itemID = itemp->getUUID();
break;
}
}
if (itemID.notNull())
{
LLViewerInventoryItem* item = gInventory.getItem(itemID);
return item;
}
return NULL;
}
void FSLSLBridge :: cleanUpBridgeFolder(std::string nameToCleanUp)
{
llinfos << "Cleaning leftover scripts and bridges for folder " << nameToCleanUp << llendl;
if( !isBridgeValid() )
{
llwarns << "Bridge no valid" << llendl;
return;
}
LLUUID catID = findFSCategory();
LLViewerInventoryCategory::cat_array_t cats;
LLViewerInventoryItem::item_array_t items;
//find all bridge and script duplicates and delete them
//NameCollectFunctor namefunctor(mCurrentFullName);
NameCollectFunctor namefunctor(nameToCleanUp);
gInventory.collectDescendentsIf(catID,cats,items,FALSE,namefunctor);
for (S32 iIndex = 0; iIndex < items.count(); iIndex++)
{
const LLViewerInventoryItem* itemp = items.get(iIndex);
if (!itemp->getIsLinkType() && (itemp->getUUID() != mpBridge->getUUID()))
{
gInventory.purgeObject(itemp->getUUID());
}
}
gInventory.notifyObservers();
}
void FSLSLBridge :: cleanUpBridgeFolder()
{
cleanUpBridgeFolder(mCurrentFullName);
}
void FSLSLBridge :: cleanUpOldVersions()
{
std::string mProcessingName;
for(int i = 1; i <= FS_BRIDGE_MAJOR_VERSION; i++)
{
int minor_tip;
if (i < FS_BRIDGE_MAJOR_VERSION)
minor_tip = FS_MAX_MINOR_VERSION;
else
minor_tip = FS_BRIDGE_MINOR_VERSION;
for (int j = 0; j < minor_tip; j++)
{
std::stringstream sstr;
sstr << FS_BRIDGE_NAME;
sstr << i;
sstr << ".";
sstr << j;
mProcessingName = sstr.str();
cleanUpBridgeFolder(mProcessingName);
}
}
}
bool FSLSLBridge :: isOldBridgeVersion(LLInventoryItem *item)
{
//if (!item)
// return false;
////if (!boost::regex_match(item->getName(), FSBridgePattern))
//std::string str = item->getName();
//(item) && boost::regex_match(item->getName(), FSBridgePattern);
//std::string tmpl = FS_BRIDGE_NAME;
//std::string::size_type found = str.find_first_of(".");
//
//while( found != std::string::npos ) {}
////std::string sMajor = str.substr(strlen(tmpl.c_str)-1, dotPos);
////std::string sMinor = str.substr(strlen(tmpl.c_str)+strlen(sMajor));
////int iMajor = atoi(sMajor);
////float fMinor = atof(sMinor);
return false;
}
void FSLSLBridge :: detachOtherBridges()
{
LLUUID catID = findFSCategory();
LLViewerInventoryCategory::cat_array_t cats;
LLViewerInventoryItem::item_array_t items;
LLViewerInventoryItem* fsBridge = findInvObject(mCurrentFullName, catID, LLAssetType::AT_OBJECT);
//detach everything except current valid bridge - if any
gInventory.collectDescendents(catID,cats,items,FALSE);
for (S32 iIndex = 0; iIndex < items.count(); iIndex++)
{
const LLViewerInventoryItem* itemp = items.get(iIndex);
if (get_is_item_worn(itemp->getUUID()) &&
((fsBridge == NULL) || (itemp->getUUID() != fsBridge->getUUID())))
{
LLVOAvatarSelf::detachAttachmentIntoInventory(itemp->getUUID());
}
}
}