Feature #500 ยป 0001-generate_packets.py-Improve-coding-style.patch
common/generate_packets.py | ||
---|---|---|
help = "enable log messages during code generation")
|
||
# When enabled: Only overwrite existing output files when they
|
||
# actually changed. This prevents make from rebuilding all dependents
|
||
# in cases where that wouldn't even be necessary.
|
||
# actually changed. This prevents the build system from rebuilding all
|
||
# dependents in cases where that wouldn't even be necessary.
|
||
script.add_argument("--lazy-overwrite", action = "store_true",
|
||
help = "only overwrite output files when their"
|
||
" contents actually changed")
|
||
... | ... | |
logs = output.add_mutually_exclusive_group()
|
||
logs.add_argument("-l", "--log-macro", default = "log_packet_detailed",
|
||
help = "use the given macro for generated log calls")
|
||
help = "use the given macro for generated log calls")
|
||
logs.add_argument("-L", "--no-logs", dest = "log_macro",
|
||
action = "store_const", const = None,
|
||
help = "disable generating log calls")
|
||
action = "store_const", const = None,
|
||
help = "disable generating log calls")
|
||
protocol.add_argument("-B", "--no-fold-bool",
|
||
dest = "fold_bool", action = "store_false",
|
||
help = "explicitly encode boolean values in the"
|
||
" packet body, rather than folding them into the"
|
||
" packet header")
|
||
dest = "fold_bool", action = "store_false",
|
||
help = "explicitly encode boolean values in the"
|
||
" packet body, rather than folding them into the"
|
||
" packet header")
|
||
output_path_args = (
|
||
# (dest, option, canonical path)
|
||
... | ... | |
* Input: {self._relative_path(path)!s:63} *
|
||
""")
|
||
f.write("""\
|
||
f.write(f"""\
|
||
* DO NOT CHANGE THIS FILE *
|
||
**************************************************************************/
|
||
... | ... | |
@contextmanager
|
||
def _wrap_cplusplus(self, file: typing.TextIO) -> typing.Iterator[None]:
|
||
"""Add code for `extern "C" {}` wrapping"""
|
||
file.write("""\
|
||
file.write(f"""\
|
||
#ifdef __cplusplus
|
||
extern "C" {
|
||
extern "C" {{
|
||
#endif /* __cplusplus */
|
||
""")
|
||
yield
|
||
file.write("""\
|
||
file.write(f"""\
|
||
#ifdef __cplusplus
|
||
}
|
||
}}
|
||
#endif /* __cplusplus */
|
||
""")
|
||
... | ... | |
def files_equal(path_a: "str | Path", path_b: "str | Path") -> bool:
|
||
"""Return whether the contents of two text files are identical"""
|
||
with Path(path_a).open() as file_a, Path(path_b).open() as file_b:
|
||
with open(path_a) as file_a, open(path_b) as file_b:
|
||
return all(a == b for a, b in zip_longest(file_a, file_b))
|
||
# Taken from https://docs.python.org/3.4/library/itertools.html#itertools-recipes
|
||
... | ... | |
return self.location
|
||
def __repr__(self) -> str:
|
||
return f"{self.__class__.__name__}({self.name!r}, {self.location!r}, {self.depth!r})"
|
||
return f"{self.__class__.__name__}({self.name!r}, {self.location!r}, {self.depth!r}, {self.json_depth!r})"
|
||
#################### Components of a packets definition ####################
|
||
... | ... | |
return ""
|
||
return f"""\
|
||
if ({self.real} > {self.declared}) {{
|
||
RECEIVE_PACKET_FIELD_ERROR({field_name}, ": truncation array");
|
||
RECEIVE_PACKET_FIELD_ERROR({field_name}, ": array truncated");
|
||
}}
|
||
"""
|
||
... | ... | |
"""Fallback constructor used when there are no matches for either
|
||
dataio type or public type."""
|
||
@staticmethod
|
||
def _resolve(
|
||
key: str,
|
||
direct: "dict[str, FieldTypeConstructor]",
|
||
patterns: "dict[typing.Pattern[str], FieldTypeConstructor]",
|
||
fallback: FieldTypeConstructor
|
||
) -> FieldTypeConstructor:
|
||
"""Helper function. Figures out which constructor to use for a given
|
||
key (dataio type or public type), and caches the result."""
|
||
try:
|
||
return direct[key]
|
||
except KeyError:
|
||
pass
|
||
for pat, ctor in patterns.items():
|
||
mo = pat.fullmatch(key)
|
||
if mo is not None:
|
||
# cache the match
|
||
direct[key] = ctor
|
||
return ctor
|
||
# cache that there was no match
|
||
direct[key] = fallback
|
||
return fallback
|
||
def __init__(self, fallback: FieldTypeConstructor):
|
||
self.dataio_types = {}
|
||
self.dataio_patterns = {}
|
||
... | ... | |
return self(*mo.groups())
|
||
def __call__(self, dataio_type: str, public_type: str) -> RawFieldType:
|
||
try:
|
||
ctor = self.dataio_types[dataio_type]
|
||
except KeyError:
|
||
pass
|
||
else:
|
||
return ctor(dataio_type, public_type)
|
||
for pat, ctor in self.dataio_patterns.items():
|
||
mo = pat.fullmatch(dataio_type)
|
||
if mo is not None:
|
||
self.dataio_types[dataio_type] = ctor
|
||
return ctor(dataio_type, public_type)
|
||
self.dataio_types[dataio_type] = self._by_public
|
||
return self._by_public(dataio_type, public_type)
|
||
ctor = self._resolve(dataio_type, self.dataio_types, self.dataio_patterns, self._by_public)
|
||
return ctor(dataio_type, public_type)
|
||
def _by_public(self, dataio_type: str, public_type: str) -> RawFieldType:
|
||
try:
|
||
ctor = self.public_types[public_type]
|
||
except KeyError:
|
||
pass
|
||
else:
|
||
return ctor(dataio_type, public_type)
|
||
for pat, ctor in self.public_patterns.items():
|
||
mo = pat.fullmatch(public_type)
|
||
if mo is not None:
|
||
self.public_types[public_type] = ctor
|
||
return ctor(dataio_type, public_type)
|
||
self.public_types[public_type] = self.fallback
|
||
return self.fallback(dataio_type, public_type)
|
||
ctor = self._resolve(public_type, self.public_types, self.public_patterns, self.fallback)
|
||
return ctor(dataio_type, public_type)
|
||
class NeedSizeType(RawFieldType):
|
||
... | ... | |
raise NotImplementedError
|
||
@abstractmethod
|
||
def get_code_handle_param(self, location: Location) -> str:
|
||
def get_code_param(self, location: Location) -> str:
|
||
"""Generate a code fragment declaring a parameter with this type for
|
||
a handle function.
|
||
... | ... | |
"""Generate a code fragment passing an argument with this type to a
|
||
handle function.
|
||
See also self.get_code_handle_param()"""
|
||
See also self.get_code_param()"""
|
||
return str(location)
|
||
def get_code_init(self, location: Location) -> str:
|
||
... | ... | |
Subclasses must override this if self.complex is True"""
|
||
if self.complex:
|
||
raise ValueError(f"default get_code_init implementation called for field {location.name} with complex type {self!r}")
|
||
return f"""\
|
||
/* no work needed for {location} */
|
||
"""
|
||
# no work needed
|
||
return ""
|
||
def get_code_copy(self, location: Location, dest: str, src: str) -> str:
|
||
"""Generate a code snippet deep-copying a field of this type from
|
||
... | ... | |
Subclasses must override this if self.complex is True"""
|
||
if self.complex:
|
||
raise ValueError(f"default get_code_free implementation called for field {location.name} with complex type {self!r}")
|
||
return f"""\
|
||
/* no work needed for {location} */
|
||
"""
|
||
# no work needed
|
||
return ""
|
||
@abstractmethod
|
||
def get_code_hash(self, location: Location) -> str:
|
||
... | ... | |
"""Internal helper function. Yield keys to compare for
|
||
type compatibility. See is_type_compatible()"""
|
||
yield self.get_code_declaration(location)
|
||
yield self.get_code_handle_param(location)
|
||
yield self.get_code_param(location)
|
||
yield self.get_code_handle_arg(location)
|
||
yield self.get_code_fill(location)
|
||
yield self.complex
|
||
... | ... | |
{self.public_type} {location};
|
||
"""
|
||
def get_code_handle_param(self, location: Location) -> str:
|
||
def get_code_param(self, location: Location) -> str:
|
||
return f"{self.public_type} {location}"
|
||
def get_code_hash(self, location: Location) -> str:
|
||
... | ... | |
super().__init__(dataio_type, public_type)
|
||
def get_code_handle_param(self, location: Location) -> str:
|
||
def get_code_param(self, location: Location) -> str:
|
||
if not location.depth:
|
||
# top level: pass by-reference
|
||
return "const " + super().get_code_handle_param(location.deeper(f"*{location}"))
|
||
return super().get_code_handle_param(location)
|
||
return "const " + super().get_code_param(location.deeper(f"*{location}"))
|
||
return super().get_code_param(location)
|
||
def get_code_handle_arg(self, location: Location) -> str:
|
||
if not location.depth:
|
||
... | ... | |
location.deeper(f"{location}[{self.size.declared}]")
|
||
)
|
||
def get_code_handle_param(self, location: Location) -> str:
|
||
def get_code_param(self, location: Location) -> str:
|
||
# add "const" if top level
|
||
pre = "" if location.depth else "const "
|
||
return pre + super().get_code_handle_param(location.deeper(f"*{location}"))
|
||
return pre + super().get_code_param(location.deeper(f"*{location}"))
|
||
@abstractmethod
|
||
def get_code_fill(self, location: Location) -> str:
|
||
... | ... | |
location.deeper(f"{location}[{self.size.declared}]")
|
||
)
|
||
def get_code_handle_param(self, location: Location) -> str:
|
||
def get_code_param(self, location: Location) -> str:
|
||
# Note: If we're fine with writing `foo_t const *fieldname`,
|
||
# we'd only need one case, .deeper(f"const *{location}")
|
||
if not location.depth:
|
||
# foo_t fieldname ~> const foo_t *fieldname
|
||
return "const " + self.elem.get_code_handle_param(location.deeper(f"*{location}"))
|
||
return "const " + self.elem.get_code_param(location.deeper(f"*{location}"))
|
||
else:
|
||
# const foo_t *fieldname ~> const foo_t *const *fieldname
|
||
# the final * is already part of {location}
|
||
return self.elem.get_code_handle_param(location.deeper(f"*const {location}"))
|
||
return self.elem.get_code_param(location.deeper(f"*const {location}"))
|
||
def get_code_init(self, location: Location) -> str:
|
||
if not self.complex:
|
||
... | ... | |
def get_code_cmp(self, location: Location) -> str:
|
||
if not self.size.constant:
|
||
# ends mid-line
|
||
head = f"""\
|
||
differ = ({self.size.old} != {self.size.real});
|
||
if (!differ) {{
|
||
"""
|
||
if (!differ) """
|
||
else:
|
||
head = """\
|
||
head = f"""\
|
||
differ = FALSE;
|
||
{
|
||
"""
|
||
inner_cmp = prefix(" ", self.elem.get_code_cmp(location.sub))
|
||
return head + f"""\
|
||
return f"""\
|
||
{head}{{
|
||
int {location.index};
|
||
for ({location.index} = 0; {location.index} < {self.size.real}; {location.index}++) {{
|
||
... | ... | |
sub = location.sub_full(2)
|
||
# Note: At the moment, we're only deep-diffing our elements
|
||
# if our array size is constant
|
||
inner_put = prefix(" ", self.elem.get_code_put(sub, self.size.constant))
|
||
value_put = prefix(" ", self.elem.get_code_put(sub, self.size.constant))
|
||
inner_cmp = prefix(" ", self.elem.get_code_cmp(sub))
|
||
index_put = prefix(" ", self.size.index_put(location.index))
|
||
index_put = prefix(" ", self.size.index_put(location.index))
|
||
index_put_sentinel = prefix(" ", self.size.index_put(self.size.real))
|
||
if not self.size.constant:
|
||
... | ... | |
for ({location.index} = 0; {location.index} < {self.size.real}; {location.index}++) {{
|
||
{inner_cmp}\
|
||
if (differ) {{
|
||
if (!differ) {{
|
||
continue;
|
||
}}
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
/* Append next diff array element. */
|
||
{location.json_subloc}->number = -1;
|
||
/* Append next diff array element. */
|
||
{location.json_subloc}->number = -1;
|
||
/* Create the diff array element. */
|
||
e |= DIO_PUT(object, &dout, &field_addr);
|
||
/* Create the diff array element. */
|
||
e |= DIO_PUT(object, &dout, &field_addr);
|
||
/* Enter diff array element (start at the index address). */
|
||
{location.json_subloc}->number = count_{location.index}++;
|
||
{location.json_subloc}->sub_location = plocation_field_new("index");
|
||
/* Enter diff array element (start at the index address). */
|
||
{location.json_subloc}->number = count_{location.index}++;
|
||
{location.json_subloc}->sub_location = plocation_field_new("index");
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
/* Write the index */
|
||
/* Write the index */
|
||
{index_put}\
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
/* Content address. */
|
||
{location.json_subloc}->sub_location->name = "data";
|
||
/* Content address. */
|
||
{location.json_subloc}->sub_location->name = "data";
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
{inner_put}\
|
||
{value_put}\
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
/* Exit diff array element. */
|
||
FC_FREE({location.json_subloc}->sub_location);
|
||
/* Exit diff array element. */
|
||
FC_FREE({location.json_subloc}->sub_location);
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
}}
|
||
}}
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
... | ... | |
{index_put_sentinel}\
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
/* Exit diff array element. */
|
||
/* Exit diff array element and array. */
|
||
FC_FREE({location.json_subloc}->sub_location);
|
||
/* Exit array. */
|
||
FC_FREE({location.json_subloc});
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
}}
|
||
... | ... | |
def _get_code_get_diff(self, location: Location) -> str:
|
||
"""Helper method. Generate array-diff get code."""
|
||
# we're nested two levels deep in the JSON structure
|
||
inner_get = prefix(" ", self.elem.get_code_get(location.sub_full(2), True))
|
||
value_get = prefix(" ", self.elem.get_code_get(location.sub_full(2), True))
|
||
index_get = prefix(" ", self.size.index_get(location))
|
||
return f"""\
|
||
{self.size.size_check_get(location.name)}\
|
||
... | ... | |
{location.json_subloc}->sub_location->name = "data";
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
{inner_get}\
|
||
{value_get}\
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
/* Move to the next diff array element. */
|
||
... | ... | |
}}
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
/* Exit diff array element. */
|
||
/* Exit diff array element and array. */
|
||
FC_FREE({location.json_subloc}->sub_location);
|
||
/* Exit array. */
|
||
FC_FREE({location.json_subloc});
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
"""
|
||
... | ... | |
{self.public_type} *{location};
|
||
"""
|
||
def get_code_handle_param(self, location: Location) -> str:
|
||
def get_code_param(self, location: Location) -> str:
|
||
if not location.depth:
|
||
return f"const {self.public_type} *{location}"
|
||
else:
|
||
... | ... | |
# vectors (like with jumbo packets)
|
||
# Though that would also mean packets larger than 64 KiB,
|
||
# which we're a long way from
|
||
size = __class__._VecSize(location)
|
||
return f"""\
|
||
if (!real_packet->{location}) {{
|
||
/* Transmit null vector as empty vector */
|
||
... | ... | |
}} else {{
|
||
int {location.index};
|
||
fc_assert(strvec_size(real_packet->{location}) < MAX_UINT16);
|
||
e |= DIO_PUT(arraylen, &dout, &field_addr, strvec_size(real_packet->{location}));
|
||
fc_assert({size.real} < MAX_UINT16);
|
||
e |= DIO_PUT(arraylen, &dout, &field_addr, {size.real});
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
/* Enter array. */
|
||
{location.json_subloc} = plocation_elem_new(0);
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
for ({location.index} = 0; {location.index} < strvec_size(real_packet->{location}); {location.index}++) {{
|
||
for ({location.index} = 0; {location.index} < {size.real}; {location.index}++) {{
|
||
const char *pstr = strvec_get(real_packet->{location}, {location.index});
|
||
if (!pstr) {{
|
||
... | ... | |
}}
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
/* Exit array. */
|
||
FC_FREE({location.json_subloc});
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
}}
|
||
... | ... | |
def _get_code_put_diff(self, location: Location) -> str:
|
||
size = __class__._VecSize(location)
|
||
size_check = prefix(" ", size.size_check_index(location.name))
|
||
index_put = prefix(" ", size.index_put(location.index))
|
||
index_put = prefix(" ", size.index_put(location.index))
|
||
index_put_sentinel = prefix(" ", size.index_put(size.real))
|
||
return f"""\
|
||
if (!real_packet->{location} || 0 == strvec_size(real_packet->{location})) {{
|
||
if (!real_packet->{location} || 0 == {size.real}) {{
|
||
/* Special case for empty vector. */
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
/* Create the object to hold the new size. */
|
||
e |= DIO_PUT(object, &dout, &field_addr);
|
||
/* Enter object (start at size address). */
|
||
{location.json_subloc} = plocation_field_new("size");
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
... | ... | |
#endif /* FREECIV_JSON_CONNECTION */
|
||
/* Write the new size */
|
||
e |= DIO_PUT(uint16, &dout, &field_addr, strvec_size(real_packet->{location}));
|
||
e |= DIO_PUT(uint16, &dout, &field_addr, {size.real});
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
/* Delta address. */
|
||
... | ... | |
{location.json_subloc}->sub_location = plocation_elem_new(0);
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
for ({location.index} = 0; {location.index} < strvec_size(real_packet->{location}); {location.index}++) {{
|
||
for ({location.index} = 0; {location.index} < {size.real}; {location.index}++) {{
|
||
const char *pstr = strvec_get(real_packet->{location}, {location.index});
|
||
if (!pstr) {{
|
||
... | ... | |
pstr = "";
|
||
}}
|
||
if ({location.index} < strvec_size(old->{location})) {{
|
||
if ({location.index} < {size.old}) {{
|
||
const char *pstr_old = strvec_get(old->{location}, {location.index});
|
||
differ = (strcmp(pstr_old ? pstr_old : "", pstr) != 0);
|
||
... | ... | |
differ = TRUE;
|
||
}}
|
||
if (differ) {{
|
||
if (!differ) {{
|
||
continue;
|
||
}}
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
/* Append next diff array element. */
|
||
{location.json_subloc}->sub_location->number = -1;
|
||
/* Append next diff array element. */
|
||
{location.json_subloc}->sub_location->number = -1;
|
||
/* Create the diff array element. */
|
||
e |= DIO_PUT(object, &dout, &field_addr);
|
||
/* Create the diff array element. */
|
||
e |= DIO_PUT(object, &dout, &field_addr);
|
||
/* Enter diff array element (start at the index address). */
|
||
{location.json_subloc}->sub_location->number = count_{location.index}++;
|
||
{location.json_subloc}->sub_location->sub_location = plocation_field_new("index");
|
||
/* Enter diff array element (start at the index address). */
|
||
{location.json_subloc}->sub_location->number = count_{location.index}++;
|
||
{location.json_subloc}->sub_location->sub_location = plocation_field_new("index");
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
/* Write the index */
|
||
/* Write the index */
|
||
{index_put}\
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
/* Content address. */
|
||
{location.json_subloc}->sub_location->sub_location->name = "data";
|
||
/* Content address. */
|
||
{location.json_subloc}->sub_location->sub_location->name = "data";
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
e |= DIO_PUT({self.dataio_type}, &dout, &field_addr, pstr);
|
||
e |= DIO_PUT({self.dataio_type}, &dout, &field_addr, pstr);
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
/* Exit diff array element. */
|
||
FC_FREE({location.json_subloc}->sub_location->sub_location);
|
||
/* Exit diff array element. */
|
||
FC_FREE({location.json_subloc}->sub_location->sub_location);
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
}}
|
||
}}
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
... | ... | |
{index_put_sentinel}\
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
/* Exit diff array element. */
|
||
/* Exit diff array element, array, and object. */
|
||
FC_FREE({location.json_subloc}->sub_location->sub_location);
|
||
/* Exit array. */
|
||
FC_FREE({location.json_subloc}->sub_location);
|
||
/* Exit object. */
|
||
FC_FREE({location.json_subloc});
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
}}
|
||
... | ... | |
return self._get_code_put_full(location)
|
||
def _get_code_get_full(self, location: Location) -> str:
|
||
size = __class__._VecSize(location)
|
||
return f"""\
|
||
{{
|
||
int {location.index};
|
||
... | ... | |
{location.json_subloc} = plocation_elem_new(0);
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
for ({location.index} = 0; {location.index} < strvec_size(real_packet->{location}); {location.index}++) {{
|
||
for ({location.index} = 0; {location.index} < {size.real}; {location.index}++) {{
|
||
char readin[MAX_LEN_PACKET];
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
... | ... | |
{location.json_subloc}->sub_location->sub_location = plocation_field_new("index");
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
/* if (strvec_size(real_packet->{location}) > 0) while (TRUE) */
|
||
while (strvec_size(real_packet->{location}) > 0) {{
|
||
/* if ({size.real} > 0) while (TRUE) */
|
||
while ({size.real} > 0) {{
|
||
int {location.index};
|
||
char readin[MAX_LEN_PACKET];
|
||
/* Read next index */
|
||
{index_get}\
|
||
if ({location.index} == strvec_size(real_packet->{location})) {{
|
||
if ({location.index} == {size.real}) {{
|
||
break;
|
||
}}
|
||
if ({location.index} > strvec_size(real_packet->{location})) {{
|
||
if ({location.index} > {size.real}) {{
|
||
RECEIVE_PACKET_FIELD_ERROR({location.name},
|
||
": unexpected value %d "
|
||
"(> vector length) in array diff",
|
||
... | ... | |
}}
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
/* Exit diff array element. */
|
||
/* Exit diff array element, array, and object. */
|
||
FC_FREE({location.json_subloc}->sub_location->sub_location);
|
||
/* Exit array. */
|
||
FC_FREE({location.json_subloc}->sub_location);
|
||
/* Exit object. */
|
||
FC_FREE({location.json_subloc});
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
"""
|
||
... | ... | |
"""A single field of a packet. Consists of a name, type information
|
||
(including array sizes) and flags."""
|
||
FIELDS_LINE_PATTERN = re.compile(r"^\s*(\w+(?:\([^()]*\))?)\s+([^;()]*?)\s*;\s*(.*?)\s*$")
|
||
FIELDS_LINE_PATTERN = re.compile(r"""
|
||
^\s*
|
||
( # field type; see also TypeRegistry.TYPE_INFO_PATTERN
|
||
\w+ # alias or dataio type
|
||
(?: # optionally, public type (if this is not an alias)
|
||
\(
|
||
[^()]*
|
||
\)
|
||
)?
|
||
)
|
||
\s+
|
||
( # zero or more field declarations
|
||
[^;()]*?
|
||
)
|
||
\s*;\s*
|
||
(.*?) # flags
|
||
\s*$
|
||
""", re.VERBOSE)
|
||
"""Matches an entire field definition line.
|
||
Groups:
|
||
... | ... | |
all(cap not in caps for cap in self.flags.remove_caps)
|
||
)
|
||
def get_declar(self) -> str:
|
||
def get_declaration(self) -> str:
|
||
"""Generate the way this field is declared in the packet struct"""
|
||
return self.type_info.get_code_declaration(Location(self.name))
|
||
def get_handle_param(self) -> str:
|
||
def get_param(self) -> str:
|
||
"""Generate the way this field is declared as a parameter of a
|
||
handle function.
|
||
handle or dsend function.
|
||
See also self.get_handle_arg()"""
|
||
return self.type_info.get_code_handle_param(Location(self.name))
|
||
return self.type_info.get_code_param(Location(self.name))
|
||
def get_handle_arg(self, packet_arrow: str) -> str:
|
||
"""Generate the way this field is passed as an argument to a handle
|
||
... | ... | |
assert self.is_key
|
||
return self.type_info.get_code_hash(Location(self.name))
|
||
def get_cmp(self) -> str:
|
||
"""Generate code checking whether this field changed.
|
||
This code is primarily used by self.get_cmp_wrapper()"""
|
||
return self.type_info.get_code_cmp(Location(self.name))
|
||
@property
|
||
def folded_into_head(self) -> bool:
|
||
"""Whether this field is folded into the packet header.
|
||
... | ... | |
See also self.get_cmp()"""
|
||
if self.folded_into_head:
|
||
if pack.is_info != "no":
|
||
cmp = self.get_cmp()
|
||
differ_part = """\
|
||
if (differ) {
|
||
info_part = f"""\
|
||
{self.get_cmp()}\
|
||
if (differ) {{
|
||
different++;
|
||
}
|
||
"""
|
||
else:
|
||
cmp = ""
|
||
differ_part = ""
|
||
}}
|
||
""" if pack.is_info != "no" else ""
|
||
return f"""\
|
||
{cmp}\
|
||
{differ_part}\
|
||
{info_part}\
|
||
/* folded into head */
|
||
if (packet->{self.name}) {{
|
||
BV_SET(fields, {i:d});
|
||
}}
|
||
"""
|
||
else:
|
||
cmp = self.get_cmp()
|
||
if pack.is_info != "no":
|
||
return f"""\
|
||
{cmp}\
|
||
if (differ) {{
|
||
info_part = f"""\
|
||
different++;
|
||
BV_SET(fields, {i:d});
|
||
}}
|
||
""" if pack.is_info != "no" else ""
|
||
"""
|
||
else:
|
||
return f"""\
|
||
{cmp}\
|
||
return f"""\
|
||
{self.get_cmp()}\
|
||
if (differ) {{
|
||
{info_part}\
|
||
BV_SET(fields, {i:d});
|
||
}}
|
||
"""
|
||
def get_put_wrapper(self, packet: "Variant", i: int, deltafragment: bool) -> str:
|
||
def get_cmp(self) -> str:
|
||
"""Generate code checking whether this field changed.
|
||
This code is primarily used by self.get_cmp_wrapper()"""
|
||
return self.type_info.get_code_cmp(Location(self.name))
|
||
def get_put_wrapper(self, packet: "Variant", index: int, deltafragment: bool) -> str:
|
||
"""Generate code conditionally putting this field iff its bit in the
|
||
`fields` bitvector is set.
|
||
... | ... | |
See also self.get_put()"""
|
||
if self.folded_into_head:
|
||
return f"""\
|
||
/* field {i:d} is folded into the header */
|
||
/* field {index:d} is folded into the header */
|
||
"""
|
||
log = f"""\
|
||
{packet.log_macro}(" field '{self.name}' has changed");
|
||
""" if packet.gen_log else ""
|
||
stats = f"""\
|
||
stats_{packet.name}_counters[{index:d}]++;
|
||
""" if packet.gen_stats else ""
|
||
put = prefix(" ", self.get_put(deltafragment))
|
||
if packet.gen_log:
|
||
f = f"""\
|
||
{packet.log_macro}(" field \'{self.name}\' has changed");
|
||
"""
|
||
else:
|
||
f=""
|
||
if packet.gen_stats:
|
||
s = f"""\
|
||
stats_{packet.name}_counters[{i:d}]++;
|
||
"""
|
||
else:
|
||
s=""
|
||
return f"""\
|
||
if (BV_ISSET(fields, {i:d})) {{
|
||
{f}\
|
||
{s}\
|
||
if (BV_ISSET(fields, {index:d})) {{
|
||
{log}\
|
||
{stats}\
|
||
{put}\
|
||
}}
|
||
"""
|
||
... | ... | |
This does not include delta-related code checking whether to
|
||
transmit the field in the first place; see self.get_put_wrapper()"""
|
||
real = self.get_put_real(deltafragment)
|
||
return f"""\
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
field_addr.name = "{self.name}";
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
e = 0;
|
||
{real}\
|
||
{self.get_put_real(deltafragment)}\
|
||
if (e) {{
|
||
log_packet_detailed("'{self.name}' field error detected");
|
||
}}
|
||
... | ... | |
return f"""\
|
||
real_packet->{self.name} = BV_ISSET(fields, {i:d});
|
||
"""
|
||
get = prefix(" ", self.get_get(deltafragment))
|
||
if packet.gen_log:
|
||
f = f"""\
|
||
log = f"""\
|
||
{packet.log_macro}(" got field '{self.name}'");
|
||
"""
|
||
else:
|
||
f=""
|
||
""" if packet.gen_log else ""
|
||
return f"""\
|
||
if (BV_ISSET(fields, {i:d})) {{
|
||
{f}\
|
||
{log}\
|
||
{get}\
|
||
}}
|
||
"""
|
||
... | ... | |
was transmitted in the first place; see self.get_get_wrapper()"""
|
||
return f"""\
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
field_addr.name = \"{self.name}\";
|
||
field_addr.name = "{self.name}";
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
{self.get_get_real(deltafragment)}\
|
||
"""
|
||
... | ... | |
packet: "Packet"
|
||
"""The packet this is a variant of"""
|
||
no: int
|
||
var_number: int
|
||
"""The numeric variant ID (not packet ID) of this variant"""
|
||
name: str
|
||
... | ... | |
See also self.keys_format"""
|
||
def __init__(self, poscaps: typing.Iterable[str], negcaps: typing.Iterable[str],
|
||
packet: "Packet", no: int):
|
||
packet: "Packet", var_number: int):
|
||
self.packet = packet
|
||
self.no = no
|
||
self.name = f"{packet.name}_{no:d}"
|
||
self.var_number = var_number
|
||
self.name = f"{packet.name}_{var_number:d}"
|
||
self.poscaps = set(poscaps)
|
||
self.negcaps = set(negcaps)
|
||
... | ... | |
See get_send()"""
|
||
return (
|
||
(not self.no_packet)
|
||
and self.delta
|
||
self.delta
|
||
and (
|
||
self.is_info != "no"
|
||
or any(
|
||
... | ... | |
@property
|
||
def send_prototype(self) -> str:
|
||
"""The prototype of this variant's send function"""
|
||
return f"static int send_{self.name}(struct connection *pc{self.packet.extra_send_args})"
|
||
return f"static int send_{self.name}(struct connection *pc{self.packet.send_params})"
|
||
@property
|
||
def send_handler(self) -> str:
|
||
def fill_send_handler(self) -> str:
|
||
"""Code to set the send handler for this variant
|
||
See get_packet_handlers_fill_initial and
|
||
... | ... | |
"""
|
||
@property
|
||
def receive_handler(self) -> str:
|
||
def fill_receive_handler(self) -> str:
|
||
"""Code to set the receive handler for this variant
|
||
See get_packet_handlers_fill_initial and
|
||
... | ... | |
static int stats_{self.name}_sent;
|
||
static int stats_{self.name}_discarded;
|
||
static int stats_{self.name}_counters[{self.bits:d}];
|
||
static char *stats_{self.name}_names[] = {{{names}}};
|
||
static const char *stats_{self.name}_names[] = {{{names}}};
|
||
"""
|
||
... | ... | |
"""
|
||
def get_report_part(self) -> str:
|
||
"""Generate the part of the delta_stats_report8) function specific
|
||
"""Generate the part of the delta_stats_report() function specific
|
||
to this packet variant"""
|
||
return f"""\
|
||
if (stats_{self.name}_sent > 0
|
||
&& stats_{self.name}_discarded != stats_{self.name}_sent) {{
|
||
log_test(\"{self.name} %d out of %d got discarded\",
|
||
log_test("{self.name} %d out of %d got discarded",
|
||
stats_{self.name}_discarded, stats_{self.name}_sent);
|
||
for (i = 0; i < {self.bits}; i++) {{
|
||
if (stats_{self.name}_counters[i] > 0) {{
|
||
log_test(\" %4d / %4d: %2d = %s\",
|
||
log_test(" %4d / %4d: %2d = %s",
|
||
stats_{self.name}_counters[i],
|
||
(stats_{self.name}_sent - stats_{self.name}_discarded),
|
||
i, stats_{self.name}_names[i]);
|
||
... | ... | |
if not self.key_fields:
|
||
return f"""\
|
||
#define hash_{self.name} hash_const
|
||
"""
|
||
intro = f"""\
|
||
... | ... | |
"""
|
||
body = """\
|
||
body = f"""\
|
||
result *= 5;
|
||
""".join(prefix(" ", field.get_hash()) for field in self.key_fields)
|
||
extro = """\
|
||
extro = f"""\
|
||
result &= 0xFFFFFFFF;
|
||
return result;
|
||
}
|
||
}}
|
||
"""
|
||
... | ... | |
if not self.key_fields:
|
||
return f"""\
|
||
#define cmp_{self.name} cmp_const
|
||
"""
|
||
# note: the names `old` and `real_packet` allow reusing
|
||
... | ... | |
"""
|
||
body = """\
|
||
body = f"""\
|
||
if (differ) {
|
||
if (differ) {{
|
||
return !differ;
|
||
}
|
||
}}
|
||
""".join(prefix(" ", field.get_cmp()) for field in self.key_fields)
|
||
extro = """\
|
||
extro = f"""\
|
||
return !differ;
|
||
}
|
||
}}
|
||
"""
|
||
return intro + body + extro
|
||
def get_send(self) -> str:
|
||
"""Generate the send function for this packet variant"""
|
||
if self.gen_stats:
|
||
report = f"""\
|
||
stats_total_sent++;
|
||
stats_{self.name}_sent++;
|
||
"""
|
||
else:
|
||
report=""
|
||
if self.gen_log:
|
||
log = f"""\
|
||
{self.log_macro}("{self.name}: sending info about ({self.keys_format})"{self.keys_arg});
|
||
"""
|
||
else:
|
||
log=""
|
||
if self.no_packet:
|
||
# empty packet, don't need anything
|
||
main_header = ""
|
||
after_header = ""
|
||
before_return = ""
|
||
main_header = after_header = before_return = ""
|
||
elif not self.packet.want_pre_send:
|
||
# no pre-send, don't need to copy the packet
|
||
main_header = f"""\
|
||
const struct {self.packet_name} *real_packet = packet;
|
||
int e;
|
||
"""
|
||
after_header = ""
|
||
before_return = ""
|
||
after_header = before_return = ""
|
||
elif not self.complex:
|
||
# bit-copy the packet
|
||
# bit-copy the packet, no cleanup needed
|
||
main_header = f"""\
|
||
/* copy packet for pre-send */
|
||
struct {self.packet_name} packet_buf = *packet;
|
||
const struct {self.packet_name} *real_packet = &packet_buf;
|
||
int e;
|
||
"""
|
||
after_header = ""
|
||
before_return = ""
|
||
after_header = before_return = ""
|
||
else:
|
||
# deep-copy the packet for pre-send, have to destroy the copy
|
||
# Note: SEND_PACKET_START has both declarations and statements,
|
||
# so we have to break the pre-send copying up across either side
|
||
copy = prefix(" ", self.get_copy("(&packet_buf)", "packet"))
|
||
main_header = f"""\
|
||
/* buffer to hold packet copy for pre-send */
|
||
... | ... | |
free_{self.packet_name}(&packet_buf);
|
||
"""
|
||
report = f"""\
|
||
stats_total_sent++;
|
||
stats_{self.name}_sent++;
|
||
""" if self.gen_stats else ""
|
||
log_key = f"""\
|
||
{self.log_macro}("{self.name}: sending info about ({self.keys_format})"{self.keys_arg});
|
||
""" if self.gen_log else ""
|
||
if not self.packet.want_pre_send:
|
||
pre = ""
|
||
pre_send = ""
|
||
elif self.no_packet:
|
||
pre = f"""\
|
||
pre_send = f"""\
|
||
pre_send_{self.packet_name}(pc, nullptr);
|
||
"""
|
||
else:
|
||
pre = f"""\
|
||
pre_send = f"""\
|
||
pre_send_{self.packet_name}(pc, &packet_buf);
|
||
"""
|
||
if not self.no_packet:
|
||
if self.delta:
|
||
if self.want_force:
|
||
diff = "force_to_send"
|
||
else:
|
||
diff = "0"
|
||
delta_header = f"""\
|
||
#ifdef FREECIV_DELTA_PROTOCOL
|
||
{self.name}_fields fields;
|
||
struct {self.packet_name} *old;
|
||
"""
|
||
if self.differ_used:
|
||
delta_header += """\
|
||
init_field_addr = f"""\
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
struct plocation field_addr;
|
||
{{
|
||
struct plocation *field_addr_tmp = plocation_field_new(nullptr);
|
||
field_addr = *field_addr_tmp;
|
||
FC_FREE(field_addr_tmp);
|
||
}}
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
""" if self.fields else ""
|
||
nondelta_body = "\n".join(
|
||
prefix(" ", field.get_put(False))
|
||
for field in self.fields
|
||
)
|
||
if self.delta:
|
||
declare_differ = f"""\
|
||
bool differ;
|
||
"""
|
||
delta_header += f"""\
|
||
struct genhash **hash = pc->phs.sent + {self.type};
|
||
"""
|
||
if self.is_info != "no":
|
||
delta_header += f"""\
|
||
int different = {diff};
|
||
"""
|
||
delta_header += """\
|
||
#endif /* FREECIV_DELTA_PROTOCOL */
|
||
"""
|
||
body = prefix(" ", self.get_delta_send_body(before_return)) + """\
|
||
#ifndef FREECIV_DELTA_PROTOCOL
|
||
""" if self.differ_used else ""
|
||
if self.is_info == "no":
|
||
declare_different = ""
|
||
elif self.want_force:
|
||
declare_different = f"""\
|
||
int different = force_to_send;
|
||
"""
|
||
else:
|
||
delta_header=""
|
||
body = """\
|
||
#if 1 /* To match endif */
|
||
declare_different = f"""\
|
||
int different = 0;
|
||
"""
|
||
body += "".join(
|
||
prefix(" ", field.get_put(False))
|
||
for field in self.fields
|
||
)
|
||
body += """\
|
||
delta_body = prefix(" ", self.get_delta_send_body(before_return))
|
||
body = f"""\
|
||
#ifdef FREECIV_DELTA_PROTOCOL
|
||
{self.name}_fields fields;
|
||
struct {self.packet_name} *old;
|
||
{declare_differ}\
|
||
{declare_different}\
|
||
struct genhash **hash = pc->phs.sent + {self.type};
|
||
{delta_body}\
|
||
#else /* FREECIV_DELTA_PROTOCOL */
|
||
{nondelta_body}\
|
||
#endif
|
||
"""
|
||
else:
|
||
body=""
|
||
delta_header=""
|
||
body = nondelta_body
|
||
if self.packet.want_post_send:
|
||
if self.no_packet:
|
||
post = f"""\
|
||
if not self.packet.want_post_send:
|
||
post_send = ""
|
||
elif self.no_packet:
|
||
post_send = f"""\
|
||
post_send_{self.packet_name}(pc, nullptr);
|
||
"""
|
||
else:
|
||
post = f"""\
|
||
post_send_{self.packet_name}(pc, real_packet);
|
||
"""
|
||
else:
|
||
post=""
|
||
if self.fields:
|
||
faddr = """\
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
struct plocation field_addr;
|
||
{
|
||
struct plocation *field_addr_tmp = plocation_field_new(nullptr);
|
||
field_addr = *field_addr_tmp;
|
||
FC_FREE(field_addr_tmp);
|
||
}
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
post_send = f"""\
|
||
post_send_{self.packet_name}(pc, real_packet);
|
||
"""
|
||
else:
|
||
faddr = ""
|
||
return "".join((
|
||
f"""\
|
||
return f"""\
|
||
{self.send_prototype}
|
||
{{
|
||
""",
|
||
main_header,
|
||
delta_header,
|
||
f"""\
|
||
{main_header}\
|
||
SEND_PACKET_START({self.type});
|
||
""",
|
||
faddr,
|
||
after_header,
|
||
log,
|
||
report,
|
||
pre,
|
||
body,
|
||
post,
|
||
before_return,
|
||
f"""\
|
||
{after_header}\
|
||
{log_key}\
|
||
{report}\
|
||
{pre_send}\
|
||
{init_field_addr}\
|
||
{body}\
|
||
{post_send}\
|
||
{before_return}\
|
||
SEND_PACKET_END({self.type});
|
||
}}
|
||
""",
|
||
))
|
||
"""
|
||
def get_delta_send_body(self, before_return: str = "") -> str:
|
||
"""Helper for get_send(). Generate the part of the send function
|
||
that computes and transmits the delta between the real packet and
|
||
the last cached packet."""
|
||
intro = f"""\
|
||
#ifdef FREECIV_DELTA_PROTOCOL
|
||
if (nullptr == *hash) {{
|
||
*hash = genhash_new_full(hash_{self.name}, cmp_{self.name},
|
||
nullptr, nullptr, nullptr, destroy_{self.packet_name});
|
||
}}
|
||
BV_CLR_ALL(fields);
|
||
if (!genhash_lookup(*hash, real_packet, (void **) &old)) {{
|
||
old = fc_malloc(sizeof(*old));
|
||
/* temporary bitcopy just to insert correctly */
|
||
*old = *real_packet;
|
||
genhash_insert(*hash, old, old);
|
||
init_{self.packet_name}(old);
|
||
"""
|
||
if self.is_info != "no":
|
||
intro += """\
|
||
force_info = """\
|
||
different = 1; /* Force to send. */
|
||
"""
|
||
intro += """\
|
||
}
|
||
"""
|
||
body = "".join(
|
||
""" if self.is_info != "no" else ""
|
||
cmp_part = "\n".join(
|
||
field.get_cmp_wrapper(i, self)
|
||
for i, field in enumerate(self.other_fields)
|
||
)
|
||
if self.gen_log:
|
||
fl = f"""\
|
||
if self.is_info != "no":
|
||
log_discard = f"""\
|
||
{self.log_macro}(" no change -> discard");
|
||
"""
|
||
else:
|
||
fl=""
|
||
if self.gen_stats:
|
||
s = f"""\
|
||
""" if self.gen_log else ""
|
||
stats_discard = f"""\
|
||
stats_{self.name}_discarded++;
|
||
"""
|
||
else:
|
||
s=""
|
||
""" if self.gen_stats else ""
|
||
discard_part = f"""\
|
||
if self.is_info != "no":
|
||
body += f"""\
|
||
if (different == 0) {{
|
||
{fl}\
|
||
{s}\
|
||
{log_discard}\
|
||
{stats_discard}\
|
||
{before_return}\
|
||
return 0;
|
||
}}
|
||
"""
|
||
else:
|
||
discard_part = ""
|
||
body += """\
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
field_addr.name = "fields";
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
e = 0;
|
||
e |= DIO_BV_PUT(&dout, &field_addr, fields);
|
||
if (e) {
|
||
log_packet_detailed("fields bitvector error detected");
|
||
}
|
||
"""
|
||
body += "".join(
|
||
field.get_put(True)
|
||
put_key = "".join(
|
||
field.get_put(True) + "\n"
|
||
for field in self.key_fields
|
||
)
|
||
body += "\n"
|
||
body += "".join(
|
||
put_other = "\n".join(
|
||
field.get_put_wrapper(self, i, True)
|
||
for i, field in enumerate(self.other_fields)
|
||
)
|
||
body += "\n"
|
||
body += self.get_copy("old", "real_packet")
|
||
copy_to_old = self.get_copy("old", "real_packet")
|
||
# Cancel some is-info packets.
|
||
for i in self.cancel:
|
||
body += f"""\
|
||
cancel_part = "".join(
|
||
f"""\
|
||
hash = pc->phs.sent + {i};
|
||
hash = pc->phs.sent + {cancel_packet_type};
|
||
if (nullptr != *hash) {{
|
||
genhash_remove(*hash, real_packet);
|
||
}}
|
||
"""
|
||
body += """\
|
||
#endif /* FREECIV_DELTA_PROTOCOL */
|
||
"""
|
||
for cancel_packet_type in self.cancel
|
||
)
|
||
return f"""\
|
||
if (nullptr == *hash) {{
|
||
*hash = genhash_new_full(hash_{self.name}, cmp_{self.name},
|
||
nullptr, nullptr, nullptr, destroy_{self.packet_name});
|
||
}}
|
||
BV_CLR_ALL(fields);
|
||
if (!genhash_lookup(*hash, real_packet, (void **) &old)) {{
|
||
old = fc_malloc(sizeof(*old));
|
||
/* temporary bitcopy just to insert correctly */
|
||
*old = *real_packet;
|
||
genhash_insert(*hash, old, old);
|
||
init_{self.packet_name}(old);
|
||
{force_info}\
|
||
}}
|
||
{cmp_part}\
|
||
{discard_part}\
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
field_addr.name = "fields";
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
e = 0;
|
||
e |= DIO_BV_PUT(&dout, &field_addr, fields);
|
||
if (e) {{
|
||
log_packet_detailed("fields bitvector error detected");
|
||
}}
|
||
return intro+body
|
||
{put_key}\
|
||
{put_other}\
|
||
{copy_to_old}\
|
||
{cancel_part}\
|
||
"""
|
||
def get_receive(self) -> str:
|
||
"""Generate the receive function for this packet variant"""
|
||
if self.delta:
|
||
delta_header = f"""\
|
||
delta_header = f"""\
|
||
#ifdef FREECIV_DELTA_PROTOCOL
|
||
{self.name}_fields fields;
|
||
struct {self.packet_name} *old;
|
||
struct genhash **hash = pc->phs.received + {self.type};
|
||
#endif /* FREECIV_DELTA_PROTOCOL */
|
||
"""
|
||
delta_body1 = """\
|
||
""" if self.delta else ""
|
||
init_field_addr = f"""\
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
struct plocation field_addr;
|
||
{{
|
||
struct plocation *field_addr_tmp = plocation_field_new(nullptr);
|
||
field_addr = *field_addr_tmp;
|
||
FC_FREE(field_addr_tmp);
|
||
}}
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
""" if self.fields else ""
|
||
get_delta_bv = f"""\
|
||
#ifdef FREECIV_DELTA_PROTOCOL
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
field_addr.name = "fields";
|
||
#endif /* FREECIV_JSON_CONNECTION */
|
||
DIO_BV_GET(&din, &field_addr, fields);
|
||
"""
|
||
body1 = "".join(
|
||
prefix(" ", field.get_get(True))
|
||
for field in self.key_fields
|
||
)
|
||
body1 += """\
|
||
#endif /* FREECIV_DELTA_PROTOCOL */
|
||
#else /* FREECIV_DELTA_PROTOCOL */
|
||
"""
|
||
body2 = prefix(" ", self.get_delta_receive_body())
|
||
else:
|
||
delta_header=""
|
||
delta_body1=""
|
||
body1 = """\
|
||
#if 1 /* To match endif */
|
||
"""
|
||
body2=""
|
||
nondelta = "".join(
|
||
""" if self.delta else ""
|
||
get_key = "".join(
|
||
prefix(" ", field.get_get(True)) + "\n"
|
||
for field in self.key_fields
|
||
)
|
||
log_key = f"""\
|
||
{self.log_macro}("{self.name}: got info about ({self.keys_format})"{self.keys_arg});
|
||
""" if self.gen_log else ""
|
||
nondelta_body = "\n".join(
|
||
prefix(" ", field.get_get(False))
|
||
for field in self.fields
|
||
) or """\
|
||
for field in self.other_fields
|
||
) or f"""\
|
||
real_packet->__dummy = 0xff;
|
||
"""
|
||
body1 += nondelta + """\
|
||
#endif
|
||
"""
|
||
if self.gen_log:
|
||
log = f"""\
|
||
{self.log_macro}("{self.name}: got info about ({self.keys_format})"{self.keys_arg});
|
||
"""
|
||
else:
|
||
log=""
|
||
if self.delta:
|
||
delta_body = prefix(" ", self.get_delta_receive_body())
|
||
get_body = f"""\
|
||
#ifdef FREECIV_DELTA_PROTOCOL
|
||
{delta_body}\
|
||
if self.packet.want_post_recv:
|
||
post = f"""\
|
||
post_receive_{self.packet_name}(pc, real_packet);
|
||
#else /* FREECIV_DELTA_PROTOCOL */
|
||
{nondelta_body}\
|
||
#endif /* FREECIV_DELTA_PROTOCOL */
|
||
"""
|
||
else:
|
||
post=""
|
||
get_body = nondelta_body
|
||
if self.fields:
|
||
faddr = """\
|
||
#ifdef FREECIV_JSON_CONNECTION
|
||
struct plocation field_addr;
|
||
{
|
||
struct plocation *field_addr_tmp = plocation_field_new(nullptr);
|
||
field_addr = *field_addr_tmp;
|
||
FC_FREE(field_addr_tmp);
|
||
}
|
||
#endif /* FREECIV_JSON_CONNECTION */
|