From cb95457e19cb38e7201b33a163c50760eee6dd88 Mon Sep 17 00:00:00 2001 From: Beq Date: Sun, 18 Dec 2022 00:18:56 +0000 Subject: [PATCH 1/8] A couple of quick python helpers download_list.py produces a notecard ready listing of downloads from a local folder parse_avatar_lad produces a list of attachment points in "priority" (id) order. --- fsutils/download_list.py | 203 ++++++++++++++++++++++++++++++++++++ fsutils/parse_avatar_lad.py | 42 ++++++++ 2 files changed, 245 insertions(+) create mode 100644 fsutils/download_list.py create mode 100644 fsutils/parse_avatar_lad.py diff --git a/fsutils/download_list.py b/fsutils/download_list.py new file mode 100644 index 0000000000..13defa2290 --- /dev/null +++ b/fsutils/download_list.py @@ -0,0 +1,203 @@ +#!/bin/python3 +import argparse +import os +import sys +import time + +# 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 + +# We want to get the following output by looping over the files +# DOWNLOADS +# ------------------------------------------------------------------------------------------------------- + +# Windows for SL 64 bit +# https://downloads.firestormviewer.org/preview/windows/Phoenix-Firestorm-Releasex64-6-6-8-68355_Setup.exe + +# MD5: 3094776F5DB11B6A959B0F3AED068C6A + +# Windows for SL 32 bit +# https://downloads.firestormviewer.org/preview/windows/Phoenix-Firestorm-Release-6-6-8-68355_Setup.exe + +# MD5: 2F960B3353971FFF63307B5210D306F5 + +# Windows for Opensim 64bit +# https://downloads.firestormviewer.org/preview/windows/Phoenix-FirestormOS-Releasex64-6-6-8-68355_Setup.exe + +# MD5: 6218D7B826538BB956699F9581532ECE + +# Windows for Opensim 32bit +# https://downloads.firestormviewer.org/preview/windows/Phoenix-FirestormOS-Release-6-6-8-68355_Setup.exe + +# MD5: D636CAFD287B4C8B96D726FE2A145327 + +# ------------------------------------------------------------------------------------------------------- +# Mac OSX for SL +# https://downloads.firestormviewer.org/preview/mac/Phoenix-Firestorm-Releasex64-6-6-8-68355.dmg + +# MD5: DA5AF534690328078B0B7BCEEA8D6959 + +# Mac OSX for Opensim +# https://downloads.firestormviewer.org/preview/mac/Phoenix-FirestormOS-Releasex64-6-6-8-68355.dmg + +# MD5: 16CA020E73760D8205E2314D07EEC90E + +# ------------------------------------------------------------------------------------------------------- +# Linux for SL +# https://downloads.firestormviewer.org/preview/linux/Phoenix-Firestorm-Releasex64-6-6-8-68355.tar.xz + +# MD5: 1A0C50065077B92889FFBC651E4278E4 + +# Linux for Opensim +# https://downloads.firestormviewer.org/preview/linux/Phoenix-FirestormOS-Releasex64-6-6-8-68355.tar.xz + +# MD5: 9D5D8021F376194B42F6E7D8E537E45E + +# ------------------------------------------------------------------------------------------------------- + +def run_cmd(cmd): + # print(cmd) + return os.popen(cmd).read() + +#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:] + return md5sum + + +# parse args first arg optional -r (release) second arg mandatory string path_to_directory + +parser = argparse.ArgumentParser( + prog="print_download_list", + description="Prints the list of files for download and their md5 checksums" + ) +parser.add_argument("-r", "--release", required=False, default=False, action="store_true") +# add path_to_directory required parameter to parser +parser.add_argument("path_to_directory") + +args = parser.parse_args() +path_to_directory = args.path_to_directory +release = args.release + +platforms_printable = {"windows":"MS Windows", "mac":"MacOS", "linux":"Linux"} +dirs = ["windows", "mac", "linux"] + +print(''' +DOWNLOADS''') +file_dict = {} +md5_dict = {} +for dir in dirs: + # print(f"looking in {os.path.join(sys.argv[1], dir)}") + dir = dir.lower() + files = get_files(os.path.join(args.path_to_directory, dir)) + for file in files: + full_file = os.path.join(sys.argv[1], dir, file) + md5 = get_md5(full_file) + if(dir=="windows"): + # print(f"testing {file} as {full_file}") + if "Firestorm-Release-" in os.path.basename(file): + file_dict["SLWin32"] = full_file + md5_dict["SLWin32"] = md5 + elif "FirestormOS-Release-" in os.path.basename(file): + file_dict["OSWin32"] = full_file + md5_dict["OSWin32"] = md5 + elif "Firestorm-Releasex64-" in os.path.basename(file): + file_dict["SLWin64"] = full_file + md5_dict["SLWin64"] = md5 + elif "FirestormOS-Releasex64-" in os.path.basename(file): + file_dict["OSWin64"] = full_file + md5_dict["OSWin64"] = md5 + if(dir=="mac"): + # print(f"testing {file} as {full_file}") + if "Firestorm-Releasex64-" in os.path.basename(file): + # print(f"storing {file} as SLMac64") + file_dict["SLMac64"] = full_file + md5_dict["SLMac64"] = md5 + elif "FirestormOS-Releasex64-" in os.path.basename(file): + file_dict["OSMac64"] = full_file + md5_dict["OSMac64"] = md5 + if(dir=="linux"): + # print(f"testing {file} as {full_file}") + if "Firestorm-Releasex64-" in os.path.basename(file): + file_dict["SLLinux64"] = full_file + md5_dict["SLLinux64"] = md5 + elif "FirestormOS-Releasex64-" in os.path.basename(file): + file_dict["OSLinux64"] = full_file + md5_dict["OSLinux64"] = 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 + +for dir in dirs: + print(f'''------------------------------------------------------------------------------------------------------- +{platforms_printable[dir]} +''') + dir=dir.lower() + if(dir=="linux"): + print ("Linux for SL") + print ( "{}/{}/{}".format(download_root,dir,os.path.basename(file_dict["SLLinux64"])) ) + print () + print ( "MD5: {}".format(md5_dict["SLLinux64"]) ) + print () + print ("Linux for OpenSim") + print ( "{}/{}/{}".format(download_root,dir,os.path.basename(file_dict["OSLinux64"])) ) + print () + print ( "MD5: {}".format(md5_dict["OSLinux64"]) ) + print () + if(dir=="mac"): + print ("MacOS for SL") + print ( "{}/{}/{}".format(download_root,dir,os.path.basename(file_dict["SLMac64"])) ) + print () + print ( "MD5: {}".format(md5_dict["SLMac64"]) ) + print () + print ("MacOS for OpenSim") + print ( "{}/{}/{}".format(download_root,dir,os.path.basename(file_dict["OSMac64"])) ) + print () + print ( "MD5: {}".format(md5_dict["OSMac64"]) ) + print () + if(dir=="windows"): + print ("Windows for SL 64-bit") + print ( "{}/{}/{}".format(download_root,dir,os.path.basename(file_dict["SLWin64"])) ) + print () + print ( "MD5: {}".format(md5_dict["SLWin64"]) ) + print () + print ("Windows for SL 32-Bit") + print ( "{}/{}/{}".format(download_root,dir,os.path.basename(file_dict["SLWin32"])) ) + print () + print ( "MD5: {}".format(md5_dict["SLWin32"]) ) + print () + print ("Windows for OS 64-bit") + print ( "{}/{}/{}".format(download_root,dir,os.path.basename(file_dict["OSWin64"])) ) + print () + print ( "MD5: {}".format(md5_dict["OSWin64"]) ) + print () + print ("Windows for OS 32-Bit") + print ( "{}/{}/{}".format(download_root,dir,os.path.basename(file_dict["OSWin32"])) ) + print () + print ( "MD5: {}".format(md5_dict["OSWin32"]) ) + print () + +print(''' +-------------------------------------------------------------------------------------------------------''') + + + diff --git a/fsutils/parse_avatar_lad.py b/fsutils/parse_avatar_lad.py new file mode 100644 index 0000000000..3e15a07922 --- /dev/null +++ b/fsutils/parse_avatar_lad.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python3 + +# open the avatar_lad.xml file and load it as a dom object + +from xml.dom import minidom +import os +import sys +import argparse + +parser = argparse.ArgumentParser() +# add positional argument for avatar_lad.xml +parser.add_argument("avatar_lad", help="path to avatar_lad.xml file") + +args = parser.parse_args() + + +# using minidom open the avatar_lad.xml file and load it as a dom object + + +# open the avatar_lad.xml file and load it as a dom object + +dom = minidom.parse(open(args.avatar_lad, "rb")) + + +# get the first element in the dom object + +root = dom.documentElement + +# find the "skeleton" child node +skeleton = root.getElementsByTagName("skeleton")[0] + + +for child in skeleton.childNodes: + if child.nodeType == child.ELEMENT_NODE: + # if tagname is "attachment_point" and 'hud' attribute doesn't exist print id and name + if child.tagName == "attachment_point": + if not child.hasAttribute("hud"): + print(f"{child.getAttribute('id')} - {child.getAttribute('name')}") + else: + print(f"{child.getAttribute('id')} - [HUD] {child.getAttribute('name')}") + + From e422e80377c221cf5391bc45f26af21c551f8852 Mon Sep 17 00:00:00 2001 From: Beq Date: Mon, 19 Dec 2022 10:49:11 +0000 Subject: [PATCH 2/8] Refactor and improve to accept github zip archives and autounpack --- fsutils/download_list.py | 170 ++++++++++++++++++++------------------- 1 file changed, 89 insertions(+), 81 deletions(-) diff --git a/fsutils/download_list.py b/fsutils/download_list.py index 13defa2290..5981e7c511 100644 --- a/fsutils/download_list.py +++ b/fsutils/download_list.py @@ -3,6 +3,9 @@ import argparse import os import sys import time +import zipfile +import glob +import shutil # iterate over the files in a directory and pass them to a command line subshell def get_files(path): @@ -78,6 +81,23 @@ def get_md5(mdfile): md5sum = md5sum[1:] return md5sum +def unzip_file(zip_file, unzip_dir): + with zipfile.ZipFile(zip_file, 'r') as zip_ref: + zip_ref.extractall(unzip_dir) + +def flatten_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 + file_path = os.path.join(root, file) + # Move the file to the root directory + shutil.move(file_path, tree_root) + for dir in flatten_dirs: + # Construct the full path to the subdirectory + subdir_path = os.path.join(root, dir) + # Delete the subdirectory and its contents + shutil.rmtree(subdir_path) + # parse args first arg optional -r (release) second arg mandatory string path_to_directory @@ -85,59 +105,68 @@ parser = argparse.ArgumentParser( prog="print_download_list", description="Prints the list of files for download and their md5 checksums" ) -parser.add_argument("-r", "--release", required=False, default=False, action="store_true") +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") # add path_to_directory required parameter to parser -parser.add_argument("path_to_directory") +parser.add_argument("path_to_directory", help="path to the directory in which we'll look for the files") args = parser.parse_args() path_to_directory = args.path_to_directory release = args.release -platforms_printable = {"windows":"MS Windows", "mac":"MacOS", "linux":"Linux"} dirs = ["windows", "mac", "linux"] -print(''' -DOWNLOADS''') +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")) + for dir in dirs: + flatten_tree(os.path.join(args.path_to_directory, dir)) + # Now move the symbols files to the symbols folder + symbols_folder = os.path.join(args.path_to_directory, "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) + 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) + for sym_file in symbol_archives: + print(f"Moving {sym_file} to {symbols_folder}") + shutil.move(sym_file, symbols_folder) + + file_dict = {} md5_dict = {} + for dir in dirs: - # print(f"looking in {os.path.join(sys.argv[1], dir)}") dir = dir.lower() files = get_files(os.path.join(args.path_to_directory, dir)) for file in files: - full_file = os.path.join(sys.argv[1], dir, file) + full_file = os.path.join(args.path_to_directory, dir, file) md5 = get_md5(full_file) - if(dir=="windows"): - # print(f"testing {file} as {full_file}") - if "Firestorm-Release-" in os.path.basename(file): - file_dict["SLWin32"] = full_file - md5_dict["SLWin32"] = md5 - elif "FirestormOS-Release-" in os.path.basename(file): - file_dict["OSWin32"] = full_file - md5_dict["OSWin32"] = md5 - elif "Firestorm-Releasex64-" in os.path.basename(file): - file_dict["SLWin64"] = full_file - md5_dict["SLWin64"] = md5 - elif "FirestormOS-Releasex64-" in os.path.basename(file): - file_dict["OSWin64"] = full_file - md5_dict["OSWin64"] = md5 - if(dir=="mac"): - # print(f"testing {file} as {full_file}") - if "Firestorm-Releasex64-" in os.path.basename(file): - # print(f"storing {file} as SLMac64") - file_dict["SLMac64"] = full_file - md5_dict["SLMac64"] = md5 - elif "FirestormOS-Releasex64-" in os.path.basename(file): - file_dict["OSMac64"] = full_file - md5_dict["OSMac64"] = md5 - if(dir=="linux"): - # print(f"testing {file} as {full_file}") - if "Firestorm-Releasex64-" in os.path.basename(file): - file_dict["SLLinux64"] = full_file - md5_dict["SLLinux64"] = md5 - elif "FirestormOS-Releasex64-" in os.path.basename(file): - file_dict["OSLinux64"] = full_file - md5_dict["OSLinux64"] = md5 + base_name = os.path.basename(file) + if "-Release-" 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" @@ -147,57 +176,36 @@ if args.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() - if(dir=="linux"): - print ("Linux for SL") - print ( "{}/{}/{}".format(download_root,dir,os.path.basename(file_dict["SLLinux64"])) ) + wordsize = "64" + platform = f"{platforms_printable[dir]}" + for grid in ["SL", "OS"]: + grid_printable = f"{grids_printable[grid]}" + 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["SLLinux64"]) ) - print () - print ("Linux for OpenSim") - print ( "{}/{}/{}".format(download_root,dir,os.path.basename(file_dict["OSLinux64"])) ) - print () - print ( "MD5: {}".format(md5_dict["OSLinux64"]) ) - print () - if(dir=="mac"): - print ("MacOS for SL") - print ( "{}/{}/{}".format(download_root,dir,os.path.basename(file_dict["SLMac64"])) ) - print () - print ( "MD5: {}".format(md5_dict["SLMac64"]) ) - print () - print ("MacOS for OpenSim") - print ( "{}/{}/{}".format(download_root,dir,os.path.basename(file_dict["OSMac64"])) ) - print () - print ( "MD5: {}".format(md5_dict["OSMac64"]) ) - print () - if(dir=="windows"): - print ("Windows for SL 64-bit") - print ( "{}/{}/{}".format(download_root,dir,os.path.basename(file_dict["SLWin64"])) ) - print () - print ( "MD5: {}".format(md5_dict["SLWin64"]) ) - print () - print ("Windows for SL 32-Bit") - print ( "{}/{}/{}".format(download_root,dir,os.path.basename(file_dict["SLWin32"])) ) - print () - print ( "MD5: {}".format(md5_dict["SLWin32"]) ) - print () - print ("Windows for OS 64-bit") - print ( "{}/{}/{}".format(download_root,dir,os.path.basename(file_dict["OSWin64"])) ) - print () - print ( "MD5: {}".format(md5_dict["OSWin64"]) ) - print () - print ("Windows for OS 32-Bit") - print ( "{}/{}/{}".format(download_root,dir,os.path.basename(file_dict["OSWin32"])) ) - print () - print ( "MD5: {}".format(md5_dict["OSWin32"]) ) + 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" print(''' -------------------------------------------------------------------------------------------------------''') - - From d668b30002a571a9beea5b5949fb072270279d72 Mon Sep 17 00:00:00 2001 From: Beq Date: Sat, 17 Dec 2022 16:31:16 +0000 Subject: [PATCH 3/8] Revert "Rewrite the inventory corruption fix to reduce possible side-effects" This reverts commit 5ddbef4a2588e504ab71a468b006b129cb7bd0df. --- indra/newview/llappviewer.cpp | 99 +++++++++++------------------------ indra/newview/llappviewer.h | 1 - 2 files changed, 32 insertions(+), 68 deletions(-) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 7e4d01b2e8..5b10a43a0a 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -1773,9 +1773,7 @@ bool LLAppViewer::doFrame() { LLVoiceClient::getInstance()->terminate(); } - // [FIRE-32453] [BUG-232971] disconnect sooner to force the cache write. - persistCachesAndSettings(); - // + disconnectViewer(); resumeMainloopTimeout(); } @@ -2058,11 +2056,13 @@ bool LLAppViewer::cleanup() // Give any remaining SLPlugin instances a chance to exit cleanly. LLPluginProcessParent::shutdown(); + // [FIRE-32453] [BUG-232971] disconnect sooner to force the cache write. + // disconnectViewer(); + // LLViewerCamera::deleteSingleton(); - disconnectViewer(); + // LL_INFOS() << "Viewer disconnected" << LL_ENDL; LLViewerCamera::deleteSingleton(); - - LL_INFOS() << "Viewer disconnected" << LL_ENDL; + // if (gKeyboard) { gKeyboard->resetKeys(); @@ -4865,7 +4865,8 @@ void LLAppViewer::removeDumpDir() void LLAppViewer::forceQuit() { // [FIRE-32453] [BUG-232971] disconnect sooner to force the cache write. - persistCachesAndSettings(); + disconnectViewer(); + LL_INFOS() << "Viewer disconnected" << LL_ENDL; // LLApp::setQuitting(); } @@ -6364,48 +6365,14 @@ void LLAppViewer::idleNetwork() mAgentRegionLastAlive = this_region_alive; } } -void LLAppViewer::persistCachesAndSettings() -{ - // Save inventory to disk if appropriate - if (gInventory.isInventoryUsable() - && gAgent.getID().notNull()) // Shouldn't be null at this stage - { - LL_INFOS() << "Saving Inventory Cache" << LL_ENDL; - gInventory.cache(gInventory.getRootFolderID(), gAgent.getID()); - if (gInventory.getLibraryRootFolderID().notNull() - && gInventory.getLibraryOwnerID().notNull()) - { - gInventory.cache( - gInventory.getLibraryRootFolderID(), - gInventory.getLibraryOwnerID()); - } - LL_INFOS() << "Saving Inventory Cache : COMPLETED" << LL_ENDL; - } - else - { - LL_INFOS() << "Not Saving Inventory Cache : Inventory is currently unusable" << LL_ENDL; - } - // Persist name cache - LLAvatarNameCache::instance().setCustomNameCheckCallback(LLAvatarNameCache::custom_name_check_callback_t()); // Contact sets - LL_INFOS() << "Saving Name Cache" << LL_ENDL; - saveNameCache(); - LL_INFOS() << "Saving Name Cache : COMPLETED" << LL_ENDL; - // Save experience cache if appropriate - if (LLExperienceCache::instanceExists()) - { - LL_INFOS() << "Saving Experience Cache" << LL_ENDL; - LLExperienceCache::instance().cleanup(); - LL_INFOS() << "Saving Experience Cache : COMPLETED" << LL_ENDL; - } - -} void LLAppViewer::disconnectViewer() { if (gDisconnected) { return; } + gDisconnected = TRUE;// [FIRE-32453] [BUG-232971] disconnect sooner to force the cache write. // // Cleanup after quitting. // @@ -6435,32 +6402,30 @@ void LLAppViewer::disconnectViewer() { LLSelectMgr::getInstance()->deselectAll(); } - // [FIRE-32453] [BUG-232971] Persist before disconnect - // Moved to separate function - // // save inventory if appropriate - // if (gInventory.isInventoryUsable() - // && gAgent.getID().notNull()) // Shouldn't be null at this stage - // { - // gInventory.cache(gInventory.getRootFolderID(), gAgent.getID()); - // if (gInventory.getLibraryRootFolderID().notNull() - // && gInventory.getLibraryOwnerID().notNull()) - // { - // gInventory.cache( - // gInventory.getLibraryRootFolderID(), - // gInventory.getLibraryOwnerID()); - // } - // } - // LLAvatarNameCache::instance().setCustomNameCheckCallback(LLAvatarNameCache::custom_name_check_callback_t()); // Contact sets - // saveNameCache(); - // if (LLExperienceCache::instanceExists()) - // { - // // TODO: LLExperienceCache::cleanup() logic should be moved to - // // cleanupSingleton(). - // LLExperienceCache::instance().cleanup(); - // } + // save inventory if appropriate + if (gInventory.isInventoryUsable() + && gAgent.getID().notNull()) // Shouldn't be null at this stage + { + gInventory.cache(gInventory.getRootFolderID(), gAgent.getID()); + if (gInventory.getLibraryRootFolderID().notNull() + && gInventory.getLibraryOwnerID().notNull()) + { + gInventory.cache( + gInventory.getLibraryRootFolderID(), + gInventory.getLibraryOwnerID()); + } + } + + LLAvatarNameCache::instance().setCustomNameCheckCallback(LLAvatarNameCache::custom_name_check_callback_t()); // Contact sets + saveNameCache(); + if (LLExperienceCache::instanceExists()) + { + // TODO: LLExperienceCache::cleanup() logic should be moved to + // cleanupSingleton(). + LLExperienceCache::instance().cleanup(); + } - // // close inventory interface, close all windows LLSidepanelInventory::cleanup(); @@ -6490,7 +6455,7 @@ void LLAppViewer::disconnectViewer() LLDestroyClassList::instance().fireCallbacks(); cleanup_xfer_manager(); - gDisconnected = TRUE; + // gDisconnected = TRUE; // [FIRE-32453] [BUG-232971] disconnect sooner to force the cache write. // Pass the connection state to LLUrlEntryParcel not to attempt // parcel info requests while disconnected. diff --git a/indra/newview/llappviewer.h b/indra/newview/llappviewer.h index c548e3c5b8..323cd21c5a 100644 --- a/indra/newview/llappviewer.h +++ b/indra/newview/llappviewer.h @@ -287,7 +287,6 @@ private: void idleNetwork(); void sendLogoutRequest(); - void persistCachesAndSettings(); void disconnectViewer(); // *FIX: the app viewer class should be some sort of singleton, no? From 49a0cdecc716db57470e646d24c7affb4ffa2851 Mon Sep 17 00:00:00 2001 From: Beq Date: Thu, 8 Dec 2022 23:47:07 +0000 Subject: [PATCH 4/8] Revert "[FIRE-32453] fixup bug introduced by moving the camera shutdown" This reverts commit 9d22f014479404346ce4fd9d700548ef68fe3f0d. --- indra/newview/llappviewer.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index 5b10a43a0a..e76fe74219 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2061,7 +2061,6 @@ bool LLAppViewer::cleanup() // LLViewerCamera::deleteSingleton(); // LL_INFOS() << "Viewer disconnected" << LL_ENDL; - LLViewerCamera::deleteSingleton(); // if (gKeyboard) { @@ -4866,6 +4865,7 @@ void LLAppViewer::forceQuit() { // [FIRE-32453] [BUG-232971] disconnect sooner to force the cache write. disconnectViewer(); + LLViewerCamera::deleteSingleton(); LL_INFOS() << "Viewer disconnected" << LL_ENDL; // LLApp::setQuitting(); From 915d39bb8a476bb44713b7eba79083db8f451653 Mon Sep 17 00:00:00 2001 From: Beq Date: Sun, 27 Nov 2022 23:01:42 +0000 Subject: [PATCH 5/8] Revert "[FIRE-32453] [BUG-232971] disconnect sooner to force the cache write" This reverts commit dbc4ab5147efb837b0f32a20375e7e2a5304dd1d. --- indra/newview/llappviewer.cpp | 25 +++++++------------------ 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/indra/newview/llappviewer.cpp b/indra/newview/llappviewer.cpp index e76fe74219..28f0ec4936 100644 --- a/indra/newview/llappviewer.cpp +++ b/indra/newview/llappviewer.cpp @@ -2056,12 +2056,12 @@ bool LLAppViewer::cleanup() // Give any remaining SLPlugin instances a chance to exit cleanly. LLPluginProcessParent::shutdown(); - // [FIRE-32453] [BUG-232971] disconnect sooner to force the cache write. - // disconnectViewer(); - // LLViewerCamera::deleteSingleton(); - // LL_INFOS() << "Viewer disconnected" << LL_ENDL; - // + disconnectViewer(); + LLViewerCamera::deleteSingleton(); + + LL_INFOS() << "Viewer disconnected" << LL_ENDL; + if (gKeyboard) { gKeyboard->resetKeys(); @@ -4863,11 +4863,6 @@ void LLAppViewer::removeDumpDir() void LLAppViewer::forceQuit() { - // [FIRE-32453] [BUG-232971] disconnect sooner to force the cache write. - disconnectViewer(); - LLViewerCamera::deleteSingleton(); - LL_INFOS() << "Viewer disconnected" << LL_ENDL; - // LLApp::setQuitting(); } @@ -6338,12 +6333,7 @@ void LLAppViewer::idleNetwork() } } add(LLStatViewer::NUM_NEW_OBJECTS, gObjectList.mNumNewObjects); - // [FIRE-32453] [BUG-232971] disconnect sooner to force the cache write. - if(gDisconnected) - { - return; - } - // + // Retransmit unacknowledged packets. gXferManager->retransmitUnackedPackets(); gAssetStorage->checkForTimeouts(); @@ -6372,7 +6362,6 @@ void LLAppViewer::disconnectViewer() { return; } - gDisconnected = TRUE;// [FIRE-32453] [BUG-232971] disconnect sooner to force the cache write. // // Cleanup after quitting. // @@ -6455,7 +6444,7 @@ void LLAppViewer::disconnectViewer() LLDestroyClassList::instance().fireCallbacks(); cleanup_xfer_manager(); - // gDisconnected = TRUE; // [FIRE-32453] [BUG-232971] disconnect sooner to force the cache write. + gDisconnected = TRUE; // Pass the connection state to LLUrlEntryParcel not to attempt // parcel info requests while disconnected. From e1479bd185c73f3952ba9e343f810e09bf63a5bd Mon Sep 17 00:00:00 2001 From: Beq Date: Wed, 21 Dec 2022 02:07:58 +0000 Subject: [PATCH 6/8] [FIRE-32453][BUG-232971] Improve shutdown behaviour. (Ty Rick Daylight) Alter close down behaviour to ensure that the main UI window remains open until the process finishes persisting data and other close down activities. --- indra/llcommon/llthreadsafequeue.h | 6 +++ indra/llcommon/threadpool.h | 8 +++- indra/llwindow/llwindowwin32.cpp | 68 ++++++++++++++++++++++++++---- 3 files changed, 72 insertions(+), 10 deletions(-) diff --git a/indra/llcommon/llthreadsafequeue.h b/indra/llcommon/llthreadsafequeue.h index 16b3966ad2..6a629b5405 100644 --- a/indra/llcommon/llthreadsafequeue.h +++ b/indra/llcommon/llthreadsafequeue.h @@ -308,7 +308,13 @@ bool LLThreadSafeQueue::push_(lock_t& lock, T&& element) { LL_PROFILE_ZONE_SCOPED_CATEGORY_THREAD; if (mStorage.size() >= mCapacity) + // Make thread queue capacity hangs visible + // return false; + { + LL_WARNS("ThreadPool") << "Threadsafe queue push_(lockacquired) queue full " << mStorage.size() << " >= " << mCapacity << LL_ENDL; return false; + } + // mStorage.push(std::forward(element)); lock.unlock(); diff --git a/indra/llcommon/threadpool.h b/indra/llcommon/threadpool.h index f8eec3b457..e995d9331e 100644 --- a/indra/llcommon/threadpool.h +++ b/indra/llcommon/threadpool.h @@ -46,7 +46,10 @@ namespace LL * ThreadPool listens for application shutdown messages on the "LLApp" * LLEventPump. Call close() to shut down this ThreadPool early. */ - void close(); + // [FIRE-32453][BUG-232971] Improve shutdown behaviour. + // void close(); + virtual void close(); + // std::string getName() const { return mName; } size_t getWidth() const { return mThreads.size(); } @@ -61,7 +64,8 @@ namespace LL private: void run(const std::string& name); - + + protected: // [FIRE-32453][BUG-232971] Improve shutdown behaviour. WorkQueue mQueue; std::string mName; size_t mThreadCount; diff --git a/indra/llwindow/llwindowwin32.cpp b/indra/llwindow/llwindowwin32.cpp index f002219b3e..4fab03ebee 100644 --- a/indra/llwindow/llwindowwin32.cpp +++ b/indra/llwindow/llwindowwin32.cpp @@ -37,6 +37,7 @@ #include "llwindowcallbacks.h" // Linden library includes +#include "llapp.h" // [FIRE-32453][BUG-232971] Improve shutdown behaviour. #include "llerror.h" #include "llexception.h" #include "llfasttimer.h" @@ -88,6 +89,7 @@ const UINT WM_DUMMY_(WM_USER + 0x0017); const UINT WM_POST_FUNCTION_(WM_USER + 0x0018); extern BOOL gDebugWindowProc; +extern BOOL gDisconnected; // [FIRE-32453][BUG-232971] Improve shutdown behaviour. static std::thread::id sWindowThreadId; static std::thread::id sMainThreadId; @@ -346,6 +348,7 @@ struct LLWindowWin32::LLWindowWin32Thread : public LL::ThreadPool LLWindowWin32Thread(); void run() override; + void close() override; // [FIRE-32453][BUG-232971] Improve shutdown behaviour. /// called by main thread to post work to this window thread template @@ -4735,6 +4738,31 @@ private: std::string mPrev; }; +// [FIRE-32453][BUG-232971] Improve shutdown behaviour. +// Provide a close() override that ignores the initial close triggered by the threadpool detecting "quit" +// but waits for the viewer to be disconected and the second close call triggered by the app window destructor +void LLWindowWin32::LLWindowWin32Thread::close() +{ + assert_main_thread(); + if (! mQueue.isClosed() && gDisconnected) + { + LL_DEBUGS("ThreadPool") << mName << " closing queue and joining threads" << LL_ENDL; + mQueue.close(); + for (auto& pair: mThreads) + { + LL_DEBUGS("ThreadPool") << mName << " waiting on thread " << pair.first << LL_ENDL; + // if mName does not contain the word Window + pair.second.join(); + } + LL_DEBUGS("ThreadPool") << mName << " shutdown complete" << LL_ENDL; + } + else + { + LL_DEBUGS("ThreadPool") << mName << " shutdown request ignored - not yet disconneced." << LL_ENDL; + } +} +// + void LLWindowWin32::LLWindowWin32Thread::run() { sWindowThreadId = std::this_thread::get_id(); @@ -4766,20 +4794,30 @@ void LLWindowWin32::LLWindowWin32Thread::run() ", ", msg.wParam, ")"); TranslateMessage(&msg); DispatchMessage(&msg); - - mMessageQueue.pushFront(msg); + // [FIRE-32453][BUG-232971] Improve shutdown behaviour. + // mMessageQueue.pushFront(msg); + try + { + // Nobody is reading this queue once we are quitting. Writing to it causes a hang. + if(!LLApp::isQuitting()) + mMessageQueue.pushFront(msg); + } + catch (const LLThreadSafeQueueInterrupt&) + { + // Shutdown timing is tricky. The main thread can end up trying + // to post a cursor position after having closed the WorkQueue. + logger.always("Message procesing tried to push() to closed MessageQueue - caught"); + } + // } } { LL_PROFILE_ZONE_NAMED_CATEGORY_WIN32("w32t - Function Queue"); - logger.onChange("runPending()"); + logger.onChange("runPending()"); //process any pending functions getQueue().runPending(); } - // This is very verbose even for debug so it has it's own debug tag - LL_DEBUGS("WindowVerbose") << "PreCheck Done - closed=" << getQueue().isClosed() << " size=" << getQueue().size() << LL_ENDL; - // #if 0 { @@ -4794,12 +4832,26 @@ void LLWindowWin32::LLWindowWin32Thread::run() void LLWindowWin32::post(const std::function& func) { - mFunctionQueue.pushFront(func); + try + { + mFunctionQueue.pushFront(func); + } + catch (const LLThreadSafeQueueInterrupt&) + { + LL_INFOS("Window") << "push function to closed Queue caught" << LL_ENDL; + } } void LLWindowWin32::postMouseButtonEvent(const std::function& func) { - mMouseQueue.pushFront(func); + try + { + mMouseQueue.pushFront(func); + } + catch (const LLThreadSafeQueueInterrupt&) + { + LL_INFOS("Window") << "push mouse event to closed Queue caught" << LL_ENDL; + } } void LLWindowWin32::kickWindowThread(HWND windowHandle) From 93aa37cc8b5d8de39c5ec96ed390e24a528bbb6e Mon Sep 17 00:00:00 2001 From: PanteraPolnocy Date: Wed, 21 Dec 2022 21:46:44 +0100 Subject: [PATCH 7/8] FIRE-32543 Change Display Name - "You cannot change ... " message overlaps other text --- indra/newview/skins/default/xui/en/floater_display_name.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/indra/newview/skins/default/xui/en/floater_display_name.xml b/indra/newview/skins/default/xui/en/floater_display_name.xml index f49123a46a..ac4a70bab7 100644 --- a/indra/newview/skins/default/xui/en/floater_display_name.xml +++ b/indra/newview/skins/default/xui/en/floater_display_name.xml @@ -3,7 +3,7 @@ legacy_header_height="18" can_minimize="false" can_close="false" - height="200" + height="215" layout="topleft" name="Display Name" help_topic="display_name" @@ -30,12 +30,12 @@ length="1" follows="left|top" font="SansSerif" - height="16" + height="20" layout="topleft" left="25" text_color="EmphasisColor" name="lockout_text" - top="50" + top="65" use_ellipses="true" visible="false" width="410" From cf3f577e3253d846527d618c038a4d1505a4834d Mon Sep 17 00:00:00 2001 From: Andrey Kleshchev Date: Wed, 14 Dec 2022 20:49:56 +0200 Subject: [PATCH 8/8] SL-18384 Fix NSException for keyboard handling Affects accent keys for diacritical marks --- indra/llwindow/llopenglview-objc.mm | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/indra/llwindow/llopenglview-objc.mm b/indra/llwindow/llopenglview-objc.mm index 899ae77929..457fe29c72 100644 --- a/indra/llwindow/llopenglview-objc.mm +++ b/indra/llwindow/llopenglview-objc.mm @@ -505,7 +505,12 @@ attributedStringInfo getSegments(NSAttributedString *str) // e.g. OS Window for upload something or Input Window... // mModifiers instance variable is for insertText: or insertText:replacementRange: (by Pell Smit) mModifiers = [theEvent modifierFlags]; - unichar ch = [[theEvent charactersIgnoringModifiers] characterAtIndex:0]; + NSString *str_no_modifiers = [theEvent charactersIgnoringModifiers]; + unichar ch = 0; + if (str_no_modifiers.length) + { + ch = [str_no_modifiers characterAtIndex:0]; + } bool acceptsText = mHasMarkedText ? false : callKeyDown(&eventData, keycode, mModifiers, ch); if (acceptsText &&