Page MenuHomeWildfire Games
Paste P296

Find broken tokens
ActivePublic

Authored by Stan on Feb 2 2023, 11:57 PM.
Tags
None
Referenced Files
F3293538: Find broken tokens
Feb 3 2023, 12:16 PM
F3292986: Find broken tokens
Feb 3 2023, 12:08 AM
F3292977: Find broken tokens
Feb 2 2023, 11:57 PM
Subscribers
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# -*- mode: python-mode; python-indent-offset: 4; -*-
# SPDX-License-Identifier: MIT
# SPDX-FileCopyrightText: © 2023 Stanislas Daniel Claude Dolcini
from html import entities
from logging import getLogger, StreamHandler, INFO, WARNING, Formatter, Filter
from urllib import request
from xml.etree import ElementTree
import argparse
import json
import os
import pathlib
import psutil
import subprocess
import sys
from sys import stdout, stderr
class InterceptableLogger():
def __init__(self, logger_name : str):
self.logger = getLogger(logger_name)
self.logger.setLevel(INFO)
ch = StreamHandler(stdout)
ch.setLevel(INFO)
ch.setFormatter(Formatter('%(levelname)s - %(message)s'))
f1 = SingleLevelFilter(INFO, False)
ch.addFilter(f1)
self.logger.addHandler(ch)
errorch = StreamHandler(stderr)
errorch.setLevel(WARNING)
errorch.setFormatter(Formatter('%(levelname)s - %(message)s'))
self.logger.addHandler(errorch)
def info(self, message):
self.logger.info(message)
def warn(self, message):
self.logger.warn(message)
def error(self, message):
self.logger.error(message)
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 ProductionQueueValidator():
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.verbose = verbose
self.url = url
self.logger = InterceptableLogger(__name__)
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)
output = b""
while b'RL interface listening on 127.0.0.1:9090\n' != output:
output = process.stdout.readline()
if self.verbose:
self.logger.info(output)
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 templateMap = new Map();
for (const templateName of cmpTemplateManager.FindAllTemplates(false))
{
const template = cmpTemplateManager.GetTemplateWithoutValidation(templateName)
const finalTemplate = {}
for(const component of ["Identity"])
{
const componentData = template[component];
if (!componentData)
continue;
const requirements = componentData.Requirements;
if (!requirements)
continue;
const techs = requirements.Techs;
if (!techs || !techs._string)
continue;
const techArray = techs._string.split(" ");
if(techArray.length > 1 && !requirements.Tooltip){
finalTemplate[component] = Object.assign({}, template[component])
templateMap[templateName] = techArray
}
}
}
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:
self.kill(process.pid)
def kill(self, proc_pid):
if self.verbose:
self.logger.info(f"Killing 0 A.D.");
process = psutil.Process(proc_pid)
for proc in process.children(recursive=True):
proc.kill()
process.kill()
def run (self):
template_data = self.get_templates_from_engine()
if self.verbose:
self.logger.info(f"Looking for templates with missing production queue.");
print(template_data)
if __name__ == '__main__':
script_dir = os.path.dirname(os.path.realpath(__file__))
default_root = os.path.join(script_dir, '..', '..', '..')
parser = argparse.ArgumentParser(description='Find templates which have broken tooltip issues.')
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('-u', '--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 = ProductionQueueValidator(args.root, args.mods.split(','), args.url, args.verbose)
validator.run()

Event Timeline

Thanks Stan, coupled with checkrefs.py, this works great to check the templates.

cd "$HOME/Developer/0ad" || exit 1

# https://code.wildfiregames.com/P277
P277="Production_Queue_Validator.py"
echo "Downloading $P277"
echo '{"ids":[277]}' | arc call-conduit -- paste.query | jq -r '.response | .[].content' >"$P277"
# the script is written for windows, get rid of the .exe
sed -i '' 's/\.exe//' "$P277"
echo "Running..."
python3 "$P277" -r .

# https://code.wildfiregames.com/P296
P296="Tech_Tokens.py"
echo
echo "Downloading $P296"
echo '{"ids":[296]}' | arc call-conduit -- paste.query | jq -r '.response | .[].content' >"$P296"
echo "Running..."
python3 "$P296" -r .

Interesting approach, thanks for sharing :) Fixed the exe issue.

Stan changed the visibility from "All Users" to "Public (No Login Required)".Nov 2 2024, 2:30 PM