From 638c52576b8023b6aaa7656d38b667fe4bd2707e Mon Sep 17 00:00:00 2001 From: Taka Date: Fri, 29 May 2020 10:52:32 +0000 Subject: [PATCH] Updated snippet ID's to include their owning player nationality. --- vasl_templates/webapp/static/snippets.js | 11 +- vasl_templates/webapp/static/vassal.js | 13 +- .../tests/fixtures/update-vsav/full.vsav | Bin 5462 -> 6046 bytes .../tests/fixtures/update-vsav/latw.vsav | Bin 2538 -> 2994 bytes .../player-owned-labels-legacy.vsav | Bin 0 -> 2431 bytes .../update-vsav/player-owned-labels.vsav | Bin 0 -> 2444 bytes .../vo-notes/allied-minor/ordnance/6.png | Bin 0 -> 1339 bytes .../vo-notes/allied-minor/vehicles/17.png | Bin 0 -> 1339 bytes .../fixtures/vo-notes/american/ordnance/2.png | Bin 0 -> 1339 bytes .../fixtures/vo-notes/american/vehicles/1.png | Bin 0 -> 1339 bytes .../vo-notes/american/vehicles/c.html | 1 + vasl_templates/webapp/tests/test_vassal.py | 282 ++++++++++++------ vasl_templates/webapp/vassal.py | 12 +- vassal-shim/release/vassal-shim.jar | Bin 28324 -> 29286 bytes vassal-shim/src/vassal_shim/AppBoolean.java | 17 ++ vassal-shim/src/vassal_shim/VassalShim.java | 83 +++++- 16 files changed, 300 insertions(+), 119 deletions(-) create mode 100644 vasl_templates/webapp/tests/fixtures/update-vsav/player-owned-labels-legacy.vsav create mode 100644 vasl_templates/webapp/tests/fixtures/update-vsav/player-owned-labels.vsav create mode 100644 vasl_templates/webapp/tests/fixtures/vo-notes/allied-minor/ordnance/6.png create mode 100644 vasl_templates/webapp/tests/fixtures/vo-notes/allied-minor/vehicles/17.png create mode 100644 vasl_templates/webapp/tests/fixtures/vo-notes/american/ordnance/2.png create mode 100644 vasl_templates/webapp/tests/fixtures/vo-notes/american/vehicles/1.png create mode 100644 vasl_templates/webapp/tests/fixtures/vo-notes/american/vehicles/c.html create mode 100644 vassal-shim/src/vassal_shim/AppBoolean.java diff --git a/vasl_templates/webapp/static/snippets.js b/vasl_templates/webapp/static/snippets.js index 62eb28b..5b03012 100644 --- a/vasl_templates/webapp/static/snippets.js +++ b/vasl_templates/webapp/static/snippets.js @@ -152,13 +152,14 @@ function make_snippet( $btn, params, extra_params, show_date_warnings ) // set player-specific parameters var player_no = get_player_no_for_element( $btn ) ; + var player_nat = get_player_nat( player_no ) ; if ( player_no ) { params.PLAYER_NAME = get_nationality_display_name( params["PLAYER_"+player_no] ) ; var colors = get_player_colors( player_no ) ; params.OB_COLOR = colors[0] ; params.OB_COLOR_2 = colors[2] ; if ( gUserSettings["include-flags-in-snippets"] ) - params.PLAYER_FLAG = make_player_flag_url( get_player_nat(player_no), true ) ; + params.PLAYER_FLAG = make_player_flag_url( player_nat, true ) ; } // set the snippet ID @@ -171,6 +172,8 @@ function make_snippet( $btn, params, extra_params, show_date_warnings ) params.SNIPPET_ID = template_id + "." + data.id ; } else params.SNIPPET_ID = template_id ; + if ( player_nat ) + params.SNIPPET_ID = player_nat + "/" + params.SNIPPET_ID ; // set the vehicle/ordnance labels if ( template_id.indexOf( "_vehicles_" ) !== -1 ) { @@ -733,8 +736,10 @@ function _make_snippet_image_filename( snippet ) if ( ! snippet.save_name ) { // no save filename was specified, generate one automatically fname = snippet.snippet_id ; - if ( fname.substr( 0, 7 ) === "extras/" ) - fname = fname.substr( 7 ) ; + // strip off "extras/" and owning player nationalities + var pos = fname.indexOf( "/" ) ; + if ( pos >= 0 ) + fname = fname.substr( pos+1 ) ; fname = fname.replace( /_|-/g, " " ) ; // handle characters that are not allowed in filenames fname = fname.replace( /:|\||\//g, "-" ) ; diff --git a/vasl_templates/webapp/static/vassal.js b/vasl_templates/webapp/static/vassal.js index 680d086..1451381 100644 --- a/vasl_templates/webapp/static/vassal.js +++ b/vasl_templates/webapp/static/vassal.js @@ -11,7 +11,12 @@ function _do_update_vsav( vsav_data, fname ) var snippets = _generate_snippets() ; // send a request to update the VSAV - var data = { filename: fname, vsav_data: vsav_data, snippets: snippets } ; + var data = { + filename: fname, + vsav_data: vsav_data, + players: [ get_player_nat(1), get_player_nat(2) ], + snippets: snippets + } ; $.ajax( { url: gUpdateVsavUrl, type: "POST", @@ -111,9 +116,9 @@ function _generate_snippets() var data ; if ( ["scenario_note","ob_setup","ob_note"].indexOf( template_id ) !== -1 ) { data = $btn.parent().parent().data( "sortable2-data" ) ; - if ( player_no ) + if ( player_no ) { snippet_id = template_id + "_" + player_no + "." + data.id ; - else + } else snippet_id = template_id + "." + data.id ; extra_params = get_simple_note_snippet_extra_params( $btn ) ; } @@ -141,6 +146,8 @@ function _generate_snippets() return ; } } + if ( player_no ) + snippet_id = get_player_nat(player_no) + "/" + snippet_id ; snippets[snippet_id] = { content: make_snippet( $btn, params, extra_params, false ).content, auto_create: ! no_autocreate[template_id] && ! inactive, diff --git a/vasl_templates/webapp/tests/fixtures/update-vsav/full.vsav b/vasl_templates/webapp/tests/fixtures/update-vsav/full.vsav index b3df730028b86fbd0b0c90d3bb46ffafb34ff002..78f96d91ed937ef24e6712f3ac9e8d6c0c139ab7 100644 GIT binary patch delta 5871 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 diff --git a/vasl_templates/webapp/tests/fixtures/update-vsav/latw.vsav b/vasl_templates/webapp/tests/fixtures/update-vsav/latw.vsav index d41feb45cccb8ad566a7047aad42f68375cc0b15..490d8901253b3de61dad24dc323335b7f9fad28e 100644 GIT binary patch delta 2770 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 delta 2328 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 Lp6xKHpV9vT&k9)@ diff --git a/vasl_templates/webapp/tests/fixtures/update-vsav/player-owned-labels-legacy.vsav b/vasl_templates/webapp/tests/fixtures/update-vsav/player-owned-labels-legacy.vsav new file mode 100644 index 0000000000000000000000000000000000000000..9c60e3b076ac887d0c68c955955cb98296b4faa7 GIT binary patch literal 2431 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 literal 0 HcmV?d00001 diff --git a/vasl_templates/webapp/tests/fixtures/update-vsav/player-owned-labels.vsav b/vasl_templates/webapp/tests/fixtures/update-vsav/player-owned-labels.vsav new file mode 100644 index 0000000000000000000000000000000000000000..b41f7f2da8839c46ef0b13c8ddf737efb2e44175 GIT binary patch literal 2444 zcmZ`*c{CJi8=uJ1XhJF?OUSs^G}(=@OB04-vL_5(Ta6_M&hpl<l+bFi! za?z_%**wv_s%2_UgqAZpr_Fl2L`&LZT%mteI+EeZl6j`kXQU*s4|!6AxE)R-{hIK?u%$0b&#z0|!~0prB6Xc$j4T37H=1nTI|{i*qiw z+212ja0d?ZLMx2Qm6WE|UKG5xn&hYx@jMdo>Fd1(;Jm%pdc`!yH%#WFk$z&|!B#vP z&!u*=?|^TAa)`r48!Pj74>ARD9{5no$?3ZDG4FuM?{2n;wTO9x^NL5W)yX)hd=^be z=FIz+-m5j%b6-X_?t^Z@g$%+nK5$8EY4$L3I*InFI#r2Bfht$GLaR$`*6$)N-~Z8{ z(AGrUpgM{~t`eD7O_W0>0$#?dJ+V=r*mjrDhGr!YnT{;T^4&0J=-H+Zk0D<|rUICX zNX_EXAeebLeP@)t<|=7<_@wC>3WTH^VRDgJ6Q;FkTOt*03+4D>7VNFTGa2ESN-)pT zrKV!?oJ&JQ(1AuUOpSPoeyC{1GnJ7y;MuWK?6RQhQ* zj>XsAdg%>m;IOhE!Y(I2h?>u7I0X`jE18p98{ooeNU_{NPd|~~yT+h@5^>mS9B4Fa zwQa3AKZJCFlPWo6UA6n?j6&o)BoasE!6SX46dkN@j6R zp1DI$p}xcwnS;IP4t8a&)xu}euz4f&{nnEjy_cr_;R!M^r{$jFzYB`d_^-rAxySPf zIX@lOmSc`=n^g*Zm~1_q) z>N;zN9Jx3e9dl1ZS=BUa8NBMK%nmpe$-Jq%7!~rlq}MfPy_pfo%4Qmtry$G85sQ)| zzCoQjm!2#qMb5>?Kiu}4?VZ{hpklWx^At{*CHFEdjgHiz8of<>yAdc zXf1EKcIN81t&+DnOG2V_Z`TI+{)spfeNs?|-^J^ltN^YU zvIVMtmLK&+`pgt4tK3@e56n{oOQ@;%QEiABBsI9;4xf4yNZr7mnL&!ZiL$OR10kOk zn%lmA)wTrS_& zQfMcvMqLAw-L9}YSF$in4WNg=X65s@=N>RDv7E4ho`Tz?E11lbjbk>`g<0Mdmxs(r z)iPA$A8YkoehtNYgvf9@1POC}vA{EaQ!fn)~CZ=RH+gMX{n>3g- zY?Dk;ZMoNpYP3dW&j+`p&P1?|qlI&pJX_Pn*-DwNsJjHM7dX(j$5zr`vFxVo$iXst z?WB0nw)>LUD|StP*Rge{KjE>>^~Nz$<`Q`s#qZ)=WW%%v2M_*CeW9o~@lvfBCFB#` zjo&^*XS;Z|2mV-uAbKqq$*R`ixQE44`f?2AeSg(1<-%{F1AUNXAAM^+u9W){XRK1} zNilB&;I|(Y9RsJ2y3P1nF=I8FJ^K)t=+o#AHHmMd{qspzK@fH7>xB_BziPsFHY7g!nFv+Y8pW0Q*nNsN%8EH}EqQR0#0S|+8J@w1cf`=WaZX1@Qm? z;Sc}-@vk$K_rEjLJ=ndfqP(TkMjo!L(ADw2%I-`b1Yvuw6W(Rr+5xY%1A=b!5)qZP z6H}EI7D^T-wnh7oTtvLO1soWhZMuBr%PZQ~ue9vbFYZ_5KFBSoOVKCDUq0F0*Ede9 z>^q`sd=btWQm)#=~n#m@f_46;if)|6M9@H|ihg8RF;V zxhvK#t^H8^X#rD~+_rHq8E*iv0Af=VLw#66I%<~rKH;?YwNhroBigJ7$?~o)a8xyh zVQCgk|HH6%^?X*OS>UJkxeG}@!vuh415+w=6s6YZ?S3l>zy$bhn90B}VX?}0i-b(ZT_}_x_PulOM?RD`N pOm@5eH*MFZf3kiTvS;WoEbX#>*^7X4aqq=AcMpE|?fE;!e*g&&Z1VsB literal 0 HcmV?d00001 diff --git a/vasl_templates/webapp/tests/fixtures/vo-notes/allied-minor/ordnance/6.png b/vasl_templates/webapp/tests/fixtures/vo-notes/allied-minor/ordnance/6.png new file mode 100644 index 0000000000000000000000000000000000000000..62b26786bb3c169f871ad6e5fa6f6712e0ee5a5f GIT binary patch literal 1339 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\"'\\)", - "players": re.compile( r"Russian:.*German:" ), + "players": re.compile( r"American:.*Belgian:" ), "victory_conditions": "Just do it!", "ssr": re.compile( r"Modified SSR #1.*Modified SSR #2" ), "scenario_note.1": "Modified scenario note #1", "scenario_note.2": "Modified scenario note #2", - "ob_setup_1.1": "Modified Russian setup #1", "ob_setup_1.2": "Modified Russian setup #2", - "ob_setup_1.3": "Modified Russian setup #3", "ob_setup_1.4": "Modified Russian setup #4", - "ob_setup_1.5": "Modified Russian setup #5", - "ob_note_1.1": "Modified Russian note #1", - "ob_vehicles_1": "T-34/85", - "ob_ordnance_1": "82mm BM obr. 37", - "ob_setup_2.1": "Modified German setup #1", - "ob_note_2.1": "Modified German note #1", "ob_note_2.2": "Modified German note #2", - "ob_note_2.3": "Modified German note #3", "ob_note_2.4": "Modified German note #4", - "ob_note_2.5": "Modified German note #5", - "pf": "Panzerfaust", "atmm": "Anti-Tank Magnetic Mines", - "ob_vehicles_2": "PzKpfw VG", - "ob_ordnance_2": "3.7cm PaK 35/36", + "american/ob_setup_1.1": "Modified American setup #1", + "american/ob_setup_1.2": "Modified American setup #2", + "american/ob_setup_1.3": "Modified American setup #3", + "american/ob_setup_1.4": "Modified American setup #4", + "american/ob_setup_1.5": "Modified American setup #5", + "american/ob_note_1.1": "Modified American note #1", + "american/ob_vehicles_1": "M2A4", + "american/ob_ordnance_1": "M19 60mm Mortar", + "american/baz": "Bazooka", + "belgian/ob_setup_2.1": "Modified Belgian setup #1", + "belgian/ob_note_2.1": "Modified Belgian note #1", + "belgian/ob_note_2.2": "Modified Belgian note #2", + "belgian/ob_note_2.3": "Modified Belgian note #3", + "belgian/ob_note_2.4": "Modified Belgian note #4", + "belgian/ob_note_2.5": "Modified Belgian note #5", + "belgian/ob_vehicles_2": "T-15(b)", + "belgian/ob_ordnance_2": "DBT", } if enable_vo_notes: - expected[ "ob_vehicle_note_1.1" ] = re.compile( - r'T-34/85.*' + expected[ "american/ob_vehicle_note_1.1" ] = re.compile( + r'M2A4.*' ) - expected[ "ob_vehicles_ma_notes_1" ] = "J: Unavailable." - expected[ "ob_ordnance_note_2.1" ] = re.compile( - r'3.7cm PaK 35/36.*' + expected[ "american/ob_vehicles_ma_notes_1" ] = re.compile( + "B: Unavailable." + ".*" \ + "C: American Multi-Applicable Vehicle Note \"C\"." + ".*" \ + "P: Unavailable." ) - expected[ "ob_vehicles_ma_notes_2" ] = "H: Unavailable." - expected[ "ob_ordnance_ma_notes_2" ] = re.compile( - r"B: German Multi-Applicable Ordnance Note \"B\"." + ".*" \ - r"N: Unavailable." + ".*" \ - r"P: Unavailable." + expected[ "american/ob_ordnance_note_1.1" ] = re.compile( + r'M19 60mm Mortar.*' + ) + expected[ "american/ob_ordnance_ma_notes_1" ] = "F: Unavailable." + expected[ "belgian/ob_vehicle_note_2.1" ] = re.compile( + r'T-15\(b\)..*' + ) + expected[ "belgian/ob_vehicles_ma_notes_2" ] = \ + "A: Allied Minor Multi-Applicable Vehicle Note \"A\"." + expected[ "belgian/ob_ordnance_note_2.1" ] = re.compile( + r'DBT.*' + ) + expected[ "belgian/ob_ordnance_ma_notes_2" ] = re.compile( + r"A: Allied Minor Multi-Applicable Ordnance Note \"A\"." + ".*" \ + r"B: Unavailable." + ".*" \ + r"D: Unavailable." ) _check_vsav_dump( updated_vsav_dump, expected ) + # update the VASL scenario again (nothing should change) updated_vsav_data = _update_vsav( temp_file.name, {} ) assert updated_vsav_data == b"No changes." @@ -216,7 +247,7 @@ def test_latw_autocreate( webapp, webdriver ): } ) updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 4 } ) _check_vsav_dump( updated_vsav_dump, { - "pf": "Panzerfaust", + "german/pf": "Panzerfaust", }, ignore_labels ) # update the scenario (German/Russian, JAN/44) @@ -225,7 +256,7 @@ def test_latw_autocreate( webapp, webdriver ): } ) updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 5 } ) _check_vsav_dump( updated_vsav_dump, { - "pf": "Panzerfaust", "atmm": "ATMM check:", + "german/pf": "Panzerfaust", "german/atmm": "ATMM check:", }, ignore_labels ) # update the scenario (British/American, no date) @@ -272,10 +303,10 @@ def test_latw_update( webapp, webdriver ): fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/latw.vsav" ) vsav_dump = _dump_vsav( fname ) _check_vsav_dump( vsav_dump, { - "psk": "Panzerschrek", "atmm": "ATMM check:", # nb: the PF label has no snippet ID - "mol-p": "TH#", # nb: the MOL label has no snippet ID - "piat": "TH#", - "baz": "Bazooka", + "german/psk": "Panzerschrek", "german/atmm": "ATMM check:", # nb: the PF label has no snippet ID + "russian/mol-p": "TH#", # nb: the MOL label has no snippet ID + "british/piat": "TH#", + "american/baz": "Bazooka", }, ignore_labels ) # update the scenario (German/Russian, no date) @@ -283,22 +314,26 @@ def test_latw_update( webapp, webdriver ): # NOTE: We changed the MOL-P template (to add custom list bullets), so the snippet is different # to when this test was originally written, and so #updated changed from 2 to 3. # NOTE: Same thing happened when we factored out the common CSS into common.css :-/ Sigh... - updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 3, "updated": 5, "deleted": 2 } ) + updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 3, "updated": 5 } ) _check_vsav_dump( updated_vsav_dump, { - "pf": "Panzerfaust", "psk": "Panzerschrek", "atmm": "ATMM check:", # nb: the PF label now has a snippet ID - "mol": "Kindling Attempt:", "mol-p": "TH#", # nb: the MOL label now has a snippet ID - # nb: the PIAT and BAZ labels are now gone + "german/pf": "Panzerfaust", # nb: the PF label now has a snippet ID + "german/psk": "Panzerschrek", "german/atmm": "ATMM check:", + "russian/mol": "Kindling Attempt:", "russian/mol-p": "TH#", # nb: the MOL label now has a snippet ID + # NOTE: We used to delete the PIAT and BAZ labels, but this no longer happens with player-owned labels. + "british/piat": "TH#", "american/baz": "Bazooka", }, ignore_labels ) # update the scenario (British/American, DEC/1943) load_scenario_params( { "scenario": { "PLAYER_1": "british", "PLAYER_2": "american", "SCENARIO_DATE": "12/31/1943" } } ) - updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 3, "updated": 2, "deleted": 3 } ) + updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 3, "updated": 2 } ) _check_vsav_dump( updated_vsav_dump, { - # nb: the PSK/ATMM and MOL-P label are now gone - "piat": "TH#", - "baz": "Bazooka ('43)", # nb: this has changed from '45 + # NOTE: We used to delete the PSK/ATMM/MOL-P labels, but this no longer happens with player-owned labels. + "german/psk": "Panzerschrek", "german/atmm": "ATMM check:", + "russian/mol-p": "TH#", # nb: the MOL label now has a snippet ID + "british/piat": "TH#", + "american/baz": "Bazooka ('43)", # nb: this has changed from '45 }, ignore_labels ) # run the test @@ -379,28 +414,29 @@ def test_update_legacy_labels( webapp, webdriver ): "victory_conditions": "five Level 3 hill hexes", "ssr": re.compile( r"no wind at start.*must take a TC" ), "scenario_note.1": "Download the scenario card", - "ob_setup_1.1": "whole hex of Board 3", - "ob_setup_1.2": "Enter on Turn 2", "ob_setup_1.3": "Enter on Turn 5", - "ob_vehicles_1": re.compile( r"T-34 M43.*SU-152.*SU-122.*ZIS-5" ), - "ob_setup_2.1": "whole hex of Board 4", - "ob_setup_2.2": "Enter on Turn 1", "ob_setup_2.3": "Enter on Turn 2", "ob_setup_2.4": "Enter on Turn 4", - "ob_setup_2.5": "Enter on Turn 5", "ob_setup_2.6": "Enter on Turn 8", - "ob_note_2.1": "80+mm Battalion Mortar", - "ob_note_2.2": "100+mm OBA", - "ob_vehicles_2": re.compile( + "russian/ob_setup_1.1": "whole hex of Board 3", + "russian/ob_setup_1.2": "Enter on Turn 2", "russian/ob_setup_1.3": "Enter on Turn 5", + "russian/ob_vehicles_1": re.compile( r"T-34 M43.*SU-152.*SU-122.*ZIS-5" ), + "german/ob_setup_2.1": "whole hex of Board 4", + "german/ob_setup_2.2": "Enter on Turn 1", "german/ob_setup_2.3": "Enter on Turn 2", + "german/ob_setup_2.4": "Enter on Turn 4", "german/ob_setup_2.5": "Enter on Turn 5", + "german/ob_setup_2.6": "Enter on Turn 8", + "german/ob_note_2.1": "80+mm Battalion Mortar", + "german/ob_note_2.2": "100+mm OBA", + "german/ob_vehicles_2": re.compile( r"PzKpfw IVH.*PzKpfw IIIN.*StuG IIIG \(L\).*StuH 42.*SPW 250/1.*SPW 251/1.*SPW 251/sMG" ), - "ob_ordnance_2": re.compile( r"7.5cm PaK 40.*5cm PaK 38" ), - "pf": "Panzerfaust", "atmm": "Anti-Tank Magnetic Mines", + "german/ob_ordnance_2": re.compile( r"7.5cm PaK 40.*5cm PaK 38" ), + "german/pf": "Panzerfaust", "german/atmm": "Anti-Tank Magnetic Mines", } if enable_vo_notes: - expected[ "ob_vehicle_note_1.1" ] = re.compile( + expected[ "russian/ob_vehicle_note_1.1" ] = re.compile( r'T-34 M43.*' ) - expected[ "ob_ordnance_note_2.2" ] = re.compile( + expected[ "german/ob_ordnance_note_2.2" ] = re.compile( r'5cm PaK 38.*' ) - expected[ "ob_vehicles_ma_notes_2" ] = re.compile( + expected[ "german/ob_vehicles_ma_notes_2" ] = re.compile( r"B: German Multi-Applicable Vehicle Note \"B\"." + ".*" \ r"C: German Multi-Applicable Vehicle Note \"C\"." + ".*" \ r"J: Unavailable." + ".*" \ @@ -410,7 +446,7 @@ def test_update_legacy_labels( webapp, webdriver ): r"Q: Unavailable." + ".*" \ r"S: Unavailable." + ".*" ) - expected["ob_ordnance_ma_notes_2"] = r"N: Unavailable." + expected["german/ob_ordnance_ma_notes_2"] = r"N: Unavailable." _check_vsav_dump( updated_vsav_dump, expected ) # run the test @@ -451,8 +487,8 @@ def test_update_legacy_latw_labels( webapp, webdriver ): } ) updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 3, "updated": 5 } ) _check_vsav_dump( updated_vsav_dump, { - "pf": "Panzerfaust", "psk": "Panzerschrek", "atmm": "ATMM check:", - "mol": "Kindling Attempt:", "mol-p": "TH#", + "german/pf": "Panzerfaust", "german/psk": "Panzerschrek", "german/atmm": "ATMM check:", + "russian/mol": "Kindling Attempt:", "russian/mol-p": "TH#", }, ignore_labels ) labels = _get_vsav_labels( updated_vsav_dump ) # nb: the legacy labels left in place: the scenario comment, and the PIAT/BAZ labels @@ -464,8 +500,8 @@ def test_update_legacy_latw_labels( webapp, webdriver ): } ) updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 3, "updated": 2 } ) _check_vsav_dump( updated_vsav_dump, { - "piat": "PIAT", - "baz": "Bazooka ('45)", + "british/piat": "PIAT", + "american/baz": "Bazooka ('45)", }, ignore_labels ) labels = _get_vsav_labels( updated_vsav_dump ) # nb: the legacy labels left in place: the scenario comment, the PF/PSK/ATMM and MOL/MOL-P labels @@ -475,8 +511,8 @@ def test_update_legacy_latw_labels( webapp, webdriver ): load_scenario_params( { "scenario": { "PLAYER_1": "german", "PLAYER_2": "russian", "SCENARIO_DATE": "" } } ) updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 3, "updated": 5 } ) _check_vsav_dump( updated_vsav_dump, { - "pf": "Panzerfaust", "psk": "Panzerschrek", "atmm": "ATMM check:", - "mol": "Kindling Attempt:", "mol-p": "TH#", + "german/pf": "Panzerfaust", "german/psk": "Panzerschrek", "german/atmm": "ATMM check:", + "russian/mol": "Kindling Attempt:", "russian/mol-p": "TH#", }, ignore_labels ) labels = _get_vsav_labels( updated_vsav_dump ) # nb: the legacy labels left in place: the scenario comment, the PIAT/BAZ labels @@ -486,8 +522,8 @@ def test_update_legacy_latw_labels( webapp, webdriver ): load_scenario_params( { "scenario": { "PLAYER_1": "british", "PLAYER_2": "american", "SCENARIO_DATE": "" } } ) updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 3, "updated": 2 } ) _check_vsav_dump( updated_vsav_dump, { - "piat": "PIAT", - "baz": "Bazooka", + "british/piat": "PIAT", + "american/baz": "Bazooka", }, ignore_labels ) labels = _get_vsav_labels( updated_vsav_dump ) # nb: the legacy labels left in place: the scenario comment, the PF/PSK/ATMM, MOL/MOL-P and BAZ labels @@ -496,6 +532,63 @@ def test_update_legacy_latw_labels( webapp, webdriver ): # run the test _run_tests( control_tests, do_test, False ) +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +@pytest.mark.skipif( not pytest.config.option.vasl_mods, reason="--vasl-mods not specified" ) #pylint: disable=no-member +@pytest.mark.skipif( not pytest.config.option.vassal, reason="--vassal not specified" ) #pylint: disable=no-member +@pytest.mark.skipif( pytest.config.option.short_tests, reason="--short-tests specified" ) #pylint: disable=no-member +def test_player_owned_labels( webapp, webdriver ): + """Test how we update labels owned by different player nationalities.""" + + # initialize + control_tests = init_webapp( webapp, webdriver, vsav_persistence=1, + reset = lambda ct: ct.set_data_dir( dtype="real" ) + ) + + # initialize + load_scenario_params( { + "scenario": { + "SCENARIO_NAME": "Player-owned labels", + "SCENARIO_DATE": "01/01/1940", + "PLAYER_1": "german", + "PLAYER_2": "american", + }, + "ob1": { "OB_SETUPS_1": [ { "caption": "german setup #1" } ] }, + "ob2": { "OB_SETUPS_2": [ { "caption": "american setup #1" } ] }, + } ) + + def do_test(): #pylint: disable=missing-docstring + + # update a legacy scenario (i.e. labels have *not* been tagged with their owner player nationality) + # NOTE: We expect to see 4 labels updated: + # - the 2 OB setup labels (they will get the new-style ID's) + # - scenario (timestamp) + # - players (new American player) + fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/player-owned-labels-legacy.vsav" ) + updated_vsav_dump = _update_vsav_and_dump( fname, { "updated": 4 } ) + _check_vsav_dump( updated_vsav_dump , { + "german/ob_setup_1.1": "german setup #1", + "american/ob_setup_2.1": "american setup #1", + }, ignore=["scenario","players","victory_conditions"] ) + + # update a new-style scenario (i.e. labels *have* been tagged with their owner player nationality) + # NOTE: We expect to see 1 label created: + # - a new American OB setup label + # and 2 labels updated: + # - scenario (timestamp) + # - players (new American player) + # The existing Russian OB setup label should be ignored and left in-place. + fname = os.path.join( os.path.split(__file__)[0], "fixtures/update-vsav/player-owned-labels.vsav" ) + updated_vsav_dump = _update_vsav_and_dump( fname, { "created": 1, "updated": 2 } ) + _check_vsav_dump( updated_vsav_dump , { + "german/ob_setup_1.1": "german setup #1", + "american/ob_setup_2.1": "american setup #1", + "russian/ob_setup_2.1": "russian setup #1", + }, ignore=["scenario","players","victory_conditions"] ) + + # run the test against all versions of VASSAL+VASL + _run_tests( control_tests, do_test, True ) + # --------------------------------------------------------------------- @pytest.mark.skipif( not pytest.config.option.vasl_mods, reason="--vasl-mods not specified" ) #pylint: disable=no-member @@ -814,11 +907,10 @@ def _check_vsav_dump( vsav_dump, expected, ignore=None ): def _get_vsav_labels( vsav_dump ): """Extract the labels from a VSAV dump.""" - # NOTE: We used to see things like: - # Map0;119;44;6295 - # but from 6.5.0, we're getting: - # Main Map;119;44;6295 - matches = re.finditer( r"AddPiece: DynamicProperty/User-Labeled.*?- (Main )?Map", vsav_dump, re.DOTALL ) + matches = re.finditer( r"AddPiece: DynamicProperty/User-Labeled.*?^\s*?(?=[^- ])", + vsav_dump, + re.MULTILINE+re.DOTALL + ) labels = [ mo.group() for mo in matches ] regex = re.compile( r".*?" ) matches = [ regex.search(label) for label in labels ] diff --git a/vasl_templates/webapp/vassal.py b/vasl_templates/webapp/vassal.py index b90ba4c..89444d8 100644 --- a/vasl_templates/webapp/vassal.py +++ b/vasl_templates/webapp/vassal.py @@ -26,13 +26,14 @@ SUPPORTED_VASSAL_VERSIONS_DISPLAY = "3.2.15-.17" # --------------------------------------------------------------------- @app.route( "/update-vsav", methods=["POST"] ) -def update_vsav(): #pylint: disable=too-many-statements +def update_vsav(): #pylint: disable=too-many-statements,too-many-locals """Update labels in a VASL scenario file.""" # parse the request start_time = time.time() vsav_data = request.json[ "vsav_data" ] vsav_filename = request.json[ "filename" ] + players = request.json[ "players" ] snippets = request.json[ "snippets" ] # initialize @@ -59,7 +60,7 @@ def update_vsav(): #pylint: disable=too-many-statements with TempFile() as snippets_file: # save the snippets in a temp file - xml = _save_snippets( snippets, snippets_file, logger ) + xml = _save_snippets( snippets, players, snippets_file, logger ) snippets_file.close() fname = app.config.get( "UPDATE_VSAV_SNIPPETS" ) # nb: for diagnosing problems if fname: @@ -106,7 +107,7 @@ def update_vsav(): #pylint: disable=too-many-statements }, } ) -def _save_snippets( snippets, fp, logger ): #pylint: disable=too-many-locals +def _save_snippets( snippets, players, fp, logger ): #pylint: disable=too-many-locals """Save the snippets in a file. NOTE: We save the snippets as XML because Java :-/ @@ -122,7 +123,12 @@ def _save_snippets( snippets, fp, logger ): #pylint: disable=too-many-locals # will run ridiculously slowly, since we will be launching a new webdriver for each snippet. # We optimize for the case where things work properly... :-/ + # add the player details root = ET.Element( "snippets" ) + ET.SubElement( root, "player1", nat=players[0] ) + ET.SubElement( root, "player2", nat=players[1] ) + + # add the snippets for snippet_id,snippet_info in snippets.items(): # add the next snippet diff --git a/vassal-shim/release/vassal-shim.jar b/vassal-shim/release/vassal-shim.jar index ed55dcf34a4a2ae50177fbde831b7bfbce177194..f8a099dc9255b9ff9231dd4c5314ac07d522a9c5 100644 GIT binary patch delta 15159 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 A&Hw-a delta 14229 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 snippets = parseSnippets( snippetsFilename ) ; + String[] players = new String[2] ; + Map snippets = new HashMap() ; + parseSnippets( snippetsFilename, players, snippets ) ; // load the scenario configureBoards() ; @@ -214,8 +217,39 @@ public class VassalShim // extract the labels from the scenario Map ourLabels = new HashMap() ; ArrayList otherLabels = new ArrayList() ; - logger.info( "Searching the VASL scenario for labels..." ) ; - extractLabels( cmd, ourLabels, otherLabels ) ; + logger.info( "Searching the VASL scenario for labels (players={};{})...", players[0], players[1] ) ; + AppBoolean hasPlayerOwnedLabels = new AppBoolean( false ) ; + extractLabels( cmd, players, hasPlayerOwnedLabels, ourLabels, otherLabels ) ; + + // NOTE: vasl-templates v1.2 started tagging labels with their owning player e.g. "germans/ob_setup_1.1". + // This is so that we can ignore labels owned by nationalities not directly involved in the scenario. + // For example, if it's Germans vs. Americans, the Americans might have borrowed some British tanks, + // and so the save file might contain British labels (for the setup instructions and Chapter H notes). + // If we updated such a scenario, the old code would delete the British labels, since it couldn't tell + // the difference between a British "ob_setup_1.1" label and an American one. But now labels are tagged + // with their nationality, we can process only German and American labels, and ignore the British ones. + // However, if don't see any of these new-style labels, we must be updating an older save file, and so + // we want to retain the old behavior, which means we need to revert the new-style snippet ID's back + // into the old format. + if ( ! hasPlayerOwnedLabels.getVal() ) { + logger.debug( "Converting snippet ID's to legacy format:" ) ; + // locate new-style snippet ID's + ArrayList< String[] > snippetIdsToReplace = new ArrayList< String[] >() ; + Iterator< Map.Entry > iter2 = snippets.entrySet().iterator() ; + while( iter2.hasNext() ) { + String snippetId = iter2.next().getKey() ; + int pos = snippetId.indexOf( "/" ) ; + if ( pos >= 0 ) + snippetIdsToReplace.add( new String[]{ snippetId, snippetId.substring(pos+1) } ) ; + } + // replace the new-style snippet ID's with their old-style version + for ( int i=0 ; i < snippetIdsToReplace.size() ; ++i ) { + String[] snippetIds = snippetIdsToReplace.get( i ) ; + logger.debug( "- {} => {}", snippetIds[0], snippetIds[1] ) ; + snippets.put( snippetIds[1], snippets.get(snippetIds[0]) ) ; + snippets.remove( snippetIds[0] ) ; + } + } // update the labels from the snippets Map< String, ArrayList > labelReport = processSnippets( ourLabels, otherLabels, snippets ) ; @@ -232,17 +266,24 @@ public class VassalShim // any possible problems caused by reusing the current session (e.g. there might be some saved state somewhere). } - private Map parseSnippets( String snippetsFilename ) throws IOException, ParserConfigurationException, SAXException, XPathExpressionException + private void parseSnippets( String snippetsFilename, String[] players, Map snippets ) throws IOException, ParserConfigurationException, SAXException, XPathExpressionException { logger.info( "Loading snippets: {}", snippetsFilename ) ; - Map snippets = new HashMap() ; - // load the snippets + // load the XML DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance() ; DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder() ; Document doc = docBuilder.parse( new File( snippetsFilename ) ) ; doc.getDocumentElement().normalize() ; - NodeList nodes = doc.getElementsByTagName( "snippet" ) ; + + // get the player details + NodeList nodes = doc.getElementsByTagName( "player1" ) ; + players[0] = ((Element)nodes.item(0)).getAttribute( "nat" ) ; + nodes = doc.getElementsByTagName( "player2" ) ; + players[1] = ((Element)nodes.item(0)).getAttribute( "nat" ) ; + + // load the snippets + nodes = doc.getElementsByTagName( "snippet" ) ; for ( int i=0 ; i < nodes.getLength() ; ++i ) { Node node = nodes.item( i ) ; if ( node.getNodeType() != Node.ELEMENT_NODE ) @@ -256,11 +297,9 @@ public class VassalShim ) ; snippets.put( snippet.snippetId, snippet ) ; } - - return snippets ; } - private void extractLabels( Command cmd, Map ourLabels, ArrayList otherLabels ) + private void extractLabels( Command cmd, String[] players, AppBoolean hasPlayerOwnedLabels, Map ourLabels, ArrayList otherLabels ) { // check if this command is a label we're interested in // NOTE: We shouldn't really be looking at the object type, see analyzeScenario(). @@ -279,10 +318,24 @@ public class VassalShim // check if the label is one of ours String snippetId = isVaslTemplatesLabel( fields, GamePieceLabelFields.FIELD_INDEX_LABEL1 ) ; if ( snippetId != null ) { - logger.debug( "- Found label (1): {}", snippetId ) ; - ourLabels.put( snippetId, - new GamePieceLabelFields( target, separators, fields, GamePieceLabelFields.FIELD_INDEX_LABEL1 ) - ) ; + boolean addSnippet = true ; + // check if the label is associated with a player nationality + int pos = snippetId.indexOf( '/' ) ; + if ( pos >= 0 ) { + // yup - the nationality must be one of the 2 passed in to us + String nat = snippetId.substring( 0, pos ) ; + hasPlayerOwnedLabels.setVal( true ) ; + if ( ! nat.equals( players[0] ) && ! nat.equals( players[1] ) ) { + addSnippet = false ; + logger.debug( "- Skipping label: {} (owner={})", snippetId, nat ) ; + } + } + if ( addSnippet ) { + logger.debug( "- Found label (1): {}", snippetId ) ; + ourLabels.put( snippetId, + new GamePieceLabelFields( target, separators, fields, GamePieceLabelFields.FIELD_INDEX_LABEL1 ) + ) ; + } } else { snippetId = isVaslTemplatesLabel( fields, GamePieceLabelFields.FIELD_INDEX_LABEL2 ) ; @@ -302,7 +355,7 @@ public class VassalShim // extract labels in sub-commands for ( Command c: cmd.getSubCommands() ) - extractLabels( c, ourLabels, otherLabels ) ; + extractLabels( c, players, hasPlayerOwnedLabels, ourLabels, otherLabels ) ; } private String isVaslTemplatesLabel( ArrayList fields, int fieldIndex )