indexing description: "[ A group of connections and objects. Groups are a central concept in the multiplayer framework. Every group is a set of connections and objects. A connection may belong to as many groups as it want, but is always member of an additional `personal_group', that only holds one connection and no objects. Objects on the other hand, can only belong to one single group. Changes in the objects state are reflected to all other peers (= connections in the group) in the same group. It is also possible to publish events to all connections of a group using the `net_event_container_object'. A group also manages timeouts in network activity for personal groups. You may for example use low timeout value for a game group, but a higher for a chat group. ]" date: "$Date$" revision: "$Revision$" class EM_NET_GROUP inherit EM_NET_OBJECT_TYPES rename make_and_initialize_factory as make_object_types export {NONE} all end EM_NET_EVENT_PROCESSOR rename make as make_event_processor end EM_TIME_SINGLETON export {NONE} all end EM_DELAYED_PROCEDURES rename make as make_delayed_procedures export {NONE} all end create {EM_NET_BASE} make feature {NONE} -- Initialization make (a_name: STRING; a_base: EM_NET_BASE[EM_NET_OBJECT_TYPES]) is -- Initialize a new socket group. -- `a_name' is the unique group name. -- `net_base' is an instance of the network base class that manages the group. require a_name_not_void: a_name /= Void a_net_base_not_void: a_base /= Void do make_delayed_procedures make_event_processor create connections.make create objects.make create acknowledged_event.make (10) create timeout_event name := a_name base := a_base base.groups.force (Current, name) net_event_container_object := create_em_net_event_container_object objects.put_first (net_event_container_object) set_max_inactivity_time (5000) add_timed_procedure (agent cleanup_ack_table (30000), 60000) ensure name_set: name = a_name net_event_container_object_created: net_event_container_object /= Void connection_list_created: connections /= Void object_list_createdt: objects /= Void end feature -- Information name: STRING -- Name of the group feature {EM_NET_EVENT_OBJECT, EM_NET_EVENT_CONTAINER_OBJECT, EM_NET_GROUP} -- Access net_event_container_object: EM_NET_EVENT_CONTAINER_OBJECT -- Event container object. base: EM_NET_BASE[EM_NET_OBJECT_TYPES] -- Network base class, that manages the group feature -- Element change add_object (an_object: EM_NET_OBJECT) is -- Add `an_object' to the group. require an_object_not_void: an_object /= Void not_has_an_object: not has_object(an_object) do check not_already_added: not base.has_object (an_object) end objects.put_first (an_object) base.add_object_internal (an_object) ensure object_added_to_base: base.has_object(an_object) object_added: has_object (an_object) end remove_object (an_object: EM_NET_OBJECT) is -- Remove `an_object' from the group. require an_object_not_void: an_object /= Void has_an_object: has_object (an_object) do objects.delete (an_object) base.remove_object_internal (an_object) ensure object_deleted: not has_object (an_object) end add_connection (a_connection: EM_NET_CONNECTION) is -- Add `a_connection' to the group. require a_connection_not_void: a_connection /= Void not_has_a_connection: not has_connection(a_connection) do connections.put_first (a_connection) -- a_connection.join(Current) ensure connection_added: has_connection (a_connection) end remove_connection (a_connection: EM_NET_CONNECTION) is -- Remove `a_connection' from the group. require a_connection_not_void: a_connection /= Void has_connection: has_connection(a_connection) do connections.delete (a_connection) ensure connection_removed: not has_connection (a_connection) end feature -- Status information has_object (an_object: EM_NET_OBJECT): BOOLEAN is -- Does `an_object' exist in the group? require an_object_not_void: an_object /= Void do Result := objects.has (an_object) end has_connection (a_connection: EM_NET_CONNECTION): BOOLEAN is -- Does `a_connection' exist in the group? require a_connection_not_void: a_connection /= Void do Result := connections.has (a_connection) end feature -- Operations send_ack (an_id: INTEGER) is -- Send an acknowledgement for `an_id' to all clients in the group. local ack: EM_NET_2PC_ACK do debug ("em_mp") io.put_string ("Sending acknowledgement for event with ID " + an_id.out + "%N") end ack := create_em_net_2pc_ack ack.set_event_id (an_id) net_event_container_object.publish (ack) end set_max_inactivity_time(a_time_in_ms: INTEGER) is -- Set `max_inactivity_time' to `a_time_in_ms'. require a_time_in_ms_positive: a_time_in_ms > 0 do max_inactivity_time := a_time_in_ms ensure max_inactivity_time_set: max_inactivity_time = a_time_in_ms end feature -- Attributes max_inactivity_time: INTEGER -- Maximum inactivity time -- This is not directly involved into the framework. -- But can be used to implement a more complex timeout mechanism timeout_event: EM_EVENT_CHANNEL[TUPLE[EM_NET_CONNECTION]] -- Timeout event -- This is not directly involved into the framework. -- But can be used to implement a more complex timeout mechanism feature {EM_NET_BASE,EM_NET_SERVER_DISCOVERY_RESPONSE,EM_NET_2PC_EVENT_OBJECT,EM_NET_EVENT_CONTAINER_OBJECT} -- Implementation connections: DS_LINKED_LIST[EM_NET_CONNECTION] -- Linked list of sockets of the current group objects: DS_LINKED_LIST[EM_NET_OBJECT] -- Linked list of objects of the current group acknowledged_event: DS_HASH_TABLE[INTEGER,INTEGER] -- Hashtable of acknowledged events. -- The key is event id. -- The value is the time of the ack. This is used to cleanup the list prediodically. feature {NONE} -- Implementation cleanup_ack_table (age: INTEGER) is -- Cleanup acknowledged events that are older than `age' milliseconds. local cursor: DS_HASH_TABLE_CURSOR[INTEGER,INTEGER] current_time: INTEGER do cursor := acknowledged_event.new_cursor current_time := time.ticks from cursor.start until cursor.after loop if (current_time - cursor.item) > age then acknowledged_event.remove (cursor.key) end cursor.forth end add_timed_procedure (agent cleanup_ack_table (age), 60000) end invariant net_event_container_object_exists: net_event_container_object /= Void net_event_container_object_linked: objects.has (net_event_container_object) base_set: base /= Void group_correct_linked: base.group (name) = Current end