Compare commits

...

30 Commits

Author SHA1 Message Date
Pacman Ghost 85616567f8 Updated the help. 2 weeks ago
Pacman Ghost f20693f8ae Added support for VASL 6.6.8 and VASSAL 3.7.9. 2 weeks ago
Pacman Ghost f87acbea56 Handle SIGTERM and shutdown gracefully. 2 weeks ago
Pacman Ghost 19002a03e5 Updated the version strings. 2 weeks ago
Pacman Ghost 396ebcef73 Allow ASL_RULEBOOK2_BASE_URL to be set from an env.var. 1 month ago
Pacman Ghost 24106fb7e5 Updated the documentation. 5 months ago
Pacman Ghost 2b4bc9045a Updated for VASL 6.6.7 and VASSAL 3.7.5. 5 months ago
Pacman Ghost 71b07accd8 Disable the context help button in the title bar (Windows). 6 months ago
Pacman Ghost acdb8351ef Added a link to the repo issues page in the About box. 6 months ago
Pacman Ghost 5606fe0d1a Show a better error message if Java is not configured. 6 months ago
Pacman Ghost cb0369b85a Show the "Program Info" menu item in the desktop program. 6 months ago
Pacman Ghost fdaf2f0cde Added logging for testing different VASSAL+VASL combinations. 6 months ago
Pacman Ghost bb075ef0da Tidied up some test fixtures. 6 months ago
Pacman Ghost a30cbbb616 Applied errata from ASLJ 13 for when the KMC get black OBA numbers. 6 months ago
Pacman Ghost 3e7daad138 Use blue counters for some French ordnance. 6 months ago
Pacman Ghost 2c6f0f2ba3 Updated the dependencies. 6 months ago
Pacman Ghost 127c5d46e0 Updated the dependencies. 7 months ago
Pacman Ghost 4df0f5cf77 Updated the version number to v1.12. 1 year ago
Pacman Ghost 8269917bee Updated the documentation 1 year ago
Pacman Ghost a274775bf7 Updated for VASSAL 3.6.14 and VASL 6.6.6. 1 year ago
Pacman Ghost 1c28ba4373 Show a better error message if the VASL data directory is not there. 1 year ago
Pacman Ghost bc87c398bb Added options to configure logging when running Docker. 1 year ago
Pacman Ghost 1e72a57d8d Added options to store data files outside a Docker container. 1 year ago
Pacman Ghost 8b8c3b2698 Fixed how external links in the help are opened. 1 year ago
Pacman Ghost 51a5bcee90 Booby Trap labels are now associated with a player nationality. 1 year ago
Pacman Ghost 6f070eb17a Correctly flag pieces from extensions as small. 1 year ago
Pacman Ghost 21d7aa2b68 Tweaked the display format for the Docker build timestamp. 1 year ago
Pacman Ghost 632e0e7e6d Tweaked a Trumbowyg icon. 1 year ago
Pacman Ghost 2c02163c46 Don't automatically create THH labels. 1 year ago
Pacman Ghost 20739d78de Updated the version strings. 1 year ago
  1. 2
      .pylintrc
  2. 14
      Dockerfile
  3. 1
      README.md
  4. 23
      conftest.py
  5. 1
      docker/config/site.cfg
  6. 6
      docker/run.sh
  7. 14
      requirements-dev.txt
  8. 13
      requirements.txt
  9. 97
      run-container.sh
  10. 2
      setup.py
  11. 5
      vasl_templates/about.py
  12. 3
      vasl_templates/main.py
  13. 8
      vasl_templates/tools/get_piece_info.py
  14. 26
      vasl_templates/ui/about.ui
  15. 10
      vasl_templates/webapp/__init__.py
  16. 3
      vasl_templates/webapp/config/constants.py
  17. 2
      vasl_templates/webapp/config/site.cfg.example
  18. 11
      vasl_templates/webapp/data/default-template-pack/extras/booby-traps.j2
  19. 3
      vasl_templates/webapp/data/default-template-pack/national-capabilities.json
  20. 6
      vasl_templates/webapp/data/ordnance/french.json
  21. 61
      vasl_templates/webapp/data/vasl-6.6.6/expected-multiple-images.json
  22. 2
      vasl_templates/webapp/data/vasl-6.6.6/online-counter-images.json
  23. 71
      vasl_templates/webapp/data/vasl-6.6.6/piece-info.json
  24. 147
      vasl_templates/webapp/data/vasl-6.6.6/vasl-overrides.json
  25. 72
      vasl_templates/webapp/data/vasl-6.6.7/expected-multiple-images.json
  26. 2
      vasl_templates/webapp/data/vasl-6.6.7/online-counter-images.json
  27. 71
      vasl_templates/webapp/data/vasl-6.6.7/piece-info.json
  28. 147
      vasl_templates/webapp/data/vasl-6.6.7/vasl-overrides.json
  29. 72
      vasl_templates/webapp/data/vasl-6.6.8/expected-multiple-images.json
  30. 2
      vasl_templates/webapp/data/vasl-6.6.8/online-counter-images.json
  31. 71
      vasl_templates/webapp/data/vasl-6.6.8/piece-info.json
  32. 147
      vasl_templates/webapp/data/vasl-6.6.8/vasl-overrides.json
  33. 4
      vasl_templates/webapp/downloads.py
  34. 8
      vasl_templates/webapp/main.py
  35. 2
      vasl_templates/webapp/scenarios.py
  36. 2
      vasl_templates/webapp/static/extras.js
  37. BIN
      vasl_templates/webapp/static/help/images/asl-rulebook2.png
  38. BIN
      vasl_templates/webapp/static/help/images/asl-rulebook2.small.png
  39. 31
      vasl_templates/webapp/static/help/index.html
  40. 2
      vasl_templates/webapp/static/help/main.js
  41. BIN
      vasl_templates/webapp/static/images/trumbowyg/back-color.png
  42. 2
      vasl_templates/webapp/static/main.js
  43. 4
      vasl_templates/webapp/static/vassal.js
  44. 83
      vasl_templates/webapp/templates/index.html
  45. 2
      vasl_templates/webapp/tests/control_tests.py
  46. 2
      vasl_templates/webapp/tests/control_tests_servicer.py
  47. 0
      vasl_templates/webapp/tests/fixtures/counters/6.5.0.txt
  48. 0
      vasl_templates/webapp/tests/fixtures/counters/6.5.1.txt
  49. 5
      vasl_templates/webapp/tests/fixtures/counters/6.6.0.txt
  50. 5
      vasl_templates/webapp/tests/fixtures/counters/6.6.1.txt
  51. 5
      vasl_templates/webapp/tests/fixtures/counters/6.6.2.txt
  52. 5
      vasl_templates/webapp/tests/fixtures/counters/6.6.3.txt
  53. 5
      vasl_templates/webapp/tests/fixtures/counters/6.6.4.txt
  54. 5
      vasl_templates/webapp/tests/fixtures/counters/6.6.5.txt
  55. 1363
      vasl_templates/webapp/tests/fixtures/counters/6.6.6.txt
  56. 1363
      vasl_templates/webapp/tests/fixtures/counters/6.6.7.txt
  57. 1363
      vasl_templates/webapp/tests/fixtures/counters/6.6.8.txt
  58. 1610
      vasl_templates/webapp/tests/proto/generated/control_tests_pb2.py
  59. 2
      vasl_templates/webapp/tests/proto/set_data_dir_request.py
  60. 2
      vasl_templates/webapp/tests/proto/set_default_template_pack_request.py
  61. 2
      vasl_templates/webapp/tests/proto/set_vasl_version_request.py
  62. 2
      vasl_templates/webapp/tests/proto/set_veh_ord_notes_dir_request.py
  63. 4
      vasl_templates/webapp/tests/test_counters.py
  64. 4
      vasl_templates/webapp/tests/test_national_capabilities.py
  65. 5
      vasl_templates/webapp/tests/test_scenario_search.py
  66. 2
      vasl_templates/webapp/tests/test_vasl_extensions.py
  67. 38
      vasl_templates/webapp/tests/test_vassal.py
  68. 9
      vasl_templates/webapp/tests/utils.py
  69. 34
      vasl_templates/webapp/vasl_mod.py
  70. 14
      vasl_templates/webapp/vassal.py
  71. 14
      vasl_templates/webapp/webdriver.py
  72. BIN
      vassal-shim/release/vassal-shim.jar
  73. 8
      vassal-shim/src/vassal_shim/VassalShim.java

@ -480,4 +480,4 @@ known-third-party=enchant
# Exceptions that will emit a warning when being caught. Defaults to
# "Exception"
overgeneral-exceptions=Exception
overgeneral-exceptions=builtins.Exception

@ -1,28 +1,28 @@
# NOTE: Use the run-container.sh script to build and launch this container.
# NOTE: Multi-stage builds require Docker >= 17.05.
FROM rockylinux:8.5 AS base
FROM rockylinux:9.1 AS base
# update packages and install requirements
RUN dnf -y upgrade-minimal && \
dnf install -y python39
dnf install -y python3.11
# NOTE: We don't need the following stuff for the build step, but it's nice to not have to re-install
# it all every time we change the requirements :-/
# install Java
ARG JAVA_URL=https://download.oracle.com/java/17/archive/jdk-17.0.2_linux-x64_bin.tar.gz
RUN curl -s "$JAVA_URL" | tar -xz -C /usr/bin/
RUN dnf install -y java-17-openjdk
# install Firefox
ARG FIREFOX_URL=https://ftp.mozilla.org/pub/firefox/releases/94.0.2/linux-x86_64/en-US/firefox-94.0.2.tar.bz2
# NOTE: We could install this using dnf, but the version of geckodriver needs to match it.
ARG FIREFOX_URL=https://ftp.mozilla.org/pub/firefox/releases/117.0.1/linux-x86_64/en-US/firefox-117.0.1.tar.bz2
RUN dnf install -y bzip2 xorg-x11-server-Xvfb gtk3 dbus-glib && \
curl -s "$FIREFOX_URL" | tar -jx -C /usr/local/ && \
ln -s /usr/local/firefox/firefox /usr/bin/firefox && \
echo "exclude=firefox" >>/etc/dnf/dnf.conf
# install geckodriver
ARG GECKODRIVER_URL=https://github.com/mozilla/geckodriver/releases/download/v0.31.0/geckodriver-v0.31.0-linux64.tar.gz
ARG GECKODRIVER_URL=https://github.com/mozilla/geckodriver/releases/download/v0.33.0/geckodriver-v0.33.0-linux64.tar.gz
RUN curl -sL "$GECKODRIVER_URL" | tar -xz -C /usr/bin/
# clean up
@ -33,7 +33,7 @@ RUN dnf clean all
FROM base AS build
# set up a virtualenv
RUN python3 -m venv /opt/venv
RUN python3.11 -m venv /opt/venv
ENV PATH="/opt/venv/bin:$PATH"
RUN pip install --upgrade pip

@ -18,3 +18,4 @@ You can find more examples of the program in action [here](examples/).
* [Setting up Chapter H data](https://vasl-templates.org/help?tab=chapterh)
* [Writing your own templates](https://vasl-templates.org/help?tab=templatepacks)
* [For developers](https://vasl-templates.org/help?tab=fordevelopers)
* [FAQ](https://vasl-templates.org/FAQ)

@ -1,6 +1,7 @@
""" pytest support functions. """
import os
import shutil
import threading
import json
import re
@ -10,8 +11,6 @@ import urllib.request
from urllib.error import URLError
import pytest
from flask import url_for
from vasl_templates.webapp import app
from vasl_templates.webapp.tests import utils
from vasl_templates.webapp.tests.control_tests import ControlTests
@ -19,6 +18,7 @@ from vasl_templates.webapp.tests.control_tests import ControlTests
FLASK_WEBAPP_PORT = 5011
_pytest_options = None
_orig_url_for = app.url_for
# ---------------------------------------------------------------------
@ -129,7 +129,9 @@ def _make_webapp():
# NOTE: It's not a bad idea to bypass the clipboard, even when running in a browser,
# to avoid problems if something else uses the clipboard while the tests are running.
kwargs["store_clipboard"] = 1
url = url_for( endpoint, _external=True, **kwargs )
if kwargs.get( "_external" ) is None:
kwargs["_external"] = True
url = _orig_url_for( endpoint, **kwargs )
url = url.replace( "http://localhost", app.base_url )
return url
app.url_for = make_webapp_url
@ -187,9 +189,9 @@ def _make_webapp():
# we won't have even got this far, since it needs to be there to drive the browser.
# NOTE: This will have no effect if we're talking to a remote server, but we can live with that.
if _pytest_options.webdriver == "firefox":
app.config[ "WEBDRIVER_PATH" ] = "geckodriver"
app.config[ "WEBDRIVER_PATH" ] = shutil.which( "geckodriver" )
elif _pytest_options.webdriver == "chrome":
app.config[ "WEBDRIVER_PATH" ] = "chromedriver"
app.config[ "WEBDRIVER_PATH" ] = shutil.which( "chromedriver" )
return app
@ -216,15 +218,18 @@ def webdriver( request ):
# initialize
driver = request.config.getoption( "--webdriver" )
from selenium import webdriver as wb
log_fname = os.path.join( tempfile.gettempdir(), "webdriver-pytest.log" )
if driver == "firefox":
service = wb.firefox.service.Service(
log_output = os.path.join( tempfile.gettempdir(), "webdriver-pytest.log" )
)
options = wb.FirefoxOptions()
options.headless = _pytest_options.headless
service = wb.firefox.service.Service( log_path=log_fname )
if _pytest_options.headless:
options.add_argument( "--headless" )
driver = wb.Firefox( options=options, service=service )
elif driver == "chrome":
options = wb.ChromeOptions()
options.headless = _pytest_options.headless
if _pytest_options.headless:
options.add_argument( "--headless" )
options.add_argument( "--disable-gpu" )
driver = wb.Chrome( options=options )
else:

@ -2,5 +2,4 @@
IS_CONTAINER = 1
JAVA_PATH = /usr/bin/jdk-17.0.2/bin/java
WEBDRIVER_PATH = /usr/bin/geckodriver

@ -6,6 +6,10 @@ export DISPLAY=:10.0
Xvfb :10 -ac 1>/tmp/xvfb.log 2>/tmp/xvfb.err &
# run the webapp server
python3 /app/vasl_templates/webapp/run_server.py \
# IMPORTANT! This script runs as PID 1, which is the only process that will receive signals,
# so we must replace it with the Python webserver process if it is to receive e.g. SIGTERM,
# which we must handle if "docker stop" and scaling down in Kubernetes is to work (otherwise
# things timeout and we get SIGKILL'ed).
exec python3 /app/vasl_templates/webapp/run_server.py \
--addr 0.0.0.0 \
--force-init-delay 30

@ -1,7 +1,7 @@
pytest==7.1.2
grpcio-tools==1.46.3
tabulate==0.8.9
lxml==4.9.0
pylint==2.14.1
pytest-pylint==0.18.0
pyinstaller==5.1
pytest==7.4.2
grpcio-tools==1.58.0
tabulate==0.9.0
lxml==4.9.3
pylint==2.17.5
pytest-pylint==0.19.0
pyinstaller==5.13.2

@ -1,9 +1,10 @@
# python 3.10.4
# python 3.11.4
flask==2.1.2
pyyaml==6.0
pillow==9.1.1
selenium==4.2.0
flask==2.3.3
pyyaml==6.0.1
# NOTE: Pillow 9.5.0 is the last version that provides 32-bit wheels.
pillow==9.5.0
selenium==4.12.0
waitress==2.1.2
appdirs==1.4.4
click==8.1.3
click==8.1.7

@ -14,6 +14,11 @@ function main
VASL_BOARDS=
CHAPTER_H_NOTES=
USER_FILES=
LOGGING_CONFIG=
VASSAL_SHIM_LOGGING_CONFIG=
ASA_INDEX=
ROAR_INDEX=
VO_NOTES_IMAGE_CACHE=
TEMPLATE_PACK=
IMAGE_TAG=latest
CONTAINER_NAME=vasl-templates
@ -31,7 +36,7 @@ function main
print_help
exit 0
fi
params="$(getopt -o p:v:e:k:t:d -l port:,control-tests-port:,vassal:,vasl:,vasl-extensions:,boards:,chapter-h:,template-pack:,user-files:,tag:,name:,detach,no-build,build-arg:,build-network:,run-network:,test-data-vassal:,test-data-vasl-mods:,help --name "$0" -- "$@")"
params="$(getopt -o p:v:e:k:t:d -l port:,control-tests-port:,vassal:,vasl:,vasl-extensions:,boards:,chapter-h:,template-pack:,user-files:,logging:,vassal-shim-logging:,asa-index:,roar-index:,vo-notes-image-cache:,tag:,name:,detach,no-build,build-arg:,build-network:,run-network:,test-data-vassal:,test-data-vasl-mods:,help --name "$0" -- "$@")"
if [ $? -ne 0 ]; then exit 1; fi
eval set -- "$params"
while true; do
@ -60,6 +65,21 @@ function main
-k | --template-pack)
TEMPLATE_PACK=$2
shift 2 ;;
--logging)
LOGGING_CONFIG=$2
shift 2 ;;
--vassal-shim-logging)
VASSAL_SHIM_LOGGING_CONFIG=$2
shift 2 ;;
--asa-index)
ASA_INDEX=$2
shift 2 ;;
--roar-index)
ROAR_INDEX=$2
shift 2 ;;
--vo-notes-image-cache)
VO_NOTES_IMAGE_CACHE=$2
shift 2 ;;
-t | --tag)
IMAGE_TAG=$2
shift 2 ;;
@ -186,7 +206,67 @@ function main
fi
mpoint=/data/template-pack
TEMPLATE_PACK_VOLUME="--volume $target:$mpoint"
TEMPLATE_PACK_ENV="--env DEFAULT_TEMPLATE_PACK=$mpoint --env DEFAULT_TEMPLATE_PACK_TARGET"
TEMPLATE_PACK_ENV="--env DEFAULT_TEMPLATE_PACK=$mpoint"
fi
# check if logging has been configured
if [ -n "$LOGGING_CONFIG" ]; then
target=$( get_target FILE "$LOGGING_CONFIG" )
if [ -z "$target" ]; then
echo "Can't find the logging config file: $LOGGING_CONFIG"
exit 1
fi
mpoint=/app/vasl_templates/webapp/config/logging.yaml
LOGGING_CONFIG_VOLUME="--volume $target:$mpoint"
fi
if [ -n "$VASSAL_SHIM_LOGGING_CONFIG" ]; then
target=$( get_target FILE "$VASSAL_SHIM_LOGGING_CONFIG" )
if [ -z "$target" ]; then
echo "Can't find the VASSAL shim logging config file: $VASSAL_SHIM_LOGGING_CONFIG"
exit 1
fi
mpoint=/app/vassal-shim/release/logback-test.xml
VASSAL_SHIM_LOGGING_CONFIG_VOLUME="--volume $target:$mpoint"
fi
# check if external ASA/ROAR index files have been specified
# NOTE: We don't need to pass env.vars into the container, or anything like that. The code already
# saves the downloaded files in /tmp/ (inside the container), so all we need to do is map these files
# to the specified external files.
if [ -n "$ASA_INDEX" ]; then
target=$( realpath --no-symlinks "$ASA_INDEX" )
if [ ! -f "$target" ]; then
if ! touch "$target" 2>/dev/null; then
echo "Can't find the ASA index file: $ASA_INDEX"
exit 1
fi
fi
mpoint=/tmp/asl-scenario-archive.json
ASA_INDEX_VOLUME="--volume $target:$mpoint"
fi
if [ -n "$ROAR_INDEX" ]; then
target=$( realpath --no-symlinks "$ROAR_INDEX" )
if [ ! -f "$target" ]; then
if ! touch "$target" 2>/dev/null ; then
echo "Can't find the ROAR index file: $ASA_INDEX"
exit 1
fi
fi
mpoint=/tmp/roar-scenario-index.json
ROAR_INDEX_VOLUME="--volume $target:$mpoint"
fi
# check if an external v/o notes image cache directory has been specified
if [ -n "$VO_NOTES_IMAGE_CACHE" ]; then
target=$( realpath --no-symlinks "$VO_NOTES_IMAGE_CACHE" )
if [ ! -d "$target" ]; then
if ! mkdir "$target" 2>/dev/null; then
echo "Can't find the V/O notes image cache directory: $VO_NOTES_IMAGE_CACHE"
exit 1
fi
fi
mpoint=/tmp/vo-notes-image-cache/
VO_NOTES_IMAGE_CACHE_VOLUME="--volume $target:$mpoint"
fi
# check if testing has been enabled
@ -219,6 +299,8 @@ function main
--env DOCKER_CONTAINER_NAME="$CONTAINER_NAME" \
$CONTROL_TESTS_PORT_RUN \
$VASSAL_VOLUME $VASL_MOD_VOLUME $VASL_EXTNS_VOLUME $VASL_BOARDS_VOLUME $CHAPTER_H_NOTES_VOLUME $TEMPLATE_PACK_VOLUME $USER_FILES_VOLUME \
$LOGGING_CONFIG_VOLUME $VASSAL_SHIM_LOGGING_CONFIG_VOLUME \
$ASA_INDEX_VOLUME $ROAR_INDEX_VOLUME $VO_NOTES_IMAGE_CACHE_VOLUME \
$VASSAL_ENV $VASL_MOD_ENV $VASL_EXTNS_ENV $VASL_BOARDS_ENV $CHAPTER_H_NOTES_ENV $TEMPLATE_PACK_ENV $USER_FILES_ENV \
$RUN_NETWORK $DETACH \
$TEST_DATA_VASSAL $TEST_DATA_VASL_MODS \
@ -288,10 +370,15 @@ function print_help {
--build-network Docker network to use when building the image.
--run-network Docker network to use when running the container.
Options for storing data files outside the container (so that they can be re-used):
--asa-index Path to the ASL Scenario Archive index file (downloaded).
--roar-index Path to the ROAR index file (downloaded).
--vo-notes-image-cache Cache directory for images generated for vehicle/ordnance notes.
Options for the test suite:
--control-tests-port Remote test control port number.
--test-data-vassal Directory containing VASSAL releases.
--test-data-vasl-mods Directory containing VASL modules.
--control-tests-port Remote test control port number.
--test-data-vassal Directory containing VASSAL releases.
--test-data-vasl-mods Directory containing VASL modules.
NOTE: If the port the webapp server is listening on *inside* the container is different
to the port exposed *outside* the container, webdriver image generation (e.g. Shift-Click

@ -28,7 +28,7 @@ def parse_requirements( fname ):
setup(
name = "vasl_templates",
version = "1.10", # nb: also update constants.py
version = "1.13", # nb: also update constants.py
description = "Create HTML snippets for use in VASL.",
license = "AGPLv3",
url = "https://code.pacman-ghost.com/public/vasl-templates",

@ -11,7 +11,7 @@ from PyQt5.QtCore import QUrl
from PyQt5.QtGui import QDesktopServices, QIcon, QCursor
from PyQt5.QtWidgets import QDialog
from vasl_templates.webapp.config.constants import APP_NAME, APP_VERSION, APP_HOME_URL, IS_FROZEN
from vasl_templates.webapp.config.constants import APP_NAME, APP_VERSION, APP_HOME_URL, APP_ISSUES_URL, IS_FROZEN
from vasl_templates.utils import get_build_info
# ---------------------------------------------------------------------
@ -60,6 +60,9 @@ class AboutDialog( QDialog ):
self.home_url.setText( "Visit us at <a href='{}'>{}</a>.".format(
APP_HOME_URL, mo.group(1) if mo else APP_HOME_URL
) )
self.issues_url.setText( "Report a bug, request a feature, ask a question <a href='{}'>here</a>.".format(
APP_ISSUES_URL
) )
def on_app_icon_clicked( self, event ): #pylint: disable=unused-argument
"""Click handler."""

@ -243,6 +243,9 @@ def _do_main( template_pack, default_scenario, remote_debugging, debug ): #pylin
if is_windows():
QApplication.setStyle( "windowsvista" )
# disable the context help button in the title bar (Windows only)
QApplication.setAttribute( PyQt5.QtCore.Qt.AA_DisableWindowContextHelpButton )
# check if we should disable the embedded browser
disable_browser = webapp.config.get( "DISABLE_WEBENGINEVIEW" )

@ -64,6 +64,14 @@ for piece_info in doc.getroot():
if info:
report[ gpid ] = info
# FUDGE! These are from extensions - it's not worth trying to figure these out programtically.
report[ "adf:1948" ] = { "is_small": True } # BFP Blood & Jungle: Dutch Brandt 47mm Mortar
report[ "adf:75" ] = { "is_small": True } # BFP Blood & Jungle: Indonesian Type 89 Heavy Grenade Launcher
report[ "adf:77" ] = { "is_small": True } # BFP Blood & Jungle: Indonesian Type 97 Automatic Gun
report[ "adf:76" ] = { "is_small": True } # BFP Blood & Jungle: Indonesian Year-11 Flat-Trajectory INF Gun
report[ "adf:1407" ] = { "is_small": True } # BFP Poland In Flames: German 2cm Tankbusche S-18
report[ "08d:75" ] = { "is_small": True } # Fight For Seoul: American M20(L) 75mm Recoilless Rifle
# output the final report
print( "{" )
lines = []

@ -10,7 +10,7 @@
<x>0</x>
<y>0</y>
<width>460</width>
<height>195</height>
<height>215</height>
</rect>
</property>
<property name="windowTitle">
@ -88,7 +88,7 @@
<property name="geometry">
<rect>
<x>19</x>
<y>152</y>
<y>172</y>
<width>425</width>
<height>31</height>
</rect>
@ -147,6 +147,28 @@
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="issues_url">
<property name="geometry">
<rect>
<x>80</x>
<y>142</y>
<width>361</width>
<height>21</height>
</rect>
</property>
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Preferred">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>*** ISSUES URL ***</string>
</property>
<property name="openExternalLinks">
<bool>true</bool>
</property>
</widget>
<widget class="QLabel" name="license">
<property name="geometry">
<rect>

@ -129,8 +129,8 @@ def _is_flask_child_process():
# ---------------------------------------------------------------------
def _on_sigint( signum, stack ): #pylint: disable=unused-argument
"""Clean up after a SIGINT."""
def _on_sig( signum, stack ): #pylint: disable=unused-argument
"""Clean up after a SIGINT/SIGTERM."""
# FUDGE! Since we added gRPC test control, we want to shutdown properly and clean things up (e.g. temp files
# created by the gRPC service), but the Flask reloader complicates what we have to do here horribly :-(
@ -230,6 +230,7 @@ _set_config_from_env( "VASL_EXTNS_DIR" )
_set_config_from_env( "BOARDS_DIR" )
_set_config_from_env( "CHAPTER_H_NOTES_DIR" )
_set_config_from_env( "USER_FILES_DIR" )
_set_config_from_env( "ASL_RULEBOOK2_BASE_URL" )
# NOTE: The Docker container also sets DEFAULT_TEMPLATE_PACK, but we read it directly from
# the environment variable, since it is not something that is stored in app.config.
@ -257,7 +258,10 @@ import vasl_templates.webapp.downloads #pylint: disable=cyclic-import
import vasl_templates.webapp.lfa #pylint: disable=cyclic-import
# install our signal handler (must be done in the main thread)
signal.signal( signal.SIGINT, _on_sigint )
signal.signal( signal.SIGINT, _on_sig )
# NOTE: We must handle SIGTERM, so that "docker stop" and scaling down in Kubernetes
# work properly (otherwise it times out and we get SIGKILL'ed :-/).
signal.signal( signal.SIGTERM, _on_sig )
# register startup initialization
app.before_request( _on_request )

@ -4,9 +4,10 @@ import sys
import os
APP_NAME = "VASL Templates"
APP_VERSION = "v1.10" # nb: also update setup.py
APP_VERSION = "v1.13" # nb: also update setup.py
APP_DESCRIPTION = "Generate HTML for use in VASL scenarios."
APP_HOME_URL = "https://vasl-templates.org"
APP_ISSUES_URL = "https://code.pacman-ghost.com/public/vasl-templates/issues"
if getattr( sys, "frozen", False ):
IS_FROZEN = True

@ -2,7 +2,7 @@
; configure VASSAL and VASL
VASSAL_DIR = ...configure the VASSAL installation directory...
VASL_MOD = ...configure the VASL module (e.g. vasl-6.6.5.vmod)...
VASL_MOD = ...configure the VASL module (e.g. vasl-6.6.7.vmod)...
VASL_EXTNS_DIR = ...configure the VASL extensions directory...
BOARDS_DIR = ...configure the VASL boards directory...

@ -3,13 +3,20 @@
<!-- vasl-templates:name Booby Traps -->
<!-- vasl-templates:description Data chart for Booby Traps. -->
<!-- player = {{PLAYER_DROPLIST:|Player}}
<!-- boards = {{BOARDS*:/8|Board(s)}} -->
<head>
<meta charset="utf-8">
<style>
.header {
background: {{PLAYER_COLORS[PLAYER_DROPLIST][0]}} ;
border-bottom: 1px solid {{PLAYER_COLORS[PLAYER_DROPLIST][2]}} ;
padding: 2px 5px ;
font-size: 105% ; font-weight: bold ;
}
.header .level { font-size: 90% ; font-style: italic ; }
{{CSS:common}}
td.header { background: #f0f0f0 ; border-bottom: 1px solid #c0c0c0 ; padding: 2px 5px ; font-size: 105% ; font-weight: bold ; }
</style>
</head>
@ -17,7 +24,7 @@ td.header { background: #f0f0f0 ; border-bottom: 1px solid #c0c0c0 ; padding: 2p
<tr>
<td class="header">
Booby Traps (Level {{LEVEL:A::B::C/3|Level}})
<img src="{{PLAYER_FLAGS[PLAYER_DROPLIST]}}?prefh={{PLAYER_FLAG_SIZE_LARGE}}" width="{{PLAYER_FLAG_SIZE_LARGE}}" height="{{PLAYER_FLAG_SIZE_LARGE}}">&nbsp;Booby Traps <span class="level">(Level {{LEVEL:A::B::C/3|Level}})</span>
<tr>
<td style="padding:2px 5px;">

@ -306,7 +306,8 @@
},
"kfw-rok": {
"th_color": "{! -08/1950 = Red | 09/1950-04/1951 = Red (ROK) ; Black (KMC) | 05/1951- = Black | ??? !}",
"_comment_": "Errata (ASLJ 13 p48): KMC: '9/50+ black' s.b. '8/50+ black",
"th_color": "{! -07/1950 = Red | 08/1950-04/1951 = Red (ROK) ; Black (KMC) | 05/1951- = Black | ??? !}",
"oba": [ "{! 06/1950- = 10B | ??? !}", "3R",
"{? 09/1950- | Plentiful Ammo included | Plentiful Ammo included (KMC) | Plentiful Ammo included (ROK: 9/50+) ?}",
"{! 06/1950-08/1950 = ROK: 6B/3R !}"

@ -5,7 +5,7 @@
"note_number": "1",
"notes": [ "A", "B", "C\u2020", "E" ],
"id": "fr/o:000",
"gpid": 1636
"gpid": 12830
},
{ "name": "Mortier de 60 mle 35",
"type": "MTR",
@ -13,7 +13,7 @@
"note_number": "2\u2020",
"notes": [ "A", "B", "V" ],
"id": "fr/o:001",
"gpid": 1641
"gpid": 1633
},
{ "name": "Mortier de 81 mle 27/31",
"type": "MTR",
@ -22,7 +22,7 @@
"note_number": "3\u2020",
"notes": [ "A", "B", "D", "V" ],
"id": "fr/o:002",
"gpid": 1667
"gpid": [ 1665, 1667 ]
},
{ "name": "Fusil Antichar Boys",
"type": "ATR",

@ -0,0 +1,61 @@
{
"1555": {
"name": "2pdr Portee",
"front_images": [ "br/vehicles/portee.gif", "br/vehicles/portee0.gif" ],
"back_images": null
},
"2212": {
"name": "76* INF FRC",
"front_images": [ "al/gun/alINF76.gif", "al/gun/alINF76u.gif" ],
"back_images": "al/gun/alINF76b.gif"
},
"2698": {
"name": "SPW 251/10",
"front_images": "ge/veh/spw25110.gif",
"back_images": [ "No_ATR.gif", "No_PSK.gif" ]
},
"6765": {
"name": "81* MTR Krh/32",
"front_images": "fi/gun/fi81mmMTR.png",
"back_images": [ "fi/gun/fi81mmMTR.png", "fi/gun/fi81mmMTRB.png" ]
},
"6782": {
"name": "81* MTR Savu M42",
"front_images": [ "fi/gun/fi81mmMTR SavuB.png", "fi/gun/fi81mmMTR Savu.png" ],
"back_images": "fi/gun/fi81mmMTR SavuB.png"
},
"7409": {
"name": "76 ItK/28 B(s)",
"front_images": "fi/gun/fiAA76L.png",
"back_images": [ "fi/gun/fiAA76L.png", "fi/gun/fiAA76LB.png" ]
},
"adf:1828": {
"name": "105 ART wz.29",
"front_images": "po/gun/poARTwz29-BFP.png",
"back_images": [ "po/gun/poARTwz29-BFP.png", "po/gun/poARTwz29-BFPb.png" ]
},
"adf:1829": {
"name": "120* ART wz09.31",
"front_images": "po/gun/poARTwz0931-BFP.png",
"back_images": [ "po/gun/poARTwz0931-BFP.png", "po/gun/poARTwz0931-BFPb.png" ]
},
"adf:1830": {
"name": "155 ART wz.17",
"front_images": "po/gun/poARTwz17-BFP.png",
"back_images": [ "po/gun/poARTwz17-BFP.png", "po/gun/poARTwz17-BFPb.png" ]
},
"3b5:3676": {
"name": "M19A1 MGMC",
"front_images": [ "us/veh/usM19A1MGMC(trailer)KFW.png", "us/veh/usM19A1MGMC(KFW).png" ],
"back_images": null
}
}

@ -0,0 +1,71 @@
{
"6996": {"is_small": true},
"485": {"is_small": true},
"850": {"is_small": true},
"849": {"is_small": true},
"12689": {"is_small": true},
"856": {"is_small": true},
"857": {"is_small": true},
"11336": {"is_small": true},
"858": {"is_small": true},
"11337": {"is_small": true},
"1149": {"is_small": true},
"1153": {"is_small": true},
"12687": {"is_small": true},
"3b5:7613": {"is_small": true},
"11359": {"is_small": true},
"3b5:11259": {"is_small": true},
"1632": {"is_small": true},
"1633": {"is_small": true},
"1648": {"is_small": true},
"12830": {"is_small": true},
"1982": {"is_small": true},
"1983": {"is_small": true},
"1984": {"is_small": true},
"1985": {"is_small": true},
"1986": {"is_small": true},
"1987": {"is_small": true},
"1988": {"is_small": true},
"2172": {"is_small": true},
"2173": {"is_small": true},
"2176": {"is_small": true},
"2179": {"is_small": true},
"11391": {"is_small": true},
"11392": {"is_small": true},
"11395": {"is_small": true},
"11396": {"is_small": true},
"11440": {"is_small": true},
"3b5:8401": {"is_small": true},
"3b5:8402": {"is_small": true},
"2465": {"is_small": true},
"2474": {"is_small": true},
"3252": {"is_small": true},
"3253": {"is_small": true},
"3263": {"is_small": true},
"3422": {"is_small": true},
"3428": {"is_small": true},
"6730": {"is_small": true},
"3605": {"is_small": true},
"3608": {"is_small": true},
"6763": {"is_small": true},
"3679": {"is_small": true},
"3680": {"is_small": true},
"3681": {"is_small": true},
"3682": {"is_small": true},
"3691": {"is_small": true},
"3692": {"is_small": true},
"3959": {"is_small": true},
"11558": {"is_small": true},
"11559": {"is_small": true},
"3b5:10150": {"is_small": true},
"3b5:10151": {"is_small": true},
"11600": {"is_small": true},
"11604": {"is_small": true},
"3b5:7871": {"is_small": true},
"adf:1948": {"is_small": true},
"adf:75": {"is_small": true},
"adf:77": {"is_small": true},
"adf:76": {"is_small": true},
"adf:1407": {"is_small": true},
"08d:75": {"is_small": true}
}

@ -0,0 +1,147 @@
{
"2474": {
"expected": {
"name": "Goliath",
"front_images": [ "ge/gegol.gif", "ge/gegolb.gif" ],
"back_images": null
},
"updated": {
"front_images": "ge/gegol.gif"
}
},
"1555": {
"expected": {
"name": "2pdr Portee",
"front_images": "br/vehicles/portee.gif",
"back_images": [ "br/vehicles/portee.gif", "br/vehicles/portee0.gif" ]
},
"updated": {
"front_images": [ "br/vehicles/portee.gif", "br/vehicles/portee0.gif" ],
"back_images": null
}
},
"3463": {
"expected": {
"name": "75L AA 75/46",
"front_images": [ "it/gun/itAA7546.gif", "it/gun/itAA7546b.gif" ],
"back_images": [ "it/gun/itAA7546b.gif", "it/gun/itAA7546lb.gif" ]
},
"updated": {
"front_images": "it/gun/itAA7546.gif",
"back_images": "it/gun/itAA7546b.gif"
}
},
"3776": {
"expected": {
"name": "37* INF Skoda IG",
"front_images": [ "ax/gun/buIN37s.gif", "ax/gun/buIN37s2.gif" ],
"back_images": "ax/gun/buIN37sb.gif"
},
"updated": {
"front_images": "ax/gun/buIN37s.gif"
}
},
"3777": {
"expected": {
"name": "70* INF Skoda IG",
"front_images": [ "ax/gun/buIN37s.gif", "ax/gun/buIN37s2.gif" ],
"back_images": "ax/gun/buIN37sb.gif"
},
"updated": {
"front_images": "ax/gun/buIN37s2.gif"
}
},
"6802": {
"expected": {
"name": "20L (4) AA",
"front_images": [ "fi/gun/fi20L4 _2.png", "fi/gun/fi20L4 _2 LIM.png" ],
"back_images": null
},
"updated": {
"front_images": "fi/gun/fi20L4 _2.png"
}
},
"6803": {
"expected": {
"name": "20L VKT (12) AA",
"front_images": [ "fi/gun/fi20L12.png", "fi/gun/fi20L12L.png" ],
"back_images": null
},
"updated": {
"front_images": "fi/gun/fi20L12.png"
}
},
"6804": {
"expected": {
"name": "40L Bofors AA (s)",
"front_images": [ "fi/gun/fi40L.png", "fi/gun/fi40LL.png" ],
"back_images": null
},
"updated": {
"front_images": "fi/gun/fi40L.png"
}
},
"adf:1824": {
"expected": {
"name": "37L AT PTP obr. 30",
"front_images": "ru/gun/ruAT37L.gif",
"back_images": "ru/gun/ruAT37Lb.gif"
},
"updated": {
"front_images": "ru/gun/ru37LPTPobr30.png"
}
},
"adf:1822": {
"expected": {
"name": "37* INF PP obr. 15R",
"front_images": "ru/gun/ruINF37s.gif",
"back_images": "ru/gun/ruINF37sb.gif"
},
"updated": {
"front_images": "ru/gun/ru37PPobr15R.png"
}
},
"adf:1823": {
"expected": {
"name": "76* INF PP obr. 27",
"front_images": "ru/gun/ruINF76s.gif",
"back_images": "ru/gun/ruINF76sb.gif"
},
"updated": {
"front_images": "ru/gun/ru76PPobr27.png"
}
},
"3b5:10093": {
"expected": {
"name": "SL truck",
"front_images": [ "sh/SL3b(KFW).png", "sh/SL4b(KFW).png", "sh/SL5b(KFW).png", "sh/SL6b(KFW).png", "sh/SL1b(KFW).png", "sh/SL2b(KFW).png" ],
"back_images": [ "sh/SL3(KFW).png", "sh/SL4(KFW).png", "sh/SL5(KFW).png", "sh/SL6(KFW).png", "sh/SL1(KFW).png", "sh/SL2(KFW).png" ]
},
"updated": {
"front_images": "us/veh/usSearchlight(KFW).png",
"back_images": null
}
},
"08d:75": {
"expected": {
"name": "RCL 75*",
"front_images": "amrcl75-malf.png",
"back_images": "dm-75rcl.gif"
},
"updated": {
"front_images": "amrcl75.png"
}
}
}

@ -0,0 +1,72 @@
{
"1555": {
"name": "2pdr Portee",
"front_images": [ "br/vehicles/portee.gif", "br/vehicles/portee0.gif" ],
"back_images": null
},
"2212": {
"name": "76* INF FRC",
"front_images": [ "al/gun/alINF76.gif", "al/gun/alINF76u.gif" ],
"back_images": "al/gun/alINF76b.gif"
},
"2698": {
"name": "SPW 251/10",
"front_images": "ge/veh/spw25110.gif",
"back_images": [ "No_ATR.gif", "No_PSK.gif" ]
},
"6765": {
"name": "81* MTR Krh/32",
"front_images": "fi/gun/fi81mmMTR.png",
"back_images": [ "fi/gun/fi81mmMTR.png", "fi/gun/fi81mmMTRB.png" ]
},
"6782": {
"name": "81* MTR Savu M42",
"front_images": [ "fi/gun/fi81mmMTR SavuB.png", "fi/gun/fi81mmMTR Savu.png" ],
"back_images": "fi/gun/fi81mmMTR SavuB.png"
},
"6797": {
"name": "20L (4) AA (g)",
"front_images": [ "fi/gun/fi20L4.png", "fi/gun/fi20L4L.png" ]
},
"6801": {
"name": "20L (6) AA (g)",
"front_images": "fi/gun/fi20L6.png",
"back_images": [ "fi/gun/fi20L6.png", "fi/gun/fi20L6L.png" ]
},
"7409": {
"name": "76 ItK/28 B(s)",
"front_images": "fi/gun/fiAA76L.png",
"back_images": [ "fi/gun/fiAA76L.png", "fi/gun/fiAA76LB.png" ]
},
"adf:1828": {
"name": "105 ART wz.29",
"front_images": "po/gun/poARTwz29-BFP.png",
"back_images": [ "po/gun/poARTwz29-BFP.png", "po/gun/poARTwz29-BFPb.png" ]
},
"adf:1829": {
"name": "120* ART wz09.31",
"front_images": "po/gun/poARTwz0931-BFP.png",
"back_images": [ "po/gun/poARTwz0931-BFP.png", "po/gun/poARTwz0931-BFPb.png" ]
},
"adf:1830": {
"name": "155 ART wz.17",
"front_images": "po/gun/poARTwz17-BFP.png",
"back_images": [ "po/gun/poARTwz17-BFP.png", "po/gun/poARTwz17-BFPb.png" ]
},
"3b5:3676": {
"name": "M19A1 MGMC",
"front_images": [ "us/veh/usM19A1MGMC(trailer)KFW.png", "us/veh/usM19A1MGMC(KFW).png" ],
"back_images": null
}
}

@ -0,0 +1,71 @@
{
"6996": {"is_small": true},
"485": {"is_small": true},
"850": {"is_small": true},
"849": {"is_small": true},
"12689": {"is_small": true},
"856": {"is_small": true},
"857": {"is_small": true},
"11336": {"is_small": true},
"858": {"is_small": true},
"11337": {"is_small": true},
"1149": {"is_small": true},
"1153": {"is_small": true},
"12687": {"is_small": true},
"3b5:7613": {"is_small": true},
"11359": {"is_small": true},
"3b5:11259": {"is_small": true},
"1632": {"is_small": true},
"1633": {"is_small": true},
"1648": {"is_small": true},
"12830": {"is_small": true},
"1982": {"is_small": true},
"1983": {"is_small": true},
"1984": {"is_small": true},
"1985": {"is_small": true},
"1986": {"is_small": true},
"1987": {"is_small": true},
"1988": {"is_small": true},
"2172": {"is_small": true},
"2173": {"is_small": true},
"2176": {"is_small": true},
"2179": {"is_small": true},
"11391": {"is_small": true},
"11392": {"is_small": true},
"11395": {"is_small": true},
"11396": {"is_small": true},
"11440": {"is_small": true},
"3b5:8401": {"is_small": true},
"3b5:8402": {"is_small": true},
"2465": {"is_small": true},
"2474": {"is_small": true},
"3252": {"is_small": true},
"3253": {"is_small": true},
"3263": {"is_small": true},
"3422": {"is_small": true},
"3428": {"is_small": true},
"6730": {"is_small": true},
"3605": {"is_small": true},
"3608": {"is_small": true},
"6763": {"is_small": true},
"3679": {"is_small": true},
"3680": {"is_small": true},
"3681": {"is_small": true},
"3682": {"is_small": true},
"3691": {"is_small": true},
"3692": {"is_small": true},
"3959": {"is_small": true},
"11558": {"is_small": true},
"11559": {"is_small": true},
"3b5:10150": {"is_small": true},
"3b5:10151": {"is_small": true},
"11600": {"is_small": true},
"11604": {"is_small": true},
"3b5:7871": {"is_small": true},
"adf:1948": {"is_small": true},
"adf:75": {"is_small": true},
"adf:77": {"is_small": true},
"adf:76": {"is_small": true},
"adf:1407": {"is_small": true},
"08d:75": {"is_small": true}
}

@ -0,0 +1,147 @@
{
"2474": {
"expected": {
"name": "Goliath",
"front_images": [ "ge/gegol.gif", "ge/gegolb.gif" ],
"back_images": null
},
"updated": {
"front_images": "ge/gegol.gif"
}
},
"1555": {
"expected": {
"name": "2pdr Portee",
"front_images": "br/vehicles/portee.gif",
"back_images": [ "br/vehicles/portee.gif", "br/vehicles/portee0.gif" ]
},
"updated": {
"front_images": [ "br/vehicles/portee.gif", "br/vehicles/portee0.gif" ],
"back_images": null
}
},
"3463": {
"expected": {
"name": "75L AA 75/46",
"front_images": [ "it/gun/itAA7546.gif", "it/gun/itAA7546b.gif" ],
"back_images": [ "it/gun/itAA7546b.gif", "it/gun/itAA7546lb.gif" ]
},
"updated": {
"front_images": "it/gun/itAA7546.gif",
"back_images": "it/gun/itAA7546b.gif"
}
},
"3776": {
"expected": {
"name": "37* INF Skoda IG",
"front_images": [ "ax/gun/buIN37s.gif", "ax/gun/buIN37s2.gif" ],
"back_images": "ax/gun/buIN37sb.gif"
},
"updated": {
"front_images": "ax/gun/buIN37s.gif"
}
},
"3777": {
"expected": {
"name": "70* INF Skoda IG",
"front_images": [ "ax/gun/buIN37s.gif", "ax/gun/buIN37s2.gif" ],
"back_images": "ax/gun/buIN37sb.gif"
},
"updated": {
"front_images": "ax/gun/buIN37s2.gif"
}
},
"6802": {
"expected": {
"name": "20L (4) AA",
"front_images": [ "fi/gun/fi20L4 _2.png", "fi/gun/fi20L4 _2 LIM.png" ],
"back_images": null
},
"updated": {
"front_images": "fi/gun/fi20L4 _2.png"
}
},
"6803": {
"expected": {
"name": "20L VKT (12) AA",
"front_images": [ "fi/gun/fi20L12.png", "fi/gun/fi20L12L.png" ],
"back_images": null
},
"updated": {
"front_images": "fi/gun/fi20L12.png"
}
},
"6804": {
"expected": {
"name": "40L Bofors AA (s)",
"front_images": [ "fi/gun/fi40L.png", "fi/gun/fi40LL.png" ],
"back_images": null
},
"updated": {
"front_images": "fi/gun/fi40L.png"
}
},
"adf:1824": {
"expected": {
"name": "37L AT PTP obr. 30",
"front_images": "ru/gun/ruAT37L.gif",
"back_images": "ru/gun/ruAT37Lb.gif"
},
"updated": {
"front_images": "ru/gun/ru37LPTPobr30.png"
}
},
"adf:1822": {
"expected": {
"name": "37* INF PP obr. 15R",
"front_images": "ru/gun/ruINF37s.gif",
"back_images": "ru/gun/ruINF37sb.gif"
},
"updated": {
"front_images": "ru/gun/ru37PPobr15R.png"
}
},
"adf:1823": {
"expected": {
"name": "76* INF PP obr. 27",
"front_images": "ru/gun/ruINF76s.gif",
"back_images": "ru/gun/ruINF76sb.gif"
},
"updated": {
"front_images": "ru/gun/ru76PPobr27.png"
}
},
"3b5:10093": {
"expected": {
"name": "SL truck",
"front_images": [ "sh/SL3b(KFW).png", "sh/SL4b(KFW).png", "sh/SL5b(KFW).png", "sh/SL6b(KFW).png", "sh/SL1b(KFW).png", "sh/SL2b(KFW).png" ],
"back_images": [ "sh/SL3(KFW).png", "sh/SL4(KFW).png", "sh/SL5(KFW).png", "sh/SL6(KFW).png", "sh/SL1(KFW).png", "sh/SL2(KFW).png" ]
},
"updated": {
"front_images": "us/veh/usSearchlight(KFW).png",
"back_images": null
}
},
"08d:75": {
"expected": {
"name": "RCL 75*",
"front_images": "amrcl75-malf.png",
"back_images": "dm-75rcl.gif"
},
"updated": {
"front_images": "amrcl75.png"
}
}
}

@ -0,0 +1,72 @@
{
"1555": {
"name": "2pdr Portee",
"front_images": [ "br/vehicles/portee.gif", "br/vehicles/portee0.gif" ],
"back_images": null
},
"2212": {
"name": "76* INF FRC",
"front_images": [ "al/gun/alINF76.gif", "al/gun/alINF76u.gif" ],
"back_images": "al/gun/alINF76b.gif"
},
"2698": {
"name": "SPW 251/10",
"front_images": "ge/veh/spw25110.gif",
"back_images": [ "No_ATR.gif", "No_PSK.gif" ]
},
"6765": {
"name": "81* MTR Krh/32",
"front_images": "fi/gun/fi81mmMTR.png",
"back_images": [ "fi/gun/fi81mmMTR.png", "fi/gun/fi81mmMTRB.png" ]
},
"6782": {
"name": "81* MTR Savu M42",
"front_images": [ "fi/gun/fi81mmMTR SavuB.png", "fi/gun/fi81mmMTR Savu.png" ],
"back_images": "fi/gun/fi81mmMTR SavuB.png"
},
"6797": {
"name": "20L (4) AA (g)",
"front_images": [ "fi/gun/fi20L4.png", "fi/gun/fi20L4L.png" ]
},
"6801": {
"name": "20L (6) AA (g)",
"front_images": "fi/gun/fi20L6.png",
"back_images": [ "fi/gun/fi20L6.png", "fi/gun/fi20L6L.png" ]
},
"7409": {
"name": "76 ItK/28 B(s)",
"front_images": "fi/gun/fiAA76L.png",
"back_images": [ "fi/gun/fiAA76L.png", "fi/gun/fiAA76LB.png" ]
},
"adf:1828": {
"name": "105 ART wz.29",
"front_images": "po/gun/poARTwz29-BFP.png",
"back_images": [ "po/gun/poARTwz29-BFP.png", "po/gun/poARTwz29-BFPb.png" ]
},
"adf:1829": {
"name": "120* ART wz09.31",
"front_images": "po/gun/poARTwz0931-BFP.png",
"back_images": [ "po/gun/poARTwz0931-BFP.png", "po/gun/poARTwz0931-BFPb.png" ]
},
"adf:1830": {
"name": "155 ART wz.17",
"front_images": "po/gun/poARTwz17-BFP.png",
"back_images": [ "po/gun/poARTwz17-BFP.png", "po/gun/poARTwz17-BFPb.png" ]
},
"3b5:3676": {
"name": "M19A1 MGMC",
"front_images": [ "us/veh/usM19A1MGMC(trailer)KFW.png", "us/veh/usM19A1MGMC(KFW).png" ],
"back_images": null
}
}

@ -0,0 +1,71 @@
{
"6996": {"is_small": true},
"485": {"is_small": true},
"850": {"is_small": true},
"849": {"is_small": true},
"12689": {"is_small": true},
"856": {"is_small": true},
"857": {"is_small": true},
"11336": {"is_small": true},
"858": {"is_small": true},
"11337": {"is_small": true},
"1149": {"is_small": true},
"1153": {"is_small": true},
"12687": {"is_small": true},
"3b5:7613": {"is_small": true},
"11359": {"is_small": true},
"3b5:11259": {"is_small": true},
"1632": {"is_small": true},
"1633": {"is_small": true},
"1648": {"is_small": true},
"12830": {"is_small": true},
"1982": {"is_small": true},
"1983": {"is_small": true},
"1984": {"is_small": true},
"1985": {"is_small": true},
"1986": {"is_small": true},
"1987": {"is_small": true},
"1988": {"is_small": true},
"2172": {"is_small": true},
"2173": {"is_small": true},
"2176": {"is_small": true},
"2179": {"is_small": true},
"11391": {"is_small": true},
"11392": {"is_small": true},
"11395": {"is_small": true},
"11396": {"is_small": true},
"11440": {"is_small": true},
"3b5:8401": {"is_small": true},
"3b5:8402": {"is_small": true},
"2465": {"is_small": true},
"2474": {"is_small": true},
"3252": {"is_small": true},
"3253": {"is_small": true},
"3263": {"is_small": true},
"3422": {"is_small": true},
"3428": {"is_small": true},
"6730": {"is_small": true},
"3605": {"is_small": true},
"3608": {"is_small": true},
"6763": {"is_small": true},
"3679": {"is_small": true},
"3680": {"is_small": true},
"3681": {"is_small": true},
"3682": {"is_small": true},
"3691": {"is_small": true},
"3692": {"is_small": true},
"3959": {"is_small": true},
"11558": {"is_small": true},
"11559": {"is_small": true},
"3b5:10150": {"is_small": true},
"3b5:10151": {"is_small": true},
"11600": {"is_small": true},
"11604": {"is_small": true},
"3b5:7871": {"is_small": true},
"adf:1948": {"is_small": true},
"adf:75": {"is_small": true},
"adf:77": {"is_small": true},
"adf:76": {"is_small": true},
"adf:1407": {"is_small": true},
"08d:75": {"is_small": true}
}

@ -0,0 +1,147 @@
{
"2474": {
"expected": {
"name": "Goliath",
"front_images": [ "ge/gegol.gif", "ge/gegolb.gif" ],
"back_images": null
},
"updated": {
"front_images": "ge/gegol.gif"
}
},
"1555": {
"expected": {
"name": "2pdr Portee",
"front_images": "br/vehicles/portee.gif",
"back_images": [ "br/vehicles/portee.gif", "br/vehicles/portee0.gif" ]
},
"updated": {
"front_images": [ "br/vehicles/portee.gif", "br/vehicles/portee0.gif" ],
"back_images": null
}
},
"3463": {
"expected": {
"name": "75L AA 75/46",
"front_images": [ "it/gun/itAA7546.gif", "it/gun/itAA7546b.gif" ],
"back_images": [ "it/gun/itAA7546b.gif", "it/gun/itAA7546lb.gif" ]
},
"updated": {
"front_images": "it/gun/itAA7546.gif",
"back_images": "it/gun/itAA7546b.gif"
}
},
"3776": {
"expected": {
"name": "37* INF Skoda IG",
"front_images": [ "ax/gun/buIN37s.gif", "ax/gun/buIN37s2.gif" ],
"back_images": "ax/gun/buIN37sb.gif"
},
"updated": {
"front_images": "ax/gun/buIN37s.gif"
}
},
"3777": {
"expected": {
"name": "70* INF Skoda IG",
"front_images": [ "ax/gun/buIN37s.gif", "ax/gun/buIN37s2.gif" ],
"back_images": "ax/gun/buIN37sb.gif"
},
"updated": {
"front_images": "ax/gun/buIN37s2.gif"
}
},
"6802": {
"expected": {
"name": "20L (4) AA",
"front_images": [ "fi/gun/fi20L4 _2.png", "fi/gun/fi20L4 _2 LIM.png" ],
"back_images": null
},
"updated": {
"front_images": "fi/gun/fi20L4 _2.png"
}
},
"6803": {
"expected": {
"name": "20L VKT (12) AA",
"front_images": [ "fi/gun/fi20L12.png", "fi/gun/fi20L12L.png" ],
"back_images": null
},
"updated": {
"front_images": "fi/gun/fi20L12.png"
}
},
"6804": {
"expected": {
"name": "40L Bofors AA (s)",
"front_images": [ "fi/gun/fi40L.png", "fi/gun/fi40LL.png" ],
"back_images": null
},
"updated": {
"front_images": "fi/gun/fi40L.png"
}
},
"adf:1824": {
"expected": {
"name": "37L AT PTP obr. 30",
"front_images": "ru/gun/ruAT37L.gif",
"back_images": "ru/gun/ruAT37Lb.gif"
},
"updated": {
"front_images": "ru/gun/ru37LPTPobr30.png"
}
},
"adf:1822": {
"expected": {
"name": "37* INF PP obr. 15R",
"front_images": "ru/gun/ruINF37s.gif",
"back_images": "ru/gun/ruINF37sb.gif"
},
"updated": {
"front_images": "ru/gun/ru37PPobr15R.png"
}
},
"adf:1823": {
"expected": {
"name": "76* INF PP obr. 27",
"front_images": "ru/gun/ruINF76s.gif",
"back_images": "ru/gun/ruINF76sb.gif"
},
"updated": {
"front_images": "ru/gun/ru76PPobr27.png"
}
},
"3b5:10093": {
"expected": {
"name": "SL truck",
"front_images": [ "sh/SL3b(KFW).png", "sh/SL4b(KFW).png", "sh/SL5b(KFW).png", "sh/SL6b(KFW).png", "sh/SL1b(KFW).png", "sh/SL2b(KFW).png" ],
"back_images": [ "sh/SL3(KFW).png", "sh/SL4(KFW).png", "sh/SL5(KFW).png", "sh/SL6(KFW).png", "sh/SL1(KFW).png", "sh/SL2(KFW).png" ]
},
"updated": {
"front_images": "us/veh/usSearchlight(KFW).png",
"back_images": null
}
},
"08d:75": {
"expected": {
"name": "RCL 75*",
"front_images": "amrcl75-malf.png",
"back_images": "dm-75rcl.gif"
},
"updated": {
"front_images": "amrcl75.png"
}
}
}

@ -65,6 +65,8 @@ class DownloadedFile:
if len(data) < 1024 and os.path.isfile( data ):
with open( data, "r", encoding="utf-8" ) as fp:
data = fp.read()
if len( data ) == 0:
return
self._data = json.loads( data )
# notify the owner
if self.on_data:
@ -115,7 +117,7 @@ class DownloadedFile:
_logger.info( "Download of the %s file has been disabled.", df.key )
continue
ttl *= 60*60
if os.path.isfile( df.cache_fname ):
if os.path.isfile( df.cache_fname ) and os.stat( df.cache_fname ).st_size > 0:
# yup - check how long ago it was downloaded
mtime = os.path.getmtime( df.cache_fname )
age = int( time.time() - mtime )

@ -235,9 +235,13 @@ def get_program_info():
return tstamp + timedelta( minutes=tz_offset )
# set the basic details
try:
vassal_version = VassalShim.get_version()
except Exception: #pylint: disable=broad-except
vassal_version = "???"
params = {
"APP_VERSION": vasl_templates.webapp.config.constants.APP_VERSION,
"VASSAL_VERSION": VassalShim.get_version()
"VASSAL_VERSION": vassal_version
}
build_git_info = get_build_git_info()
if build_git_info:
@ -283,7 +287,7 @@ def get_program_info():
params[ "DOCKER_IMAGE_NAME" ] = os.environ.get( "DOCKER_IMAGE_NAME" )
params[ "DOCKER_IMAGE_TIMESTAMP" ] = datetime.strftime(
parse_timestamp( os.environ.get( "DOCKER_IMAGE_TIMESTAMP" ) ),
"%H:%M %d %b %Y"
"%d %b %Y %H:%M"
)
params[ "DOCKER_CONTAINER_NAME" ] = os.environ.get( "DOCKER_CONTAINER_NAME" )
with open( "/proc/self/cgroup", "r", encoding="utf-8" ) as fp:

@ -539,7 +539,7 @@ def prepare_asa_upload(): #pylint: disable=too-many-locals
max_size = parse_int( app.config.get( "ASA_MAX_SCREENSHOT_SIZE" ), 200 ) * 1024
if len(screenshot_data) > max_size:
ratio = math.sqrt( float(max_size) / len(screenshot_data) )
img = img.resize( ( int(img.width * ratio), int(img.height * ratio) ), Image.ANTIALIAS )
img = img.resize( ( int(img.width * ratio), int(img.height * ratio) ), Image.LANCZOS )
# add a border
border_size = parse_int( app.config.get( "ASA_SCREENSHOT_BORDER_SIZE" ), 5 )
img = ImageOps.expand( img, border_size, (255,255,255,255) )

@ -115,7 +115,7 @@ function _show_extra_template( template_id )
buf.push( "<option>", param.options[j], "</option>" ) ;
buf.push( "</select>" ) ;
} else if ( param.type === "player-droplist" ) {
buf.push( "<select name='_PLAYER_DROPLIST_' style='width:11.5em;'>" ) ;
buf.push( "<select class='param' name='PLAYER_DROPLIST' style='width:11.5em;'>" ) ;
add_player_nats( buf ) ;
buf.push( "</select>" ) ;
} else if ( param.type === "player-color-droplist" ) {

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

@ -54,8 +54,8 @@ If you have Docker installed, the webapp can be run in a container e.g.
<div class="code">
./run-container.sh \
--port 5010 \
--vassal ~/vassal-3.6.7/ \
--vasl ~/vasl/vasl-6.6.5.vmod \
--vassal ~/vassal-3.7.9/ \
--vasl ~/vasl/vasl-6.6.8.vmod \
--vasl-extensions ~/vasl/extensions/ \
--boards ~/vasl/boards/ \
--chapter-h ~/vasl/chapter-h/ \
@ -73,7 +73,7 @@ If you have Docker installed, the webapp can be run in a container e.g.
<p> You can also run the program directly from the source code. Get a copy from <a href="https://code.pacman-ghost.com/public/vasl-templates">here</a> in the usual way, by <tt>git clone</tt>'ing it, or downloading a ZIP and unpacking it somewhere.
<p> The web server was written and tested using Python 3.10.4, but it doesn't do anything particularly funky, so any recent version of Python <em>should</em> work.
<p> The web server was written and tested using Python 3.11.4, but it doesn't do anything particularly funky, so any recent version of Python <em>should</em> work.
<p> While not essential, it is <em>strongly</em> recommended that you set up a <a href="https://virtualenv.pypa.io/en/stable/">virtual environment</a> first. Then <tt>cd</tt> into the project's root directory, and install the requirements (note the trailing period):
<div class="code">
@ -101,6 +101,18 @@ pip install --editable .[gui]
<p> Then, run the <tt>vasl-templates</tt> command.
<a name="asl-rulebook2"></a>
<h2> Integrating with <tt>asl-rulebook2</tt></h2>
<p> <img src="images/asl-rulebook2.png" class="preview" style="float:right;">
If you are using <a href="https://code.pacman-ghost.com/public/asl-rulebook2"><tt>asl-rulebook2</tt></a>, you can connect it to this program, and vehicle/ordnance entries will show an icon that you can click to open their Chapter H entries.
<p> In your <tt>site.cfg</tt>, configure the base URL of the <tt>asl-rulebook2</tt> application e.g.
<div class="code">
[Site Config]
ASL_RULEBOOK2_BASE_URL = http://localhost:5020
</div>
<a name="install-webdriver"></a>
<h2>Installing a webdriver</h2>
@ -154,11 +166,11 @@ Configuring the program
<p> Choose <em>Settings</em> from the <em>File</em> menu and configure the highlighted settings. As a guide, here are some example settings:
<table class="settings">
<tr> <td class="key"> VASSAL&nbsp;installation: </td> <td class="val"> <nobr>C:\Program Files\VASSAL-3.6.7\</nobr>
<tr> <td class="key"> VASL&nbsp;module: </td> <td class="val"> <nobr>C:\bin\vasl\vasl-6.6.5.vmod</nobr>
<tr> <td class="key"> VASSAL&nbsp;installation: </td> <td class="val"> <nobr>C:\Program Files\VASSAL-3.7.9\</nobr>
<tr> <td class="key"> VASL&nbsp;module: </td> <td class="val"> <nobr>C:\bin\vasl\vasl-6.6.8.vmod</nobr>
<tr> <td class="key"> VASL&nbsp;extensions: </td> <td class="val"> <nobr>C:\bin\vasl\extensions\</nobr>
<tr> <td class="key"> VASL&nbsp;boards: </td> <td class="val"> <nobr>C:\bin\vasl\boards\</nobr>
<tr> <td class="key" valign="top"> Java: </td> <td class="val"> <nobr>C:\Program Files\VASSAL-3.6.7\jre\bin\java.exe</nobr>
<tr> <td class="key" valign="top"> Java: </td> <td class="val"> <nobr>C:\Program Files\VASSAL-3.7.9\jre\bin\java.exe</nobr>
<div class="hint" style="margin:0.5em 1em;"> Leave this field blank to use the Java that comes with VASSAL (Windows only), or on your PATH. </div>
<tr> <td class="key"> Web&nbsp;driver: </td> <td class="val"> <nobr>C:\bin\geckodriver.exe</nobr>
</table>
@ -548,6 +560,13 @@ When you're writing a new template file, it would be painful to have to ZIP up a
<a name="dev-setup"></a>
<h2 style="margin-top:0;"> Setting up </h2>
<div class="info" style="float:right;max-width:40%;margin-left:1em;">
If you want to run the GUI, you will also need to install the GUI requirements:
<div class="code">
pip install --editable .[gui]
</div>
</div>
<p> After cloning the repo, install the <em>developer</em> requirements:
<div class="code">
pip install --editable .[dev]

@ -20,7 +20,7 @@ $(document).ready( function() {
$( "a" ).each( function() {
$(this).click( function(evt) {
var url = $(this).attr( "href" ) ;
if ( url[0] !== "#" && url.substring(0,16) !== "http://localhost" && url.substring(0,16) !== "http://127.0.0.1" && url.indexOf( "code.pacman-ghost.com" ) === -1 ) {
if ( url[0] !== "#" && url.substring(0,16) !== "http://localhost" && url.substring(0,16) !== "http://127.0.0.1" ) {
window.open( url ) ;
evt.preventDefault() ;
return false ;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 438 B

After

Width:  |  Height:  |  Size: 446 B

@ -77,8 +77,6 @@ $(document).ready( function () {
program_info: { label: "Program info", icon: imagesDir+"/info.png", action: show_program_info },
show_help: { label: "Help", icon: imagesDir+"/help.png", action: show_help },
} ;
if ( getUrlParam( "pyqt" ) )
delete menuItems.program_info ;
$menu.popmenu( menuItems ) ;
// nb: we only show the popmenu on left click (not the normal right-click)
$menu.off( "contextmenu" ) ;

@ -105,8 +105,8 @@ function _generate_snippets()
// figure out which templates we don't want to auto-create labels for
var no_autocreate = {} ;
for ( var template_id in NATIONALITY_SPECIFIC_BUTTONS ) {
if ( ["pf","atmm","thh"].indexOf( template_id ) !== -1 || template_id.substring(0,3) === "pf-" ) {
// NOTE: PF, ATMM, THH are always available as an inherent part of a squad's capabilities
if ( ["pf","atmm"].indexOf( template_id ) !== -1 || template_id.substring(0,3) === "pf-" ) {
// NOTE: PF and ATMM are always available as an inherent part of a squad's capabilities
// (subject to date restrictions), so we always auto-create these labels, unlike, say MOL or BAZ,
// which are only present by SSR or OB counter).
continue ;

@ -141,34 +141,66 @@
<script src="{{url_for('static',filename='trumbowyg/plugins/table/trumbowyg.table.min.js')}}"></script>
<script src="{{url_for('static',filename='DOMPurify/purify.min.js')}}"></script>
<script src="{{url_for('static',filename='utils.js')}}"></script>
<script>
function removeUrlTestParams( url )
{
// remove any URL parameters
// NOTE: We can't use URL.searchParams since we might be passed a relative URL.
// FUDGE! The test suite is giving us absolute, encoded URL's :-/
url = strReplaceAll( url, "&amp;", "&" ) ;
var match = url.match( new RegExp( "^http://.+?:\\d+?/" ) ) ;
if ( match )
url = url.substring( match[0].length - 1 ) ;
var pos = url.indexOf( "?" ) ;
if ( pos >= 0 ) {
params = url.substring( pos+1 ).split( "&" ) ;
url = url.substring( 0, pos ) ;
var nKeeps = 0 ;
params.forEach( function( param ) {
var keep = true ;
[ "store_msgs", "disable_close_window_check", "store_clipboard" ].forEach( function( key ) {
if ( param.substring( 0, key.length+1 ) === key + "=" )
keep = false ;
} ) ;
if ( keep ) {
url += ( nKeeps === 0 ) ? "?" : "&" ;
url += param ;
nKeeps += 1 ;
}
} ) ;
}
return url ;
}
gAppName = "{{APP_NAME}}" ;
gAppVersion = "{{APP_VERSION}}" ;
gImagesBaseUrl = "{{url_for('static',filename='images')}}" ;
gAppConfigUrl = "{{url_for('get_app_config')}}" ;
gGetStartupMsgsUrl = "{{url_for('get_startup_msgs')}}" ;
gGetTemplatePackUrl = "{{url_for('get_template_pack')}}" ;
gGetAslRulebook2VoNoteTargetsUrl = "{{url_for('get_asl_rulebook2_vo_note_targets')}}" ;
gShowAslRulebook2VoNoteUrl = "{{url_for('show_asl_rulebook2_target',target='TARGET')}}" ;
gGetDefaultScenarioUrl = "{{url_for('get_default_scenario')}}" ;
gVehicleListingsUrl = "{{url_for('get_vehicle_listings',merge_common=1)}}" ;
gOrdnanceListingsUrl = "{{url_for('get_ordnance_listings',merge_common=1)}}" ;
gVehicleNotesUrl = "{{url_for('get_vehicle_notes')}}" ;
gOrdnanceNotesUrl = "{{url_for('get_ordnance_notes')}}" ;
gGetVaslPieceInfoUrl = "{{url_for('get_vasl_piece_info')}}" ;
gGetOnlineCounterImagesUrl = "{{url_for('get_online_counter_images')}}" ;
gGetScenarioIndexUrl = "{{url_for('get_scenario_index')}}" ;
gGetScenarioUrl = "{{url_for('get_scenario',scenario_id='ID')}}" ;
gGetScenarioCardUrl = "{{url_for('get_scenario_card',scenario_id='ID')}}" ;
gGetRoarScenarioIndexUrl = "{{url_for('get_roar_scenario_index')}}" ;
gAnalyzeVsavUrl = "{{url_for('analyze_vsav')}}" ;
gAnalyzeVlogsUrl = "{{url_for('analyze_vlogs')}}" ;
gUpdateVsavUrl = "{{url_for('update_vsav')}}" ;
gPrepareAsaUploadUrl = "{{url_for('prepare_asa_upload')}}" ;
gOnSuccessfulAsaUploadUrl = "{{url_for('on_successful_asa_upload',scenario_id='ID')}}" ;
gMakeSnippetImageUrl = "{{url_for('make_snippet_image')}}" ;
gGetProgramInfoUrl = "{{url_for('get_program_info')}}" ;
gHelpUrl = "{{url_for('show_help')}}" ;
gImagesBaseUrl = removeUrlTestParams( "{{url_for('static',filename='images')}}" ) ;
gAppConfigUrl = removeUrlTestParams( "{{url_for('get_app_config')}}" ) ;
gGetStartupMsgsUrl = removeUrlTestParams( "{{url_for('get_startup_msgs')}}" ) ;
gGetTemplatePackUrl = removeUrlTestParams( "{{url_for('get_template_pack')}}" ) ;
gGetAslRulebook2VoNoteTargetsUrl = removeUrlTestParams( "{{url_for('get_asl_rulebook2_vo_note_targets')}}" ) ;
gShowAslRulebook2VoNoteUrl = removeUrlTestParams( "{{url_for('show_asl_rulebook2_target',target='TARGET')}}" ) ;
gGetDefaultScenarioUrl = removeUrlTestParams( "{{url_for('get_default_scenario')}}" ) ;
gVehicleListingsUrl = removeUrlTestParams( "{{url_for('get_vehicle_listings',merge_common=1)}}" ) ;
gOrdnanceListingsUrl = removeUrlTestParams( "{{url_for('get_ordnance_listings',merge_common=1)}}" ) ;
gVehicleNotesUrl = removeUrlTestParams( "{{url_for('get_vehicle_notes')}}" ) ;
gOrdnanceNotesUrl = removeUrlTestParams( "{{url_for('get_ordnance_notes')}}" ) ;
gGetVaslPieceInfoUrl = removeUrlTestParams( "{{url_for('get_vasl_piece_info')}}" ) ;
gGetOnlineCounterImagesUrl = removeUrlTestParams( "{{url_for('get_online_counter_images')}}" ) ;
gGetScenarioIndexUrl = removeUrlTestParams( "{{url_for('get_scenario_index')}}" ) ;
gGetScenarioUrl = removeUrlTestParams( "{{url_for('get_scenario',scenario_id='ID')}}" ) ;
gGetScenarioCardUrl = removeUrlTestParams( "{{url_for('get_scenario_card',scenario_id='ID')}}" ) ;
gGetRoarScenarioIndexUrl = removeUrlTestParams( "{{url_for('get_roar_scenario_index')}}" ) ;
gAnalyzeVsavUrl = removeUrlTestParams( "{{url_for('analyze_vsav')}}" ) ;
gAnalyzeVlogsUrl = removeUrlTestParams( "{{url_for('analyze_vlogs')}}" ) ;
gUpdateVsavUrl = removeUrlTestParams( "{{url_for('update_vsav')}}" ) ;
gPrepareAsaUploadUrl = removeUrlTestParams( "{{url_for('prepare_asa_upload')}}" ) ;
gOnSuccessfulAsaUploadUrl = removeUrlTestParams( "{{url_for('on_successful_asa_upload',scenario_id='ID')}}" ) ;
gMakeSnippetImageUrl = removeUrlTestParams( "{{url_for('make_snippet_image')}}" ) ;
gGetProgramInfoUrl = removeUrlTestParams( "{{url_for('get_program_info')}}" ) ;
gHelpUrl = removeUrlTestParams( "{{url_for('show_help')}}" ) ;
</script>
<script src="{{url_for('static',filename='main.js')}}"></script>
@ -190,7 +222,6 @@ gHelpUrl = "{{url_for('show_help')}}" ;
<script src="{{url_for('static',filename='user_settings.js')}}"></script>
<script src="{{url_for('static',filename='html-editor.js')}}"></script>
<script src="{{url_for('static',filename='jQueryHandlers.js')}}"></script>
<script src="{{url_for('static',filename='utils.js')}}"></script>
<script src="{{url_for('static',filename='timer.js')}}"></script>
{%include "testing.html"%}

@ -9,6 +9,7 @@ from google.protobuf.empty_pb2 import Empty #pylint: disable=no-name-in-module
from vasl_templates.webapp.tests.proto.generated.control_tests_pb2_grpc import ControlTestsStub
from vasl_templates.webapp.tests.proto.utils import enum_from_string
#pylint: disable=no-name-in-module
from vasl_templates.webapp.tests.proto.generated.control_tests_pb2 import \
SetVassalVersionRequest, SetVaslVersionRequest, SetVaslExtnInfoDirRequest, SetGpidRemappingsRequest, \
SetDataDirRequest, SetDefaultScenarioRequest, SetDefaultTemplatePackRequest, \
@ -17,6 +18,7 @@ from vasl_templates.webapp.tests.proto.generated.control_tests_pb2 import \
DumpVsavRequest, GetVaslPiecesRequest, \
SetAppConfigValRequest, DeleteAppConfigValRequest, \
SaveTempFileRequest
#pylint: enable=no-name-in-module
# ---------------------------------------------------------------------

@ -28,6 +28,7 @@ from vasl_templates.webapp import \
from vasl_templates.webapp.tests.proto.generated.control_tests_pb2_grpc \
import ControlTestsServicer as BaseControlTestsServicer
#pylint: disable=no-name-in-module
from vasl_templates.webapp.tests.proto.generated.control_tests_pb2 import \
SetVassalVersionRequest, SetVaslVersionRequest, SetVaslExtnInfoDirRequest, SetGpidRemappingsRequest, \
SetDataDirRequest, SetDefaultScenarioRequest, SetDefaultTemplatePackRequest, \
@ -39,6 +40,7 @@ from vasl_templates.webapp.tests.proto.generated.control_tests_pb2 import \
GetVassalVersionsResponse, GetVaslVersionsResponse, GetVaslExtnsResponse, GetVaslModWarningsResponse, \
GetLastSnippetImageResponse, GetLastAsaUploadResponse, \
DumpVsavResponse, GetVaslPiecesResponse, GetAppConfigResponse
#pylint: enable=no-name-in-module
# nb: these are defined as a convenience
_VaslExtnsTypes_NONE = SetVaslVersionRequest.VaslExtnsType.NONE #pylint: disable=no-member

@ -412,9 +412,9 @@ GPID Name Front images
1575 2-1/2 ton Truck(a) br/vehicles/ton212.gif
1577 7-1/2 ton Truck(a) br/vehicles/ton712.gif
1632 37* INF fr/frINF.gif fr/frINFb.gif
1636 50 MTR(f) fr/frMTR.gif fr/frMTRb.gif
1641 60* MTR(a) br/brMTRa.gif br/brMTRab.gif
1633 60* MTR fr/frMTR60.gif fr/frMTR60b.gif
1648 ATR fr/frATR.gif fr/frATRb.gif
1665 81* MTR mle 27/31 fr/gun/frMTR81s.gif fr/gun/frMTR81sb.gif
1667 81* MTR(f) mle 27/31 fr/gun/frMTR81sf.gif fr/gun/frMTR81sfb.gif
1669 25LL AT SA-L mle 34 fr/gun/frAT25LL.gif fr/gun/frAT25LLb.gif
1670 47L AT SA mle 37 APX <41 fr/gun/frAT47L-40.gif fr/gun/frAT47Lb.gif
@ -1281,6 +1281,7 @@ GPID Name Front images
12687 OML 2in MTR (KW) br/brMTR.gif br/brMTRb.gif
12689 60* MTR M2 (KW) am/amMTR.gif am/amMTRb.gif
12730 IP Carrier AOV br/vehicles/ipcaov.gif
12830 50 MTR ff/ffMTR50.png ff/ffMTR50b.png
3b5:10093 SL truck us/veh/usSearchlight(KFW).png
3b5:10114 57LL AT PTP obr. 43 cc/gun/ccAT57LL(KFW).png cc/gun/ccAT57LLm(KFW).png
3b5:10115 70* INF Type 92 cc/gun/ccINF70(KFW).png cc/gun/ccINF70m(KFW).png

@ -412,9 +412,9 @@ GPID Name Front images
1575 2-1/2 ton Truck(a) br/vehicles/ton212.gif
1577 7-1/2 ton Truck(a) br/vehicles/ton712.gif
1632 37* INF fr/frINF.gif fr/frINFb.gif
1636 50 MTR(f) fr/frMTR.gif fr/frMTRb.gif
1641 60* MTR(a) br/brMTRa.gif br/brMTRab.gif
1633 60* MTR fr/frMTR60.gif fr/frMTR60b.gif
1648 ATR fr/frATR.gif fr/frATRb.gif
1665 81* MTR mle 27/31 fr/gun/frMTR81s.gif fr/gun/frMTR81sb.gif
1667 81* MTR(f) mle 27/31 fr/gun/frMTR81sf.gif fr/gun/frMTR81sfb.gif
1669 25LL AT SA-L mle 34 fr/gun/frAT25LL.gif fr/gun/frAT25LLb.gif
1670 47L AT SA mle 37 APX <41 fr/gun/frAT47L-40.gif fr/gun/frAT47Lb.gif
@ -1281,6 +1281,7 @@ GPID Name Front images
12687 OML 2in MTR (KW) br/brMTR.gif br/brMTRb.gif
12689 60* MTR M2 (KW) am/amMTR.gif am/amMTRb.gif
12730 IP Carrier AOV br/vehicles/ipcaov.gif
12830 50 MTR ff/ffMTR50.png ff/ffMTR50b.png
3b5:10093 SL truck us/veh/usSearchlight(KFW).png
3b5:10114 57LL AT PTP obr. 43 cc/gun/ccAT57LL(KFW).png cc/gun/ccAT57LLm(KFW).png
3b5:10115 70* INF Type 92 cc/gun/ccINF70(KFW).png cc/gun/ccINF70m(KFW).png

@ -412,9 +412,9 @@ GPID Name Front images
1575 2-1/2 ton Truck(a) br/vehicles/ton212.gif
1577 7-1/2 ton Truck(a) br/vehicles/ton712.gif
1632 37* INF fr/frINF.gif fr/frINFb.gif
1636 50 MTR(f) fr/frMTR.gif fr/frMTRb.gif
1641 60* MTR(a) br/brMTRa.gif br/brMTRab.gif
1633 60* MTR fr/frMTR60.gif fr/frMTR60b.gif
1648 ATR fr/frATR.gif fr/frATRb.gif
1665 81* MTR mle 27/31 fr/gun/frMTR81s.gif fr/gun/frMTR81sb.gif
1667 81* MTR(f) mle 27/31 fr/gun/frMTR81sf.gif fr/gun/frMTR81sfb.gif
1669 25LL AT SA-L mle 34 fr/gun/frAT25LL.gif fr/gun/frAT25LLb.gif
1670 47L AT SA mle 37 APX <41 fr/gun/frAT47L-40.gif fr/gun/frAT47Lb.gif
@ -1281,6 +1281,7 @@ GPID Name Front images
12687 OML 2in MTR (KW) br/brMTR.gif br/brMTRb.gif
12689 60* MTR M2 (KW) am/amMTR.gif am/amMTRb.gif
12730 IP Carrier AOV br/vehicles/ipcaov.gif
12830 50 MTR ff/ffMTR50.png ff/ffMTR50b.png
3b5:10093 SL truck us/veh/usSearchlight(KFW).png
3b5:10114 57LL AT PTP obr. 43 cc/gun/ccAT57LL(KFW).png cc/gun/ccAT57LLm(KFW).png
3b5:10115 70* INF Type 92 cc/gun/ccINF70(KFW).png cc/gun/ccINF70m(KFW).png

@ -412,9 +412,9 @@ GPID Name Front images
1575 2-1/2 ton Truck(a) br/vehicles/ton212.gif
1577 7-1/2 ton Truck(a) br/vehicles/ton712.gif
1632 37* INF fr/frINF.gif fr/frINFb.gif
1636 50 MTR(f) fr/frMTR.gif fr/frMTRb.gif
1641 60* MTR(a) br/brMTRa.gif br/brMTRab.gif
1633 60* MTR fr/frMTR60.gif fr/frMTR60b.gif
1648 ATR fr/frATR.gif fr/frATRb.gif
1665 81* MTR mle 27/31 fr/gun/frMTR81s.gif fr/gun/frMTR81sb.gif
1667 81* MTR(f) mle 27/31 fr/gun/frMTR81sf.gif fr/gun/frMTR81sfb.gif
1669 25LL AT SA-L mle 34 fr/gun/frAT25LL.gif fr/gun/frAT25LLb.gif
1670 47L AT SA mle 37 APX <41 fr/gun/frAT47L-40.gif fr/gun/frAT47Lb.gif
@ -1281,6 +1281,7 @@ GPID Name Front images
12687 OML 2in MTR (KW) br/brMTR.gif br/brMTRb.gif
12689 60* MTR M2 (KW) am/amMTR.gif am/amMTRb.gif
12730 IP Carrier AOV br/vehicles/ipcaov.gif
12830 50 MTR ff/ffMTR50.png ff/ffMTR50b.png
3b5:10093 SL truck us/veh/usSearchlight(KFW).png
3b5:10114 57LL AT PTP obr. 43 cc/gun/ccAT57LL(KFW).png cc/gun/ccAT57LLm(KFW).png
3b5:10115 70* INF Type 92 cc/gun/ccINF70(KFW).png cc/gun/ccINF70m(KFW).png

@ -412,9 +412,9 @@ GPID Name Front images
1575 2-1/2 ton Truck(a) br/vehicles/ton212.gif
1577 7-1/2 ton Truck(a) br/vehicles/ton712.gif
1632 37* INF fr/frINF.gif fr/frINFb.gif
1636 50 MTR(f) fr/frMTR.gif fr/frMTRb.gif
1641 60* MTR(a) br/brMTRa.gif br/brMTRab.gif
1633 60* MTR fr/frMTR60.gif fr/frMTR60b.gif
1648 ATR fr/frATR.gif fr/frATRb.gif
1665 81* MTR mle 27/31 fr/gun/frMTR81s.gif fr/gun/frMTR81sb.gif
1667 81* MTR(f) mle 27/31 fr/gun/frMTR81sf.gif fr/gun/frMTR81sfb.gif
1669 25LL AT SA-L mle 34 fr/gun/frAT25LL.gif fr/gun/frAT25LLb.gif
1670 47L AT SA mle 37 APX <41 fr/gun/frAT47L-40.gif fr/gun/frAT47Lb.gif
@ -1281,6 +1281,7 @@ GPID Name Front images
12687 OML 2in MTR (KW) br/brMTR.gif br/brMTRb.gif
12689 60* MTR M2 (KW) am/amMTR.gif am/amMTRb.gif
12730 IP Carrier AOV br/vehicles/ipcaov.gif
12830 50 MTR ff/ffMTR50.png ff/ffMTR50b.png
13832 81* MTR sv/gun/svMTR81s.gif sv/gun/svMTR81sb.gif
13835 37L AT sv/gun/svAT37L.gif sv/gun/svAT37Lb.gif
13836 84* ART sv/gun/svART84s.gif sv/gun/svART84sb.gif

@ -412,9 +412,9 @@ GPID Name Front images
1575 2-1/2 ton Truck(a) br/vehicles/ton212.gif
1577 7-1/2 ton Truck(a) br/vehicles/ton712.gif
1632 37* INF fr/frINF.gif fr/frINFb.gif
1636 50 MTR(f) fr/frMTR.gif fr/frMTRb.gif
1641 60* MTR(a) br/brMTRa.gif br/brMTRab.gif
1633 60* MTR fr/frMTR60.gif fr/frMTR60b.gif
1648 ATR fr/frATR.gif fr/frATRb.gif
1665 81* MTR mle 27/31 fr/gun/frMTR81s.gif fr/gun/frMTR81sb.gif
1667 81* MTR(f) mle 27/31 fr/gun/frMTR81sf.gif fr/gun/frMTR81sfb.gif
1669 25LL AT SA-L mle 34 fr/gun/frAT25LL.gif fr/gun/frAT25LLb.gif
1670 47L AT SA mle 37 APX <41 fr/gun/frAT47L-40.gif fr/gun/frAT47Lb.gif
@ -1281,6 +1281,7 @@ GPID Name Front images
12687 OML 2in MTR (KW) br/brMTR.gif br/brMTRb.gif
12689 60* MTR M2 (KW) am/amMTR.gif am/amMTRb.gif
12730 IP Carrier AOV br/vehicles/ipcaov.gif
12830 50 MTR ff/ffMTR50.png ff/ffMTR50b.png
13832 81* MTR sv/gun/svMTR81s.gif sv/gun/svMTR81sb.gif
13835 37L AT sv/gun/svAT37L.gif sv/gun/svAT37Lb.gif
13836 84* ART sv/gun/svART84s.gif sv/gun/svART84sb.gif

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

@ -1,6 +1,6 @@
""" Injected functions for SetDataDirRequest. """
from .generated.control_tests_pb2 import SetDataDirRequest
from .generated.control_tests_pb2 import SetDataDirRequest #pylint: disable=no-name-in-module
from .utils import enum_to_string
# ---------------------------------------------------------------------

@ -1,6 +1,6 @@
""" Injected functions for SetDefaultTemplatePackRequest. """
from vasl_templates.webapp.tests.proto.generated.control_tests_pb2 import SetDefaultTemplatePackRequest
from vasl_templates.webapp.tests.proto.generated.control_tests_pb2 import SetDefaultTemplatePackRequest #pylint: disable=no-name-in-module
from .utils import enum_to_string
# ---------------------------------------------------------------------

@ -1,6 +1,6 @@
""" Injected functions for SetVaslVersionRequest. """
from vasl_templates.webapp.tests.proto.generated.control_tests_pb2 import SetVaslVersionRequest
from vasl_templates.webapp.tests.proto.generated.control_tests_pb2 import SetVaslVersionRequest #pylint: disable=no-name-in-module
from .utils import enum_to_string
# ---------------------------------------------------------------------

@ -1,6 +1,6 @@
""" Injected functions for SetVehOrdNotesDirRequest. """
from vasl_templates.webapp.tests.proto.generated.control_tests_pb2 import SetVehOrdNotesDirRequest
from vasl_templates.webapp.tests.proto.generated.control_tests_pb2 import SetVehOrdNotesDirRequest #pylint: disable=no-name-in-module
from .utils import enum_to_string
# ---------------------------------------------------------------------

@ -63,7 +63,7 @@ def test_counter_images( webapp, webdriver ): #pylint: disable=too-many-locals
return (code == 200 and data) or (code == 404 and not data)
# initialize
check_dir = os.path.join( os.path.split(__file__)[0], "fixtures" )
check_dir = os.path.join( os.path.split(__file__)[0], "fixtures/counters" )
save_dir = os.environ.get( "COUNTERS_SAVEDIR" ) # nb: define this to save the generated reports
if save_dir:
if os.path.isdir( save_dir ):
@ -87,7 +87,7 @@ def test_counter_images( webapp, webdriver ): #pylint: disable=too-many-locals
init_webapp( webapp, webdriver )
# figure out what we're expecting to see
fname = os.path.join( check_dir, "vasl-pieces-{}.txt".format(
fname = os.path.join( check_dir, "{}.txt".format(
aliases.get( vasl_version, vasl_version )
) )
with open( fname, "r", encoding="utf-8" ) as fp:

@ -328,8 +328,8 @@ def test_time_based_national_capabilities( webapp, webdriver ):
comments = [ "Plentiful Ammo included (KMC)", "ROK: 6B/3R" ]
)
check_oba( "kfw-rok", "Korea", 10, 1950, "10B", "3R", plentiful=True )
check_th_color( "kfw-rok", "Korea", 8, 1950, "Red TH#" )
check_th_color( "kfw-rok", "Korea", 9, 1950, "Red TH# (ROK) ; Black (KMC)" )
check_th_color( "kfw-rok", "Korea", 7, 1950, "Red TH#" )
check_th_color( "kfw-rok", "Korea", 8, 1950, "Red TH# (ROK) ; Black (KMC)" )
check_th_color( "kfw-rok", "Korea", 5, 1951, "Black TH#" )
# test the CPVA national Capabilities

@ -15,7 +15,8 @@ from vasl_templates.webapp.tests.test_vassal import run_vassal_tests
from vasl_templates.webapp.tests.utils import init_webapp, select_tab, new_scenario, \
set_player, set_template_params, set_scenario_date, get_player_nat, get_theater, set_theater, \
get_turn_track_nturns, \
wait_for, wait_for_elem, find_child, find_children, get_css_classes, set_stored_msg, click_dialog_button
wait_for, wait_for_elem, find_child, find_children, get_css_classes, set_stored_msg, click_dialog_button, \
remove_url_params
# ---------------------------------------------------------------------
@ -833,7 +834,7 @@ def _unload_scenario_card(): #pylint: disable=too-many-branches,too-many-locals
# unload the icons
icons = set(
c.get_attribute( "src" )
remove_url_params( c.get_attribute( "src" ) )
for c in find_children( ".info .icons img", card )
)
if icons:

@ -448,7 +448,7 @@ def _check_warning_msgs( webapp, expected ):
warnings = webapp.control_tests.get_vasl_mod_warnings()
if expected:
assert len(warnings) == 1
if isinstance( expected, typing.re.Pattern ):
if isinstance( expected, typing.Pattern ):
assert expected.search( warnings[0] )
else:
assert warnings[0].startswith( expected )

@ -1,11 +1,11 @@
""" Test VASSAL integration. """
import os
import traceback
import re
import json
import base64
import random
import typing.re #pylint: disable=import-error
from vasl_templates.webapp.vassal import VassalShim
from vasl_templates.webapp.utils import TempFile, change_extn, compare_version_strings
@ -878,14 +878,32 @@ def run_vassal_tests( webapp, func, vasl_extns_type=None,
assert False, "Can't find a valid combination of VASSAL and VASL."
# run the test for each VASSAL+VASL
for vassal_version in vassal_versions:
for vasl_version in vasl_versions:
if not VassalShim.is_compatible_version( vassal_version, vasl_version ):
continue
webapp.control_tests \
.set_vassal_version( vassal_version ) \
.set_vasl_version( vasl_version, vasl_extns_type )
func()
log_fname = os.environ.get( "RUN_VASSAL_TESTS_LOG" ) # nb: define this to log activity
log_file = open( log_fname, "w", encoding="utf-8" ) if log_fname else None #pylint: disable=consider-using-with
def log( fmt, *args, **kwargs ):
if log_file:
print( fmt.format( *args, **kwargs ), file=log_file )
log_file.flush()
try:
for vassal_version in vassal_versions:
for vasl_version in vasl_versions:
if not VassalShim.is_compatible_version( vassal_version, vasl_version ):
continue
log( "Running tests for VASSAL {}, VASL {}...", vassal_version, vasl_version )
webapp.control_tests \
.set_vassal_version( vassal_version ) \
.set_vasl_version( vasl_version, vasl_extns_type )
try:
func()
log ( "- OK.\n" )
except Exception as exc: #pylint: disable=broad-except
log( "- Failed: {}\n{}", exc,
re.sub( "^", " ", traceback.format_exc(), flags=re.MULTILINE )
)
#raise
finally:
if log_file:
log_file.close()
# ---------------------------------------------------------------------
@ -963,7 +981,7 @@ def _check_vsav_dump( vsav_dump, expected, ignore=None ):
# compare what we extracted from the dump with what's expected
for snippet_id in expected:
if isinstance( expected[snippet_id], typing.re.Pattern ):
if isinstance( expected[snippet_id], re.Pattern ):
rc = expected[snippet_id].search( labels[snippet_id] ) is not None
else:
assert isinstance( expected[snippet_id], str )

@ -671,7 +671,7 @@ def wait_for_clipboard( timeout, expected, contains=None, transform=None ):
if transform:
clipboard = transform( clipboard )
if contains is None:
if isinstance( expected, typing.re.Pattern ):
if isinstance( expected, typing.Pattern ):
return expected.search( clipboard ) is not None
else:
return expected == clipboard
@ -749,6 +749,13 @@ def get_css_classes( elem ):
classes = elem.get_attribute( "class" )
return classes.split() if classes else []
def remove_url_params( url ):
"""Remove parameters from a URL."""
pos = url.find( "?" )
if pos < 0:
return url
return url[:pos]
# ---------------------------------------------------------------------
def get_all_loggers():

@ -16,8 +16,10 @@ from vasl_templates.webapp.config.constants import DATA_DIR
from vasl_templates.webapp.vo import get_vo_listings
from vasl_templates.webapp.utils import compare_version_strings
SUPPORTED_VASL_MOD_VERSIONS = [ "6.6.0", "6.6.1", "6.6.2", "6.6.3", "6.6.3.1", "6.6.4", "6.6.5" ]
SUPPORTED_VASL_MOD_VERSIONS_DISPLAY = "6.6.0-.3, 6.6.3.1, 6.6.4-.5"
SUPPORTED_VASL_MOD_VERSIONS = [
"6.6.0", "6.6.1", "6.6.2", "6.6.3", "6.6.3.1", "6.6.4", "6.6.5", "6.6.6", "6.6.7", "6.6.8"
]
SUPPORTED_VASL_MOD_VERSIONS_DISPLAY = "6.6.0-.8, 6.6.3.1"
_zip_file_lock = threading.Lock()
@ -25,6 +27,12 @@ _warnings = [] # nb: for the test suite
# ---------------------------------------------------------------------
class CantFindDataDirException( Exception ):
"""Raised if the VASL data directory is missing."""
def __init__( self, vasl_version ):
super().__init__()
self.vasl_version = vasl_version
def set_vasl_mod( vmod_fname, msg_store ):
"""Install a new global VaslMod object."""
globvars.vasl_mod = None
@ -36,6 +44,13 @@ def set_vasl_mod( vmod_fname, msg_store ):
extns = _load_vasl_extns( extns_dir, msg_store )
try:
vasl_mod = VaslMod( vmod_fname, DATA_DIR, extns )
except CantFindDataDirException as ex:
msg = "Can't find the data directory for VASL {}." \
" Are you running an older version of this program?".format( ex.vasl_version )
_logger.error( "%s", msg )
if msg_store:
msg_store.error( msg )
return
except Exception as ex: #pylint: disable=broad-except
msg = "Can't load the VASL module: {}".format( ex )
_logger.error( "%s", msg )
@ -265,11 +280,20 @@ class VaslMod:
aliases = json.load( fp )
self.vasl_version = aliases.get( self.vasl_real_version, self.vasl_real_version )
# locate the VASL data files
# NOTE: We do this explicitly because people were running older versions of vasl-templates, configured
# with a version of VASL that it didn't know about. An exception was thrown when we tried to open
# the data files below, which was caught and reported correctly, but the error message was a little opaque,
# so we check for this case explicitly, and try to show something a little more comprehensible :-/
dname = os.path.join( data_dir, "vasl-"+self.vasl_version )
if not os.path.isdir( dname ):
raise CantFindDataDirException( self.vasl_version )
# load our overrides
fname = os.path.join( data_dir, "vasl-"+self.vasl_version, "vasl-overrides.json" )
fname = os.path.join( data_dir, dname, "vasl-overrides.json" )
with open( fname, "r", encoding="utf-8" ) as fp:
vasl_overrides = json.load( fp )
fname = os.path.join( data_dir, "vasl-"+self.vasl_version, "expected-multiple-images.json" )
fname = os.path.join( data_dir, dname, "expected-multiple-images.json" )
with open( fname, "r", encoding="utf-8" ) as fp:
expected_multiple_images = json.load( fp )
@ -277,7 +301,7 @@ class VaslMod:
target_gpids = get_vo_gpids( self )
# parse the VASL module and any extensions
fname = os.path.join( data_dir, "vasl-"+self.vasl_version, "piece-info.json" )
fname = os.path.join( data_dir, dname, "piece-info.json" )
with open( fname, "r", encoding="utf-8" ) as fp:
piece_info = json.load( fp )
for i,files in enumerate( self._files ):

@ -38,6 +38,9 @@ from vasl_templates.webapp.vasl_mod import get_reverse_remapped_gpid
# 6.6.3 | 3.5.8 16+36
# 6.6.4 | 3.6.6 17.0.2+8-LTS
# 6.6.5 | 3.6.7 18.0.1
# 6.6.6 | 3.6.14 19.0.2+7
# 6.6.7 | 3.7.5 21 (2023-09-19 LTS)
# 6.6.8 | 3.7.9 21.0.2 (2024-01-16 LTS)
# NOTE: VASSAL+VASL back-compat has gone out the window :-/ We have to tie versions of VASL
# to specific versions of VASSAL. Sigh...
SUPPORTED_VASSAL_VERSIONS = {
@ -45,10 +48,13 @@ SUPPORTED_VASSAL_VERSIONS = {
"3.4.6": [ "6.6.0", "6.6.1" ],
"3.5.5": [ "6.6.0", "6.6.1", "6.6.2" ],
"3.5.8": [ "6.6.0", "6.6.1", "6.6.2", "6.6.3", "6.6.3.1" ],
"3.6.6": [ "6.6.0", "6.6.1", "6.6.2", "6.6.3", "6.6.3.1", "6.6.4", "6.6.5" ],
"3.6.7": [ "6.6.0", "6.6.1", "6.6.2", "6.6.3", "6.6.3.1", "6.6.4", "6.6.5" ],
"3.6.6": [ "6.6.0", "6.6.1", "6.6.2", "6.6.3", "6.6.3.1", "6.6.4", "6.6.5", "6.6.6", "6.6.7" ],
"3.6.7": [ "6.6.0", "6.6.1", "6.6.2", "6.6.3", "6.6.3.1", "6.6.4", "6.6.5", "6.6.6", "6.6.7" ],
"3.6.14": [ "6.6.0", "6.6.1", "6.6.2", "6.6.3", "6.6.3.1", "6.6.4", "6.6.5", "6.6.6", "6.6.7" ],
"3.7.5": [ "6.6.2", "6.6.3", "6.6.3.1", "6.6.4", "6.6.5", "6.6.6", "6.6.7", "6.6.8" ],
"3.7.9": [ "6.6.2", "6.6.3", "6.6.3.1", "6.6.4", "6.6.5", "6.6.6", "6.6.7", "6.6.8" ],
}
SUPPORTED_VASSAL_VERSIONS_DISPLAY = "3.4.2, 3.4.6, 3.5.5, 3.5.8, 3.6.6, 3.6.7"
SUPPORTED_VASSAL_VERSIONS_DISPLAY = "3.4.2, 3.4.6, 3.5.5, 3.5.8, 3.6.6, 3.6.7, 3.6.14"
# ---------------------------------------------------------------------
@ -433,6 +439,8 @@ class VassalShim:
if compare_version_strings( mo.group(), "3.3.0" ) < 0:
# we're using a legacy version of VASSAL - use Java 8
java_path = java8_path
if not java_path:
raise SimpleError( "Java has not been configured." )
# prepare the command
class_path = app.config.get( "JAVA_CLASS_PATH" )

@ -61,7 +61,7 @@ class WebDriver:
log_fname = globvars.user_profile.webdriver_log_fname
if "chromedriver" in webdriver_path:
options = webdriver.ChromeOptions()
options.headless = True
options.add_argument( "--headless" )
options.add_argument( "--no-sandbox" ) # nb: need this on the rPi 4
options.add_argument( "--no-proxy-server" )
# OMG! The chromedriver looks for Chrome/Chromium in a hard-coded, fixed location (the default
@ -70,23 +70,23 @@ class WebDriver:
if chrome_path:
options.binary_location = chrome_path
service = webdriver.chrome.service.Service(
webdriver_path, log_path=log_fname
webdriver_path, log_output=log_fname
)
if is_windows():
service.creationflags = 0x8000000 # win32process.CREATE_NO_WINDOW
service.creation_flags = 0x8000000 # win32process.CREATE_NO_WINDOW
self.driver = webdriver.Chrome(
options=options, service=service
)
elif "geckodriver" in webdriver_path:
options = webdriver.FirefoxOptions()
options.headless = True
options.add_argument( "--headless" )
service = webdriver.firefox.service.Service(
webdriver_path, log_path=log_fname
webdriver_path, log_output=log_fname
)
if is_windows():
service.creationflags = 0x8000000 # win32process.CREATE_NO_WINDOW
service.creation_flags = 0x8000000 # win32process.CREATE_NO_WINDOW
self.driver = webdriver.Firefox(
options=options, proxy=None, service=service
options=options, service=service
)
else:
raise SimpleError( "Can't identify webdriver: {}".format( webdriver_path ) )

@ -61,6 +61,7 @@ import VASSAL.counters.Decorator ;
import VASSAL.counters.DynamicProperty ;
import VASSAL.counters.PieceCloner ;
import VASSAL.preferences.Prefs ;
import VASSAL.configure.StringConfigurer ;
import VASSAL.tools.DataArchive ;
import VASSAL.tools.DialogUtils ;
@ -1131,6 +1132,13 @@ public class VassalShim
private Command loadScenario( String scenarioFilename ) throws IOException
{
// FUDGE! Need this for VASL 6.6.7 (but for some reason, older VASSAL+VASL combinations that used to work
// started breaking, as well :-/).
Prefs prefs = GameModule.getGameModule().getPrefs() ;
String SHOW_MARK_MOVED = "showMarkMoved" ;
prefs.addOption( null, new StringConfigurer( SHOW_MARK_MOVED, null ) ) ;
prefs.setValue( SHOW_MARK_MOVED, false ) ;
// load the scenario
disableBoardWarnings() ;
logger.info( "Loading scenario: {}", scenarioFilename ) ;

Loading…
Cancel
Save