diff --git a/Dockerfile b/Dockerfile index 3c7e41c..eaa9371 100644 --- a/Dockerfile +++ b/Dockerfile @@ -6,6 +6,7 @@ # docker run --rm -it --name vasl-templates \ # -p 5010:5010 \ # -v .../vasl-6.4.3.vmod:/data/vasl.vmod \ +# -v .../vasl-extensions:/data/vasl-extensions \ # vasl-templates # If you have Chapter H data, add the following: # -v .../chapter-h-notes:/data/chapter-h-notes diff --git a/conftest.py b/conftest.py index 510a16e..e670713 100644 --- a/conftest.py +++ b/conftest.py @@ -54,6 +54,10 @@ def pytest_addoption( parser ): "--vasl-mods", action="store", dest="vasl_mods", default=None, help="Directory containing the VASL .vmod file(s)." ) + parser.addoption( + "--vasl-extensions", action="store", dest="vasl_extensions", default=None, + help="Directory containing the VASL extensions." + ) # NOTE: Some tests require VASSAL to be installed. This option allows the caller to specify # where it is (multiple installations can be placed in sub-directories). diff --git a/docker/config/debug.cfg b/docker/config/debug.cfg index bbc32f9..42791a0 100644 --- a/docker/config/debug.cfg +++ b/docker/config/debug.cfg @@ -1,3 +1,4 @@ [Debug] -TEST_VASL_MODS = /test-data/vasl-vmods +TEST_VASL_MODS = /test-data/vasl-vmods/ +TEST_VASL_EXTENSIONS_DIR = /test-data/vasl-extensions/ diff --git a/docker/config/site.cfg b/docker/config/site.cfg index 54816ee..c01c6b7 100644 --- a/docker/config/site.cfg +++ b/docker/config/site.cfg @@ -3,4 +3,5 @@ FLASK_HOST = 0.0.0.0 VASL_MOD = /data/vasl.vmod +VASL_EXTENSIONS = /data/vasl-extensions/ CHAPTER_H_NOTES = /data/chapter-h-notes/ diff --git a/vasl_templates/main.py b/vasl_templates/main.py index e65f0c5..843feab 100755 --- a/vasl_templates/main.py +++ b/vasl_templates/main.py @@ -114,7 +114,7 @@ def _do_main( template_pack, default_scenario, remote_debugging, debug ): #pylin # install the server settings try: from vasl_templates.server_settings import install_server_settings #pylint: disable=cyclic-import - install_server_settings() + install_server_settings( True ) except Exception as ex: #pylint: disable=broad-except from vasl_templates.main_window import MainWindow #pylint: disable=cyclic-import MainWindow.showErrorMsg( diff --git a/vasl_templates/server_settings.py b/vasl_templates/server_settings.py index 7cba99d..1b9302a 100644 --- a/vasl_templates/server_settings.py +++ b/vasl_templates/server_settings.py @@ -8,10 +8,10 @@ from PyQt5.QtGui import QIcon from vasl_templates.main import app_settings from vasl_templates.main_window import MainWindow -from vasl_templates.webapp.config.constants import DATA_DIR -from vasl_templates.webapp.vassal import SUPPORTED_VASSAL_VERSIONS_DISPLAY -from vasl_templates.webapp.file_server.vasl_mod import VaslMod, SUPPORTED_VASL_MOD_VERSIONS_DISPLAY -from vasl_templates.webapp.files import install_vasl_mod +from vasl_templates.utils import show_msg_store +from vasl_templates.webapp.vassal import VassalShim, SUPPORTED_VASSAL_VERSIONS_DISPLAY +from vasl_templates.webapp.utils import MsgStore +from vasl_templates.webapp.file_server.vasl_mod import set_vasl_mod, SUPPORTED_VASL_MOD_VERSIONS_DISPLAY # --------------------------------------------------------------------- @@ -122,7 +122,7 @@ class ServerSettingsDialog( QDialog ): # NOTE: We should really do this before saving the new settings, but that's more trouble # than it's worth at this stage... :-/ try: - install_server_settings() + install_server_settings( False ) except Exception as ex: #pylint: disable=broad-except MainWindow.showErrorMsg( "Couldn't install the server settings:\n\n{}".format( ex ) ) return @@ -148,7 +148,7 @@ def _make_exe_filter_string(): # --------------------------------------------------------------------- -def install_server_settings(): +def install_server_settings( is_startup ): """Install the server settings.""" # install the server settings @@ -159,10 +159,20 @@ def install_server_settings(): app.config["JAVA_PATH"] = app_settings.value( "ServerSettings/java-path" ) app.config["WEBDRIVER_PATH"] = app_settings.value( "ServerSettings/webdriver-path" ) + # initialize + if is_startup: + # nb: we let the web page show startup messages + msg_store = None + else: + msg_store = MsgStore() + # load the VASL module fname = app_settings.value( "ServerSettings/vasl-mod" ) - if fname: - vasl_mod = VaslMod( fname, DATA_DIR ) - else: - vasl_mod = None - install_vasl_mod( vasl_mod ) + set_vasl_mod( fname, msg_store ) + + # check the VASSAL version + VassalShim.check_vassal_version( msg_store ) + + # show any messages + if msg_store: + show_msg_store( msg_store ) diff --git a/vasl_templates/utils.py b/vasl_templates/utils.py index 4024e20..51535c4 100644 --- a/vasl_templates/utils.py +++ b/vasl_templates/utils.py @@ -27,3 +27,15 @@ def catch_exceptions( caption="EXCEPTION", retval=None ): return retval return wrapper return decorator + +# --------------------------------------------------------------------- + +def show_msg_store( msg_store ): + """Show messages in a MsgStore.""" + + # NOTE: It would be nice to show a single dialog with all the messages, each one tagged with + # a pretty little icon, but for now, we just show a message box for each message :-/ + from vasl_templates.main_window import MainWindow + for msg_type in ("error","warning"): + for msg in msg_store.get_msgs( msg_type ): + MainWindow.showErrorMsg( msg ) diff --git a/vasl_templates/webapp/data/extensions/kgs-v1.1.json b/vasl_templates/webapp/data/extensions/kgs-v1.1.json new file mode 100644 index 0000000..bd795bf --- /dev/null +++ b/vasl_templates/webapp/data/extensions/kgs-v1.1.json @@ -0,0 +1,21 @@ +{ + +"extensionId": "f97", +"version": "v1.1", + +"vehicles": [ + +{ + "_comment_": "Matilda II(b)", + "id": "ru/v:078", + "gpid": [ "f97:178", "f97:184" ] +}, +{ + "_comment_": "T-60 M40", + "id": "ru/v:004", + "gpid": [ "f97:186" ] +} + +] + +} diff --git a/vasl_templates/webapp/file_server/utils.py b/vasl_templates/webapp/file_server/utils.py index 83871cb..9b3a436 100644 --- a/vasl_templates/webapp/file_server/utils.py +++ b/vasl_templates/webapp/file_server/utils.py @@ -5,28 +5,40 @@ import json # --------------------------------------------------------------------- -def get_vo_gpids( data_dir ): +def get_vo_gpids( data_dir, extns ): #pylint: disable=too-many-locals,too-many-branches """Get the GPID's for the vehicles/ordnance.""" gpids = set() - for vo_type in ("vehicles","ordnance"): - dname = os.path.join( data_dir, vo_type ) + for vo_type in ("vehicles","ordnance"): #pylint: disable=too-many-nested-blocks # process each file + dname = os.path.join( data_dir, vo_type ) for root,_,fnames in os.walk(dname): for fname in fnames: if os.path.splitext(fname)[1] != ".json": continue # load the GPID's from the next file + # NOTE: We originally assumed that GPID's are integers, but the main VASL build file started + # to have non-numeric values, as do, apparently, extensions :-/ For back-compat, we support both. entries = json.load( open( os.path.join(root,fname), "r" ) ) for entry in entries: - if isinstance( entry["gpid"], list): - gpids.update( get_effective_gpid(gpid) for gpid in entry["gpid"] ) - else: - gpids.add( entry["gpid"] ) - - gpids.remove( None ) + entry_gpids = entry[ "gpid" ] + if not isinstance( entry_gpids, list ): + entry_gpids = [ entry_gpids ] + for gpid in entry_gpids: + if gpid: + gpids.add( get_effective_gpid( str(gpid) ) ) + + # process any extensions + if extns: + for extn in extns: + extn_info = extn[1] + for vo_type in ["vehicles","ordnance"]: + if vo_type not in extn_info: + continue + for piece in extn_info[vo_type]: + gpids.update( piece["gpid"] ) return gpids @@ -40,8 +52,8 @@ def get_vo_gpids( data_dir ): # will break. This kind of thing is going to happen again, so we provide a generic mechanism # for dealing with this kind of thing... GPID_REMAPPINGS = { - 7140: 2775, # SdKfz 10/5 - 7146: 2772, # SdKfz 10/4 + "7140": "2775", # SdKfz 10/5 + "7146": "2772", # SdKfz 10/4 } def get_effective_gpid( gpid ): diff --git a/vasl_templates/webapp/file_server/vasl_mod.py b/vasl_templates/webapp/file_server/vasl_mod.py index a878f98..4365d1c 100644 --- a/vasl_templates/webapp/file_server/vasl_mod.py +++ b/vasl_templates/webapp/file_server/vasl_mod.py @@ -1,7 +1,9 @@ -""" Serve files from a VASL module file. """ +""" Wrapper around a VASL module file and extensions. """ import os +import threading import json +import glob import zipfile import re import xml.etree.ElementTree @@ -9,6 +11,8 @@ import xml.etree.ElementTree import logging _logger = logging.getLogger( "vasl_mod" ) +from vasl_templates.webapp import app +from vasl_templates.webapp.config.constants import DATA_DIR from vasl_templates.webapp.file_server.utils import get_vo_gpids, get_effective_gpid SUPPORTED_VASL_MOD_VERSIONS = [ "6.4.0", "6.4.1", "6.4.2", "6.4.3" ] @@ -16,16 +20,150 @@ SUPPORTED_VASL_MOD_VERSIONS_DISPLAY = "6.4.0-6.4.3" # --------------------------------------------------------------------- +# NOTE: The lock only controls access to the _vasl_mod variable, not the VaslMod object it points to. +# In practice this doesn't really matter, since it will be loaded once at startup, then never changes; +# it's only the tests that are constantly changing the underlying object. +_vasl_mod_lock = threading.RLock() +_vasl_mod = None + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +def get_vasl_mod(): + """Return the global VaslMod object.""" + with _vasl_mod_lock: + global _vasl_mod + if _vasl_mod is None: + # check if a VASL module has been configured + # NOTE: We will be doing this check every time someone wants the global VaslMod object, + # even if one hasn't been configured, but in all likelihood, everyone will have it configured, + # in which case, the check will only be done once, and the global _vasl_mod variable set. + fname = app.config.get( "VASL_MOD" ) + if fname: + # yup - load it + from vasl_templates.webapp.main import startup_msg_store #pylint: disable=cyclic-import + set_vasl_mod( fname, startup_msg_store ) + return _vasl_mod + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +def set_vasl_mod( vmod_fname, msg_store ): + """Install a new global VaslMod object.""" + with _vasl_mod_lock: + global _vasl_mod + if vmod_fname: + extns_dir = app.config.get( "VASL_EXTENSIONS_DIR" ) + extns = _load_vasl_extns( extns_dir, msg_store ) + _vasl_mod = VaslMod( vmod_fname, DATA_DIR, extns ) + if _vasl_mod.vasl_version not in SUPPORTED_VASL_MOD_VERSIONS: + msg_store.warning( + "VASL {} is unsupported.
Things might work, but they might not...".format( + _vasl_mod.vasl_version + ) + ) + else: + _vasl_mod = None + +def _load_vasl_extns( extn_dir, msg_store ): #pylint: disable=too-many-locals + """Locate VASL extensions and their corresponding vehicle/ordnance info files.""" + + if not extn_dir: + return [] + + # load our extension info files + all_extn_info = {} + if "_VASL_EXTN_INFO_DIR_" in app.config: + dname = app.config["_VASL_EXTN_INFO_DIR_"] # nb: for the test suite + else: + dname = os.path.join( DATA_DIR, "extensions" ) + for fname in glob.glob( os.path.join(dname,"*.json") ): + _logger.debug( "Loading VASL extension info: %s", fname ) + with open( fname, "r" ) as fp: + extn_info = json.load( fp ) + all_extn_info[ ( extn_info["extensionId"], extn_info["version"] ) ] = extn_info + _logger.debug( "- id=%s ; version=%s", extn_info["extensionId"], extn_info["version"] ) + + # figure out what filename extensions we will recognize + valid_fname_extns = app.config.get( "VASL_EXTENSION_FILENAME_EXTNS", ".mdx .zip" ) + valid_fname_extns = valid_fname_extns.replace( ";", " " ).replace( ",", " " ).split() + + # process each VASL extension + extns = [] + for extn_fname in os.listdir( extn_dir ): + + # check if this is a file we're interested in + if os.path.splitext(extn_fname)[1] not in valid_fname_extns: + continue + extn_fname = os.path.join( extn_dir, extn_fname ) + + # try to load the extension + _logger.debug( "Loading VASL extension: %s", extn_fname ) + try: + zip_file = zipfile.ZipFile( extn_fname, "r" ) + except zipfile.BadZipFile: + msg_store.warning( "Can't load VASL extension (not a ZIP file): {}", extn_fname, logger=_logger ) + continue + try: + build_info = zip_file.read( "buildFile" ) + except KeyError: + msg_store.warning( "Missing buildFile: {}", extn_fname, logger=_logger ) + continue + doc = xml.etree.ElementTree.fromstring( build_info ) + node = doc.findall( "." )[0] + if node.tag != "VASSAL.build.module.ModuleExtension": + msg_store.warning( "Unexpected root node ({}) for VASL extension: {}", + node.tag, extn_fname, logger=_logger + ) + continue + + # get the extension's ID and version string + extn_id = node.attrib.get( "extensionId" ) + if not extn_id: + msg_store.warning( "Can't find ID for VASL extension: {}", extn_fname, logger=_logger ) + continue + extn_version = node.attrib.get( "version" ) + if not extn_version: + msg_store.warning( "Can't find version for VASL extension: {}", extn_fname, logger=_logger ) + continue + _logger.debug( "- id=%s ; version=%s", extn_id, extn_version ) + + # check if we have a corresponding info file + extn_info = all_extn_info.get( ( extn_id, extn_version ) ) + if not extn_info: + msg_store.warning( "Not loading VASL extension \"{}\".
No extension info file for {}/{}.".format(
+ os.path.split(extn_fname)[1], extn_id, extn_version
+ ) )
+ _logger.warning( "Not loading VASL extension (no info file for %s/%s): %s",
+ extn_id, extn_version, extn_fname
+ )
+ continue
+
+ # yup - add the extension to the list
+ extns.append( ( extn_fname, extn_info ) )
+
+ return extns
+
+# ---------------------------------------------------------------------
+
class VaslMod:
- """Serve files from a VASL module file."""
+ """Wrapper around a VASL module file and extensions."""
+
+ def __init__( self, fname, data_dir, extns ) :
- def __init__( self, fname, data_dir ) :
# initialize
- self.pieces = {}
- # parse the VASL module file
- _logger.info( "Loading VASL module: %s", fname )
- self.zip_file = zipfile.ZipFile( fname, "r" )
- self.vasl_version = self._parse_vmod( data_dir )
+ self.filename = fname
+ self.extns = extns
+
+ # initialize
+ self._pieces = {}
+ self._files = [ ( zipfile.ZipFile(fname,"r"), None ) ]
+ if extns:
+ for extn in extns:
+ self._files.append(
+ ( zipfile.ZipFile(extn[0],"r"), extn[1] )
+ )
+
+ # load the VASL module and any extensions
+ self.vasl_version = self._load_vmod( data_dir )
if self.vasl_version not in SUPPORTED_VASL_MOD_VERSIONS:
_logger.warning( "Unsupported VASL version: %s", self.vasl_version )
@@ -34,11 +172,11 @@ class VaslMod:
# get the image path
gpid = get_effective_gpid( gpid )
- if gpid not in self.pieces:
+ if gpid not in self._pieces:
return None, None
- entry = self.pieces[ get_effective_gpid( gpid ) ]
+ piece = self._pieces[ get_effective_gpid( gpid ) ]
assert side in ("front","back")
- image_paths = entry[ side+"_images" ]
+ image_paths = piece[ side + "_images" ]
if not image_paths:
return None, None
if not isinstance( image_paths, list ):
@@ -50,7 +188,7 @@ class VaslMod:
# load the image data
image_path = os.path.join( "images", image_path )
image_path = re.sub( r"[\\/]+", "/", image_path ) # nb: in case we're on Windows :-/
- image_data = self.zip_file.read( image_path )
+ image_data = piece[ "zip_file" ].read( image_path )
return image_path, image_data
@@ -68,11 +206,19 @@ class VaslMod:
"back_images": image_count( p, "back_images" ),
"is_small": p["is_small"],
}
- for p in self.pieces.values()
+ for p in self._pieces.values()
}
- def _parse_vmod( self, data_dir ): #pylint: disable=too-many-branches,too-many-locals
- """Parse a .vmod file."""
+ def get_extns( self ):
+ """Return the loaded VASL extensions."""
+ return [
+ ( files[0].filename, files[1] )
+ for files in self._files
+ if files[1]
+ ]
+
+ def _load_vmod( self, data_dir ): #pylint: disable=too-many-branches,too-many-locals
+ """Load a VASL module file and any extensions."""
# load our overrides
fname = os.path.join( data_dir, "vasl-overrides.json" )
@@ -81,30 +227,54 @@ class VaslMod:
expected_multiple_images = json.load( open( fname, "r" ) )
# figure out which pieces we're interested in
- target_gpids = get_vo_gpids( data_dir )
+ target_gpids = get_vo_gpids( data_dir, self.get_extns() )
+
+ # parse the VASL module and any extensions
+ for i,files in enumerate( self._files ):
+ _logger.info( "Loading VASL %s: %s", ("module" if i == 0 else "extension"), files[0].filename )
+ version = self._parse_zip_file( files[0], target_gpids, vasl_overrides, expected_multiple_images )
+ if i == 0:
+ vasl_version = version
+
+ # make sure we found all the pieces we need
+ _logger.info( "Loaded %d pieces.", len(self._pieces) )
+ if target_gpids:
+ _logger.warning( "Couldn't find pieces: %s", target_gpids )
+
+ # make sure all the overrides defined were used
+ if vasl_overrides:
+ gpids = ", ".join( vasl_overrides.keys() )
+ _logger.warning( "Unused VASL overrides: %s", gpids )
+ if expected_multiple_images:
+ gpids = ", ".join( expected_multiple_images.keys() )
+ _logger.warning( "Expected multiple images but didn't find them: %s", gpids )
+
+ return vasl_version
+
+ def _parse_zip_file( self, zip_file, target_gpids, vasl_overrides, expected_multiple_images ): #pylint: disable=too-many-locals
+ """Parse a VASL module or extension."""
+
+ # load the build file
+ build_info = zip_file.read( "buildFile" )
+ doc = xml.etree.ElementTree.fromstring( build_info )
def check_override( gpid, piece, override ):
"""Check that the values in an override entry match what we have."""
for key in override:
if piece[key] != override[key]:
- _logger.warning( "Unexpected value in VASL override for '%s' (gpid=%d): %s", key, gpid, piece[key] )
+ _logger.warning( "Unexpected value in VASL override for '%s' (gpid=%s): %s", key, gpid, piece[key] )
return False
return True
- # parse the VASL build info
- build_info = self.zip_file.read( "buildFile" )
- doc = xml.etree.ElementTree.fromstring( build_info )
+ # iterate over each PieceSlot in the build file
for node in doc.iter( "VASSAL.build.widget.PieceSlot" ):
# load the next entry
- # FUDGE! 6.4.3 introduced weird GPID's for "Hex Grid" pieces :-/
- if node.attrib["gpid"].startswith( "4d0:" ):
- continue
- gpid = int( node.attrib["gpid"] )
+ gpid = node.attrib[ "gpid" ]
if gpid not in target_gpids:
continue
- if gpid in self.pieces:
- _logger.warning( "Found duplicate GPID: %d", gpid )
+ if gpid in self._pieces:
+ _logger.warning( "Found duplicate GPID: %s", gpid )
front_images, back_images = self._get_image_paths( gpid, node.text )
piece = {
"gpid": gpid,
@@ -112,43 +282,31 @@ class VaslMod:
"front_images": front_images,
"back_images": back_images,
"is_small": int(node.attrib["height"]) <= 48,
+ "zip_file": zip_file,
}
# check if we want to override any values
- override = vasl_overrides.get( str(gpid) )
+ override = vasl_overrides.get( gpid )
if override:
if check_override( gpid, piece, override["expected"] ):
for key in override["updated"]:
piece[key] = override["updated"][key]
- del vasl_overrides[ str(gpid) ]
+ del vasl_overrides[ gpid ]
# save the loaded entry
- self.pieces[gpid] = piece
+ self._pieces[ gpid ] = piece
target_gpids.remove( gpid )
_logger.debug( "- Loaded piece: %s", piece )
# check for multiple images
if isinstance(piece["front_images"],list) or isinstance(piece["back_images"],list):
- expected = expected_multiple_images.get( str(gpid) )
+ expected = expected_multiple_images.get( gpid )
if expected:
check_override( gpid, piece, expected )
- del expected_multiple_images[ str(gpid) ]
+ del expected_multiple_images[ gpid ]
else:
_logger.warning( "Found multiple images: %s", piece )
- # make sure we found all the pieces we need
- _logger.info( "Loaded %d pieces.", len(self.pieces) )
- if target_gpids:
- _logger.warning( "Couldn't find pieces: %s", target_gpids )
-
- # make sure all the overrides defined were used
- if vasl_overrides:
- gpids = ", ".join( vasl_overrides.keys() )
- _logger.warning( "Unused VASL overrides: %s", gpids )
- if expected_multiple_images:
- gpids = ", ".join( expected_multiple_images.keys() )
- _logger.warning( "Expected multiple images but didn't find them: %s", gpids )
-
return doc.attrib.get( "version" )
@staticmethod
@@ -180,7 +338,7 @@ class VaslMod:
fields = [ f for f in fields if f ]
return fields
if not fields:
- _logger.warning( "Couldn't find any image paths for gpid=%d.", gpid )
+ _logger.warning( "Couldn't find any image paths for gpid=%s.", gpid )
return None, None
if len(fields) == 1:
# the piece only has front image(s)
@@ -188,14 +346,14 @@ class VaslMod:
else:
# the piece has front and back image(s)
if len(fields) > 2:
- _logger.warning( "Found > 2 image paths for gpid=%d", gpid )
+ _logger.warning( "Found > 2 image paths for gpid=%s", gpid )
front_images, back_images = split_fields(fields[1]), split_fields(fields[0])
# ignore dismantled ordnance
if len(front_images) > 1:
if front_images[-1].endswith( "dm" ):
if back_images[-1].endswith( "dmb" ):
- _logger.debug( "Ignoring dismantled images: gpid=%d, front=%s, back=%s",
+ _logger.debug( "Ignoring dismantled images: gpid=%s, front=%s, back=%s",
gpid, front_images, back_images
)
front_images.pop()
@@ -207,7 +365,7 @@ class VaslMod:
if len(front_images) > 1:
if front_images[-1].endswith( "l" ):
if back_images[-1].endswith( ("lb","l-b") ):
- _logger.debug( "Ignoring limbered images: gpid=%d, front=%s, back=%s",
+ _logger.debug( "Ignoring limbered images: gpid=%s, front=%s, back=%s",
gpid, front_images, back_images
)
front_images.pop()
@@ -216,7 +374,7 @@ class VaslMod:
_logger.warning( "Unexpected limbered images: %s %s", front_images, back_images )
elif front_images[-1].endswith( "B.png" ) and front_images[0] == front_images[-1][:-5]+".png":
# nb: this is for Finnish Guns
- _logger.debug( "Ignoring limbered images: gpid=%d, front=%s, back=%s",
+ _logger.debug( "Ignoring limbered images: gpid=%s, front=%s, back=%s",
gpid, front_images, back_images
)
front_images.pop()
diff --git a/vasl_templates/webapp/files.py b/vasl_templates/webapp/files.py
index 7b606de..07e2a76 100644
--- a/vasl_templates/webapp/files.py
+++ b/vasl_templates/webapp/files.py
@@ -9,12 +9,7 @@ import mimetypes
from flask import send_file, send_from_directory, jsonify, redirect, url_for, abort
from vasl_templates.webapp import app
-from vasl_templates.webapp.file_server.vasl_mod import VaslMod
-from vasl_templates.webapp.config.constants import DATA_DIR
-
-vasl_mod = None
-if app.config.get( "VASL_MOD" ):
- vasl_mod = VaslMod( app.config["VASL_MOD"], DATA_DIR )
+from vasl_templates.webapp.file_server.vasl_mod import get_vasl_mod
# ---------------------------------------------------------------------
@@ -64,24 +59,18 @@ def get_user_file( path ):
# ---------------------------------------------------------------------
-def install_vasl_mod( new_vasl_mod ):
- """Install a new VASL module."""
- global vasl_mod
- vasl_mod = new_vasl_mod
-
-# ---------------------------------------------------------------------
-
@app.route( "/counter/ Things might work, but they might not...".format( version )
+ )
+
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
class VassalShimError( Exception ):
@@ -350,25 +357,3 @@ class VassalShimError( Exception ):
self.retcode = retcode
self.stdout = stdout
self.stderr = stderr
-
-# ---------------------------------------------------------------------
-
-@app.route( "/check-vassal-version" )
-def check_vassal_version():
- """Check if we're running a supported version of VASSAL."""
- vassal_dir = app.config.get( "VASSAL_DIR" )
- if vassal_dir:
- vassal_shim = VassalShim()
- version = vassal_shim.get_version()
- if version not in SUPPORTED_VASSAL_VERSIONS:
- return "VASSAL {} is unsupported. Things might work, but they might not...".format( version )
- return ""
-
-# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-
-@app.route( "/check-vasl-version" )
-def check_vasl_version():
- """Check if we're running a supported version of VASL."""
- if vasl_mod and vasl_mod.vasl_version not in SUPPORTED_VASL_MOD_VERSIONS:
- return "VASL {} is unsupported. Things might work, but they might not...".format( vasl_mod.vasl_version )
- return ""
diff --git a/vasl_templates/webapp/vo.py b/vasl_templates/webapp/vo.py
index a1effd4..91e50df 100644
--- a/vasl_templates/webapp/vo.py
+++ b/vasl_templates/webapp/vo.py
@@ -2,11 +2,13 @@
import os
import json
+import logging
from flask import request, render_template, jsonify, abort
from vasl_templates.webapp import app
from vasl_templates.webapp.config.constants import DATA_DIR
+from vasl_templates.webapp.file_server.vasl_mod import get_vasl_mod
# ---------------------------------------------------------------------
@@ -22,7 +24,7 @@ def get_ordnance_listings():
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
-def _do_get_listings( listings_type ): #pylint: disable=too-many-branches
+def _do_get_listings( vo_type ): #pylint: disable=too-many-locals,too-many-branches
"""Load the vehicle/ordnance listings."""
# locate the data directory
@@ -30,7 +32,7 @@ def _do_get_listings( listings_type ): #pylint: disable=too-many-branches
dname = DATA_DIR # nb: always use the real data for reports, not the test fixtures
else:
dname = app.config.get( "DATA_DIR", DATA_DIR )
- dname = os.path.join( dname, listings_type )
+ dname = os.path.join( dname, vo_type )
if not os.path.isdir( dname ):
abort( 404 )
@@ -61,7 +63,7 @@ def _do_get_listings( listings_type ): #pylint: disable=too-many-branches
listings[nat].extend( listings[minor_type+"-common"] )
del listings[ minor_type+"-common" ]
# merge landing craft
- if listings_type == "vehicles":
+ if vo_type == "vehicles":
for lc in listings.get("landing-craft",[]):
if lc["name"] in ("Daihatsu","Shohatsu"):
listings["japanese"].append( lc )
@@ -69,8 +71,46 @@ def _do_get_listings( listings_type ): #pylint: disable=too-many-branches
listings["american"].append( lc )
listings["british"].append( lc )
+ # apply any changes for VASL extensions
+ vasl_mod = get_vasl_mod()
+ if vasl_mod:
+ # build an index of the pieces
+ piece_index = {}
+ for nat,pieces in listings.items():
+ for piece in pieces:
+ piece_index[ piece["id"] ] = piece
+ # process each VASL extension
+ for extn in vasl_mod.get_extns():
+ if vo_type not in extn[1]:
+ continue
+ _apply_extn_info( extn[0], extn[1], piece_index, vo_type )
+
return jsonify( listings )
+def _apply_extn_info( extn_fname, extn_info, piece_index, vo_type ):
+ """Update the vehicle/ordnance listings for the specified VASL extension."""
+
+ # initialize
+ logger = logging.getLogger( "vasl_mod" )
+ logger.info( "Updating %s for VASL extension: %s", vo_type, os.path.split(extn_fname)[1] )
+
+ # process each entry
+ for entry in extn_info[vo_type]:
+ piece = piece_index.get( entry["id"] )
+ if piece:
+ # update an existing piece
+ logger.debug( "- Updating GPID's for %s: %s", entry["id"], entry["gpid"] )
+ if piece["gpid"]:
+ prev_gpids = piece["gpid"]
+ if not isinstance( piece["gpid"], list ):
+ piece["gpid"] = [ piece["gpid"] ]
+ piece["gpid"].extend( entry["gpid"] )
+ else:
+ prev_gpids = "(none)"
+ piece["gpid"] = entry["gpid"]
+ logger.debug( " - %s => %s", prev_gpids, piece["gpid"] )
+ else:
+ logger.warning( "- Updating V/O entry with extension info not supported: %s", entry["id"] )
# ---------------------------------------------------------------------
@app.route( "/