浏览代码

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

Bastien Sevajol 7 年前
父节点
当前提交
ba34616dc2
共有 7 个文件被更改,包括 256 次插入38 次删除
  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 查看文件

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 查看文件

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 查看文件

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 查看文件

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 查看文件

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 查看文件

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 查看文件

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