11#!/usr/bin/env -S deno run -A
22import { encodeHex } from "jsr:@std/encoding/hex" ;
33import { crypto } from "jsr:@std/crypto" ;
4+ import { auth , GridRange , Sheets , Spreadsheet } from 'https://googleapis.deno.dev/v1/sheets:v4.ts' ;
45
56//import { MainManifest, VersionData, OmniarchiveMainManifest, OmniVersionManifest } from './types.d.ts';
67
@@ -213,8 +214,6 @@ const mirrorMap: Map<MirrorId, LocalOriginalId> = new Map([
213214 [ "13w16b" , "13w16b-2151" ] ,
214215 [ "13w23b" , "13w23b-0101" ] ,
215216 [ "1.6" , "1.6-1517" ] ,
216- // TODO look at this
217- [ "1.6.2" , "1.6.2-091847" ] ,
218217 [ "13w38c" , "13w38c-1516" ] ,
219218 [ "1.7" , "1.7-1602" ] ,
220219 [ "14w04b" , "14w04b-1554" ] ,
@@ -241,6 +240,7 @@ const mergedMirrorMap: Map<MirrorId, [client: LocalOriginalId, server: LocalOrig
241240 [ "b1.9-pre4" , [ "b1.9-pre4-1435" , "b1.9-pre4-1441" ] ] ,
242241 [ "13w16a" , [ "13w16a-192037" , "13w16a-191517" ] ] ,
243242 [ "13w22a" , [ "13w22a-1434" , "13w22a-1608" ] ] ,
243+ [ "1.6.2" , [ "1.6.2-091847" , "1.6.2-080933" ] ] ,
244244 [ "1.6.3" , [ "1.6.3-171231" , "1.6.3-171031" ] ] ,
245245 [ "13w36a" , [ "13w36a-1446" , "13w36a-1330" ] ] ,
246246 [ "13w36b" , [ "13w36b-1307" , "13w36b-1233" ] ] ,
@@ -261,12 +261,20 @@ const weirdMergeMap: Map<LocalId, [client: ExternalOriginalId, server: IndexOrig
261261 [ "14w04a-1526" , [ "14w04a" , "14w04a-1526" ] ] ,
262262 [ "1.7.5-02260922" , [ "1.7.5" , "1.7.5-02260922" ] ]
263263] ) ;
264+ type Reason = string ;
265+ const exemptOtherVersions : Map < VersionId , Reason > = new Map ( [
266+ [ "b1.1-1245" , "The Omniarchive manifest version offers the server for b1.1_01; if the server downloads were switched, every download would match" ] ,
267+ [ "b1.1_01" , "The Omniarchive manifest version offers the server for b1.1-1245; if the server downloads were switched, every download would match" ] ,
268+ [ "1.0.0" , "The Omniarchive manifest has the server for 1.0.1; otherwise, every other download matches" ] ,
269+ [ "12w18a" , "The local manifest version has an extra \"client_zip\" download; otherwise, every other download matches" ] ,
270+ [ "12w19a" , "The local manifest version has an extra \"client_zip\" download; otherwise, every other download matches" ] ,
271+ [ "1.7.7-091529" , "The local manifest version includes the servers associated with this client; the client download otherwise matches" ] ,
272+ [ "1.12-pre3-1316" , "The Omniarchive manifest version offers downloads for 1.12-pre3-1409; none of the downloads match, but if the Omniarchive manifest gets fixed, the downloads would match" ]
273+ ] ) ;
264274
265275const shouldSkip = ( key : string ) : boolean => ! ( standaloneSevers . includes ( key ) || orphanServers . includes ( key ) ||
266276 mirrorlessRenameMap . has ( key ) || mirrorMap . has ( key ) || reverseMirrorMap . has ( key ) || mergedMirrorMap . has ( key ) || weirdMergeMap . has ( key ) ) ;
267277
268- export { standaloneSevers , renamedStandaloneServers , orphanServers , renamedOrphanServers , mirrorlessRenameMap , mirrorMap , reverseMirrorMap , mergedMirrorMap , weirdMergeMap }
269-
270278function compareLocalWithOmniarchive ( localVersionsMap : Map < string , VersionManifest > , remoteVersionsMap : Map < string , OmniVersionManifest > ) {
271279 const missingExternal = [ ] ;
272280
@@ -306,6 +314,209 @@ function compareLocalWithOmniarchive(localVersionsMap: Map<string, VersionManife
306314 console . log ( `\nLocal manifest is missing ${ missingLocal . length } versions` ) ;
307315}
308316
317+ function isWithinRangePredicate ( rowIndex : number ) {
318+ return ( range : GridRange ) => rowIndex > range . startRowIndex ! && rowIndex < range . endRowIndex !
319+ }
320+
321+ async function readSpreadsheetVersions ( spreadsheetPromise : Promise < Spreadsheet > ) : Promise < [ clients : Set < string > , servers : Set < string > ] > {
322+ const [ clients , servers ] = [ new Set < string > ( ) , new Set < string > ( ) ] ;
323+
324+ const sheets = ( await spreadsheetPromise ) . sheets ! . filter ( ( sheet ) => sheet . properties ! . sheetId !== 1427179805 && sheet . properties ! . sheetId !== 1915497658 ) ;
325+ sheets . forEach ( ( sheet ) => {
326+ const sheetId = sheet . properties ! . sheetId ;
327+ const merges = sheet . merges ! . filter ( ( range ) => range . startColumnIndex === 1 ) ;
328+ sheet . data ! [ 0 ] . rowData ! . forEach ( ( row , rowIndex , rowData ) => {
329+ const potentialVersion = row . values ! [ 1 ] . formattedValue ;
330+ if ( potentialVersion !== 'ID' ) {
331+ if ( ( sheetId === 872531987 || sheetId === 804883379 ) && potentialVersion ) clients . add ( potentialVersion ) ;
332+ else if ( ( sheetId === 2126693093 || sheetId === 59329510 ) && potentialVersion ) servers . add ( potentialVersion ) ;
333+ else if ( sheetId === 65188128 ) {
334+ const id = potentialVersion ?? rowData [ merges . find ( isWithinRangePredicate ( rowIndex ) ) ! . startRowIndex ! ] . values ! [ 1 ] . formattedValue ! ;
335+ const type = row . values ! [ 6 ] . formattedValue ! ;
336+ if ( type . startsWith ( 'Client' ) || type === 'EXE' ) clients . add ( id ) ;
337+ else if ( type . startsWith ( 'Server' ) ) servers . add ( id ) ;
338+ }
339+ }
340+ } ) ;
341+ } ) ;
342+
343+ return [ clients , servers ] ;
344+ }
345+
346+ async function verifyVersions ( localVersionsMap : Map < string , VersionManifest > , remoteVersionsMap : Map < string , OmniVersionManifest > , spreadsheetPromise : Promise < [ clients : Set < string > , servers : Set < string > ] > ) {
347+ const [ spreadsheetClients , spreadsheetServers ] = await spreadsheetPromise ;
348+
349+ const logServerResult = ( version : string , isCorrect : boolean , renamedServerMap : Map < LocalId , IndexOriginalId > ) => {
350+ const mappedVersion = renamedServerMap . get ( version ) ?? version ;
351+ const exists = spreadsheetServers . has ( mappedVersion ) ;
352+
353+ const correctString = `${ version } is ${ isCorrect ? 'correct' : 'incorrect' } ` ;
354+ const isRenamed = version !== mappedVersion ;
355+ const existsString = `${ version } ${ exists ? `exists${ isRenamed ? ` as ${ mappedVersion } ` : '' } ` : 'does not exist' } ` ;
356+
357+ const passingArgs = [ `${ isRenamed ? '%c' : '' } ${ correctString } , ${ existsString } ` ] ;
358+ if ( isRenamed ) passingArgs . push ( 'color: blue' ) ;
359+ isCorrect && exists ? console . log ( ...passingArgs ) : console . warn ( `%c-----> ${ correctString } , ${ existsString } <-----` , 'color: red' ) ;
360+ } ;
361+
362+ const standaloneServerMap = new Map ( localVersionsMap . entries ( ) . filter ( ( value ) => standaloneSevers . includes ( value [ 0 ] ) ) ) ;
363+ if ( standaloneServerMap . size !== standaloneSevers . length ) console . warn ( '%cMap size mismatch!' , 'color: red' ) ;
364+
365+ console . log ( 'Standalone server check:' ) ;
366+ standaloneServerMap . forEach ( ( local , version ) => {
367+ const correct = remoteVersionsMap . values ( ) . some ( ( remote ) => {
368+ const remoteServer = remote . downloads ! . server ;
369+ return remoteServer && ( local . downloads ! . server ?? local . downloads ! . server_zip ) . sha1 === remoteServer . sha1
370+ } ) ;
371+
372+ logServerResult ( version , correct , renamedStandaloneServers ) ;
373+ } ) ;
374+
375+ console . log ( ) ;
376+
377+ const orphanServerMap = new Map ( localVersionsMap . entries ( ) . filter ( ( value ) => orphanServers . includes ( value [ 0 ] ) ) ) ;
378+ if ( orphanServerMap . size !== orphanServers . length ) console . warn ( '%cMap size mismatch!' , 'color: red' ) ;
379+
380+ console . log ( 'Orphan server check:' ) ;
381+ orphanServerMap . forEach ( ( local , version ) => {
382+ const correct = ! remoteVersionsMap . values ( ) . some ( ( remote ) => {
383+ const remoteServer = remote . downloads ! . server ;
384+ return remoteServer && ( local . downloads ! . server ?? local . downloads ! . server_zip ) . sha1 === remoteServer . sha1 ;
385+ } ) ;
386+
387+ logServerResult ( version , correct , renamedOrphanServers )
388+ } ) ;
389+
390+ console . log ( ) ;
391+
392+ const downloadTypes = new Set < string > ( ) ;
393+ localVersionsMap . forEach ( ( version ) => {
394+ Object . keys ( version . downloads ! ) . forEach ( ( key ) => downloadTypes . add ( key ) ) ;
395+ } ) ;
396+
397+ console . log ( `All download types: ${ downloadTypes . values ( ) . toArray ( ) } ` ) ;
398+
399+ console . log ( ) ;
400+
401+ const mirrorlessVersionMap = new Map ( localVersionsMap . entries ( ) . filter ( ( value ) => mirrorlessRenameMap . has ( value [ 0 ] ) ) ) ;
402+ if ( mirrorlessVersionMap . size !== mirrorlessRenameMap . size ) console . warn ( '%cMap size mismatch!' , 'color: red' ) ;
403+
404+ console . log ( 'Mirrorless version rename check:' ) ;
405+ mirrorlessVersionMap . forEach ( ( local , version ) => {
406+ // let correct = !localVersionsMap.values().some((otherLocal) => {
407+ // return otherLocal.id !== version && Object.values(local.downloads!).some((localDownload) => {
408+ // return Object.values(otherLocal.downloads!).some((otherLocalDownload) => localDownload.url === otherLocalDownload.url);
409+ // });
410+ // });
411+
412+ const remoteVersion = mirrorlessRenameMap . get ( version ) ! ;
413+ const correct = ! localVersionsMap . has ( remoteVersion ) && remoteVersionsMap . has ( remoteVersion ) && Object . entries ( local . downloads ! ) . every ( ( localDownload ) => {
414+ const localDownloadSide = localDownload [ 1 ] ;
415+ const remoteDownloadSide = remoteVersionsMap . get ( remoteVersion ) ! . downloads ! [ localDownload [ 0 ] ] ;
416+ return remoteDownloadSide && remoteDownloadSide . sha1 === localDownloadSide . sha1 /*&& remoteDownloadSide.url === localDownloadSide.url*/ ;
417+ } ) ;
418+
419+ correct ? console . log ( `${ version } is correct` ) : console . warn ( `%c-----> ${ version } is incorrect <-----` , 'color: red' ) ;
420+ } ) ;
421+
422+ console . log ( ) ;
423+
424+ const mirrorVersionMap = new Map ( localVersionsMap . entries ( ) . filter ( ( value ) => mirrorMap . has ( value [ 0 ] ) || reverseMirrorMap . has ( value [ 0 ] ) ) ) ;
425+ if ( mirrorVersionMap . size !== mirrorMap . size + reverseMirrorMap . size ) console . warn ( '%cMap size mismatch!' , 'color: red' ) ;
426+
427+ console . log ( 'Mirror/Reverse Mirror version check:' ) ;
428+ mirrorVersionMap . forEach ( ( local1 , version ) => {
429+ const mirroredVersion = mirrorMap . get ( version ) ?? reverseMirrorMap . get ( version ) ! ;
430+ const correct = localVersionsMap . has ( mirroredVersion ) && Object . entries ( local1 . downloads ! ) . every ( ( local1Download ) => {
431+ const local1DownloadInfo = local1Download [ 1 ] ;
432+ const local2DownloadInfo = localVersionsMap . get ( mirroredVersion ) ! . downloads ! [ local1Download [ 0 ] ] ;
433+ return local2DownloadInfo && Object . entries ( local2DownloadInfo ) . every ( ( entry ) => entry [ 1 ] === local1DownloadInfo [ entry [ 0 ] as keyof DownloadInfo ] ) ;
434+ } ) ;
435+
436+ correct ? console . log ( `${ version } is correct` ) : console . warn ( `%c-----> ${ version } is incorrect <-----` , 'color: red' ) ;
437+ } ) ;
438+
439+ console . log ( ) ;
440+
441+ const mergedMirrorVersionMap = new Map ( localVersionsMap . entries ( ) . filter ( ( value ) => mergedMirrorMap . has ( value [ 0 ] ) ) ) ;
442+ if ( mergedMirrorVersionMap . size !== mergedMirrorMap . size ) console . warn ( '%cMap size mismatch!' , 'color: red' ) ;
443+
444+ console . log ( 'Merged mirror version check:' ) ;
445+ mergedMirrorVersionMap . forEach ( ( merged , version ) => {
446+ const [ originalClient , originalServer ] = mergedMirrorMap . get ( version ) ! ;
447+ const correct = localVersionsMap . has ( originalClient ) && localVersionsMap . has ( originalServer ) && Object . entries ( merged . downloads ! ) . every ( ( mergedDownload ) => {
448+ const [ downloadType , downloadInfo ] = mergedDownload ;
449+ const originalDownloadInfo = localVersionsMap . get ( downloadType . includes ( 'client' ) ? originalClient : originalServer ) ! . downloads ! [ downloadType ] ;
450+ return originalDownloadInfo && Object . entries ( originalDownloadInfo ) . every ( ( entry ) => entry [ 1 ] === downloadInfo [ entry [ 0 ] as keyof DownloadInfo ] ) ;
451+ } ) ;
452+
453+ correct ? console . log ( `${ version } is correct` ) : console . warn ( `%c-----> ${ version } is incorrect <-----` , 'color: red' ) ;
454+ } ) ;
455+
456+ console . log ( ) ;
457+
458+ const weirdMergeVersionMap = new Map ( localVersionsMap . entries ( ) . filter ( ( value ) => weirdMergeMap . has ( value [ 0 ] ) ) ) ;
459+ if ( weirdMergeVersionMap . size !== weirdMergeMap . size ) console . warn ( '%cMap size mismatch!' , 'color: red' ) ;
460+
461+ console . log ( 'Weird merge version check:' ) ;
462+ weirdMergeVersionMap . forEach ( ( weirdMerge , version ) => {
463+ const [ remoteClient , indexServer ] = weirdMergeMap . get ( version ) ! ;
464+ const correct = remoteVersionsMap . has ( remoteClient ) && spreadsheetServers . has ( indexServer ) && Object . entries ( weirdMerge . downloads ! ) . every ( ( weirdMergeDownload ) => {
465+ const [ downloadType , downloadInfo ] = weirdMergeDownload ;
466+ if ( downloadType . includes ( 'client' ) ) {
467+ const clientDownloadInfo = remoteVersionsMap . get ( remoteClient ) ! . downloads ! [ downloadType ] ;
468+ return clientDownloadInfo && Object . entries ( clientDownloadInfo ) . every ( ( entry ) => entry [ 1 ] === downloadInfo [ entry [ 0 ] as keyof DownloadInfo ] ) ;
469+ } else return ! remoteVersionsMap . values ( ) . some ( ( remote ) => Object . values ( remote . downloads ! ) . some ( ( download ) => download . url === downloadInfo . url ) ) ;
470+ } ) ;
471+
472+ correct ? console . log ( `${ version } is correct` ) : console . warn ( `%c-----> ${ version } is incorrect <-----` , 'color: red' ) ;
473+ } ) ;
474+
475+ console . log ( ) ;
476+
477+ const allOtherVersionsMap = new Map ( localVersionsMap . entries ( ) . filter ( ( value ) => shouldSkip ( value [ 0 ] ) ) ) ;
478+
479+ console . log ( 'All other versions check (correct versions will be skipped):' ) ;
480+ let mismatches = 0 ;
481+ const unindexedClients : Set < VersionId > = new Set ( ) ;
482+ const unindexedServers : Set < VersionId > = new Set ( ) ;
483+ allOtherVersionsMap . forEach ( ( manifest , version ) => {
484+ const correct = remoteVersionsMap . has ( version ) && Object . entries ( manifest . downloads ! ) . every ( ( localDownload ) => {
485+ const localDownloadInfo = localDownload [ 1 ] ;
486+ const downloadType = localDownload [ 0 ] ;
487+ if ( downloadType === 'client' && ! spreadsheetClients . has ( version ) ) unindexedClients . add ( version ) ;
488+ if ( downloadType === 'server' && ! spreadsheetServers . has ( version ) ) unindexedServers . add ( version ) ;
489+ const remoteDownloadInfo = remoteVersionsMap . get ( version ) ! . downloads ! [ downloadType ] ;
490+ return remoteDownloadInfo && localDownloadInfo . sha1 === remoteDownloadInfo . sha1 ;
491+ } ) ;
492+
493+ if ( ! correct ) {
494+ const message = [ `%c${ version } is %s` ] ;
495+
496+ if ( exemptOtherVersions . has ( version ) ) {
497+ message . push ( 'color: orange' ) ;
498+ message . push ( `exempt, ${ exemptOtherVersions . get ( version ) ! } ` ) ;
499+ } else {
500+ unindexedClients . delete ( version ) ;
501+ unindexedServers . delete ( version ) ;
502+ message . push ( 'color: red' ) ;
503+ message . push ( `incorrect` ) ;
504+ mismatches ++ ;
505+ }
506+
507+ console . warn ( ...message ) ;
508+ }
509+ } ) ;
510+
511+ console . log ( `There are ${ mismatches } incorrect versions` ) ;
512+
513+ console . log ( ) ;
514+ unindexedClients . forEach ( ( client ) => console . warn ( `%c${ client } is correct, but not on the client index!` , 'color: magenta' ) ) ;
515+
516+ console . log ( ) ;
517+ unindexedServers . forEach ( ( server ) => console . warn ( `%c${ server } is correct, but not on the server index!` , 'color: magenta' ) ) ;
518+ }
519+
309520function checkLocalManifestEntries ( manifest : MainManifest , versionJsons : Map < string , VersionManifest > , detailsJsons : Map < string , VersionData > ) {
310521 // log manifest entries with inconsistent references to version jsons and/or details jsons
311522 for ( let i = 0 ; i < manifest . versions . length ; i ++ ) {
@@ -379,14 +590,18 @@ if (import.meta.main) (async () => {
379590 return await updateAndCacheExternalVersionJsons ( remoteManifestJson ) ;
380591 } ) ( ) ;
381592
593+ const sheetsApi = new Sheets ( auth . fromJSON ( JSON . parse ( await Deno . readTextFile ( 'google-service-account.json' ) ) ) ) ;
594+ const spreadsheetPromise = readSpreadsheetVersions ( sheetsApi . spreadsheetsGet ( '1OCxMNQLeZJi4BlKKwHx2OlzktKiLEwFXnmCrSdAFwYQ' , { includeGridData : true } ) ) ;
595+
382596 console . log ( 'Locally stored manifests loaded' ) ;
383597
384598 while ( true ) {
385599 console . log ( '\nWelcome to the new version manifest update and compare tool!' ) ;
386600 console . log ( '1: Validate the main manifest and the local version jsons and details json' ) ;
387601 console . log ( '2: Update sha1 hashes in the main versions manifest' ) ;
388602 console . log ( '3: Compare local manifests with external (Omniarchive) manifests' ) ;
389- console . log ( '4: Update and cache external (Omniarchive) manifests' ) ;
603+ console . log ( '4: Verify if lists and maps of modified version IDs are correct' ) ;
604+ console . log ( '5: Update and cache external (Omniarchive) manifests' ) ;
390605 console . log ( 'E: Exit' ) ;
391606 const option = prompt ( 'Choose an option: ' ) ;
392607 console . log ( ) ;
@@ -401,7 +616,10 @@ if (import.meta.main) (async () => {
401616 case '3' :
402617 compareLocalWithOmniarchive ( localVersionJsonsMap , remoteVersionJsonsMap ) ;
403618 break ;
404- case '4' : {
619+ case '4' :
620+ await verifyVersions ( localVersionJsonsMap , remoteVersionJsonsMap , spreadsheetPromise ) ;
621+ break ;
622+ case '5' : {
405623 const confirmation = confirm ( 'Are you sure? This will take a while' ) ;
406624 console . log ( ) ;
407625 if ( confirmation ) remoteVersionJsonsMap = await updateAndCacheExternalVersionJsons ( remoteManifestJson ) ;
0 commit comments