Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Verified mods auto-downloading #542

Closed
wants to merge 60 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
6fe5518
feat: load verifiedmods module in Northstar.Client
Alystrasz Sep 17, 2022
307f2cc
feat: check if missing mod is verified
Alystrasz Sep 17, 2022
dda01b0
refactor: run OnServerSelected in a thread
Alystrasz Sep 18, 2022
528f123
feat: add "downloading mod" dialog
Alystrasz Sep 18, 2022
b6f6582
Merge branch 'R2Northstar:main' into feat/verified-mods
Alystrasz Nov 25, 2022
a826e35
feat: call download method if mod is missing and verified
Alystrasz Nov 30, 2022
7f50077
feat: display loading screen while download is in progress
Alystrasz Nov 30, 2022
60d3adc
refactor: store mod name+version in variables (to avoid fetching them…
Alystrasz Nov 30, 2022
a293b10
feat: prevent user from closing mod downloading dialog while downloading
Alystrasz Nov 30, 2022
20559f9
feat: close dialog after download
Alystrasz Nov 30, 2022
8806698
refactor: use NS prefix on all methods
Alystrasz Dec 3, 2022
a6e3033
feat: reload mods once mod download has ended
Alystrasz Dec 5, 2022
417f6eb
Merge branch 'R2Northstar:main' into feat/verified-mods
Alystrasz Dec 9, 2022
73182ae
fix: verifiedmods.gnut is typed
Alystrasz Dec 13, 2022
43845cb
refactor: reload mods at once (after downloading them all)
Alystrasz Dec 13, 2022
dc5c4d8
Merge branch 'R2Northstar:main' into feat/verified-mods
Alystrasz Dec 20, 2022
2b7075a
feat: display mod downloading progress in dialog header
Alystrasz Dec 26, 2022
250a18c
feat: display download progress in dialog body
Alystrasz Dec 27, 2022
72c74c3
feat: display zip extraction stats
Alystrasz Dec 27, 2022
5d3a5a5
feat: display extraction progress for big files
Alystrasz Dec 27, 2022
99c8a74
docs: add some comments
Alystrasz Dec 27, 2022
4f53936
refactor: format messages with format method
Alystrasz Dec 28, 2022
f4aff6f
refactor: store megabyte size in a constant
Alystrasz Dec 28, 2022
2242071
refactor: add ModDownloadProgress struct
Alystrasz Dec 28, 2022
83b7fbb
refactor: update dialog UI instead of rebuilding it
Alystrasz Dec 28, 2022
b66236c
fix: typo
Alystrasz Dec 28, 2022
9c1a615
Update Northstar.Client/mod/scripts/vscripts/ui/menu_ns_serverbrowser…
Alystrasz Dec 29, 2022
3403f47
Update Northstar.Client/mod/scripts/vscripts/ui/menu_ns_serverbrowser…
Alystrasz Dec 29, 2022
1bf849a
style: add newline at end of file
Alystrasz Dec 29, 2022
87c38db
fix: typo
Alystrasz Dec 29, 2022
c9902c7
style: too many semicolons
Alystrasz Dec 29, 2022
465558c
style: add missing spaces
Alystrasz Dec 29, 2022
3a67eb9
style: add missing spaces
Alystrasz Dec 29, 2022
b386748
style: add missing space
Alystrasz Dec 29, 2022
98005e2
style: add missing spaces
Alystrasz Dec 29, 2022
da18e28
style: add missing spaces and remove semicolon
Alystrasz Dec 29, 2022
2bee483
style: add uppercase first letter to Build method
Alystrasz Dec 29, 2022
e3b3a9c
refactor: rename async method to OnServerSelected_Threaded
Alystrasz Dec 29, 2022
34a3b29
fix: do not download mod if it's already there
Alystrasz Dec 29, 2022
9bd4926
refactor: move MB constant outside loops
Alystrasz Dec 29, 2022
03f47c3
Merge branch 'main' into feat/verified-mods
Alystrasz Jan 7, 2023
805ebb9
feat: display an error dialog if mod extraction failed
Alystrasz Jan 8, 2023
de3b9d6
feat: localize error dialog messages
Alystrasz Jan 8, 2023
1237a3e
feat: localize all user-facing messages
Alystrasz Jan 8, 2023
faf8212
Merge branch 'main' into feat/verified-mods
Alystrasz Jan 10, 2023
538393a
Merge branch 'main' into feat/verified-mods
Alystrasz Jan 17, 2023
45e48bb
refactor: remove verifiedmods.gnut file
Alystrasz Jan 24, 2023
038864e
Merge branch 'main' into feat/verified-mods
Alystrasz Jan 25, 2023
d6d8870
refactor: rename progress struct build method to BuildModDownloadingP…
Alystrasz Feb 2, 2023
68b91f9
refactor: rename progress struct build method to BuildModDownloadingP…
Alystrasz Feb 2, 2023
7c192af
Merge branch 'R2Northstar:main' into feat/verified-mods
Alystrasz Mar 10, 2023
e489df2
feat: add native methods declaration to nativefuncs.json file
Alystrasz Mar 10, 2023
7e7f917
Revert "feat: add native methods declaration to nativefuncs.json file"
Alystrasz Mar 10, 2023
6de50c4
feat: add native methods declaration to nativefuncs.json file
Alystrasz Mar 10, 2023
8f69fda
build: move methods declaration to UI section
Alystrasz Mar 10, 2023
ced4217
build: fix NSIsModBeingDownloaded signature
Alystrasz Mar 10, 2023
f729bb0
build: fix NSGetCurrentDownloadProgress return type
Alystrasz Mar 10, 2023
96c1301
Merge branch 'R2Northstar:main' into feat/verified-mods
Alystrasz Mar 14, 2023
bcd4bc2
Merge branch 'main' into feat/verified-mods
Alystrasz May 19, 2023
b4e5118
Merge branch 'main' into feat/verified-mods
Alystrasz Jul 22, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions .github/nativefuncs.json
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,36 @@
"returnTypeString":"bool",
"argTypes":"string modName"
},
{
"name": "NSIsModVerified",
"helpText": "Tells if a mod is verified or not.",
"returnTypeString": "bool",
"argTypes": "string modName, string modVersion"
},
{
"name": "NSDownloadMod",
"helpText": "Starts downloading a mod; returns nothing because actual mod downloading is done in a thread, as not to block UI.",
"returnTypeString": "void",
"argTypes": "string modname, string modVersion"
},
{
"name": "NSIsModBeingDownloaded",
"helpText": "Tells if a mod is being downloaded; this is used by UI to check if it should display loading dialog to the user.",
"returnTypeString": "bool",
"argTypes": "string modName"
},
{
"name": "NSGetCurrentDownloadProgress",
"helpText": "Returns progression in percentage of current download/extraction.",
"returnTypeString": "array<float>",
"argTypes": ""
},
{
"name": "NSGetModExtractionResult",
"helpText": "Returns result of mod download/extraction.",
"returnTypeString": "string",
"argTypes": ""
},
{
"name":"NSGetModConvarsByModName",
"helpText":"",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,24 @@ Press Yes if you agree to this. This choice can be changed in the mods menu at a
"INVALID_MASTERSERVER_TOKEN" "Invalid or expired masterserver token"
"JSON_PARSE_ERROR" "Error parsing json response"
"UNSUPPORTED_VERSION" "The version you are using is no longer supported"

"DOWNLOADING_MOD_TITLE" "Downloading mod"
"DOWNLOADING_MOD_TITLE_WITH_PROGRESS" "Downloading mod (%s1%)"
"DOWNLOADING_MOD_TEXT" "Downloading %s1 v%s2..."
"DOWNLOADING_MOD_TEXT_WITH_PROGRESS" "Downloading %s1 v%s2...\n(%s3 %s4 / %s5 %s6)"
"EXTRACTING_MOD_TITLE" "Extracting mod (%s1%)"
"EXTRACTING_MOD_TEXT" "Extracting %s1 v%s2...\n(%s3/%s4 files)"
"FAILED_DOWNLOADING" "Failed downloading %s1"
"MISSING_MOD" "Missing mod %s1 v%s2"
"MOD_NOT_VERIFIED" "(mod is not verified, and couldn't be downloaded automatically)"
"MEGABYTE_SMALL" "MB"

"FAILED" "Mod extraction failed. Check logs for more details."
"FAILED_READING_ARCHIVE" "An error occurred while reading mod archive."
"FAILED_WRITING_TO_DISK" "An error occurred while extracting mod files to the filesystem."
"MOD_FETCHING_FAILED" "Mod archive could not be downloaded from Thunderstore."
"MOD_CORRUPTED" "Downloaded archive checksum does not match verified signature."
"NO_DISK_SPACE_AVAILABLE" "There is not enough space on your disk."

// Mod Settings
"MOD_SETTINGS" "Mod Settings"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,24 @@ Choisissez Oui si vous êtes d'accord. Ce choix peut être modifié à tout inst
"JSON_PARSE_ERROR" "Une erreur est survenue durant l'analyse JSON"
"UNSUPPORTED_VERSION" "La version que vous utilisez n'est plus supportée"

"DOWNLOADING_MOD_TITLE" "Téléchargement du mod"
"DOWNLOADING_MOD_TITLE_WITH_PROGRESS" "Téléchargement du mod (%s1%)"
"DOWNLOADING_MOD_TEXT" "Téléchargement de %s1 v%s2..."
"DOWNLOADING_MOD_TEXT_WITH_PROGRESS" "Téléchargement de %s1 v%s2...\n(%s3 %s4 / %s5 %s6)"
"EXTRACTING_MOD_TITLE" "Extraction du mod (%s1%)"
"EXTRACTING_MOD_TEXT" "Extraction de %s1 v%s2...\n(%s3/%s4 fichiers)"
"FAILED_DOWNLOADING" "Echec du téléchargement de %s1"
"MISSING_MOD" "Mod manquant : %s1 v%s2"
"MOD_NOT_VERIFIED" "(ce mod n'est pas vérifié, et n'a donc pas pu être automatiquement téléchargé)"
"MEGABYTE_SMALL" "Mo"

"FAILED" "L'extraction du mod a échoué.\nVeuillez lire le journal d'événements pour plus d'informations."
"FAILED_READING_ARCHIVE" "Une erreur est survenue lors de la lecture de l'archive."
"FAILED_WRITING_TO_DISK" "Une erreur est survenue lors de l'extraction de fichiers."
"MOD_FETCHING_FAILED" "L'archive du mod n'a pas pu être téléchargée depuis Thunderstore."
"MOD_CORRUPTED" "La somme de contrôle de l'archive ne correspond pas à la signature vérifiée."
"NO_DISK_SPACE_AVAILABLE" "Il n'y a plus assez d'espace disque."

"MOD_SETTINGS" "Paramètres de mod"
"NORTHSTAR_BASE_SETTINGS" "Paramètres de base de Northstar"
"ONLY_HOST_MATCH_SETTINGS" "Seul l'hôte peut changer les paramètres de match privé"
Expand Down
164 changes: 159 additions & 5 deletions Northstar.Client/mod/scripts/vscripts/ui/menu_ns_serverbrowser.nut
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,41 @@ struct {
array< void functionref( ServerInfo ) > connectCallbacks
} file

enum eProgressType
{
DOWNLOAD = 0,
EXTRACTION = 1
}

// This struct holds information about the currently downloaded mod:
struct ModDownloadProgress {
float received // received quantity
float total // total quantity expected
float receivedPercentage // ratio of received / expected (computed in native for PERFS!)
int progressType // nature of previous data (0=download stats [in MBs], 1=extraction stats [in files count])

// The following information should only be used with big files (as there's no need to display this
// for small files due to extraction speed):
float currentFileExtracted // extracted size of current file
float currentFileTotal // total size of current file
}

const int MB = 1024*1000;

/**
* Builds up a ModDownloadProgress struct from values retrieved from native code.
**/
ModDownloadProgress function BuildModDownloadingProgress(array<float> values)
{
ModDownloadProgress p
p.received = values[0]
p.total = values[1]
p.receivedPercentage = values[2]
p.progressType = int(values[3])
p.currentFileExtracted = values[4]
p.currentFileTotal = values[5]
return p
}

bool function FloatsEqual( float arg1, float arg2, float epsilon )
{
Expand Down Expand Up @@ -951,6 +985,11 @@ string function FillInServerModsLabel( array<RequiredModInfo> mods )


void function OnServerSelected( var button )
{
thread OnServerSelected_Threaded(button);
}

void function OnServerSelected_Threaded( var button )
{
if ( NSIsRequestingServerList() || NSGetServerCount() == 0 || file.serverListRequestFailed )
return
Expand All @@ -959,15 +998,123 @@ void function OnServerSelected( var button )

file.lastSelectedServer = server

// Count mods that have been successfully downloaded
int downloadedMods = 0;

// check mods
foreach ( RequiredModInfo mod in server.requiredMods )
{
if ( !NSGetModNames().contains( mod.name ) )
string modName = mod.name;
string modVersion = mod.version;
print( format("Server requires mod \"%s\" v%s.", modName, modVersion) )

// If mod is locally installed already, no need to download it.
if (NSGetModNames().contains( modName ) && NSGetModVersionByModName( modName ) == modVersion && !NSIsModEnabled( modName ))
{
print( format("Required mod \"%s\" is locally there but disabled, enabling it.", modName) )
NSSetModEnabled( modName, true )
downloadedMods++
continue
}

if ( !NSGetModNames().contains( modName ) )
{
string fullModName = "\"" + modName + "\""

// check if mod is verified
bool modIsVerified = false
if ( NSIsModVerified( modName, modVersion ) )
{
bool modIsVerified = true
string MB_UNIT = Localize( "#MEGABYTE_SMALL" )
NSDownloadMod( modName, modVersion )

// Downloading mod UI
DialogData dialogData
dialogData.header = Localize( "#DOWNLOADING_MOD_TITLE" )
dialogData.message = Localize( "#DOWNLOADING_MOD_TEXT", fullModName, modVersion )
dialogData.showSpinner = true;
// Prevent user from closing dialog
dialogData.forceChoice = true;

OpenDialog( dialogData )
// Save reference to UI elements, to update their content
var menu = GetMenu( "Dialog" )
var header = Hud_GetChild( menu, "DialogHeader" )
var body = GetSingleElementByClassname( menu, "DialogMessageClass" )

while( NSIsModBeingDownloaded(modName) )
{
// This array holds a bunch of progress information about mod.
ModDownloadProgress downloadStats = BuildModDownloadingProgress( NSGetCurrentDownloadProgress() );
bool isDownloading = downloadStats.progressType == eProgressType.DOWNLOAD;

// Mod is being downloaded.
if (isDownloading)
Alystrasz marked this conversation as resolved.
Show resolved Hide resolved
{
Hud_SetText( header, Localize( "#DOWNLOADING_MOD_TITLE_WITH_PROGRESS", downloadStats.receivedPercentage) )
Hud_SetText( body, Localize( "#DOWNLOADING_MOD_TEXT_WITH_PROGRESS", fullModName, modVersion, floor(downloadStats.received / MB), MB_UNIT, floor(downloadStats.total / MB), MB_UNIT))
}
else // Mod is being extracted.
{
Hud_SetText( header, Localize( "#EXTRACTING_MOD_TITLE", downloadStats.receivedPercentage ) )
string text = Localize( "#EXTRACTING_MOD_TEXT", fullModName, modVersion, floor(downloadStats.received), floor(downloadStats.total) )

// We only display extraction progress for big files (> 15MB), for users not to think Northstar has crashed.
float filesize = downloadStats.currentFileTotal;
if ( filesize > 15 * MB )
{
text += format( " [%i%s / %i%s]", floor(downloadStats.currentFileExtracted / MB), MB_UNIT, floor(downloadStats.currentFileTotal / MB), MB_UNIT )
}

Hud_SetText( body, text )
}

WaitFrame();
}

// Close loading dialog
CloseActiveMenu()

// Check result of downloading and extraction
string result = NSGetModExtractionResult()
if ( result == "OK" )
{
downloadedMods++
continue
}

// If mod downloading or extraction failed, we display the error
// and exit current thread.
else
{
DialogData dialogData
dialogData.header = Localize( "#FAILED_DOWNLOADING", modName )
dialogData.message = Localize( format("#%s", result) )
dialogData.image = $"ui/menu/common/dialog_error"

#if PC_PROG
AddDialogButton( dialogData, "#DISMISS" )
AddDialogFooter( dialogData, "#A_BUTTON_SELECT" )
#endif
AddDialogFooter( dialogData, "#B_BUTTON_DISMISS_RUI" )

OpenDialog( dialogData )
return
}

}

DialogData dialogData
dialogData.header = "#ERROR"
dialogData.message = format( "Missing mod \"%s\" v%s", mod.name, mod.version )
dialogData.message = Localize( "#MISSING_MOD", fullModName, modVersion )
dialogData.image = $"ui/menu/common/dialog_error"

if (!modIsVerified)
{
dialogData.message += "\n" + Localize( "#MOD_NOT_VERIFIED" )
}

#if PC_PROG
AddDialogButton( dialogData, "#DISMISS" )

Expand All @@ -982,8 +1129,8 @@ void function OnServerSelected( var button )
else
{
// this uses semver https://semver.org
array<string> serverModVersion = split( mod.name, "." )
array<string> clientModVersion = split( NSGetModVersionByModName( mod.name ), "." )
array<string> serverModVersion = split( modVersion, "." )
array<string> clientModVersion = split( NSGetModVersionByModName( modName ), "." )

bool semverFail = false
// if server has invalid semver don't bother checking
Expand All @@ -1001,7 +1148,7 @@ void function OnServerSelected( var button )
{
DialogData dialogData
dialogData.header = "#ERROR"
dialogData.message = format( "Server has mod \"%s\" v%s while we have v%s", mod.name, mod.version, NSGetModVersionByModName( mod.name ) )
dialogData.message = format( "Server has mod \"%s\" v%s while we have v%s", modName, modVersion, NSGetModVersionByModName( mod.name ) )
dialogData.image = $"ui/menu/common/dialog_error"

#if PC_PROG
Expand All @@ -1018,6 +1165,13 @@ void function OnServerSelected( var button )
}
}

// Make Northstar aware new mods have been added
if ( downloadedMods > 0 )
{
print("Some new mods have been downloaded or enabled, reloading mods.")
NSReloadMods();
Alystrasz marked this conversation as resolved.
Show resolved Hide resolved
}

if ( server.requiresPassword )
{
OnCloseServerBrowserMenu()
Expand Down