Ansariel 2023-05-02 10:45:50 +02:00
commit d32853dbef
15 changed files with 366 additions and 1356 deletions

View File

@ -1,8 +1,14 @@
name: Build viewer
on: push
env:
on:
push:
branches:
- "*release"
- master
schedule:
- cron: '00 03 * * *' # Run every day at 3am UTC
env:
AUTOBUILD_VARIABLES_FILE: ${{github.workspace}}/build-variables/variables
EXTRA_ARGS: -DFMODSTUDIO=ON -DUSE_KDU=ON --crashreporting
EXTRA_ARGS: -DUSE_FMODSTUDIO=ON -DUSE_KDU=ON --crashreporting
build_secrets_checkout: ${{github.workspace}}/signing
@ -67,15 +73,22 @@ jobs:
- name: find channel from Branch name
run: |
if [[ "${{ github.ref_name }}" == *"Release"* ]]; then
FS_RELEASE_CHAN="Release"
if [[ "${{ github.ref_name }}" == *Release* ]]; then
FS_RELEASE_TYPE=Release
else
FS_RELEASE_CHAN="Beta"
if [[ "${{github.event_name}}" == 'schedule' ]]; then
FS_RELEASE_TYPE=Nightly
else
FS_RELEASE_TYPE=Beta
fi
fi
if [[ "${{ matrix.addrsize }}" == "64" ]]; then
FS_RELEASE_CHAN="${FS_RELEASE_CHAN}x64"
FS_RELEASE_CHAN="${FS_RELEASE_TYPE}x64"
else
FS_RELEASE_CHAN=${FS_RELEASE_TYPE}
fi
echo "FS_RELEASE_CHAN=\"${FS_RELEASE_CHAN}\"" >> $GITHUB_ENV
echo "FS_RELEASE_TYPE=${FS_RELEASE_TYPE}" >> $GITHUB_ENV
echo "FS_RELEASE_CHAN=${FS_RELEASE_CHAN}" >> $GITHUB_ENV
echo "Building for channel ${FS_RELEASE_CHAN}"
shell: bash
@ -187,18 +200,18 @@ jobs:
run: rm *${{ env.fallback_platform }}*bz2
shell: bash
- name: Configure
run: autobuild configure --debug -c ReleaseFS -A${{matrix.addrsize}} -- --package --chan ${{env.FS_RELEASE_CHAN}} ${{env.EXTRA_ARGS}} ${{env.FS_GRID}}
run: autobuild configure -c ReleaseFS -A${{matrix.addrsize}} -- --package --chan ${{env.FS_RELEASE_CHAN}} ${{env.EXTRA_ARGS}} ${{env.FS_GRID}}
shell: bash
- name: build
run: autobuild build --debug -c ReleaseFS -A${{matrix.addrsize}} --no-configure
run: autobuild build -c ReleaseFS -A${{matrix.addrsize}} --no-configure
shell: bash
- name: publish Windows artifacts
- name: Publish artifacts
if: runner.os == 'Windows'
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.os }}-${{matrix.addrsize}}-${{matrix.grid}}-artifacts.zip
name: ${{ env.FS_RELEASE_TYPE }}-${{ matrix.os }}-${{ matrix.addrsize }}-${{ matrix.grid }}-artifacts.zip
path: |
build-*/newview/Release/*Setup.exe
build-*/newview/Release/*.xz
@ -207,7 +220,7 @@ jobs:
if: runner.os == 'Linux'
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.os }}-${{matrix.addrsize}}-${{matrix.grid}}-artifacts.zip
name: ${{ env.FS_RELEASE_TYPE }}-${{ matrix.os }}-${{matrix.addrsize}}-${{matrix.grid}}-artifacts.zip
path: |
build-linux-*/newview/*.xz
build-linux-*/newview/*.bz2
@ -216,7 +229,53 @@ jobs:
if: runner.os == 'macOS'
uses: actions/upload-artifact@v3
with:
name: ${{ matrix.os }}-${{matrix.addrsize}}-${{matrix.grid}}-artifacts.zip
name: ${{ env.FS_RELEASE_TYPE }}-${{ matrix.os }}-${{matrix.addrsize}}-${{matrix.grid}}-artifacts.zip
path: |
build-darwin-*/newview/*.dmg
build-darwin-*/newview/*.bz2
deploy:
runs-on: ubuntu-latest
needs: build_matrix
if: always()
steps:
- name: Checkout files
uses: Bhacaz/checkout-files@v2
with:
files: fsutils/download_list.py
branch: ${{ github.head_ref || github.ref_name || 'master' }}
- name: Install discord-webhook library
run: pip install discord-webhook
- name: find channel from Branch name
run: |
if [[ "${{ github.ref_name }}" == *Release* ]]; then
FS_RELEASE_FOLDER=release
else
if [[ "${{github.event_name}}" == 'schedule' ]]; then
FS_RELEASE_FOLDER=nightly
else
FS_RELEASE_FOLDER=preview
fi
fi
echo "FS_RELEASE_FOLDER=${FS_RELEASE_FOLDER}" >> $GITHUB_ENV
- name: Download artifacts
uses: actions/download-artifact@v3
id: download
with:
path: to_deploy
- name: List artifacts download
run: ls -R
working-directory: ${{steps.download.outputs.download-path}}
- name: Reorganise artifacts ready for server upload.
run: python ./fsutils/download_list.py -u ${{steps.download.outputs.download-path}} -w ${{ secrets.RELEASE_WEBHOOK_URL }}
- name: Setup rclone and download the folder
uses: beqjanus/setup-rclone@main
with:
rclone_config: ${{ secrets.RCLONE_CONFIG }}
- name: Copy files to remote host
run: rclone copy ${{steps.download.outputs.download-path}}/${{ env.FS_RELEASE_FOLDER }} fs_deploy:${{ env.FS_RELEASE_FOLDER }}

View File

@ -6,14 +6,9 @@ import time
import zipfile
import glob
import shutil
from discord_webhook import DiscordWebhook
# iterate over the files in a directory and pass them to a command line subshell
def get_files(path):
files = []
for root, dirs, files in os.walk(path):
# print(f"Found : {files}")
return files
return None
# run a command line subshell and return the output
@ -65,6 +60,14 @@ def get_files(path):
# MD5: 9D5D8021F376194B42F6E7D8E537E45E
# -------------------------------------------------------------------------------------------------------
# iterate over the files in a directory and pass them to a command line subshell
def get_files(path):
files = []
for root, dirs, filenames in os.walk(path):
for filename in filenames:
files.append(filename)
print(f"Found : {files} on {path}")
return files
def run_cmd(cmd):
# print(cmd)
@ -73,12 +76,12 @@ def run_cmd(cmd):
#using the md5sum command get the md5 for the file
def get_md5(mdfile):
# print(f"mdfile is {mdfile}")
md5sum = run_cmd(f"md5sum {mdfile}")
#split md5sum on space
md5sum = md5sum.split()[0]
#remove leading '\'
md5sum = md5sum[1:]
print(f"generating md5sum for {mdfile} as {md5sum}")
return md5sum
def unzip_file(zip_file, unzip_dir):
@ -86,6 +89,7 @@ def unzip_file(zip_file, unzip_dir):
zip_ref.extractall(unzip_dir)
def flatten_tree(tree_root):
print(f"Flattening tree {tree_root}")
for root, flatten_dirs, files in os.walk(tree_root, topdown=False):
for file in files:
# Construct the full path to the file
@ -107,6 +111,8 @@ parser = argparse.ArgumentParser(
)
parser.add_argument("-r", "--release", required=False, default=False, action="store_true", help="use the release folder in the target URL")
parser.add_argument("-u", "--unzip", required=False, default=False, action="store_true", help="unzip the github artifact first")
parser.add_argument("-w", "--webhook", help="post details to the webhook")
# add path_to_directory required parameter to parser
parser.add_argument("path_to_directory", help="path to the directory in which we'll look for the files")
@ -114,102 +120,162 @@ args = parser.parse_args()
path_to_directory = args.path_to_directory
release = args.release
# Create a webhook object with the webhook URL
if args.webhook:
webhook = DiscordWebhook(url=args.webhook)
dirs = ["windows", "mac", "linux"]
if args.unzip:
# unzip the github artifact for this OS (`dir`) into the folder `dir`
# get the .zip files in args.path_to_directory using glob
zips = glob.glob(f"{args.path_to_directory}/*.zip")
for file in zips:
# print(f"unzipping {file}")
if "ubuntu" in file.lower():
unzip_file(file, os.path.join(args.path_to_directory, "linux"))
if "windows" in file.lower():
unzip_file(file, os.path.join(args.path_to_directory, "windows"))
if "macos" in file.lower():
unzip_file(file, os.path.join(args.path_to_directory, "mac"))
# build_types is a map from Beta, Release and Nightly to folder names preview release and nightly
build_types = {
"Beta": "preview",
"Release": "release",
"Nightly": "nightly"
}
target_folder = {
"ubuntu":"linux",
"windows":"windows",
"macos":"mac"
}
# unzip the github artifact for this OS (`dir`) into the folder `dir`
# get the .zip files in args.path_to_directory using glob
print(f"Processing artifacts in {args.path_to_directory}")
build_types_created = set()
zips = glob.glob(f"{args.path_to_directory}/*.zip")
for file in zips:
# print(f"unzipping {file}")
#extract first word (delimited by '-' from the file name)
# build_type is a fullpath but we only want the last folder, remove the leading part of the path leaving just the foldername using basename
filename = os.path.basename(file)
build_type = filename.split("-")[0]
platform = filename.split("-")[1].lower()
# print(f"build_type is {build_type}")
if build_type not in build_types:
print(f"Invalid build_type {build_type} using file {file}")
continue
else:
build_folder = build_types[build_type]
build_types_created.add(build_type)
build_type_dir = os.path.join(args.path_to_directory, build_folder)
if platform not in target_folder:
print(f"Invalid platform {platform} using file {file}")
continue
unpack_folder = os.path.join(build_type_dir, target_folder[platform])
print(f"unpacking {filename} to {unpack_folder}")
if os.path.isfile(file):
# this is an actual zip file
unzip_file(file, unpack_folder)
else:
# Create the destination folder if it doesn't exist
# if not os.path.exists(unpack_folder):
# os.makedirs(unpack_folder)
# Copy the contents of the source folder to the destination folder recursively
shutil.copytree(file, unpack_folder, dirs_exist_ok=True)
output = ""
for build_type in build_types_created:
build_type_dir = os.path.join(args.path_to_directory, build_types[build_type])
if not os.path.exists(build_type_dir):
print(f"Unexpected error: {build_type_dir} does not exist, even though it was in the set.")
continue
# loop over the folder in the build_type_dir
for dir in dirs:
flatten_tree(os.path.join(args.path_to_directory, dir))
print(f"Cleaning up {dir}")
# Traverse the directory tree and move all of the files to the root directory
flatten_tree(os.path.join(build_type_dir, dir))
# Now move the symbols files to the symbols folder
symbols_folder = os.path.join(args.path_to_directory, "symbols")
# prep the symbols folder
symbols_folder = os.path.join(build_type_dir, "symbols")
os.mkdir(symbols_folder)
# Traverse the directory tree and move all of the files to the root directory
symbol_archives = glob.glob(f"{args.path_to_directory}/**/*_hvk*", recursive=True)
symbol_archives = glob.glob(f"{build_type_dir}/**/*_hvk*", recursive=True)
for sym_file in symbol_archives:
print(f"Moving {sym_file} to {symbols_folder}")
shutil.move(sym_file, symbols_folder)
symbol_archives = glob.glob(f"{args.path_to_directory}/**/*_oss*", recursive=True)
symbol_archives = glob.glob(f"{build_type_dir}/**/*_oss*", recursive=True)
for sym_file in symbol_archives:
print(f"Moving {sym_file} to {symbols_folder}")
shutil.move(sym_file, symbols_folder)
file_dict = {}
md5_dict = {}
# While we're at it, let's print the md5 listing
file_dict = {}
md5_dict = {}
platforms_printable = {"windows":"MS Windows", "mac":"MacOS", "linux":"Linux"}
grids_printable = {"SL":"Second Life", "OS":"OpenSim"}
for dir in dirs:
dir = dir.lower()
files = get_files(os.path.join(args.path_to_directory, dir))
for file in files:
full_file = os.path.join(args.path_to_directory, dir, file)
md5 = get_md5(full_file)
base_name = os.path.basename(file)
if "-Release-" in base_name or "-Beta-" in base_name:
wordsize = "32"
else:
wordsize = "64"
if "FirestormOS-" in base_name:
grid = "OS"
else:
grid = "SL"
if dir in dirs:
file_dict[f"{grid}{dir}{wordsize}"] = full_file
md5_dict[f"{grid}{dir}{wordsize}"] = md5
download_root_preview = "https://downloads.firestormviewer.org/preview"
download_root_release = "https://downloads.firestormviewer.org/release"
if args.release:
download_root = download_root_release
else:
download_root = download_root_preview
print('''
DOWNLOADS''')
platforms_printable = {"windows":"MS Windows", "mac":"MacOS", "linux":"Linux"}
grids_printable = {"SL":"Second Life", "OS":"OpenSim"}
for dir in dirs:
print(f'''-------------------------------------------------------------------------------------------------------
{platforms_printable[dir]}
''')
dir=dir.lower()
wordsize = "64"
platform = f"{platforms_printable[dir]}"
for grid in ["SL", "OS"]:
grid_printable = f"{grids_printable[grid]}"
download_root = f"https://downloads.firestormviewer.org/{build_types[build_type]}/"
for dir in dirs:
print(f"Getting files for {dir} in {build_type_dir}")
files = get_files(os.path.join(build_type_dir, dir))
try:
print (f"{platform} for {grid_printable} ({wordsize}-bit)")
print ( "{}/{}/{}".format(download_root,dir,os.path.basename(file_dict[f"{grid}{dir}{wordsize}"])) )
print ()
print ( "MD5: {}".format(md5_dict[f"{grid}{dir}{wordsize}"]) )
print ()
if(dir == "windows"):
# Need to do 32 bit as well
wordsize = "32"
print (f"{platform} for {grid_printable} ({wordsize}-bit)")
print ( "{}/{}/{}".format(download_root,dir,os.path.basename(file_dict[f"{grid}{dir}{wordsize}"])) )
print ()
print ( "MD5: {}".format(md5_dict[f"{grid}{dir}{wordsize}"]) )
print ()
wordsize = "64"
except KeyError:
print (f"{platform} for {grid_printable} ({wordsize}-bit) - NOT AVAILABLE")
print ()
for file in files:
full_file = os.path.join(build_type_dir, dir, file)
md5 = get_md5(full_file)
base_name = os.path.basename(file)
if "x64" in base_name:
wordsize = "64"
else:
wordsize = "32"
if "FirestormOS-" in base_name:
grid = "OS"
else:
grid = "SL"
print('''
-------------------------------------------------------------------------------------------------------''')
if dir in dirs:
file_dict[f"{grid}{dir}{wordsize}"] = full_file
md5_dict[f"{grid}{dir}{wordsize}"] = md5
except TypeError:
print(f"No files found for {dir} in {build_type_dir}")
output += f'''
DOWNLOADS - {build_type}
'''
output += f'''-------------------------------------------------------------------------------------------------------
{platforms_printable[dir]}
'''
dir = dir.lower()
wordsize = "64"
platform = f"{platforms_printable[dir]}"
for grid in ["SL", "OS"]:
grid_printable = f"{grids_printable[grid]}"
try:
output += f"{platform} for {grid_printable} ({wordsize}-bit)\n"
output += f"{download_root}/{dir}/{os.path.basename(file_dict[f'{grid}{dir}{wordsize}'])}\n"
output += "\n"
output += f"MD5: {md5_dict[f'{grid}{dir}{wordsize}']}\n"
output += "\n"
if dir == "windows":
# Need to do 32 bit as well
wordsize = "32"
output += f"{platform} for {grid_printable} ({wordsize}-bit)\n"
output += f"{download_root}/{dir}/{os.path.basename(file_dict[f'{grid}{dir}{wordsize}'])}\n"
output += "\n"
output += f"MD5: {md5_dict[f'{grid}{dir}{wordsize}']}\n"
output += "\n"
wordsize = "64"
except KeyError:
output += f"{platform} for {grid_printable} ({wordsize}-bit) - NOT AVAILABLE\n"
output += "\n"
output += '''
-------------------------------------------------------------------------------------------------------
'''
if args.webhook:
# Add the message to the webhook
webhook.set_content(content=output)
# Send the webhook
response = webhook.execute()
# Print the response
print(f"Webhook response: {response}")
print(output)

View File

@ -290,6 +290,20 @@ F32 LLSettingsWater::getModifiedWaterFogDensity(bool underwater) const
if (underwater && underwater_fog_mod > 0.0f)
{
underwater_fog_mod = llclamp(underwater_fog_mod, 0.0f, 10.0f);
// <FS:Beq> BUG-233797/BUG-233798 -ve underwater fog density can cause (unrecoverable) blackout.
// raising a negative number to a non-integral power results in a non-real result (which is NaN for our purposes)
// Two methods were tested, number 2 is being used:
// 1) Force the fog_mod to be integral. The effect is unlikely to be nice, but it is better than blackness.
// In this method a few of the combinations are "usable" but the water colour is effectively inverted (blue becomes yellow)
// this seems to be unlikely to be a desirable use case for the majority.
// 2) Force density to be an arbitrary non-negative (i.e. 1) when underwater and modifier is not an integer (1 was aribtrarily chosen as it gives at least some notion of fog in the transition)
// This is more restrictive, effectively forcing a density under certain conditions, but allowing the range of #1 and avoiding blackness in other cases
// at the cost of overriding the fog density.
if(fog_density < 0.0f && underwater_fog_mod != (F32)llround(underwater_fog_mod) )
{
fog_density = 1.0f;
}
// </FS:Beq>
fog_density = pow(fog_density, underwater_fog_mod);
}
return fog_density;

View File

@ -103,18 +103,18 @@ NACLAntiSpamQueueEntry* NACLAntiSpamQueue::getEntry(const LLUUID& source)
}
else
{
return NULL;
return nullptr;
}
}
void NACLAntiSpamQueue::clearEntries()
{
for (spam_queue_entry_map_t::iterator it = mEntries.begin(); it != mEntries.end(); ++it)
for (auto& [id, entry] : mEntries)
{
//AO: Only clear entries that are not blocked.
if (!it->second->getBlocked())
if (!entry->getBlocked())
{
it->second->clearEntry();
entry->clearEntry();
}
}
}
@ -139,15 +139,14 @@ void NACLAntiSpamQueue::blockEntry(const LLUUID& source)
mEntries[source]->setBlocked();
}
S32 NACLAntiSpamQueue::checkEntry(const LLUUID& name, U32 multiplier)
// Returns 0 if unblocked, 1 if check results in a new block, 2 if by an existing block
EAntispamCheckResult NACLAntiSpamQueue::checkEntry(const LLUUID& name, U32 multiplier)
{
spam_queue_entry_map_t::iterator it = mEntries.find(name);
if (it != mEntries.end())
{
if (it->second->getBlocked())
{
return 2;
return EAntispamCheckResult::ExistingBlock;
}
U32 eTime = it->second->getEntryTime();
U32 currentTime = time(0);
@ -158,11 +157,11 @@ S32 NACLAntiSpamQueue::checkEntry(const LLUUID& name, U32 multiplier)
if (eAmount > (mQueueAmount * multiplier))
{
it->second->setBlocked();
return 1;
return EAntispamCheckResult::NewBlock;
}
else
{
return 0;
return EAntispamCheckResult::Unblocked;
}
}
else
@ -170,7 +169,7 @@ S32 NACLAntiSpamQueue::checkEntry(const LLUUID& name, U32 multiplier)
it->second->clearEntry();
it->second->updateEntryAmount();
it->second->updateEntryTime();
return 0;
return EAntispamCheckResult::Unblocked;
}
}
else
@ -180,7 +179,7 @@ S32 NACLAntiSpamQueue::checkEntry(const LLUUID& name, U32 multiplier)
entry->updateEntryAmount();
entry->updateEntryTime();
mEntries[name] = entry;
return 0;
return EAntispamCheckResult::Unblocked;
}
}
@ -219,7 +218,7 @@ NACLAntiSpamRegistry::~NACLAntiSpamRegistry()
const char* NACLAntiSpamRegistry::getQueueName(EAntispamQueue queue)
{
if (queue >= ANTISPAM_QUEUE_MAX)
if (queue >= ANTISPAM_QUEUE_MAX || queue < ANTISPAM_QUEUE_CHAT)
{
return "Unknown";
}
@ -228,7 +227,7 @@ const char* NACLAntiSpamRegistry::getQueueName(EAntispamQueue queue)
void NACLAntiSpamRegistry::setRegisteredQueueTime(EAntispamQueue queue, U32 time)
{
if (queue >= ANTISPAM_QUEUE_MAX || mQueues[queue] == NULL)
if (queue >= ANTISPAM_QUEUE_MAX || queue < ANTISPAM_QUEUE_CHAT || mQueues[queue] == nullptr)
{
LL_ERRS("AntiSpam") << "CODE BUG: Attempting to use a antispam queue that was not created or was outside of the reasonable range of queues. Queue: " << getQueueName(queue) << LL_ENDL;
return;
@ -239,7 +238,7 @@ void NACLAntiSpamRegistry::setRegisteredQueueTime(EAntispamQueue queue, U32 time
void NACLAntiSpamRegistry::setRegisteredQueueAmount(EAntispamQueue queue, U32 amount)
{
if (queue >= ANTISPAM_QUEUE_MAX || mQueues[queue] == NULL)
if (queue >= ANTISPAM_QUEUE_MAX || queue < ANTISPAM_QUEUE_CHAT || mQueues[queue] == nullptr)
{
LL_ERRS("AntiSpam") << "CODE BUG: Attempting to use a antispam queue that was not created or was outside of the reasonable range of queues. Queue: " << getQueueName(queue) << LL_ENDL;
return;
@ -283,7 +282,7 @@ void NACLAntiSpamRegistry::setAllQueueAmounts(U32 amount)
void NACLAntiSpamRegistry::clearRegisteredQueue(EAntispamQueue queue)
{
if (queue >= ANTISPAM_QUEUE_MAX || mQueues[queue] == NULL)
if (queue >= ANTISPAM_QUEUE_MAX || queue < ANTISPAM_QUEUE_CHAT || mQueues[queue] == nullptr)
{
LL_ERRS("AntiSpam") << "CODE BUG: Attempting to use a antispam queue that was not created or was outside of the reasonable range of queues. Queue: " << getQueueName(queue) << LL_ENDL;
return;
@ -294,7 +293,7 @@ void NACLAntiSpamRegistry::clearRegisteredQueue(EAntispamQueue queue)
void NACLAntiSpamRegistry::purgeRegisteredQueue(EAntispamQueue queue)
{
if (queue >= ANTISPAM_QUEUE_MAX || mQueues[queue] == NULL)
if (queue >= ANTISPAM_QUEUE_MAX || queue < ANTISPAM_QUEUE_CHAT || mQueues[queue] == nullptr)
{
LL_ERRS("AntiSpam") << "CODE BUG: Attempting to use a antispam queue that was not created or was outside of the reasonable range of queues. Queue: " << getQueueName(queue) << LL_ENDL;
return;
@ -317,7 +316,7 @@ void NACLAntiSpamRegistry::blockOnQueue(EAntispamQueue queue, const LLUUID& sour
}
else
{
if (queue >= ANTISPAM_QUEUE_MAX || mQueues[queue] == NULL)
if (queue >= ANTISPAM_QUEUE_MAX || queue < ANTISPAM_QUEUE_CHAT || mQueues[queue] == nullptr)
{
LL_ERRS("AntiSpam") << "CODE BUG: Attempting to use a antispam queue that was not created or was outside of the reasonable range of queues. Queue: " << getQueueName(queue) << LL_ENDL;
return;
@ -362,14 +361,14 @@ bool NACLAntiSpamRegistry::checkQueue(EAntispamQueue queue, const LLUUID& source
}
}
S32 result = 0;
EAntispamCheckResult result{ EAntispamCheckResult::Unblocked };
if (mGlobalQueue)
{
result = checkGlobalEntry(source, multiplier);
}
else
{
if (queue >= ANTISPAM_QUEUE_MAX || mQueues[queue] == NULL)
if (queue >= ANTISPAM_QUEUE_MAX || queue < ANTISPAM_QUEUE_CHAT || mQueues[queue] == nullptr)
{
LL_ERRS("AntiSpam") << "CODE BUG: Attempting to use a antispam queue that was not created or was outside of the reasonable range of queues. Queue: " << getQueueName(queue) << LL_ENDL;
return false;
@ -377,17 +376,17 @@ bool NACLAntiSpamRegistry::checkQueue(EAntispamQueue queue, const LLUUID& source
result = mQueues[queue]->checkEntry(source, multiplier);
}
if (result == 0) // safe
if (result == EAntispamCheckResult::Unblocked) // safe
{
return false;
}
if (result == 2) // previously blocked
if (result == EAntispamCheckResult::ExistingBlock) // previously blocked
{
return true;
}
if (result == 1) // newly blocked, result == 1
if (result == EAntispamCheckResult::NewBlock) // newly blocked, result == 1
{
if (!LLMuteList::getInstance()->isMuted(source))
{
@ -402,16 +401,14 @@ bool NACLAntiSpamRegistry::checkQueue(EAntispamQueue queue, const LLUUID& source
{
bool sent = false;
for (LLWorld::region_list_t::const_iterator iter = LLWorld::getInstance()->getRegionList().begin();
iter != LLWorld::getInstance()->getRegionList().end(); ++iter)
for (auto region : LLWorld::getInstance()->getRegionList())
{
LLViewerRegion* region = *iter;
if (gMessageSystem && region && region->isAlive())
{
gMessageSystem->newMessage(_PREHASH_RequestObjectPropertiesFamily);
gMessageSystem->nextBlockFast(_PREHASH_AgentData);
gMessageSystem->addUUIDFast(_PREHASH_AgentID, gAgentID);
gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgent.getSessionID());
gMessageSystem->addUUIDFast(_PREHASH_SessionID, gAgentSessionID);
gMessageSystem->nextBlockFast(_PREHASH_ObjectData);
gMessageSystem->addU32Fast(_PREHASH_RequestFlags, 0);
gMessageSystem->addUUIDFast(_PREHASH_ObjectID, source);
@ -499,7 +496,7 @@ bool NACLAntiSpamRegistry::isBlockedOnQueue(EAntispamQueue queue, const LLUUID&
}
else
{
if (queue >= ANTISPAM_QUEUE_MAX || mQueues[queue] == NULL)
if (queue >= ANTISPAM_QUEUE_MAX || queue < ANTISPAM_QUEUE_CHAT || mQueues[queue] == nullptr)
{
LL_ERRS("AntiSpam") << "CODE BUG: Attempting to use a antispam queue that was not created or was outside of the reasonable range of queues. Queue: " << getQueueName(queue) << LL_ENDL;
return false;
@ -547,12 +544,11 @@ void NACLAntiSpamRegistry::clearAllQueues()
void NACLAntiSpamRegistry::purgeAllQueues()
{
std::map<LLUUID, LLAvatarNameCache::callback_connection_t>::iterator it = mAvatarNameCallbackConnections.begin();
for (; it != mAvatarNameCallbackConnections.end(); ++it)
for (auto& [avid, callback] : mAvatarNameCallbackConnections)
{
if (it->second.connected())
if (callback.connected())
{
it->second.disconnect();
callback.disconnect();
}
}
mAvatarNameCallbackConnections.clear();
@ -574,14 +570,14 @@ void NACLAntiSpamRegistry::purgeAllQueues()
mObjectData.clear();
}
S32 NACLAntiSpamRegistry::checkGlobalEntry(const LLUUID& source, U32 multiplier)
EAntispamCheckResult NACLAntiSpamRegistry::checkGlobalEntry(const LLUUID& source, U32 multiplier)
{
spam_queue_entry_map_t::iterator it = mGlobalEntries.find(source);
if (it != mGlobalEntries.end())
{
if (it->second->getBlocked())
{
return 2;
return EAntispamCheckResult::ExistingBlock;
}
U32 eTime = it->second->getEntryTime();
@ -592,11 +588,11 @@ S32 NACLAntiSpamRegistry::checkGlobalEntry(const LLUUID& source, U32 multiplier)
U32 eAmount = it->second->getEntryAmount();
if (eAmount > (mGlobalAmount * multiplier))
{
return 1;
return EAntispamCheckResult::NewBlock;
}
else
{
return 0;
return EAntispamCheckResult::Unblocked;
}
}
else
@ -604,7 +600,7 @@ S32 NACLAntiSpamRegistry::checkGlobalEntry(const LLUUID& source, U32 multiplier)
it->second->clearEntry();
it->second->updateEntryAmount();
it->second->updateEntryTime();
return 0;
return EAntispamCheckResult::Unblocked;
}
}
else
@ -613,15 +609,15 @@ S32 NACLAntiSpamRegistry::checkGlobalEntry(const LLUUID& source, U32 multiplier)
entry->updateEntryAmount();
entry->updateEntryTime();
mGlobalEntries[source] = entry;
return 0;
return EAntispamCheckResult::Unblocked;
}
}
void NACLAntiSpamRegistry::clearGlobalEntries()
{
for (spam_queue_entry_map_t::iterator it = mGlobalEntries.begin(); it != mGlobalEntries.end(); ++it)
for (auto& [id, entry] : mGlobalEntries)
{
it->second->clearEntry();
entry->clearEntry();
}
}

View File

@ -1,8 +1,8 @@
#ifndef NACL_ANTISPAM_H
#define NACL_ANTISPAM_H
#include <boost/unordered_map.hpp>
#include <boost/unordered_set.hpp>
#include <unordered_map>
#include <unordered_set>
#include "llsingleton.h"
#include "llavatarnamecache.h"
@ -25,6 +25,14 @@ typedef enum e_antispam_source_type
ANTISPAM_SOURCE_OBJECT
} EAntispamSource;
enum class EAntispamCheckResult
{
Unblocked,
NewBlock,
ExistingBlock
};
struct AntispamObjectData
{
std::string mName;
@ -58,8 +66,8 @@ private:
bool mBlocked;
};
typedef boost::unordered_map<LLUUID, NACLAntiSpamQueueEntry*, FSUUIDHash> spam_queue_entry_map_t;
typedef boost::unordered_set<LLUUID, FSUUIDHash> collision_sound_set_t;
typedef std::unordered_map<LLUUID, NACLAntiSpamQueueEntry*, FSUUIDHash> spam_queue_entry_map_t;
typedef std::unordered_set<LLUUID, FSUUIDHash> collision_sound_set_t;
class NACLAntiSpamQueue
{
@ -77,7 +85,7 @@ protected:
void setTime(U32 time);
void blockEntry(const LLUUID& source);
S32 checkEntry(const LLUUID& source, U32 multiplier);
EAntispamCheckResult checkEntry(const LLUUID& source, U32 multiplier);
NACLAntiSpamQueueEntry* getEntry(const LLUUID& source);
void clearEntries();
@ -119,7 +127,7 @@ private:
const char* getQueueName(EAntispamQueue queue);
void blockGlobalEntry(const LLUUID& source);
S32 checkGlobalEntry(const LLUUID& source, U32 multiplier);
EAntispamCheckResult checkGlobalEntry(const LLUUID& source, U32 multiplier);
void clearGlobalEntries();
void purgeGlobalEntries();

View File

@ -164,6 +164,49 @@ BOOL LLFloaterImagePreview::postBuild()
}
getChild<LLCheckBoxCtrl>("temp_check")->setVisible(enable_temp_uploads);
// </FS:CR>
// <FS:Zi> detect and strip empty alpha layers from images on upload
getChild<LLUICtrl>("ok_btn")->setCommitCallback(boost::bind(&LLFloaterImagePreview::onBtnUpload, this));
getChild<LLUICtrl>("uploaded_size_text")->setTextArg("[X_RES]", llformat("%d", mRawImagep->getWidth()));
getChild<LLUICtrl>("uploaded_size_text")->setTextArg("[Y_RES]", llformat("%d", mRawImagep->getHeight()));
mEmptyAlphaCheck = getChild<LLCheckBoxCtrl>("strip_alpha_check");
if (mRawImagep->getComponents() != 4)
{
getChild<LLUICtrl>("image_alpha_warning")->setVisible(false);
getChild<LLUICtrl>("uploaded_size_text")->setTextArg("[ALPHA]", getString("no_alpha"));
return true;
}
U32 imageBytes = mRawImagep->getWidth() * mRawImagep->getHeight() * 4;
U32 emptyAlphaCount = 0;
U8* data = mRawImagep->getData();
for (U32 i = 3; i < imageBytes; i += 4)
{
if (data[i] > ALPHA_EMPTY_THRESHOLD)
{
emptyAlphaCount++;
}
}
if (emptyAlphaCount > (imageBytes / 4 * ALPHA_EMPTY_THRESHOLD_RATIO))
{
getChild<LLUICtrl>("image_alpha_warning")->setVisible(true);
mEmptyAlphaCheck->setCommitCallback(boost::bind(&LLFloaterImagePreview::emptyAlphaCheckboxCallback, this));
mEmptyAlphaCheck->setValue(true);
}
else
{
getChild<LLUICtrl>("image_alpha_warning")->setVisible(false);
mEmptyAlphaCheck->setValue(false);
}
getChild<LLUICtrl>("uploaded_size_text")->setTextArg("[ALPHA]", getString(mEmptyAlphaCheck->getValue() ? "no_alpha" : "with_alpha"));
// </FS:Zi>
}
else
{
@ -181,47 +224,6 @@ BOOL LLFloaterImagePreview::postBuild()
// <FS:Zi> detect and strip empty alpha layers from images on upload
// getChild<LLUICtrl>("ok_btn")->setCommitCallback(boost::bind(&LLFloaterNameDesc::onBtnOK, this));
getChild<LLUICtrl>("ok_btn")->setCommitCallback(boost::bind(&LLFloaterImagePreview::onBtnUpload, this));
getChild<LLUICtrl>("uploaded_size_text")->setTextArg("[X_RES]", llformat("%d", mRawImagep->getWidth()));
getChild<LLUICtrl>("uploaded_size_text")->setTextArg("[Y_RES]", llformat("%d", mRawImagep->getHeight()));
mEmptyAlphaCheck = getChild<LLCheckBoxCtrl>("strip_alpha_check");
if (mRawImagep->getComponents() != 4)
{
getChild<LLUICtrl>("image_alpha_warning")->setVisible(false);
getChild<LLUICtrl>("uploaded_size_text")->setTextArg("[ALPHA]", getString("no_alpha"));
return true;
}
U32 imageBytes = mRawImagep->getWidth() * mRawImagep->getHeight() * 4;
U32 emptyAlphaCount = 0;
U8* data = mRawImagep->getData();
for (U32 i = 3; i < imageBytes; i += 4)
{
if (data[i] > ALPHA_EMPTY_THRESHOLD)
{
emptyAlphaCount++;
}
}
if (emptyAlphaCount > (imageBytes / 4 * ALPHA_EMPTY_THRESHOLD_RATIO))
{
getChild<LLUICtrl>("image_alpha_warning")->setVisible(true);
mEmptyAlphaCheck->setCommitCallback(boost::bind(&LLFloaterImagePreview::emptyAlphaCheckboxCallback, this));
mEmptyAlphaCheck->setValue(true);
}
else
{
getChild<LLUICtrl>("image_alpha_warning")->setVisible(false);
mEmptyAlphaCheck->setValue(false);
}
getChild<LLUICtrl>("uploaded_size_text")->setTextArg("[ALPHA]", getString(mEmptyAlphaCheck->getValue() ? "no_alpha" : "with_alpha"));
// </FS:Zi>
return TRUE;
}

View File

@ -86,15 +86,18 @@ void LLPanelProfileTab::setAvatarId(const LLUUID& avatar_id)
mSelfProfile = (getAvatarId() == gAgentID);
// <FS:Zi> FIRE-32179: Make drag-n-drop sharing of items possible again
LLProfileDropTarget* target = getChild<LLProfileDropTarget>("drop_target");
if (avatar_id == gAgentID)
LLProfileDropTarget* target = findChild<LLProfileDropTarget>("drop_target");
if (target)
{
// hide drop target on own profile
target->setVisible(false);
}
else
{
target->setAgentID(avatar_id);
if (avatar_id == gAgentID)
{
// hide drop target on own profile
target->setVisible(false);
}
else
{
target->setAgentID(avatar_id);
}
}
// </FS:Zi>
}

View File

@ -1201,7 +1201,9 @@ void LLPanelProfileSecondLife::resetData()
resetLoading();
// Set default image and 1:1 dimensions for it
mSecondLifePic->setValue("Generic_Person_Large");
// <FS:Ansariel> Retain texture picker for profile images
//mSecondLifePic->setValue("Generic_Person_Large");
mSecondLifePic->setImageAssetID(LLUUID::null);
mImageId = LLUUID::null;
// <FS:Ansariel> Fix LL UI/UX design accident
@ -3028,7 +3030,9 @@ void LLPanelProfileFirstLife::apply(LLAvatarData* data)
void LLPanelProfileFirstLife::resetData()
{
setDescriptionText(std::string());
mPicture->setValue("Generic_Person_Large");
// <FS:Ansariel> Retain texture picker for profile images
//mPicture->setValue("Generic_Person_Large");
mPicture->setImageAssetID(LLUUID::null);
mImageId = LLUUID::null;
// <FS:Beq> remove the buttons and just have click image to update profile

View File

@ -26,7 +26,7 @@
/>
<texture_picker
name="real_world_pic"
image_name="Generic_Person_Large"
fallback_image="Generic_Person_Large"
follows="top|left"
layout="topleft"
show_caption="false"

View File

@ -353,6 +353,7 @@
user_resize="false">
<texture_picker
name="2nd_life_pic"
fallback_image="Generic_Person_Large"
top="0"
left="0"
width="158"

View File

@ -18,6 +18,13 @@
<check_box label="Включено" name="enable_media"/>
<slider label="Голосовой чат" name="Voice Volume"/>
<check_box label="Включено" name="enable_voice_check_volume"/>
<text name="Listen media from" width="200">
Слышать мультимедиа и звуки из:
</text>
<radio_group name="media_ear_location">
<radio_item label="Позиции камеры" name="0"/>
<radio_item label="Позиции аватара" name="1"/>
</radio_group>
<text name="auto_unmute_label" width="300">
Автоматически включать звук после телепортации:
</text>
@ -51,7 +58,7 @@
</panel>
<panel name="Media Media Panel" label="Медиа">
<text name="media_autoplay_label" width="130">
Автовоспроизведение
Автовоспроизведение
</text>
<combo_box name="media_auto_play_combo" width="100">
<item label="Запрещено" name="autoplay_disabled"/>

View File

@ -1,574 +0,0 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<panel
top="0"
left="0"
width="420"
height="480"
layout="topleft"
follows="all"
name="panel_profile"
label="Profile">
<string
name="status_online">
Online
</string>
<string
name="status_offline">
Offline
</string>
<string
name="CaptionTextAcctInfo">
[ACCTTYPE]
[PAYMENTINFO]
[FIRESTORM][FSDEV][FSSUPP][FSQA][FSGW]
</string>
<!-- [AGEVERIFICATION] moved out of the above, since it's not set anyway. See
LLPanelAvatarProfile::fillAccountStatus() ...TS -->
<string
name="payment_update_link_url">
http://www.secondlife.com/account/billing.php?lang=en
</string>
<string
name="partner_edit_link_url">
http://www.secondlife.com/account/partners.php?lang=en
</string>
<string
name="my_account_link_url"
value="http://secondlife.com/account" />
<string
name="no_partner_text"
value="None" />
<string
name="no_group_text"
value="None" />
<string
name="RegisterDateFormat">
[REG_DATE]
([AGE])
</string>
<string
name="name_text_args">
[NAME]
</string>
<string
name="display_name_text_args">
[DISPLAY_NAME]
</string>
<string
name="FSDev"
value=" Developer" />
<string
name="FSSupp"
value=" Support" />
<string
name="FSQualityAssurance"
value=" Bug Hunter" />
<string
name="FSGW"
value=" Gateway" />
<!--
KC: Use view_border's around text_editor's due to text render issues with border_visible
-->
<loading_indicator
top="4"
right="-10"
height="23"
width="23"
layout="topleft"
follows="top|right"
name="progress_indicator"
visible="false" />
<text
top="8"
left="6"
height="16"
width="40"
layout="topleft"
follows="left|top"
name="Name:"
halign="right"
value="Name:" />
<view_border
left_pad="4"
right="-86"
height="16"
layout="topleft"
follows="left|top|right"
name="info_border"
bevel_style="none" />
<text_editor
bottom_delta="2"
left_delta="3"
right="-86"
height="16"
layout="topleft"
follows="left|top|right"
name="complete_name"
bg_visible="false"
border_visible="false"
allow_scroll="false"
h_pad="0"
v_pad="0"
enabled="false"
max_length="254"
value="(loading...)" />
<button
left_pad="2"
height="20"
width="20"
layout="topleft"
follows="top|right"
name="set_name"
image_overlay="Edit_Wrench"
tool_tip="Set Display Name"
visible="false"
enabled="false" />
<text
top_pad="2"
left="6"
height="16"
width="40"
layout="topleft"
follows="left|top"
name="Key:"
halign="right"
value="Key:" />
<view_border
left_pad="4"
right="-86"
height="16"
layout="topleft"
follows="left|top|right"
name="info_border2"
bevel_style="none" />
<text_editor
bottom_delta="2"
left_delta="3"
right="-86"
height="16"
layout="topleft"
follows="left|top|right"
name="user_key"
bg_visible="false"
border_visible="false"
allow_scroll="false"
h_pad="0"
v_pad="0"
enabled="false"
max_length="254"
value="(loading...)" />
<text
left_pad="2"
right="-6"
height="16"
layout="topleft"
follows="top|right"
name="status"
halign="center"
value="" />
<button
top_delta="-2"
right="-6"
height="20"
layout="topleft"
follows="top|right"
name="copy_own_uri_button"
label="Copy URI" />
<texture_picker
top_pad="3"
left="6"
height="180"
width="220"
layout="topleft"
follows="top|left"
allow_no_texture="true"
name="2nd_life_pic"
default_image_name="None"
enabled="false"
fallback_image="Generic_Person_Large" />
<text
top_pad="-179"
left_pad="4"
right="-6"
height="16"
layout="topleft"
follows="left|top|right"
name="label"
value="Born:" />
<view_border
top_pad="0"
left_delta="0"
right="-6"
height="32"
name="info_border3"
layout="topleft"
follows="left|top|right"
bevel_style="none" />
<text_editor
top_delta="2"
left_delta="2"
right="-6"
height="28"
layout="topleft"
follows="left|top|right"
name="register_date"
max_length="254"
bg_visible="false"
border_visible="false"
allow_scroll="false"
h_pad="2"
v_pad="0"
read_only="true"
translate="false"
word_wrap="true"/>
<text
top_pad="7"
left_delta="-2"
right="-6"
height="16"
layout="topleft"
follows="left|top|right"
name="label2"
value="Account:" />
<view_border
top_pad="0"
left_delta="0"
right="-6"
height="48"
layout="topleft"
follows="left|top|right"
name="acct_border"
bevel_style="none" />
<text_editor
top_delta="2"
left_delta="2"
right="-6"
height="44"
layout="topleft"
follows="left|top|right"
name="acc_status_text"
bg_visible="false"
border_visible="false"
allow_scroll="false"
h_pad="2"
v_pad="0"
read_only="true"
translate="false"
word_wrap="true"/>
<text
top_pad="7"
left_delta="-2"
right="-6"
height="16"
layout="topleft"
follows="left|top|right"
name="partner_label"
value="Partner:" />
<view_border
top_pad="0"
left_delta="0"
right="-6"
height="18"
layout="topleft"
follows="left|top|right"
name="info_border4"
bevel_style="none" />
<text
icon_positioning="left"
top_delta="2"
left_delta="2"
right="-6"
height="16"
layout="topleft"
follows="left|top|right"
name="partner_text"
max_length="254" />
<!--
<button
top_pad="8"
left="6"
height="18"
width="40"
layout="topleft"
follows="left|top"
name="bigimg"
halign="center"
label="&lt; &lt;"
label_selected="&gt; &gt;"
tool_tip="Open Full Size." />
-->
<text
top_pad="8"
left="6"
height="16"
width="55"
layout="topleft"
follows="left|top"
name="Groups:"
halign="right"
value="Groups:" />
<button
top_delta="20"
left="40"
height="20"
width="20"
layout="topleft"
follows="left|top"
name="group_invite"
label="+"
label_selected="+"
tool_tip="Invite to Group" />
<view_border
top_delta="-20"
left="66"
right="-6"
height="80"
layout="topleft"
follows="left|top|right"
name="info_border_group_list"
bevel_style="none" />
<group_list
top_delta="1"
left_delta="1"
right="-7"
height="78"
layout="topleft"
follows="left|top|right"
name="group_list"
border_visible="false"
for_agent="false" />
<text
top_pad="8"
left="6"
height="16"
width="55"
layout="topleft"
follows="left|top"
name="About:"
halign="right"
value="About:" />
<view_border
top_delta="0"
left="66"
right="-6"
height="102"
layout="topleft"
follows="all"
name="info_border5"
bevel_style="none" />
<text_editor
top_delta="1"
left_delta="1"
right="-7"
height="100"
layout="topleft"
follows="all"
name="sl_description_edit"
border_visible="false"
font="SansSerifSmall"
enabled="false"
trusted_content="false"
max_length="65000"
h_pad="2"
word_wrap="true"
parse_urls="true" />
<text
top_pad="10"
left="6"
height="16"
width="55"
layout="topleft"
follows="left|bottom"
name="Give item:"
halign="right"
value="Give item:" />
<view_border
top_delta="0"
left="66"
right="-6"
height="16"
layout="topleft"
follows="left|bottom|right"
name="drop_target_rect_vis"
bevel_style="out" />
<text
top_delta="2"
left_delta="0"
height="16"
width="321"
layout="topleft"
follows="left|bottom"
name="Give inventory"
halign="center"
tool_tip="Drop inventory items here to give them to this person.">
Drop inventory item here.
</text>
<layout_stack
bottom="-3"
left="6"
right="-6"
height="44"
layout="topleft"
follows="left|bottom|right"
name="buttonstack"
orientation="horizontal">
<layout_panel
left="2"
right="-2"
height="42"
follows="all"
layout="topleft"
name="left_buttonstack"
user_resize="false">
<button
top="2"
left="2"
height="20"
layout="topleft"
follows="left|top|right"
name="show_on_map_btn"
label="Find on Map"
label_selected="Find on Map"
tool_tip="Locate the Resident on the map" />
<button
top_pad="2"
height="20"
layout="topleft"
follows="left|top|right"
name="pay"
label="Pay"
label_selected="Pay"
tool_tip="Pay money to the Resident" />
</layout_panel>
<layout_panel
left="2"
right="-2"
follows="all"
layout="topleft"
name="middle_buttonstack"
user_resize="false">
<button
top="2"
left="2"
height="20"
layout="topleft"
follows="left|top|right"
name="teleport"
label="Offer Teleport"
label_selected="Offer Teleport"
tool_tip="Offer a teleport to the Resident" />
<button
top_pad="2"
height="20"
layout="topleft"
follows="left|top|right"
name="im"
label="Instant Message"
label_selected="Instant Message"
tool_tip="Open instant message session" />
</layout_panel>
<layout_panel
left="2"
right="-2"
follows="all"
layout="topleft"
name="right_buttonstack"
user_resize="false">
<button
top="2"
left="2"
height="20"
layout="topleft"
follows="left|top|right"
name="add_friend"
label="Add Friend"
label_selected="Add Friend"
tool_tip="Offer friendship to the Resident" />
<button
top_pad="2"
right="-40"
height="20"
layout="topleft"
follows="left|top|right"
name="block"
label="Block"
tool_tip="Block this Resident" />
<button
top_delta="0"
right="-40"
height="20"
layout="topleft"
follows="left|top|right"
name="unblock"
label="Unblock"
tool_tip="Unblock this Resident" />
<menu_button
left_pad="4"
height="20"
width="36"
layout="topleft"
follows="top|right"
name="overflow_btn"
image_overlay="OptionsMenu_Off" />
</layout_panel>
</layout_stack>
<check_box
bottom="-30"
left="63"
height="16"
width="130"
layout="topleft"
follows="left|bottom"
name="show_in_search_checkbox"
label="Show in search"
visible="false"
enabled="false" />
<profile_drop_target
top="0"
bottom="-1"
left="0"
right="-1"
layout="topleft"
follows="all"
name="drop_target"
mouse_opaque="false" />
</panel>

View File

@ -1,574 +0,0 @@
<?xml version="1.0" encoding="utf-8" standalone="yes" ?>
<panel
top="0"
left="0"
width="420"
height="480"
layout="topleft"
follows="all"
name="panel_profile"
label="Profile">
<string
name="status_online">
Online
</string>
<string
name="status_offline">
Offline
</string>
<string
name="CaptionTextAcctInfo">
[ACCTTYPE]
[PAYMENTINFO]
[FIRESTORM][FSDEV][FSSUPP][FSQA][FSGW]
</string>
<!-- [AGEVERIFICATION] moved out of the above, since it's not set anyway. See
LLPanelAvatarProfile::fillAccountStatus() ...TS -->
<string
name="payment_update_link_url">
http://www.secondlife.com/account/billing.php?lang=en
</string>
<string
name="partner_edit_link_url">
http://www.secondlife.com/account/partners.php?lang=en
</string>
<string
name="my_account_link_url"
value="http://secondlife.com/account" />
<string
name="no_partner_text"
value="None" />
<string
name="no_group_text"
value="None" />
<string
name="RegisterDateFormat">
[REG_DATE]
([AGE])
</string>
<string
name="name_text_args">
[NAME]
</string>
<string
name="display_name_text_args">
[DISPLAY_NAME]
</string>
<string
name="FSDev"
value=" Developer" />
<string
name="FSSupp"
value=" Support" />
<string
name="FSQualityAssurance"
value=" Bug Hunter" />
<string
name="FSGW"
value=" Gateway" />
<!--
KC: Use view_border's around text_editor's due to text render issues with border_visible
-->
<loading_indicator
top="4"
right="-10"
height="23"
width="23"
layout="topleft"
follows="top|right"
name="progress_indicator"
visible="false" />
<text
top="8"
left="6"
height="16"
width="40"
layout="topleft"
follows="left|top"
name="Name:"
halign="right"
value="Name:" />
<view_border
left_pad="4"
right="-86"
height="16"
layout="topleft"
follows="left|top|right"
name="info_border"
bevel_style="none" />
<text_editor
bottom_delta="2"
left_delta="3"
right="-86"
height="16"
layout="topleft"
follows="left|top|right"
name="complete_name"
bg_visible="false"
border_visible="false"
allow_scroll="false"
h_pad="0"
v_pad="0"
enabled="false"
max_length="254"
value="(loading...)" />
<button
left_pad="2"
height="20"
width="20"
layout="topleft"
follows="top|right"
name="set_name"
image_overlay="Edit_Wrench"
tool_tip="Set Display Name"
visible="false"
enabled="false" />
<text
top_pad="2"
left="6"
height="16"
width="40"
layout="topleft"
follows="left|top"
name="Key:"
halign="right"
value="Key:" />
<view_border
left_pad="4"
right="-86"
height="16"
layout="topleft"
follows="left|top|right"
name="info_border2"
bevel_style="none" />
<text_editor
bottom_delta="2"
left_delta="3"
right="-86"
height="16"
layout="topleft"
follows="left|top|right"
name="user_key"
bg_visible="false"
border_visible="false"
allow_scroll="false"
h_pad="0"
v_pad="0"
enabled="false"
max_length="254"
value="(loading...)" />
<text
left_pad="2"
right="-6"
height="16"
layout="topleft"
follows="top|right"
name="status"
halign="center"
value="" />
<button
top_delta="-2"
right="-6"
height="20"
layout="topleft"
follows="top|right"
name="copy_own_uri_button"
label="Copy URI" />
<texture_picker
top_pad="3"
left="6"
height="180"
width="220"
layout="topleft"
follows="top|left"
allow_no_texture="true"
name="2nd_life_pic"
default_image_name="None"
enabled="false"
fallback_image="Generic_Person_Large" />
<text
top_pad="-179"
left_pad="4"
right="-6"
height="16"
layout="topleft"
follows="left|top|right"
name="label"
value="Born:" />
<view_border
top_pad="0"
left_delta="0"
right="-6"
height="32"
name="info_border3"
layout="topleft"
follows="left|top|right"
bevel_style="none" />
<text_editor
top_delta="2"
left_delta="2"
right="-6"
height="28"
layout="topleft"
follows="left|top|right"
name="register_date"
max_length="254"
bg_visible="false"
border_visible="false"
allow_scroll="false"
h_pad="2"
v_pad="0"
read_only="true"
translate="false"
word_wrap="true"/>
<text
top_pad="7"
left_delta="-2"
right="-6"
height="16"
layout="topleft"
follows="left|top|right"
name="label2"
value="Account:" />
<view_border
top_pad="0"
left_delta="0"
right="-6"
height="48"
layout="topleft"
follows="left|top|right"
name="acct_border"
bevel_style="none" />
<text_editor
top_delta="2"
left_delta="2"
right="-6"
height="44"
layout="topleft"
follows="left|top|right"
name="acc_status_text"
bg_visible="false"
border_visible="false"
allow_scroll="false"
h_pad="2"
v_pad="0"
read_only="true"
translate="false"
word_wrap="true"/>
<text
top_pad="7"
left_delta="-2"
right="-6"
height="16"
layout="topleft"
follows="left|top|right"
name="partner_label"
value="Partner:" />
<view_border
top_pad="0"
left_delta="0"
right="-6"
height="18"
layout="topleft"
follows="left|top|right"
name="info_border4"
bevel_style="none" />
<text
icon_positioning="left"
top_delta="2"
left_delta="2"
right="-6"
height="16"
layout="topleft"
follows="left|top|right"
name="partner_text"
max_length="254" />
<!--
<button
top_pad="8"
left="6"
height="18"
width="40"
layout="topleft"
follows="left|top"
name="bigimg"
halign="center"
label="&lt; &lt;"
label_selected="&gt; &gt;"
tool_tip="Open Full Size." />
-->
<text
top_pad="8"
left="6"
height="16"
width="55"
layout="topleft"
follows="left|top"
name="Groups:"
halign="right"
value="Groups:" />
<button
top_delta="20"
left="40"
height="20"
width="20"
layout="topleft"
follows="left|top"
name="group_invite"
label="+"
label_selected="+"
tool_tip="Invite to Group" />
<view_border
top_delta="-20"
left="66"
right="-6"
height="80"
layout="topleft"
follows="left|top|right"
name="info_border_group_list"
bevel_style="none" />
<group_list
top_delta="1"
left_delta="1"
right="-7"
height="78"
layout="topleft"
follows="left|top|right"
name="group_list"
border_visible="false"
for_agent="false" />
<text
top_pad="8"
left="6"
height="16"
width="55"
layout="topleft"
follows="left|top"
name="About:"
halign="right"
value="About:" />
<view_border
top_delta="0"
left="66"
right="-6"
height="102"
layout="topleft"
follows="all"
name="info_border5"
bevel_style="none" />
<text_editor
top_delta="1"
left_delta="1"
right="-7"
height="100"
layout="topleft"
follows="all"
name="sl_description_edit"
border_visible="false"
font="SansSerifSmall"
enabled="false"
trusted_content="false"
max_length="65000"
h_pad="2"
word_wrap="true"
parse_urls="true" />
<text
top_pad="10"
left="6"
height="16"
width="55"
layout="topleft"
follows="left|bottom"
name="Give item:"
halign="right"
value="Give item:" />
<view_border
top_delta="0"
left="66"
right="-6"
height="16"
layout="topleft"
follows="left|bottom|right"
name="drop_target_rect_vis"
bevel_style="out" />
<text
top_delta="2"
left_delta="0"
height="16"
width="321"
layout="topleft"
follows="left|bottom"
name="Give inventory"
halign="center"
tool_tip="Drop inventory items here to give them to this person.">
Drop inventory item here.
</text>
<layout_stack
bottom="-3"
left="6"
right="-6"
height="44"
layout="topleft"
follows="left|bottom|right"
name="buttonstack"
orientation="horizontal">
<layout_panel
left="2"
right="-2"
height="42"
follows="all"
layout="topleft"
name="left_buttonstack"
user_resize="false">
<button
top="2"
left="2"
height="20"
layout="topleft"
follows="left|top|right"
name="show_on_map_btn"
label="Find on Map"
label_selected="Find on Map"
tool_tip="Locate the Resident on the map" />
<button
top_pad="2"
height="20"
layout="topleft"
follows="left|top|right"
name="pay"
label="Pay"
label_selected="Pay"
tool_tip="Pay money to the Resident" />
</layout_panel>
<layout_panel
left="2"
right="-2"
follows="all"
layout="topleft"
name="middle_buttonstack"
user_resize="false">
<button
top="2"
left="2"
height="20"
layout="topleft"
follows="left|top|right"
name="teleport"
label="Offer Teleport"
label_selected="Offer Teleport"
tool_tip="Offer a teleport to the Resident" />
<button
top_pad="2"
height="20"
layout="topleft"
follows="left|top|right"
name="im"
label="Instant Message"
label_selected="Instant Message"
tool_tip="Open instant message session" />
</layout_panel>
<layout_panel
left="2"
right="-2"
follows="all"
layout="topleft"
name="right_buttonstack"
user_resize="false">
<button
top="2"
left="2"
height="20"
layout="topleft"
follows="left|top|right"
name="add_friend"
label="Add Friend"
label_selected="Add Friend"
tool_tip="Offer friendship to the Resident" />
<button
top_pad="2"
right="-40"
height="20"
layout="topleft"
follows="left|top|right"
name="block"
label="Block"
tool_tip="Block this Resident" />
<button
top_delta="0"
right="-40"
height="20"
layout="topleft"
follows="left|top|right"
name="unblock"
label="Unblock"
tool_tip="Unblock this Resident" />
<menu_button
left_pad="4"
height="20"
width="36"
layout="topleft"
follows="top|right"
name="overflow_btn"
image_overlay="OptionsMenu_Off" />
</layout_panel>
</layout_stack>
<check_box
bottom="-30"
left="63"
height="16"
width="130"
layout="topleft"
follows="left|bottom"
name="show_in_search_checkbox"
label="Show in search"
visible="false"
enabled="false" />
<profile_drop_target
top="0"
bottom="-1"
left="0"
right="-1"
layout="topleft"
follows="all"
name="drop_target"
mouse_opaque="false" />
</panel>

View File

@ -233,6 +233,7 @@
<texture_picker
name="2nd_life_pic"
fallback_image="Generic_Person_Large"
top="0"
left="0"
width="158"

View File

@ -43,10 +43,7 @@ import tarfile
import time
import zipfile
#<FS:AO>
import shlex
import zipfile
#</FS:AO>
sys.dont_write_bytecode = True # <FS:Ansariel> Prevents creating __pycache__ directory
from fs_viewer_manifest import FSViewerManifest #<FS:ND/> Manifest extensions for Firestorm