diff --git a/.github/workflows/build_viewer.yml b/.github/workflows/build_viewer.yml
index 08872166db..8adec3ef10 100644
--- a/.github/workflows/build_viewer.yml
+++ b/.github/workflows/build_viewer.yml
@@ -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 }}
+
diff --git a/doc/contributions.txt b/doc/contributions.txt
index 814a323994..5a80916084 100755
--- a/doc/contributions.txt
+++ b/doc/contributions.txt
@@ -284,9 +284,11 @@ Beq Janus
SL-11300
SL-15709
SL-16021
- SL-16027
+ SL-18202
+ SL-18586
SL-18592
SL-18637
+ SL-19317
Beth Walcher
Bezilon Kasei
Biancaluce Robbiani
diff --git a/fsutils/download_list.py b/fsutils/download_list.py
index 0747ae7e4c..b8534efeff 100644
--- a/fsutils/download_list.py
+++ b/fsutils/download_list.py
@@ -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)
diff --git a/indra/cmake/Copy3rdPartyLibs.cmake b/indra/cmake/Copy3rdPartyLibs.cmake
index 66e272ded5..7c3e7aa128 100644
--- a/indra/cmake/Copy3rdPartyLibs.cmake
+++ b/indra/cmake/Copy3rdPartyLibs.cmake
@@ -272,6 +272,7 @@ elseif(LINUX)
libhunspell-1.3.so.0.0.0
+ libopenjp2.so
libuuid.so.16
libuuid.so.16.0.22
@@ -347,6 +348,6 @@ if(DARWIN)
# that end up in any of the above SHARED_LIB_STAGING_DIR_MUMBLE
# directories.
add_custom_command( TARGET stage_third_party_libs POST_BUILD
- COMMAND cmake -E create_symlink ${SHARED_LIB_STAGING_DIR} ${CMAKE_BINARY_DIR}/sharedlibs/Resources
+ COMMAND ${CMAKE_COMMAND} -E create_symlink ${SHARED_LIB_STAGING_DIR} ${CMAKE_BINARY_DIR}/sharedlibs/Resources
)
endif()
diff --git a/indra/edit-me-to-trigger-new-build.txt b/indra/edit-me-to-trigger-new-build.txt
index eab7c17b71..8b13789179 100644
--- a/indra/edit-me-to-trigger-new-build.txt
+++ b/indra/edit-me-to-trigger-new-build.txt
@@ -1,4 +1 @@
-euclid 5/29/2020
-euclid 7/23/2020
-euclid 4/29/2021
-euclid 10/5/2021 DRTVWR-546
+
diff --git a/indra/llcommon/tests/llprocess_test.cpp b/indra/llcommon/tests/llprocess_test.cpp
index 999d432079..81449b4a42 100644
--- a/indra/llcommon/tests/llprocess_test.cpp
+++ b/indra/llcommon/tests/llprocess_test.cpp
@@ -356,7 +356,7 @@ namespace tut
// Create a script file in a temporary place.
NamedTempFile script("py",
- "from __future__ import print_function" EOL
+ "from __future__ import print_function" EOL
"import sys" EOL
"import time" EOL
EOL
@@ -366,7 +366,7 @@ namespace tut
"time.sleep(2)" EOL
"print('stderr after wait',file=sys.stderr)" EOL
"sys.stderr.flush()" EOL
- );
+ );
// Arrange to track the history of our interaction with child: what we
// fetched, which pipe it came from, how many tries it took before we
@@ -862,8 +862,8 @@ namespace tut
set_test_name("'bogus' test");
CaptureLog recorder;
PythonProcessLauncher py(get_test_name(),
- "from __future__ import print_function\n"
- "print('Hello world')\n");
+ "from __future__ import print_function\n"
+ "print('Hello world')\n");
py.mParams.files.add(LLProcess::FileParam("bogus"));
py.mPy = LLProcess::create(py.mParams);
ensure("should have rejected 'bogus'", ! py.mPy);
@@ -878,8 +878,8 @@ namespace tut
// Replace this test with one or more real 'file' tests when we
// implement 'file' support
PythonProcessLauncher py(get_test_name(),
- "from __future__ import print_function\n"
- "print('Hello world')\n");
+ "from __future__ import print_function\n"
+ "print('Hello world')\n");
py.mParams.files.add(LLProcess::FileParam());
py.mParams.files.add(LLProcess::FileParam("file"));
py.mPy = LLProcess::create(py.mParams);
@@ -894,8 +894,8 @@ namespace tut
// implement 'tpipe' support
CaptureLog recorder;
PythonProcessLauncher py(get_test_name(),
- "from __future__ import print_function\n"
- "print('Hello world')\n");
+ "from __future__ import print_function\n"
+ "print('Hello world')\n");
py.mParams.files.add(LLProcess::FileParam());
py.mParams.files.add(LLProcess::FileParam("tpipe"));
py.mPy = LLProcess::create(py.mParams);
@@ -912,8 +912,8 @@ namespace tut
// implement 'npipe' support
CaptureLog recorder;
PythonProcessLauncher py(get_test_name(),
- "from __future__ import print_function\n"
- "print('Hello world')\n");
+ "from __future__ import print_function\n"
+ "print('Hello world')\n");
py.mParams.files.add(LLProcess::FileParam());
py.mParams.files.add(LLProcess::FileParam());
py.mParams.files.add(LLProcess::FileParam("npipe"));
@@ -989,20 +989,20 @@ namespace tut
{
set_test_name("get*Pipe() validation");
PythonProcessLauncher py(get_test_name(),
- "from __future__ import print_function\n"
- "print('this output is expected')\n");
+ "from __future__ import print_function\n"
+ "print('this output is expected')\n");
py.mParams.files.add(LLProcess::FileParam("pipe")); // pipe for stdin
py.mParams.files.add(LLProcess::FileParam()); // inherit stdout
py.mParams.files.add(LLProcess::FileParam("pipe")); // pipe for stderr
py.run();
TEST_getPipe(*py.mPy, getWritePipe, getOptWritePipe,
- LLProcess::STDIN, // VALID
- LLProcess::STDOUT, // NOPIPE
- LLProcess::STDERR); // BADPIPE
+ LLProcess::STDIN, // VALID
+ LLProcess::STDOUT, // NOPIPE
+ LLProcess::STDERR); // BADPIPE
TEST_getPipe(*py.mPy, getReadPipe, getOptReadPipe,
- LLProcess::STDERR, // VALID
- LLProcess::STDOUT, // NOPIPE
- LLProcess::STDIN); // BADPIPE
+ LLProcess::STDERR, // VALID
+ LLProcess::STDOUT, // NOPIPE
+ LLProcess::STDIN); // BADPIPE
}
template<> template<>
@@ -1129,8 +1129,8 @@ namespace tut
{
set_test_name("ReadPipe \"eof\" event");
PythonProcessLauncher py(get_test_name(),
- "from __future__ import print_function\n"
- "print('Hello from Python!')\n");
+ "from __future__ import print_function\n"
+ "print('Hello from Python!')\n");
py.mParams.files.add(LLProcess::FileParam()); // stdin
py.mParams.files.add(LLProcess::FileParam("pipe")); // stdout
py.launch();
diff --git a/indra/llinventory/llsettingswater.cpp b/indra/llinventory/llsettingswater.cpp
index 90f99e8198..29c6bf0000 100644
--- a/indra/llinventory/llsettingswater.cpp
+++ b/indra/llinventory/llsettingswater.cpp
@@ -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);
+ // 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;
+ }
+ //
fog_density = pow(fog_density, underwater_fog_mod);
}
return fog_density;
diff --git a/indra/llwindow/llwindow.cpp b/indra/llwindow/llwindow.cpp
index ef7d18ddaf..1390c7ff6b 100644
--- a/indra/llwindow/llwindow.cpp
+++ b/indra/llwindow/llwindow.cpp
@@ -126,7 +126,8 @@ LLWindow::LLWindow(LLWindowCallbacks* callbacks, BOOL fullscreen, U32 flags)
mSwapMethod(SWAP_METHOD_UNDEFINED),
mHideCursorPermanent(FALSE),
mFlags(flags),
- mHighSurrogate(0)
+ mHighSurrogate(0),
+ mRefreshRate(0)
{
}
diff --git a/indra/llwindow/llwindow.h b/indra/llwindow/llwindow.h
index d8ac4aad43..2bc9842b05 100644
--- a/indra/llwindow/llwindow.h
+++ b/indra/llwindow/llwindow.h
@@ -197,6 +197,8 @@ public:
// windows only DirectInput8 for joysticks
virtual void* getDirectInput8() { return NULL; };
virtual bool getInputDevices(U32 device_type_filter, void * devices_callback, void* userdata) { return false; };
+
+ virtual S32 getRefreshRate() { return mRefreshRate; }
protected:
LLWindow(LLWindowCallbacks* callbacks, BOOL fullscreen, U32 flags);
virtual ~LLWindow();
@@ -230,6 +232,7 @@ protected:
U16 mHighSurrogate;
S32 mMinWindowWidth;
S32 mMinWindowHeight;
+ S32 mRefreshRate;
// Handle a UTF-16 encoding unit received from keyboard.
// Converting the series of UTF-16 encoding units to UTF-32 data,
diff --git a/indra/llwindow/llwindowmacosx.cpp b/indra/llwindow/llwindowmacosx.cpp
index 31a295b34b..c3382e06c5 100644
--- a/indra/llwindow/llwindowmacosx.cpp
+++ b/indra/llwindow/llwindowmacosx.cpp
@@ -53,6 +53,8 @@ BOOL check_for_card(const char* RENDERER, const char* bad_card);
const char* cursorIDToName(int id);
//
+const S32 DEFAULT_REFRESH_RATE = 60;
+
namespace
{
NSKeyEventRef mRawKeyEvent = NULL;
@@ -664,6 +666,13 @@ BOOL LLWindowMacOSX::createContext(int x, int y, int width, int height, int bits
}
}
+ mRefreshRate = CGDisplayModeGetRefreshRate(CGDisplayCopyDisplayMode(mDisplay));
+ if(mRefreshRate == 0)
+ {
+ //consider adding more appropriate fallback later
+ mRefreshRate = DEFAULT_REFRESH_RATE;
+ }
+
// Disable vertical sync for swap
toggleVSync(enable_vsync);
diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp
index 66f53f861e..5a3cd6bfbb 100644
--- a/indra/llwindow/llwindowwin32.cpp
+++ b/indra/llwindow/llwindowwin32.cpp
@@ -586,7 +586,7 @@ LLWindowWin32::LLWindowWin32(LLWindowCallbacks* callbacks,
{
current_refresh = 60;
}
-
+ mRefreshRate = current_refresh;
//-----------------------------------------------------------------------
// Drop resolution and go fullscreen
// use a display mode with our desired size and depth, with a refresh
@@ -1065,6 +1065,7 @@ BOOL LLWindowWin32::switchContext(BOOL fullscreen, const LLCoordScreen& size, BO
{
current_refresh = 60;
}
+ mRefreshRate = current_refresh;
gGLManager.shutdownGL();
//destroy gl context
diff --git a/indra/newview/CMakeLists.txt b/indra/newview/CMakeLists.txt
index 5852dd6feb..265eedee5b 100644
--- a/indra/newview/CMakeLists.txt
+++ b/indra/newview/CMakeLists.txt
@@ -365,8 +365,8 @@ set(viewer_SOURCE_FILES
llfloaterpathfindinglinksets.cpp
llfloaterpathfindingobjects.cpp
llfloaterpay.cpp
- # llfloaterperformance.cpp replaced with fs version due to large changes and likelihood that LL version will not release.
- fsfloaterperformance.cpp
+ # llfloaterperformance.cpp
+ fsfloaterperformance.cpp # restore fs perf floater
llfloaterperms.cpp
llfloaterpostprocess.cpp
llfloaterprofile.cpp
@@ -1622,6 +1622,9 @@ set(viewer_HEADER_FILES
NACLfloaterexploresounds.h
)
+ list(APPEND viewer_SOURCE_FILES llperfstats.cpp)
+ list(APPEND viewer_HEADER_FILES llperfstats.h)
+
# Flickr / Discord keys and fsversionvalues headers are generated in here
include_directories( ${CMAKE_CURRENT_BINARY_DIR} )
@@ -1655,10 +1658,6 @@ configure_file(
list(APPEND viewer_HEADER_FILES ${CMAKE_CURRENT_BINARY_DIR}/fsversionvalues.h)
#
- # Performance stast support
- list(APPEND viewer_SOURCE_FILES fsperfstats.cpp)
- list(APPEND viewer_HEADER_FILES fsperfstats.h)
- #
source_group("CMake Rules" FILES ViewerInstall.cmake)
#build_data.json creation moved to viewer_manifest.py MAINT-6413
diff --git a/indra/newview/NACLantispam.cpp b/indra/newview/NACLantispam.cpp
index a72bbbffc8..9b5e709f10 100644
--- a/indra/newview/NACLantispam.cpp
+++ b/indra/newview/NACLantispam.cpp
@@ -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::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();
}
}
diff --git a/indra/newview/NACLantispam.h b/indra/newview/NACLantispam.h
index d5cf7889a6..cf85aa3dca 100644
--- a/indra/newview/NACLantispam.h
+++ b/indra/newview/NACLantispam.h
@@ -1,8 +1,8 @@
#ifndef NACL_ANTISPAM_H
#define NACL_ANTISPAM_H
-#include
-#include
+#include
+#include
#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 spam_queue_entry_map_t;
-typedef boost::unordered_set collision_sound_set_t;
+typedef std::unordered_map spam_queue_entry_map_t;
+typedef std::unordered_set 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();
diff --git a/indra/newview/VIEWER_VERSION.txt b/indra/newview/VIEWER_VERSION.txt
index 8a68577213..5ea36a044a 100644
--- a/indra/newview/VIEWER_VERSION.txt
+++ b/indra/newview/VIEWER_VERSION.txt
@@ -1 +1 @@
-6.6.11
+6.6.12
diff --git a/indra/newview/app_settings/settings.xml b/indra/newview/app_settings/settings.xml
index b60de07a27..b590a571b4 100644
--- a/indra/newview/app_settings/settings.xml
+++ b/indra/newview/app_settings/settings.xml
@@ -11706,6 +11706,28 @@ Change of this parameter will affect the layout of buttons in notification toast
Value
1
+ RenderClass0MemoryBandwidth
+
+ RenderCPUBasis
+
RenderComplexityColorMin
+ RenderShadowSplits
+
RenderSSAOScale
+ TargetFPS
+
+ AutoTuneFPS
+
+ AutoTuneLock
+
+ KeepAutoTuneLock
+
+ AllowSelfImpostor
+
+ ShowTunedART
+
+ RenderAvatarMaxART
+
+ AutoTuneRenderFarClipMin
+
+ AutoTuneRenderFarClipTarget
+
+ UserTargetReflections
+
+ PerfStatsCaptureEnabled
+
+ AutoTuneImpostorByDistEnabled
+
+ AutoTuneImpostorFarAwayDistance
+
+ TuningFPSStrategy
+
CameraOpacity
- FSTargetFPS
-
- FSAutoTuneFPS
-
FSPerfFloaterSmoothingPeriods
- FSPerfStatsCaptureEnabled
-
- FSAutoTuneImpostorByDistEnabled
-
- FSAutoTuneLock
-
- FSAllowSelfImpostor
-
- FSShowTunedART
-
- FSRenderAvatarMaxART
-
- FSAutoTuneRenderFarClipMin
-
- FSAutoTuneRenderFarClipTarget
-
- FSAutoTuneImpostorFarAwayDistance
-
- FSTuningFPSStrategy
-
- FSUserTargetReflections
-
FSReportRegionRestartToChat
- FSUseCoRoFor360Capture
-
SDL2IMEDefaultVerticalOffset