Browse Source

Issue #46: write xml file and xsd, begin state loader

Bastien Sevajol 5 years ago
parent
commit
ba34616dc2
7 changed files with 256 additions and 38 deletions
  1. 2 0
      config.yaml
  2. 22 0
      maps/001/state1.xml
  3. 4 0
      opencombat/exception.py
  4. 110 0
      opencombat/state.py
  5. 69 0
      opencombat/state.xsd
  6. 1 0
      requirements.txt
  7. 48 38
      run.py

+ 2 - 0
config.yaml View File

21
       draw_interior_gap: 2
21
       draw_interior_gap: 2
22
 
22
 
23
 global:
23
 global:
24
+    state_loader: "opencombat.state.StateLoader"
25
+    state_schema: "opencombat/state.xsd"
24
     cache_dir_path: 'cache'
26
     cache_dir_path: 'cache'
25
     include_path:
27
     include_path:
26
       maps:
28
       maps:

+ 22 - 0
maps/001/state1.xml View File

1
+<?xml version="1.0" encoding="UTF-8"?>
2
+<state type="before_battle">
3
+    <map>
4
+        <name>001</name>
5
+    </map>
6
+    <subjects>
7
+        <subject>
8
+            <type>opencombat.simulation.subject.ManSubject</type>
9
+            <position>1,1</position>
10
+            <direction>90</direction>
11
+            <mode>standup</mode>
12
+            <side>URSS</side>
13
+        </subject>
14
+        <subject>
15
+            <type>opencombat.simulation.subject.ManSubject</type>
16
+            <position>10,10</position>
17
+            <direction>270</direction>
18
+            <mode>hiding</mode>
19
+            <side>DE</side>
20
+        </subject>
21
+    </subjects>
22
+</state>

+ 4 - 0
opencombat/exception.py View File

15
 
15
 
16
 class WrongMode(OpenCombatException):
16
 class WrongMode(OpenCombatException):
17
     pass
17
     pass
18
+
19
+
20
+class StateLoadError(OpenCombatException):
21
+    pass

+ 110 - 0
opencombat/state.py View File

1
+# coding: utf-8
2
+import importlib
3
+import typing
4
+from io import StringIO
5
+import sys
6
+
7
+from lxml import etree
8
+
9
+from synergine2.config import Config
10
+from synergine2.log import get_logger
11
+from synergine2.simulation import Subject
12
+
13
+from opencombat.exception import StateLoadError
14
+
15
+
16
+class StateLoader(object):
17
+    def __init__(
18
+        self,
19
+        config: Config,
20
+        state_file_path: str,
21
+    ) -> None:
22
+        self.logger = get_logger('StateLoader', config)
23
+        self.config = config
24
+        self.state_file_path = state_file_path
25
+        self._validate()
26
+
27
+    def _validate(self) -> None:
28
+        # open and read schema file
29
+        schema_file_path = self.config.get(
30
+            'global.state_schema',
31
+            'opencombat/state.xsd',
32
+        )
33
+        with open(schema_file_path, 'r') as schema_file:
34
+            schema_to_check = schema_file.read()
35
+
36
+        # open and read xml file
37
+        with open(self.state_file_path, 'r') as xml_file:
38
+            xml_to_check = xml_file.read()
39
+
40
+        xmlschema_doc = etree.parse(StringIO(schema_to_check))
41
+        xmlschema = etree.XMLSchema(xmlschema_doc)
42
+
43
+        try:
44
+            doc = etree.parse(StringIO(xml_to_check))
45
+        # check for file IO error
46
+        except IOError as exc:
47
+            self.logger.error(exc)
48
+            raise StateLoadError('Invalid File "{}": {}'.format(
49
+                self.state_file_path,
50
+                str(exc),
51
+            ))
52
+        # check for XML syntax errors
53
+        except etree.XMLSyntaxError as exc:
54
+            self.logger.error(exc)
55
+            raise StateLoadError('XML Syntax Error in "{}": {}'.format(
56
+                self.state_file_path,
57
+                str(exc.error_log),
58
+            ))
59
+        except Exception as exc:
60
+            self.logger.error(exc)
61
+            raise StateLoadError('Unknown error with "{}": {}'.format(
62
+                self.state_file_path,
63
+                str(exc),
64
+            ))
65
+
66
+        # validate against schema
67
+        try:
68
+            xmlschema.assertValid(doc)
69
+        except etree.DocumentInvalid as exc:
70
+            self.logger.error(exc)
71
+            raise StateLoadError(
72
+                'Schema validation error with "{}": {}'.format(
73
+                    self.state_file_path,
74
+                    str(exc),
75
+                )
76
+            )
77
+        except Exception as exc:
78
+            self.logger.error(exc)
79
+            raise StateLoadError(
80
+                'Unknown validation error with "{}": {}'.format(
81
+                    self.state_file_path,
82
+                    str(exc),
83
+                )
84
+            )
85
+
86
+    def get_subjects(self) -> typing.List[Subject]:
87
+        raise NotImplementedError('TODO')
88
+
89
+
90
+class StateLoaderBuilder(object):
91
+    def __init__(
92
+        self,
93
+        config: Config,
94
+    ) -> None:
95
+        self.logger = get_logger('StateLoader', config)
96
+        self.config = config
97
+
98
+    def get_state_loader(
99
+        self,
100
+        state_file_path: str,
101
+    ) -> StateLoader:
102
+        class_address = self.config.get(
103
+            'global.state_loader',
104
+            'opencombat.state.StateLoader',
105
+        )
106
+        module_address = '.'.join(class_address.split('.')[:-1])
107
+        class_name = class_address.split('.')[-1]
108
+        module_ = importlib.import_module(module_address)
109
+        state_loader_class = getattr(module_, class_name)
110
+        return state_loader_class(self.config, state_file_path)

+ 69 - 0
opencombat/state.xsd View File

1
+<?xml version="1.0" encoding="UTF-8" ?>
2
+<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
3
+
4
+    <xs:element name="state" type="statetype"/>
5
+
6
+    <xs:simpleType name="statetypetype" final="restriction">
7
+        <xs:restriction base="xs:string">
8
+            <xs:enumeration value="before_battle"/>
9
+            <xs:enumeration value="during_battle"/>
10
+        </xs:restriction>
11
+    </xs:simpleType>
12
+
13
+    <xs:simpleType name="sidetype" final="restriction">
14
+        <xs:restriction base="xs:string">
15
+            <xs:enumeration value="DE"/>
16
+            <xs:enumeration value="URSS"/>
17
+            <xs:enumeration value="US"/>
18
+        </xs:restriction>
19
+    </xs:simpleType>
20
+
21
+    <xs:simpleType name="modetype" final="restriction">
22
+        <xs:restriction base="xs:string">
23
+            <xs:enumeration value="standup"/>
24
+            <xs:enumeration value="defending"/>
25
+            <xs:enumeration value="hiding"/>
26
+        </xs:restriction>
27
+    </xs:simpleType>
28
+
29
+    <xs:simpleType name="positiontype">
30
+        <xs:restriction base="xs:string">
31
+            <xs:pattern value="[0-9]+,[0-9]+"/>
32
+        </xs:restriction>
33
+    </xs:simpleType>
34
+
35
+    <xs:simpleType name="directiontype">
36
+        <xs:restriction base="xs:positiveInteger"/>
37
+    </xs:simpleType>
38
+
39
+    <xs:complexType name="statetype">
40
+        <xs:sequence>
41
+            <xs:element name="map" type="maptype" maxOccurs="1"/>
42
+            <xs:element name="subjects" type="subjectstype"/>
43
+        </xs:sequence>
44
+        <xs:attribute name="type" type="statetypetype" use="required"/>
45
+    </xs:complexType>
46
+
47
+    <xs:complexType name="subjectstype">
48
+        <xs:sequence>
49
+            <xs:element name="subject" type="subjecttype" maxOccurs="unbounded"/>
50
+        </xs:sequence>
51
+    </xs:complexType>
52
+
53
+    <xs:complexType name="subjecttype">
54
+        <xs:sequence>
55
+            <xs:element name="type" type="xs:string"/>
56
+            <xs:element name="position" type="positiontype"/>
57
+            <xs:element name="direction" type="directiontype"/>
58
+            <xs:element name="mode" type="modetype"/>
59
+            <xs:element name="side" type="sidetype"/>
60
+        </xs:sequence>
61
+    </xs:complexType>
62
+
63
+    <xs:complexType name="maptype">
64
+        <xs:sequence>
65
+            <xs:element name="name" type="xs:string"/>
66
+        </xs:sequence>
67
+    </xs:complexType>
68
+
69
+</xs:schema>

+ 1 - 0
requirements.txt View File

2
 Dijkstar==2.3.0
2
 Dijkstar==2.3.0
3
 freezegun==0.3.9
3
 freezegun==0.3.9
4
 future==0.16.0
4
 future==0.16.0
5
+lxml==4.2.1
5
 numpy==1.13.3
6
 numpy==1.13.3
6
 pluggy==0.6.0
7
 pluggy==0.6.0
7
 psutil==5.4.1
8
 psutil==5.4.1

+ 48 - 38
run.py View File

20
 from opencombat.simulation.subject import TankSubject
20
 from opencombat.simulation.subject import TankSubject
21
 from opencombat.simulation.base import TileStrategySimulation
21
 from opencombat.simulation.base import TileStrategySimulation
22
 from opencombat.simulation.base import TileStrategySubjects
22
 from opencombat.simulation.base import TileStrategySubjects
23
+from opencombat.state import StateLoaderBuilder
23
 from opencombat.terminal.base import CocosTerminal
24
 from opencombat.terminal.base import CocosTerminal
24
 
25
 
25
 
26
 
26
-def main(map_dir_path: str, seed_value: int=None):
27
+def main(
28
+    map_dir_path: str,
29
+    seed_value: int=None,
30
+    state_file_path: str=None,
31
+):
27
     if seed_value is not None:
32
     if seed_value is not None:
28
         seed(seed_value)
33
         seed(seed_value)
29
 
34
 
37
     simulation = TileStrategySimulation(config, map_file_path=map_file_path)
42
     simulation = TileStrategySimulation(config, map_file_path=map_file_path)
38
     subjects = TileStrategySubjects(simulation=simulation)
43
     subjects = TileStrategySubjects(simulation=simulation)
39
 
44
 
40
-    for position in ((10, 2), (11, 3), (11, 4), (12, 5),):
41
-        man = ManSubject(
42
-            config=config,
43
-            simulation=simulation,
44
-            position=position,
45
-            properties={
46
-                SELECTION_COLOR_RGB: DE_COLOR,
47
-                FLAG: FLAG_DE,
48
-                SIDE: 'AXIS',
49
-            }
50
-        )
51
-        subjects.append(man)
45
+    if state_file_path:
46
+        state_loader_builder = StateLoaderBuilder(config)
47
+        state_loader = state_loader_builder.get_state_loader(state_file_path)
48
+        subjects.extend(state_loader.get_subjects())
52
 
49
 
53
-    for position in ((30, 15), (31, 16), (32, 17), (33, 18),):
54
-        man = ManSubject(
55
-            config=config,
56
-            simulation=simulation,
57
-            position=position,
58
-            properties={
59
-                SELECTION_COLOR_RGB: URSS_COLOR,
60
-                FLAG: FLAG_URSS,
61
-                SIDE: 'ALLIES',
62
-            }
63
-        )
64
-        subjects.append(man)
65
-
66
-    for position in ((38, 24),):
67
-        man = TankSubject(
68
-            config=config,
69
-            simulation=simulation,
70
-            position=position,
71
-            properties={
72
-                SELECTION_COLOR_RGB: URSS_COLOR,
73
-                FLAG: FLAG_URSS,
74
-                SIDE: 'ALLIES',
75
-            }
76
-        )
77
-        subjects.append(man)
50
+    # for position in ((10, 2), (11, 3), (11, 4), (12, 5),):
51
+    #     man = ManSubject(
52
+    #         config=config,
53
+    #         simulation=simulation,
54
+    #         position=position,
55
+    #         properties={
56
+    #             SELECTION_COLOR_RGB: DE_COLOR,
57
+    #             FLAG: FLAG_DE,
58
+    #             SIDE: 'AXIS',
59
+    #         }
60
+    #     )
61
+    #     subjects.append(man)
62
+    #
63
+    # for position in ((30, 15), (31, 16), (32, 17), (33, 18),):
64
+    #     man = ManSubject(
65
+    #         config=config,
66
+    #         simulation=simulation,
67
+    #         position=position,
68
+    #         properties={
69
+    #             SELECTION_COLOR_RGB: URSS_COLOR,
70
+    #             FLAG: FLAG_URSS,
71
+    #             SIDE: 'ALLIES',
72
+    #         }
73
+    #     )
74
+    #     subjects.append(man)
75
+    #
76
+    # for position in ((38, 24),):
77
+    #     man = TankSubject(
78
+    #         config=config,
79
+    #         simulation=simulation,
80
+    #         position=position,
81
+    #         properties={
82
+    #             SELECTION_COLOR_RGB: URSS_COLOR,
83
+    #             FLAG: FLAG_URSS,
84
+    #             SIDE: 'ALLIES',
85
+    #         }
86
+    #     )
87
+    #     subjects.append(man)
78
 
88
 
79
     simulation.subjects = subjects
89
     simulation.subjects = subjects
80
 
90