11from pathlib import Path
22from typing import List , Dict , Optional
33
4- from src .backend .tile_connection import TileConnection
54from src .backend .tile_handler import TileHandler
65from src .backend .tile_status import TileStatus
76from src .config .config_manager import ConfigManager
8- from src .globals import NUM_TILES
97from src .widgets .widget_base_tile import BaseTile
108
119RuleConfig = Dict [str , List [str ]]
1210
1311
1412class RuleManager :
15- _instance = None
1613
1714 def __init__ (self ):
18- raise RuntimeError ('This is a static class' )
15+ self ._config : Dict [str , List [str ]] = {}
16+ self ._header : List [str ] = []
1917
20- @staticmethod
21- def _loadRuleFile (filename_base ) -> RuleConfig :
18+ def _loadRuleFile (self , filename_base ):
2219 filename = f"{ filename_base } .rules"
2320 data_path = Path (ConfigManager .config ()["data_path" ])
2421 if not data_path :
@@ -32,15 +29,14 @@ def _loadRuleFile(filename_base) -> RuleConfig:
3229
3330 # load file if it exists
3431 if full_file_path .is_file ():
35- config = RuleManager ._readRuleFile (full_file_path )
32+ self ._readRuleFile (full_file_path )
3633
37- # file doesn't exist
34+ # file doesn't exist, just to be explicit
3835 else :
39- config = {filename_base : [] }
40- return config
36+ self . _config = {}
37+ self . _header = []
4138
42- @staticmethod
43- def saveRule (filename , rule_name ):
39+ def saveRule (self , filename , rule_name ):
4440
4541 # remove mime type and check it if exists
4642 splits = filename .split ("." )
@@ -51,10 +47,10 @@ def saveRule(filename, rule_name):
5147 raise ValueError (f"Unknown rule mime type '{ splits [1 ]} '" )
5248 filename = splits [0 ]
5349
54- config : RuleConfig = RuleManager ._loadRuleFile (filename )
55- config [rule_name ] = [] # overwrite rules
56- config [rule_name ] = RuleManager ._createRulesFromTileHandler ()
57- RuleManager ._writeRuleFile (filename , config )
50+ self ._loadRuleFile (filename )
51+ self . _config [rule_name ] = [] # overwrite rules
52+ self . _config [rule_name ] = RuleManager ._createRulesFromTileHandler ()
53+ self ._writeRuleFile (filename )
5854
5955 @staticmethod
6056 def _createRulesFromTileHandler ():
@@ -93,20 +89,39 @@ def _createIndexRule(tile_id: int, tile_status: TileStatus):
9389 str_rotate = " ROTATE" if tile_status .rot else ""
9490 return f"{ str_index } { str_x_flip } { str_y_flip } { str_rotate } "
9591
96- @staticmethod
97- def _writeRuleFile (filename_base , config : RuleConfig ):
92+ def _writeRuleFile (self , filename_base : str ):
93+ if len (filename_base ) == 0 or filename_base [0 ] == '/' or filename_base [0 ] == '\\ ' :
94+ raise ValueError (f"the filename '{ filename_base } ' is invalid" )
95+
96+ # handle file location
9897 filename = f"{ filename_base } .rules"
99- with open (filename , 'w' ) as f :
100- for key in config .keys ():
98+ data_path = ConfigManager .config ()["data_path" ]
99+ if not data_path :
100+ full_path = filename # save at root directory
101+ else :
102+ automap_path = Path (data_path ).joinpath (Path ("editor/automap" ))
103+ if not automap_path .exists ():
104+ automap_path .mkdir ()
105+ full_path = automap_path .joinpath (filename )
106+
107+ # write file
108+ with open (str (full_path ), 'w' ) as f :
109+ # write header, may be a comment
110+ if len (self ._header ):
111+ for line in self ._header :
112+ f .write (line )
113+ f .write ('\n ' )
114+
115+ # write rules
116+ for key in self ._config .keys ():
101117 f .write (f"[{ key } ]\n " )
102- lines = config [key ]
118+ lines = self . _config [key ]
103119 for line in lines :
104120 f .write (line )
105121 f .write ('\n ' )
106122
107- @staticmethod
108- def _readRuleFile (full_file_path : Path ) -> RuleConfig :
109- config = {}
123+ def _readRuleFile (self , full_file_path : Path ):
124+ self ._config = {}
110125
111126 with open (str (full_file_path ), 'r' , encoding = 'utf-8' ) as f :
112127 section : Optional [str ] = None
@@ -118,12 +133,16 @@ def _readRuleFile(full_file_path: Path) -> RuleConfig:
118133 # handle sections
119134 if len (line ) >= 2 and line [0 ] == '[' and line [- 1 ] == ']' :
120135 section = line [1 :- 1 ]
121- config [section ] = []
136+ self . _config [section ] = []
122137
123138 # handle normal lines
124139 else :
125140 if section :
126- config [section ].append (line )
127- elif len (line ) > 0 : # ignore empty lines
141+ self ._config [section ].append (line )
142+ # https://github.com/ddnet/ddnet/blob/c7dc7b6a94528040678b7a0fab17ccb447e1d94d/src/game/editor/auto_map.cpp#L72C2-L72C146
143+ elif len (line ) > 0 and line [0 ] != '#' and line [0 ] != '\n ' and line [0 ] != '\r ' and line [0 ] != '\t ' \
144+ and line [0 ] != '\v ' and line [0 ] != ' ' :
128145 raise ValueError ("files may contain rules without section, aborting" )
129- return config
146+ else :
147+ # starting with a comment or something ignored, read header
148+ self ._header .append (line )
0 commit comments