forked from NebulaSS13/Nebula
-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ea9b20f
commit 490b0c7
Showing
6 changed files
with
197 additions
and
33 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,3 @@ | ||
pygit2>=0.27.2 | ||
bidict>=0.13.1 | ||
Pillow>=5.1.0 | ||
pygit2==1.0.1 | ||
bidict==0.13.1 | ||
Pillow==7.0.0 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,176 @@ | ||
# A script and syntax for applying path updates to maps. | ||
import re | ||
import os | ||
import argparse | ||
import frontend | ||
from dmm import * | ||
|
||
desc = """ | ||
Update dmm files given update file/string. | ||
Replacement syntax example: | ||
/turf/open/floor/plasteel/warningline : /obj/effect/turf_decal {dir = @OLD ;tag = @SKIP;icon_state = @SKIP} | ||
/turf/open/floor/plasteel/warningline : /obj/effect/turf_decal {@OLD} , /obj/thing {icon_state = @OLD:name; name = "meme"} | ||
/turf/open/floor/plasteel/warningline{dir=2} : /obj/thing | ||
New paths properties: | ||
@OLD - if used as property name copies all modified properties from original path to this one | ||
property = @SKIP - will not copy this property through when global @OLD is used. | ||
property = @OLD - will copy this modified property from original object even if global @OLD is not used | ||
property = @OLD:name - will copy [name] property from original object even if global @OLD is not used | ||
Anything else is copied as written. | ||
Old paths properties: | ||
Will be used as a filter. | ||
property = @UNSET - will apply the rule only if the property is not mapedited | ||
""" | ||
|
||
default_map_directory = "../../_maps" | ||
replacement_re = re.compile(r'\s*(?P<path>[^{]*)\s*(\{(?P<props>.*)\})?') | ||
|
||
#urgent todo: replace with actual parser, this is slow as janitor in crit | ||
split_re = re.compile(r'((?:[A-Za-z0-9_\-$]+)\s*=\s*(?:"(?:.+?)"|[^";]*)|@OLD)') | ||
|
||
|
||
def props_to_string(props): | ||
return "{{{}}}".format(";".join([f"{k} = {v}" for k, v in props.items()])) | ||
|
||
|
||
def string_to_props(propstring, verbose = False): | ||
props = dict() | ||
for raw_prop in re.split(split_re, propstring): | ||
if not raw_prop or raw_prop.strip() == ';': | ||
continue | ||
prop = raw_prop.split('=', maxsplit=1) | ||
props[prop[0].strip()] = prop[1].strip() if len(prop) > 1 else None | ||
if verbose: | ||
print("{0} to {1}".format(propstring, props)) | ||
return props | ||
|
||
|
||
def parse_rep_string(replacement_string, verbose = False): | ||
# translates /blah/blah {meme = "test",} into path,prop dictionary tuple | ||
match = re.match(replacement_re, replacement_string) | ||
path = match['path'] | ||
props = match['props'] | ||
if props: | ||
prop_dict = string_to_props(props, verbose) | ||
else: | ||
prop_dict = dict() | ||
return path.strip(), prop_dict | ||
|
||
|
||
def update_path(dmm_data, replacement_string, verbose=False): | ||
old_path_part, new_path_part = replacement_string.split(':', maxsplit=1) | ||
old_path, old_path_props = parse_rep_string(old_path_part, verbose) | ||
new_paths = list() | ||
for replacement_def in new_path_part.split(','): | ||
new_path, new_path_props = parse_rep_string(replacement_def, verbose) | ||
new_paths.append((new_path, new_path_props)) | ||
|
||
subtypes = "" | ||
if old_path.endswith("/@SUBTYPES"): | ||
old_path = old_path[:-len("/@SUBTYPES")] | ||
if verbose: | ||
print("Looking for subtypes of", old_path) | ||
subtypes = r"(?:/\w+)*" | ||
|
||
replacement_pattern = re.compile(rf"(?P<path>{re.escape(old_path)}{subtypes})\s*(:?{{(?P<props>.*)}})?$") | ||
|
||
def replace_def(match): | ||
if match['props']: | ||
old_props = string_to_props(match['props'], verbose) | ||
else: | ||
old_props = dict() | ||
for filter_prop in old_path_props: | ||
if filter_prop not in old_props: | ||
if old_path_props[filter_prop] == "@UNSET": | ||
continue | ||
else: | ||
return [match.group(0)] | ||
else: | ||
if old_props[filter_prop] != old_path_props[filter_prop] or old_path_props[filter_prop] == "@UNSET": | ||
return [match.group(0)] #does not match current filter, skip the change. | ||
if verbose: | ||
print("Found match : {0}".format(match.group(0))) | ||
out_paths = [] | ||
for new_path, new_props in new_paths: | ||
if new_path == "@OLD": | ||
out = match.group('path') | ||
else: | ||
out = new_path | ||
out_props = dict() | ||
for prop_name, prop_value in new_props.items(): | ||
if prop_name == "@OLD": | ||
out_props = dict(old_props) | ||
continue | ||
if prop_value == "@SKIP": | ||
out_props.pop(prop_name, None) | ||
continue | ||
if prop_value.startswith("@OLD"): | ||
params = prop_value.split(":") | ||
if prop_name in old_props: | ||
out_props[prop_name] = old_props[params[1]] if len(params) > 1 else old_props[prop_name] | ||
continue | ||
out_props[prop_name] = prop_value | ||
if out_props: | ||
out += props_to_string(out_props) | ||
out_paths.append(out) | ||
if verbose: | ||
print("Replacing with: {0}".format(out_paths)) | ||
return out_paths | ||
|
||
def get_result(element): | ||
match = replacement_pattern.match(element) | ||
if match: | ||
return replace_def(match) | ||
else: | ||
return [element] | ||
|
||
bad_keys = {} | ||
keys = list(dmm_data.dictionary.keys()) | ||
for definition_key in keys: | ||
def_value = dmm_data.dictionary[definition_key] | ||
new_value = tuple(y for x in def_value for y in get_result(x)) | ||
if new_value != def_value: | ||
dmm_data.overwrite_key(definition_key, new_value, bad_keys) | ||
dmm_data.reassign_bad_keys(bad_keys) | ||
|
||
|
||
def update_map(map_filepath, updates, verbose=False): | ||
print("Updating: {0}".format(map_filepath)) | ||
dmm_data = DMM.from_file(map_filepath) | ||
for update_string in updates: | ||
update_path(dmm_data, update_string, verbose) | ||
dmm_data.to_file(map_filepath, True) | ||
|
||
|
||
def update_all_maps(map_directory, updates, verbose=False): | ||
for root, _, files in os.walk(map_directory): | ||
for filepath in files: | ||
if filepath.endswith(".dmm"): | ||
path = os.path.join(root, filepath) | ||
update_map(path, updates, verbose) | ||
|
||
|
||
def main(args): | ||
if args.inline: | ||
print("Using replacement:", args.update_source) | ||
updates = [args.update_source] | ||
else: | ||
with open(args.update_source) as f: | ||
updates = [line for line in f if line and not line.startswith("#") and not line.isspace()] | ||
print(f"Using {len(updates)} replacements from file:", args.update_source) | ||
|
||
if args.map: | ||
update_map(args.map, updates, verbose=args.verbose) | ||
else: | ||
map_directory = args.directory or frontend.read_settings().map_folder | ||
update_all_maps(map_directory, updates, verbose=args.verbose) | ||
|
||
|
||
if __name__ == "__main__": | ||
parser = argparse.ArgumentParser(description=desc, formatter_class=argparse.RawTextHelpFormatter) | ||
parser.add_argument("update_source", help="update file path / line of update notation") | ||
parser.add_argument("--map", "-m", help="path to update, defaults to all maps in maps directory") | ||
parser.add_argument("--directory", "-d", help="path to maps directory, defaults to _maps/") | ||
parser.add_argument("--inline", "-i", help="treat update source as update string instead of path", action="store_true") | ||
parser.add_argument("--verbose", "-v", help="toggle detailed update information", action="store_true") | ||
main(parser.parse_args()) |