Page MenuHomeWildfire Games
Paste P266

Check whether skirmish maps have too much icons
ActivePublic

Authored by Stan on Mar 7 2022, 6:25 PM.
#!/usr/bin/env python3
# -*- mode: python-mode; python-indent-offset: 4; -*-
#
# Copyright (C) 2022 Stanislas Daniel Claude Dolcini.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
from html import entities
from logging import getLogger, StreamHandler, INFO, WARNING, Formatter, Filter
from tabnanny import verbose
from urllib import request
from xml.etree import ElementTree
import argparse
import json
import os
import pathlib
import psutil
import subprocess
import sys
class SingleLevelFilter(Filter):
def __init__(self, passlevel, reject):
self.passlevel = passlevel
self.reject = reject
def filter(self, record):
if self.reject:
return (record.levelno != self.passlevel)
else:
return (record.levelno == self.passlevel)
class MinimapIconCountValidator():
def __init__(self, vfs_root, mods=None, url=None, verbose=False):
self.mods = mods if mods is not None else []
self.vfs_root = pathlib.Path(vfs_root)
self.__init_logger
self.verbose = verbose
self.url = url
self.MAX_RELIC_COUNT = 14
self.MAX_DECENT_CC_COUNT = 5 * 8
self.MAX_ICON_COUNT = 128
self.MAX_DEFAULT_ICON_COUNT = 128 - self.MAX_DECENT_CC_COUNT - self.MAX_RELIC_COUNT
@property
def __init_logger(self):
logger = getLogger(__name__)
logger.setLevel(INFO)
# create a console handler, seems nicer to Windows and for future uses
ch = StreamHandler(sys.stdout)
ch.setLevel(INFO)
ch.setFormatter(Formatter('%(levelname)s - %(message)s'))
f1 = SingleLevelFilter(INFO, False)
ch.addFilter(f1)
logger.addHandler(ch)
errorch = StreamHandler(sys.stderr)
errorch.setLevel(WARNING)
errorch.setFormatter(Formatter('%(levelname)s - %(message)s'))
logger.addHandler(errorch)
self.logger = logger
def get_templates_from_engine(self):
pyrogenesis_path = self.vfs_root / 'binaries/system/pyrogenesis'
mod_args = ''
for mod in self.mods:
mod_args += f'-mod="{mod}" '
args = f' {mod_args}--rl-interface="{self.url}" --autostart-nonvisual --autostart="skirmishes/acropolis_bay_2p"'
process = subprocess.Popen(str(pyrogenesis_path) + args, stdout=subprocess.PIPE, shell=True)
if self.verbose:
self.logger.info(f"Launching 0.A.D with the following command {str(pyrogenesis_path) + args}");
try:
data = '''{
const cmpTemplateManager = Engine.QueryInterface(SYSTEM_ENTITY, IID_TemplateManager);
const templates = cmpTemplateManager.FindAllTemplates(false);
const templateMap = new Map();
for (const template of templates)
{
templateMap[template] = cmpTemplateManager.GetTemplate(template);
}
templateMap
}'''
if self.verbose:
self.logger.info(f"Calling http://{self.url}/evaluate to get data");
req = request.Request(f'http://{self.url}/evaluate', data=data.encode('utf-8'))
resp = request.urlopen(req)
return json.loads(resp.read())
except Exception as err:
self.logger.error(err)
finally:
if self.verbose:
self.logger.info(f"Killing 0 A.D.");
self.kill(process.pid)
def get_map_entities(self):
if self.verbose:
self.logger.info(f"Gather map template entities...");
mapfiles = self.find_files_relative('maps/scenarios', 'xml')
mapfiles.extend(self.find_files_relative('maps/skirmishes', 'xml'))
mapfiles.extend(self.find_files_relative('maps/tutorials', 'xml'))
actor_prefix = 'actor|'
resource_prefix = 'resource|'
templates = dict()
groupedTemplates = dict()
for (fp, ffp) in sorted(mapfiles):
et_map = ElementTree.parse(ffp).getroot()
entities = et_map.find('Entities')
used = [ entity.find('Template').text.strip() for entity in entities.findall('Entity') ] if entities is not None else {}
relative_path = str(fp)
for template in used:
if not template.startswith(actor_prefix) and not template.startswith(resource_prefix):
if(relative_path in templates):
templates[relative_path].append(template)
else:
templates[relative_path] = [template]
for map_key in templates:
old_templates = templates[map_key];
groupedTemplates[map_key] = dict()
for template in old_templates:
if template in groupedTemplates[map_key]:
groupedTemplates[map_key][template] += 1
else:
groupedTemplates[map_key][template] = 1
return groupedTemplates
def kill(self, proc_pid):
process = psutil.Process(proc_pid)
for proc in process.children(recursive=True):
proc.kill()
process.kill()
def find_files_relative(self, vfs_path, *ext_list):
return self.find_files(pathlib.Path(self.vfs_root / 'binaries/data/mods/'), self.mods, vfs_path, *ext_list)
def find_files(self, vfs_root, mods, vfs_path, *ext_list):
"""
returns a list of 2-size tuple with:
- Path relative to the mod base
- full Path
"""
full_exts = ['.' + ext for ext in ext_list]
def find_recursive(dp, base):
"""(relative Path, full Path) generator"""
if dp.is_dir():
if dp.name != '.svn' and dp.name != '.git' and not dp.name.endswith('~'):
for fp in dp.iterdir():
yield from find_recursive(fp, base)
elif dp.suffix in full_exts:
relative_file_path = dp.relative_to(base)
yield (relative_file_path, dp.resolve())
return [(rp, fp) for mod in mods for (rp, fp) in find_recursive(vfs_root / mod / vfs_path, vfs_root / mod)]
def run (self):
template_data = self.get_templates_from_engine()
entities = self.get_map_entities()
if self.verbose:
self.logger.info(f"Looking for maps with too many minimap icons.");
for map_key in entities:
templates = entities[map_key];
count = 0
for template in templates:
if template in template_data and 'Minimap' in template_data[template] and 'Icon' in template_data[template]['Minimap']:
count += templates[template]
if count > self.MAX_DEFAULT_ICON_COUNT :
if self.verbose:
self.logger.error(f"{map_key} has too many icons : ({count} + Average CC in 4x4 : {self.MAX_DECENT_CC_COUNT} + Max Relics : {self.MAX_RELIC_COUNT} = {count + self.MAX_RELIC_COUNT + self.MAX_RELIC_COUNT} > {self.MAX_ICON_COUNT})")
else:
self.logger.error(f"{map_key} has too many icons : ({count} > {self.MAX_DEFAULT_ICON_COUNT})")
if __name__ == '__main__':
script_dir = os.path.dirname(os.path.realpath(__file__))
default_root = os.path.join(script_dir, '..', '..', '..', 'binaries', 'data', 'mods')
parser = argparse.ArgumentParser(description='Actors/materials validator.')
parser.add_argument('-r', '--root', action='store', dest='root', default=default_root)
parser.add_argument('-m', '--mods', action='store', dest='mods', default='mod,public')
parser.add_argument('-è', '--url', action='store', dest='url', default='127.0.0.1:9090')
parser.add_argument('-v', '--verbose', action='store_true', default=False,
help="Log validation errors.")
args = parser.parse_args()
validator = MinimapIconCountValidator(args.root, args.mods.split(','), args.url, args.verbose)
validator.run()

Event Timeline

Stan created this paste.Mar 7 2022, 6:25 PM
Stan created this object with visibility "Public (No Login Required)".
C:\Dev\Perso>python minimap_icon_validator.py -v -r 0ad-svn
INFO - Launching 0.A.D with the following command 0ad-svn\binaries\system\pyrogenesis -mod="mod" -mod="public" --rl-interface="127.0.0.1:9090" --autostart-nonvisual --autostart="skirmishes/acropolis_bay_2p"
INFO - Calling http://127.0.0.1:9090/evaluate to get data
INFO - Killing 0 A.D.
INFO - Gather map template entities...
INFO - Looking for maps with too many minimap icons.
ERROR - maps\scenarios\arcadia.xml has too many icons : (102 + Average CC in 4x4 : 40 + Max Relics : 14 = 130 > 128)
ERROR - maps\scenarios\azure_coast_2p.xml has too many icons : (77 + Average CC in 4x4 : 40 + Max Relics : 14 = 105 > 128)
ERROR - maps\scenarios\azure_coast_4p.xml has too many icons : (77 + Average CC in 4x4 : 40 + Max Relics : 14 = 105 > 128)
ERROR - maps\scenarios\azure_coast_8p.xml has too many icons : (77 + Average CC in 4x4 : 40 + Max Relics : 14 = 105 > 128)
ERROR - maps\scenarios\battle_for_the_tiber.xml has too many icons : (131 + Average CC in 4x4 : 40 + Max Relics : 14 = 159 > 128)
ERROR - maps\scenarios\bridge_demo.xml has too many icons : (105 + Average CC in 4x4 : 40 + Max Relics : 14 = 133 > 128)
ERROR - maps\scenarios\multiplayer_demo.xml has too many icons : (109 + Average CC in 4x4 : 40 + Max Relics : 14 = 137 > 128)
ERROR - maps\scenarios\napata_reconstruction.xml has too many icons : (136 + Average CC in 4x4 : 40 + Max Relics : 14 = 164 > 128)
ERROR - maps\scenarios\necropolis.xml has too many icons : (163 + Average CC in 4x4 : 40 + Max Relics : 14 = 191 > 128)
ERROR - maps\scenarios\sahel.xml has too many icons : (143 + Average CC in 4x4 : 40 + Max Relics : 14 = 171 > 128)
ERROR - maps\skirmishes\alpine_mountains_3p.xml has too many icons : (195 + Average CC in 4x4 : 40 + Max Relics : 14 = 223 > 128)
ERROR - maps\skirmishes\atlas_valleys_8p.xml has too many icons : (93 + Average CC in 4x4 : 40 + Max Relics : 14 = 121 > 128)
ERROR - maps\skirmishes\barcania_3p.xml has too many icons : (103 + Average CC in 4x4 : 40 + Max Relics : 14 = 131 > 128)
ERROR - maps\skirmishes\dueling_cliffs_3pv3p.xml has too many icons : (75 + Average CC in 4x4 : 40 + Max Relics : 14 = 103 > 128)
ERROR - maps\skirmishes\egypt_3pv3p.xml has too many icons : (370 + Average CC in 4x4 : 40 + Max Relics : 14 = 398 > 128)
ERROR - maps\skirmishes\oceanside_2p.xml has too many icons : (86 + Average CC in 4x4 : 40 + Max Relics : 14 = 114 > 128)
ERROR - maps\skirmishes\persian_highlands_4p.xml has too many icons : (118 + Average CC in 4x4 : 40 + Max Relics : 14 = 146 > 128)
ERROR - maps\skirmishes\sahel_4p.xml has too many icons : (143 + Average CC in 4x4 : 40 + Max Relics : 14 = 171 > 128)
ERROR - maps\skirmishes\tarim_basin_2p.xml has too many icons : (77 + Average CC in 4x4 : 40 + Max Relics : 14 = 105 > 128)
ERROR - maps\skirmishes\tarim_basin_4p.xml has too many icons : (146 + Average CC in 4x4 : 40 + Max Relics : 14 = 174 > 128)
ERROR - maps\skirmishes\thessalian_plains_4p.xml has too many icons : (134 + Average CC in 4x4 : 40 + Max Relics : 14 = 162 > 128)
ERROR - maps\skirmishes\via_augusta_4p.xml has too many icons : (98 + Average CC in 4x4 : 40 + Max Relics : 14 = 126 > 128)

C:\Dev\Perso>python minimap_icon_validator.py  -r 0ad-svn
ERROR - maps\scenarios\arcadia.xml has too many icons : (102 >  74)
ERROR - maps\scenarios\azure_coast_2p.xml has too many icons : (77 >  74)
ERROR - maps\scenarios\azure_coast_4p.xml has too many icons : (77 >  74)
ERROR - maps\scenarios\azure_coast_8p.xml has too many icons : (77 >  74)
ERROR - maps\scenarios\battle_for_the_tiber.xml has too many icons : (131 >  74)
ERROR - maps\scenarios\bridge_demo.xml has too many icons : (105 >  74)
ERROR - maps\scenarios\multiplayer_demo.xml has too many icons : (109 >  74)
ERROR - maps\scenarios\napata_reconstruction.xml has too many icons : (136 >  74)
ERROR - maps\scenarios\necropolis.xml has too many icons : (163 >  74)
ERROR - maps\scenarios\sahel.xml has too many icons : (143 >  74)
ERROR - maps\skirmishes\alpine_mountains_3p.xml has too many icons : (195 >  74)
ERROR - maps\skirmishes\atlas_valleys_8p.xml has too many icons : (93 >  74)
ERROR - maps\skirmishes\barcania_3p.xml has too many icons : (103 >  74)
ERROR - maps\skirmishes\dueling_cliffs_3pv3p.xml has too many icons : (75 >  74)
ERROR - maps\skirmishes\egypt_3pv3p.xml has too many icons : (370 >  74)
ERROR - maps\skirmishes\oceanside_2p.xml has too many icons : (86 >  74)
ERROR - maps\skirmishes\persian_highlands_4p.xml has too many icons : (118 >  74)
ERROR - maps\skirmishes\sahel_4p.xml has too many icons : (143 >  74)
ERROR - maps\skirmishes\tarim_basin_2p.xml has too many icons : (77 >  74)
ERROR - maps\skirmishes\tarim_basin_4p.xml has too many icons : (146 >  74)
ERROR - maps\skirmishes\thessalian_plains_4p.xml has too many icons : (134 >  74)
ERROR - maps\skirmishes\via_augusta_4p.xml has too many icons : (98 >  74)