2561 def __init__(self, cfg: ScriptConfig, packet_type: str, packet_number: int, flags_text: str,
2562 lines: typing.Iterable[str], resolve_type: typing.Callable[[str], RawFieldType]):
2564 """Configuration used when generating code for this packet"""
2565 self.type = packet_type
2566 """The packet type in allcaps (PACKET_FOO), as defined in the
2569 See also self.name"""
2570 self.type_number = packet_number
2571 """The numeric ID of this packet type"""
2573 # FIXME: Once we can use Python 3.6 features, use variable
2574 # annotations instead of empty comprehensions to set element type
2575 self.cancel = [str(_) for _ in ()]
2576 """List of packet types to drop from the cache when sending or
2577 receiving this packet type"""
2580 for flag in flags_text.split(","):
2585 if flag in ("sc", "cs"):
2588 if flag == "is-info":
2589 self.is_info = "yes"
2591 if flag == "is-game-info":
2592 self.is_info = "game"
2595 self.want_dsend = True
2598 self.want_lsend = True
2601 self.want_force = True
2603 if flag == "pre-send":
2604 self.want_pre_send = True
2606 if flag == "post-send":
2607 self.want_post_send = True
2609 if flag == "post-recv":
2610 self.want_post_recv = True
2612 if flag == "no-delta":
2615 if flag == "no-handle":
2616 self.no_handle = True
2618 if flag == "handle-via-fields":
2619 self.handle_via_packet = False
2621 if flag == "handle-per-conn":
2622 self.handle_per_conn = True
2625 mo = __class__.CANCEL_PATTERN.fullmatch(flag)
2627 self.cancel.append(mo.group(1))
2630 raise ValueError("unrecognized flag for %s: %r" % (self.name, flag))
2633 raise ValueError("no directions defined for %s" % self.name)
2634 self.dirs = Directions(frozenset(dirs))
2635 """Which directions this packet can be sent in"""
2640 for field in Field.parse(self.cfg, line, resolve_type)
2642 # put key fields before all others
2643 key_fields = [field for field in raw_fields if field.is_key]
2644 other_fields = [field for field in raw_fields if not field.is_key]
2645 self.all_fields = key_fields + other_fields
2646 """List of all fields of this packet, including name duplicates for
2647 different capability variants that are compatible.
2649 Only relevant for creating Variants; self.fields should be used when
2650 not dealing with capabilities or Variants."""
2652 self.fields = [Field(_, _, _, _) for _ in []]
2653 """List of all fields of this packet, with only one field of each name"""
2654 # check for duplicate field names
2655 for next_field in self.all_fields:
2656 duplicates = [field for field in self.fields if field.name == next_field.name]
2658 self.fields.append(next_field)
2660 if not all(field.is_compatible(next_field) for field in duplicates):
2661 raise ValueError("incompatible fields with duplicate name: %s(%d).%s"
2662 % (packet_type, packet_number, next_field.name))
2664 # valid, since self.fields is already set
2667 self.handle_via_packet = False
2670 raise ValueError("requested dsend for %s without fields isn't useful" % self.name)
2672 # create cap variants
2673 all_caps = self.all_caps # valid, since self.all_fields is already set
2675 Variant(caps, all_caps.difference(caps), self, i + 100)
2676 for i, caps in enumerate(powerset(sorted(all_caps)))
2678 """List of all variants of this packet"""
2809 def get_send(self) -> str:
2810 """Generate the implementation of the send function, which sends a
2811 given packet to a given connection."""
2815 elif self.want_force:
2816 func="force_to_send"
2817 args=", packet, force_to_send"
2823{self.send_prototype}
2826 log_error("WARNING: trying to send data to the closed connection %s",
2827 conn_description(pc));
2830 fc_assert_ret_val_msg(pc->phs.handlers->send[{self.type}].{func} != NULL, -1,
2831 "Handler for {self.type} not installed");
2832 return pc->phs.handlers->send[{self.type}].{func}(pc{args});
2835""".format(self = self, func = func, args = args)
2871 def get_dsend(self) -> str:
2872 """Generate the implementation of the dsend function, which directly
2873 takes packet fields instead of a packet struct."""
2874 if not self.want_dsend: return ""
2876 prefix(" ", field.get_fill())
2877 for field in self.fields
2880{self.dsend_prototype}
2882 struct {self.name} packet, *real_packet = &packet;
2886 return send_{self.name}(pc, real_packet);
2889""".format(self = self, fill = fill)
2891 def get_dlsend(self) -> str:
2892 """Generate the implementation of the dlsend function, combining
2893 dsend and lsend functionality.
2895 See self.get_dsend() and self.get_lsend()"""
2896 if not (self.want_lsend and self.want_dsend): return ""
2898 prefix(" ", field.get_fill())
2899 for field in self.fields
2902{self.dlsend_prototype}
2904 struct {self.name} packet, *real_packet = &packet;
2908 lsend_{self.name}(dest, real_packet);
2911""".format(self = self, fill = fill)