Feature #470 » 0003-Network-protocol-strvec-field-type.patch
| common/generate_packets.py | ||
|---|---|---|
|
raise ValueError("not a valid string type")
|
||
|
if public_type != "char":
|
||
|
raise ValueError(f"string dataio type with non-char public type: {public_type!r}")
|
||
|
raise ValueError(f"string type with illegal public type: {public_type!r}")
|
||
|
super().__init__(dataio_type, public_type, size)
|
||
| ... | ... | |
|
}}
|
||
|
"""
|
||
|
DEFAULT_REGISTRY.dataio_types["string"] = DEFAULT_REGISTRY.dataio_types["estring"] = partial(NeedSizeType, cls = StringType)
|
||
|
class MemoryType(SizedType):
|
||
|
"""Type information for a memory field"""
|
||
| ... | ... | |
|
)
|
||
|
def get_code_handle_param(self, location: Location) -> str:
|
||
|
# add "const" if top level
|
||
|
pre = "" if location.depth else "const "
|
||
|
return pre + self.elem.get_code_handle_param(location.deeper(f"*{location}"))
|
||
|
# 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}"))
|
||
|
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}"))
|
||
|
def get_code_init(self, location: Location) -> str:
|
||
|
if not self.complex:
|
||
| ... | ... | |
|
# can't use direct assignment to bit-copy a raw array,
|
||
|
# even if our type is not complex
|
||
|
inner_copy = prefix(" ", self.elem.get_code_copy(location.sub, dest, src))
|
||
|
# FIXME: can't use self.size.real; have to use actual_for(src context)
|
||
|
return f"""\
|
||
|
{{
|
||
|
int {location.index};
|
||
| ... | ... | |
|
return f"{self.elem}[{self.size}]"
|
||
|
class StrvecType(FieldType):
|
||
|
"""Type information for a string vector field"""
|
||
|
dataio_type: str
|
||
|
"""How fields of this type are transmitted over network"""
|
||
|
public_type: str
|
||
|
"""How fields of this type are represented in C code"""
|
||
|
complex: bool = True
|
||
|
def __init__(self, dataio_type: str, public_type: str):
|
||
|
if dataio_type not in ("string", "estring"):
|
||
|
raise ValueError("not a valid strvec type")
|
||
|
if public_type != "struct strvec":
|
||
|
raise ValueError(f"strvec type with illegal public type: {public_type!r}")
|
||
|
self.dataio_type = dataio_type
|
||
|
self.public_type = public_type
|
||
|
def get_code_declaration(self, location: Location) -> str:
|
||
|
return f"""\
|
||
|
{self.public_type} *{location};
|
||
|
"""
|
||
|
def get_code_handle_param(self, location: Location) -> str:
|
||
|
if not location.depth:
|
||
|
return f"const {self.public_type} *{location}"
|
||
|
else:
|
||
|
# const struct strvec *const *fieldname
|
||
|
# the final * is already part of {location}
|
||
|
# initial const gets added from outside
|
||
|
return f"{self.public_type} *const {location}"
|
||
|
def get_code_init(self, location: Location) -> str:
|
||
|
# we're always allocating our vectors, even if they're empty
|
||
|
return f"""\
|
||
|
{location} = strvec_new();
|
||
|
"""
|
||
|
def get_code_fill(self, location: Location) -> str:
|
||
|
"""Generate a code snippet shallow-copying a value of this type from
|
||
|
dsend arguments into a packet struct."""
|
||
|
# safety: the packet's contents will not be modified without cloning
|
||
|
# it first, so discarding 'const' qualifier here is safe
|
||
|
return f"""\
|
||
|
real_packet->{location} = (struct strvec *) {location};
|
||
|
"""
|
||
|
def get_code_copy(self, location: Location, dest: str, src: str) -> str:
|
||
|
# dest is initialized by us ~> not null
|
||
|
# src might be a packet passed in from outside ~> could be null
|
||
|
return f"""\
|
||
|
if ({src}->{location}) {{
|
||
|
strvec_copy({dest}->{location}, {src}->{location});
|
||
|
}} else {{
|
||
|
strvec_clear({dest}->{location});
|
||
|
}}
|
||
|
"""
|
||
|
def get_code_free(self, location: Location) -> str:
|
||
|
return f"""\
|
||
|
if ({location}) {{
|
||
|
strvec_destroy({location});
|
||
|
{location} = nullptr;
|
||
|
}}
|
||
|
"""
|
||
|
def get_code_hash(self, location: Location) -> str:
|
||
|
raise ValueError(f"hash not supported for strvec type {self} in field {location.name}")
|
||
|
def get_code_cmp(self, location: Location) -> str:
|
||
|
return f"""\
|
||
|
differ = !are_strvecs_equal(old->{location}, real_packet->{location});
|
||
|
"""
|
||
|
def _get_code_put_full(self, location: Location) -> str:
|
||
|
# Note: strictly speaking, we could allow size == MAX_UINT16,
|
||
|
# but we might want to use that in the future to signal overlong
|
||
|
# vectors (like with jumbo packets)
|
||
|
# Though that would also mean packets larger than 64 KiB,
|
||
|
# which we're a long way from
|
||
|
return f"""\
|
||
|
if (!real_packet->{location}) {{
|
||
|
/* Transmit null vector as empty vector */
|
||
|
e |= DIO_PUT(arraylen, &dout, &field_addr, 0);
|
||
|
}} 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}));
|
||
|
#ifdef FREECIV_JSON_CONNECTION
|
||
|
{location.json_subloc} = plocation_elem_new(0);
|
||
|
#endif /* FREECIV_JSON_CONNECTION */
|
||
|
for ({location.index} = 0; {location.index} < strvec_size(real_packet->{location}); {location.index}++) {{
|
||
|
const char *pstr = strvec_get(real_packet->{location}, {location.index});
|
||
|
if (!pstr) {{
|
||
|
/* Transmit null strings as empty strings */
|
||
|
pstr = "";
|
||
|
}}
|
||
|
#ifdef FREECIV_JSON_CONNECTION
|
||
|
/* Next array element */
|
||
|
{location.json_subloc}->number = {location.index};
|
||
|
#endif /* FREECIV_JSON_CONNECTION */
|
||
|
e |= DIO_PUT({self.dataio_type}, &dout, &field_addr, pstr);
|
||
|
}}
|
||
|
#ifdef FREECIV_JSON_CONNECTION
|
||
|
FC_FREE({location.json_subloc});
|
||
|
#endif /* FREECIV_JSON_CONNECTION */
|
||
|
}}
|
||
|
"""
|
||
|
def get_code_put(self, location: Location, deep_diff: bool = False) -> str:
|
||
|
assert not deep_diff, "deep-diff for strvec not supported yet"
|
||
|
return self._get_code_put_full(location)
|
||
|
def _get_code_get_full(self, location: Location) -> str:
|
||
|
return f"""\
|
||
|
{{
|
||
|
int {location.index};
|
||
|
if (!DIO_GET(arraylen, &din, &field_addr, &{location.index})) {{
|
||
|
RECEIVE_PACKET_FIELD_ERROR({location.name});
|
||
|
}}
|
||
|
strvec_reserve(real_packet->{location}, {location.index});
|
||
|
#ifdef FREECIV_JSON_CONNECTION
|
||
|
{location.json_subloc} = plocation_elem_new(0);
|
||
|
#endif /* FREECIV_JSON_CONNECTION */
|
||
|
for ({location.index} = 0; {location.index} < strvec_size(real_packet->{location}); {location.index}++) {{
|
||
|
char readin[MAX_LEN_PACKET];
|
||
|
#ifdef FREECIV_JSON_CONNECTION
|
||
|
/* Next array element */
|
||
|
{location.json_subloc}->number = {location.index};
|
||
|
#endif /* FREECIV_JSON_CONNECTION */
|
||
|
if (!DIO_GET({self.dataio_type}, &din, &field_addr, readin, sizeof(readin))
|
||
|
|| !strvec_set(real_packet->{location}, {location.index}, readin)) {{
|
||
|
RECEIVE_PACKET_FIELD_ERROR({location.name});
|
||
|
}}
|
||
|
}}
|
||
|
#ifdef FREECIV_JSON_CONNECTION
|
||
|
FC_FREE({location.json_subloc});
|
||
|
#endif /* FREECIV_JSON_CONNECTION */
|
||
|
}}
|
||
|
"""
|
||
|
def get_code_get(self, location: Location, deep_diff: bool = False) -> str:
|
||
|
assert not deep_diff, "deep-diff for strvec not supported yet"
|
||
|
return self._get_code_get_full(location)
|
||
|
def __str__(self) -> str:
|
||
|
return f"{self.dataio_type}({self.public_type})"
|
||
|
def string_type_ctor(dataio_type: str, public_type: str) -> RawFieldType:
|
||
|
"""Field type constructor for both strings and string vectors"""
|
||
|
if dataio_type not in ("string", "estring"):
|
||
|
raise ValueError(f"not a valid string type: {dataio_type}")
|
||
|
if public_type == "char":
|
||
|
return NeedSizeType(dataio_type, public_type, cls = StringType)
|
||
|
elif public_type == "struct strvec":
|
||
|
return StrvecType(dataio_type, public_type)
|
||
|
else:
|
||
|
raise ValueError(f"public type {public_type} not legal for dataio type {dataio_type}")
|
||
|
DEFAULT_REGISTRY.dataio_types["string"] = DEFAULT_REGISTRY.dataio_types["estring"] = string_type_ctor
|
||
|
class Field:
|
||
|
"""A single field of a packet. Consists of a name, type information
|
||
|
(including array sizes) and flags."""
|
||
| ... | ... | |
|
def get_init(self) -> str:
|
||
|
"""Generate code initializing this field in the packet struct, after
|
||
|
the struct has already been zeroed."""
|
||
|
return self.type_info.get_code_init(Location(self.name))
|
||
|
return self.type_info.get_code_init(Location(self.name, f"packet->{self.name}"))
|
||
|
def get_copy(self, dest: str, src: str) -> str:
|
||
|
"""Generate code deep-copying this field from *src to *dest."""
|
||
| ... | ... | |
|
def get_free(self) -> str:
|
||
|
"""Generate code deinitializing this field in the packet struct
|
||
|
before destroying the packet."""
|
||
|
return self.type_info.get_code_free(Location(self.name))
|
||
|
return self.type_info.get_code_free(Location(self.name, f"packet->{self.name}"))
|
||
|
def get_hash(self) -> str:
|
||
|
"""Generate code factoring this field into a hash computation."""
|
||
| common/networking/packets.def | ||
|---|---|---|
|
type REQUIREMENT = requirement(struct requirement)
|
||
|
type ACT_PROB = action_probability(struct act_prob)
|
||
|
type STRING = string(char)
|
||
|
# A string vector encoded to a string outside the packet and field system.
|
||
|
# Marking it this way is useful as documentation. The marking can also be
|
||
|
# used in non vanilla generate_packets.py packet generators.
|
||
|
type STRVEC = STRING
|
||
|
type STRVEC = string(struct strvec)
|
||
|
type WORKLIST = worklist(struct worklist)
|
||
|
# string that is URI encoded in the JSON protocol
|
||
|
type ESTRING = estring(char)
|
||
| ... | ... | |
|
VLAYER vlayer;
|
||
|
STRVEC helptext[MAX_LEN_PACKET];
|
||
|
STRVEC helptext;
|
||
|
BV_UTYPE_FLAGS flags;
|
||
|
BV_UTYPE_ROLES roles;
|
||
| ... | ... | |
|
UINT8 reqs_count;
|
||
|
REQUIREMENT reqs[MAX_NUM_REQS:reqs_count];
|
||
|
STRVEC helptext[MAX_LEN_PACKET];
|
||
|
STRVEC helptext;
|
||
|
end
|
||
|
PACKET_RULESET_GOVERNMENT_RULER_TITLE = 143; sc, lsend
|
||
| ... | ... | |
|
UINT32 num_reqs;
|
||
|
STRING name[MAX_LEN_NAME];
|
||
|
STRING rule_name[MAX_LEN_NAME];
|
||
|
STRVEC helptext[MAX_LEN_PACKET];
|
||
|
STRVEC helptext;
|
||
|
STRING graphic_str[MAX_LEN_NAME];
|
||
|
STRING graphic_alt[MAX_LEN_NAME];
|
||
|
end
|
||
| ... | ... | |
|
STRING rule_name[MAX_LEN_NAME];
|
||
|
STRING graphic_str[MAX_LEN_NAME];
|
||
|
STRING graphic_alt[MAX_LEN_NAME];
|
||
|
STRVEC helptext[MAX_LEN_PACKET];
|
||
|
STRVEC helptext;
|
||
|
end
|
||
|
PACKET_RULESET_TERRAIN_CONTROL = 146; sc, lsend
|
||
| ... | ... | |
|
STRING soundtag[MAX_LEN_NAME];
|
||
|
STRING soundtag_alt[MAX_LEN_NAME];
|
||
|
STRING soundtag_alt2[MAX_LEN_NAME];
|
||
|
STRVEC helptext[MAX_LEN_PACKET];
|
||
|
STRVEC helptext;
|
||
|
end
|
||
|
PACKET_RULESET_IMPR_FLAG = 20; sc, lsend
|
||
| ... | ... | |
|
UINT8 color_green;
|
||
|
UINT8 color_blue;
|
||
|
STRVEC helptext[MAX_LEN_PACKET];
|
||
|
STRVEC helptext;
|
||
|
end
|
||
|
PACKET_RULESET_TERRAIN_FLAG = 231; sc, lsend
|
||
| ... | ... | |
|
UINT16 non_native_def_pct;
|
||
|
BV_UCLASS_FLAGS flags;
|
||
|
STRVEC helptext[MAX_LEN_PACKET];
|
||
|
STRVEC helptext;
|
||
|
end
|
||
|
PACKET_RULESET_EXTRA = 232; sc, lsend
|
||
| ... | ... | |
|
BV_EXTRAS bridged_over;
|
||
|
BV_EXTRAS conflicts;
|
||
|
SINT8 no_aggr_near_city;
|
||
|
STRVEC helptext[MAX_LEN_PACKET];
|
||
|
STRVEC helptext;
|
||
|
end
|
||
|
PACKET_RULESET_EXTRA_FLAG = 226; sc, lsend
|
||
| ... | ... | |
|
UINT16 to_pct;
|
||
|
UINT16 onetime_pct;
|
||
|
BV_GOODS_FLAGS flags;
|
||
|
STRVEC helptext[MAX_LEN_PACKET];
|
||
|
STRVEC helptext;
|
||
|
end
|
||
|
PACKET_RULESET_DISASTER = 224; sc, lsend
|
||
| ... | ... | |
|
PACKET_RULESET_COUNTER = 513; sc, lsend
|
||
|
STRING name[MAX_LEN_NAME];
|
||
|
STRING rule_name[MAX_LEN_NAME];
|
||
|
STRVEC helptext[MAX_LEN_PACKET];
|
||
|
STRVEC helptext;
|
||
|
UINT32 def;
|
||
|
UINT32 checkpoint;
|
||
|
COUNTER_TARGET type;
|
||
| ... | ... | |
|
STRING rule_name[MAX_LEN_NAME];
|
||
|
UINT8 reqs_count;
|
||
|
REQUIREMENT reqs[MAX_NUM_REQS:reqs_count];
|
||
|
STRVEC helptext[MAX_LEN_PACKET];
|
||
|
STRVEC helptext;
|
||
|
end
|
||
|
PACKET_RULESET_CLAUSE = 512; sc, lsend
|
||
| common/networking/packets.h | ||
|---|---|---|
|
enum packet_type packet_type);
|
||
|
bool packet_check(struct data_in *din, struct connection *pc);
|
||
|
/* Utilities to exchange strings and string vectors. */
|
||
|
#define PACKET_STRVEC_SEPARATOR '\3'
|
||
|
#define PACKET_STRVEC_COMPUTE(str, strvec) \
|
||
|
if (NULL != strvec) { \
|
||
|
strvec_to_str(strvec, PACKET_STRVEC_SEPARATOR, str, sizeof(str)); \
|
||
|
} else { \
|
||
|
str[0] = '\0'; \
|
||
|
}
|
||
|
#define PACKET_STRVEC_EXTRACT(strvec, str) \
|
||
|
if ('\0' != str[0]) { \
|
||
|
strvec = strvec_new(); \
|
||
|
strvec_from_str(strvec, PACKET_STRVEC_SEPARATOR, str); \
|
||
|
} else { \
|
||
|
strvec = NULL; \
|
||
|
/* Utilities to move string vectors in and out of packets. */
|
||
|
#define PACKET_STRVEC_INSERT(dest, src) \
|
||
|
dest = src
|
||
|
#define PACKET_STRVEC_EXTRACT(dest, src) \
|
||
|
if (src != nullptr && strvec_size(src) > 0) { \
|
||
|
dest = strvec_new(); \
|
||
|
strvec_copy(dest, src); \
|
||
|
} else { \
|
||
|
dest = nullptr; \
|
||
|
}
|
||
|
#ifdef __cplusplus
|
||
| common/networking/packets_json.h | ||
|---|---|---|
|
FREE_PACKET_STRUCT(&packet_buf); \
|
||
|
return NULL;
|
||
|
/* Utilities to exchange strings and string vectors. */
|
||
|
#define PACKET_STRVEC_SEPARATOR '\3'
|
||
|
#define PACKET_STRVEC_COMPUTE(str, strvec) \
|
||
|
if (NULL != strvec) { \
|
||
|
strvec_to_str(strvec, PACKET_STRVEC_SEPARATOR, str, sizeof(str)); \
|
||
|
} else { \
|
||
|
str[0] = '\0'; \
|
||
|
}
|
||
|
#define PACKET_STRVEC_EXTRACT(strvec, str) \
|
||
|
if ('\0' != str[0]) { \
|
||
|
strvec = strvec_new(); \
|
||
|
strvec_from_str(strvec, PACKET_STRVEC_SEPARATOR, str); \
|
||
|
} else { \
|
||
|
strvec = NULL; \
|
||
|
/* Utilities to move string vectors in and out of packets. */
|
||
|
#define PACKET_STRVEC_INSERT(dest, src) \
|
||
|
dest = src
|
||
|
#define PACKET_STRVEC_EXTRACT(dest, src) \
|
||
|
if (src != nullptr && strvec_size(src) > 0) { \
|
||
|
dest = strvec_new(); \
|
||
|
strvec_copy(dest, src); \
|
||
|
} else { \
|
||
|
dest = nullptr; \
|
||
|
}
|
||
|
#ifdef __cplusplus
|
||
| fc_version | ||
|---|---|---|
|
# On FREECIV_DEBUG builds, optional capability "debug" gets automatically
|
||
|
# appended to this.
|
||
|
#
|
||
|
NETWORK_CAPSTRING="+Freeciv.Devel-${MAIN_VERSION}-2024.Apr.17"
|
||
|
NETWORK_CAPSTRING="+Freeciv.Devel-${MAIN_VERSION}-2024.Apr.20"
|
||
|
FREECIV_DISTRIBUTOR=""
|
||
|
if test "x$FREECIV_LABEL_FORCE" != "x" ; then
|
||
| server/ruleset/ruleload.c | ||
|---|---|---|
|
packet.non_native_def_pct = c->non_native_def_pct;
|
||
|
packet.flags = c->flags;
|
||
|
PACKET_STRVEC_COMPUTE(packet.helptext, c->helptext);
|
||
|
PACKET_STRVEC_INSERT(packet.helptext, c->helptext);
|
||
|
lsend_packet_ruleset_unit_class(dest, &packet);
|
||
|
} unit_class_iterate_end;
|
||
| ... | ... | |
|
packet.work_raise_chance[i] = vlevel->work_raise_chance;
|
||
|
}
|
||
|
}
|
||
|
PACKET_STRVEC_COMPUTE(packet.helptext, u->helptext);
|
||
|
PACKET_STRVEC_INSERT(packet.helptext, u->helptext);
|
||
|
packet.worker = u->adv.worker;
|
||
| ... | ... | |
|
} requirement_vector_iterate_end;
|
||
|
packet.reqs_count = j;
|
||
|
PACKET_STRVEC_COMPUTE(packet.helptext, s->helptext);
|
||
|
PACKET_STRVEC_INSERT(packet.helptext, s->helptext);
|
||
|
lsend_packet_ruleset_specialist(dest, &packet);
|
||
|
} specialist_type_iterate_end;
|
||
| ... | ... | |
|
packet.flags = a->flags;
|
||
|
packet.cost = a->cost;
|
||
|
packet.num_reqs = a->num_reqs;
|
||
|
PACKET_STRVEC_COMPUTE(packet.helptext, a->helptext);
|
||
|
PACKET_STRVEC_INSERT(packet.helptext, a->helptext);
|
||
|
lsend_packet_ruleset_tech(dest, &packet);
|
||
|
} advance_iterate_end;
|
||
| ... | ... | |
|
packet.type = pcount->target;
|
||
|
packet.def = pcount->def;
|
||
|
PACKET_STRVEC_COMPUTE(packet.helptext, pcount->helptext);
|
||
|
PACKET_STRVEC_INSERT(packet.helptext, pcount->helptext);
|
||
|
lsend_packet_ruleset_counter(dest, &packet);
|
||
|
} city_counters_iterate_end;
|
||
|
}
|
||
| ... | ... | |
|
sz_strlcpy(packet.soundtag, b->soundtag);
|
||
|
sz_strlcpy(packet.soundtag_alt, b->soundtag_alt);
|
||
|
sz_strlcpy(packet.soundtag_alt2, b->soundtag_alt2);
|
||
|
PACKET_STRVEC_COMPUTE(packet.helptext, b->helptext);
|
||
|
PACKET_STRVEC_INSERT(packet.helptext, b->helptext);
|
||
|
lsend_packet_ruleset_building(dest, &packet);
|
||
|
} improvement_iterate_end;
|
||
| ... | ... | |
|
packet.color_green = pterrain->rgb->g;
|
||
|
packet.color_blue = pterrain->rgb->b;
|
||
|
PACKET_STRVEC_COMPUTE(packet.helptext, pterrain->helptext);
|
||
|
PACKET_STRVEC_INSERT(packet.helptext, pterrain->helptext);
|
||
|
lsend_packet_ruleset_terrain(dest, &packet);
|
||
|
} terrain_type_iterate_end;
|
||
| ... | ... | |
|
packet.bridged_over = e->bridged_over;
|
||
|
packet.conflicts = e->conflicts;
|
||
|
PACKET_STRVEC_COMPUTE(packet.helptext, e->helptext);
|
||
|
PACKET_STRVEC_INSERT(packet.helptext, e->helptext);
|
||
|
lsend_packet_ruleset_extra(dest, &packet);
|
||
|
} extra_type_iterate_end;
|
||
| ... | ... | |
|
packet.onetime_pct = g->onetime_pct;
|
||
|
packet.flags = g->flags;
|
||
|
PACKET_STRVEC_COMPUTE(packet.helptext, g->helptext);
|
||
|
PACKET_STRVEC_INSERT(packet.helptext, g->helptext);
|
||
|
lsend_packet_ruleset_goods(dest, &packet);
|
||
|
} goods_type_iterate_end;
|
||
| ... | ... | |
|
sz_strlcpy(gov.rule_name, rule_name_get(&g->name));
|
||
|
sz_strlcpy(gov.graphic_str, g->graphic_str);
|
||
|
sz_strlcpy(gov.graphic_alt, g->graphic_alt);
|
||
|
PACKET_STRVEC_COMPUTE(gov.helptext, g->helptext);
|
||
|
PACKET_STRVEC_INSERT(gov.helptext, g->helptext);
|
||
|
lsend_packet_ruleset_government(dest, &gov);
|
||
| ... | ... | |
|
} requirement_vector_iterate_end;
|
||
|
packet.reqs_count = j;
|
||
|
PACKET_STRVEC_COMPUTE(packet.helptext, pmul->helptext);
|
||
|
PACKET_STRVEC_INSERT(packet.helptext, pmul->helptext);
|
||
|
lsend_packet_ruleset_multiplier(dest, &packet);
|
||
|
} multipliers_iterate_end;
|
||