Eiffel Media API
Overview Cluster Class Classes Index      Previous Next      Top Features

em.network.multiplayer

Class EM_NET_BASE


Direct ancestors

EM_UDP_SOCKET, EM_NET_EVENT_PROCESSOR, EM_TIME_SINGLETON

Known direct descendants

EM_NET_CLIENT, EM_NET_SERVER

Features

Invariants

indexing

description

Base class for network peers in a multiplayer environment.
This class is the core of the EM multiplayer framework and implements functionality that is not
bound to a specific peer architecture (like peer-to-peer or client-server).

EM_NET_BASE is a generic class. You need to specify a generic parameter that is a subclass
of EM_NET_OBJECT_TYPES in which you register your own network objects.
See EM_NET_OBJECT_TYPES for further details about how to register your own objects.

It is very important that each peer, whose implementation is not necessarily identical to remote peers,
uses the same concrete EM_NET_OBJECT_TYPES. In other words: The (concrete) generic type of every EM_NET_BASE
in a network must conform with all other participants.

EM_NET_BASE is itself an EM_UDP_SOCKET which is used for data sending and receiving over a UDP socket.

There are currently two implementations of EM_NET_BASE:
- EM_NET_SERVER
- EM_NET_CLIENT

Together they build a client-server architecture which is usually used for network games.
For further explanations about the application of those classes have a look at the corresponding
class description.

Note: It is probably not necessary to make (any?) changes to EM_NET_BASE if you want to use your own network
architecture. For example, it is possible to create a peer to peer system just by extending EM_NET_BASE.

date

$Date: 2005/10/28 14:26:23 $

revision

$Revision: 1.65 $

deferred class

EM_NET_BASE [TYPES -> EM_NET_OBJECT_TYPES create make_and_initialize_factory end]

inherit

EM_UDP_SOCKET
EM_NET_EVENT_PROCESSOR

feature {EM_NET_SERVER, EM_NET_CLIENT} -- Initialization

make (a_port: INTEGER)

-- Initialisation

require
port_is_valid: a_port >= 0 and a_port <= 65535
ensure then
protocol_created: protocol /= Void
object_types_created: object_types /= Void
objects_created: objects /= Void
object_types_created: object_types /= Void
connections_created: connections /= Void
dynamic_connection_list_created: dynamic_connection_list /= Void
timeout_event_created: timeout_event /= Void
groups_created: groups /= Void
id_manager_created: id_manager /= Void
event_2pc_id_manager_created: event_2pc_id_manager /= Void
make_udp_socket

-- Init

-- (From EM_UDP_SOCKET)

ensure
data_received_event_created: data_received_event /= Void

feature -- Access

network_subsystem: EM_NETWORK_SUBSYSTEM

-- EiffelMedia network subsystem is

-- (From EM_SHARED_SUBSYSTEMS)

ensure
network_subsystem_not_void: Result /= void
last_packet: EM_UDP_PACKET

-- Last received packet

-- (From EM_UDP_SOCKET)

port: INTEGER

-- Local socket port

-- (From EM_SOCKET)

timer: EM_TIME

-- Returns reference to the singleton

-- (From EM_TIME_SINGLETON)

ensure
time_not_void: Result /= Void

feature -- Status setting

close

-- Close the UDP socket.

-- (From EM_SOCKET)

require
network_enabled: Network_subsystem.is_enabled
open: is_open
ensure
closed: not is_open
open

-- Open UDP Socket on a_port.

-- (From EM_SOCKET)

require
network_enabled: Network_subsystem.is_enabled
closed: not is_open
port_set: 1024 < port and port < 65536
ensure then
open: is_open

feature -- Element change

set_net_fps (a_net_fps_value: INTEGER)

-- Set net_fps to a_net_fps_value.

require
positive_net_fps_value: a_net_fps_value > 0
ensure
net_fps_set: net_fps = a_net_fps_value
set_port (a_port: INTEGER)

-- Set port to a_port.

-- (From EM_SOCKET)

require
a_port_in_range: 1024 < a_port and a_port < 65536
ensure
port_set: port = a_port
subscribe_by_event (an_event: EM_NET_EVENT_OBJECT; a_procedure: PROCEDURE [ANY, TUPLE])

-- Subscribe a_procedure to an_event.

-- (From EM_NET_EVENT_PROCESSOR)

subscribe_by_type_id (an_event_type_id: INTEGER; a_procedure: PROCEDURE [ANY, TUPLE])

-- Subscribe a_procedure to an event with an_event_type_id.

-- (From EM_NET_EVENT_PROCESSOR)

unsubscribe_by_event (an_event: EM_NET_OBJECT; a_procedure: PROCEDURE [ANY, TUPLE])

-- Unsubscribe a_procedure from an_event.

-- (From EM_NET_EVENT_PROCESSOR)

unsubscribe_by_type_id (an_event_type_id: INTEGER; a_procedure: PROCEDURE [ANY, TUPLE])

-- Unsubscribe a_procedure from an event with an_event_type_id.

-- (From EM_NET_EVENT_PROCESSOR)

feature -- Basic operations

add_timed_procedure (a_procedure: PROCEDURE[ANY,TUPLE]; a_time_offset_in_ms: INTEGER)

-- Add a_procedure which will be called in TIME_OFFSET miliseconds from "now" in the same thread!
-- Use the feature "process" to call all procedures whose time is up.
-- You could use EM_TIME_SINGLETON for global timing: The EM_EVENT_LOOP will call "process" each loop once.
-- You can also use the EM_EVENT_LOOP to have a per scene executeion.

-- (From EM_DELAYED_PROCEDURES)

require
a_procedure /=Voida_time_offset_in_ms >= 0
process_timed_procedures_in_same_thread

-- executes all stored procedures whose time is up...
-- Make sure you don't add procedures which use a lot of computation time. Only small, short
-- procedures are appropriate. Otherwise use add_timed_callback which runs functions in a different thread
-- to avoid a laggy reaction to other events.

-- (From EM_DELAYED_PROCEDURES)

send (a_packet: EM_UDP_PACKET)

-- Send a_packet to its destination.

-- (From EM_UDP_SOCKET)

require
open: is_open
send_direct (a_destination: EM_INET_SOCKET_ADDRESS; a_string: STRING)

-- Send a_string to a_destination.

-- (From EM_UDP_SOCKET)

wipe_out_all_timed_procedures

-- clear the whole "delayed procedure call" list (procedures added with add_procedure, executed with execute)

-- (From EM_DELAYED_PROCEDURES)

feature -- Events

data_received_event: EM_EVENT_TYPE [TUPLE[EM_UDP_PACKET]]

-- Handle a received packet.

-- (From EM_UDP_SOCKET)

send_failed_event: EM_EVENT_TYPE [TUPLE [INTEGER]]

-- Send failed event.
-- Passed parameter is the error code: Em_error_resolve_ip or Em_error_send_failed

-- (From EM_UDP_SOCKET)

feature -- Network

disable_auto_transmit

-- Disable auto transmission.

require
ensure
auto_transmit_disabled: not is_auto_transmit_enabled
enable_auto_transmit

-- Enable auto transmission of packets.
-- The mechanism tries to send packets with a frequency of net_fps.

require
not_enabled: not is_auto_transmit_enabled
ensure
auto_transmit_enabled: is_auto_transmit_enabled
send_update

-- Send updated objects for each group to clients.

feature -- Event handling

publish (an_event: EM_NET_EVENT_OBJECT)

-- Publish an_event to the standard group.

require
an_event_not_void: an_event /= Void
timeout_event: EM_EVENT_TYPE[TUPLE[EM_NET_CONNECTION]]

-- Event that is called if a connection timeout occurs

feature -- Connection handling

create_connection (a_socket_address: EM_INET_SOCKET_ADDRESS)

-- Create a connection and monitor it for timeouts.

require
a_socket_address_not_void: a_socket_address /= Void
create_static_connection (a_socket_address: EM_INET_SOCKET_ADDRESS)

-- Create a connection and don't monitor it for timeouts.
-- Use this for broadcasts and other cases where you have mainly outgoing traffic.

require
a_socket_address_not_void: a_socket_address /= Void
delete_connection (a_connection: EM_NET_CONNECTION)

-- Delete a_connection.

require
a_connection_not_void: a_connection /= Void
ensure
connection_removed: not connections.has_item (a_connection)
connection_from_dynamic_list_removed: not dynamic_connection_list.has (a_connection)
last_created_connection: EM_NET_CONNECTION

-- Last created connection

feature -- Time synchronisation

disable_time_sync

-- Disable time synchronisation.

enable_time_sync

-- Enable time synchronisation.

is_time_sync_enabled: BOOLEAN

-- Is time synchronisation enabled?

set_time_sync_group (a_group: EM_NET_GROUP)

-- Set time_sync_group to a_group.

require
a_group_not_void: a_group /= Void
ensure
protocol_group_updated: protocol.time_sync_group = time_sync_group
time: INTEGER

-- Time in milliseconds!

time_sync_group: EM_NET_GROUP

-- Group used for time synchronisation

feature -- Object management

add_object (an_object: EM_NET_OBJECT)

-- Add an_object to the standard group.

require
an_object_not_void: an_object /= Void
ensure
added_to_standard_group: standard_group.has_object (an_object)
has_object (an_object: EM_NET_OBJECT) : BOOLEAN

-- Does the standard group contain an_object?

require
an_object_not_void: an_object /= Void
remove_object (an_object: EM_NET_OBJECT)

-- Remove an_object from the standard group.

require
an_object_not_void: an_object /= Void
standard_group_has_object: standard_group.has_object (an_object)
ensure
removed_from_standard_group: not standard_group.has_object (an_object)

feature -- Group management

create_group (a_group_name: STRING)

-- Create a new group with a_group_name and make it available in last_created_group.
-- a_group_name must be unique.

require
group_not_exist: not group_exists_by_name (a_group_name)
group (a_group_name: STRING): EM_NET_GROUP

-- Group with group_name

require
group_name_not_void: a_group_name /= Void
group_exists_by_name: group_exists_by_name (a_group_name)
group_exists (a_group: EM_NET_GROUP): BOOLEAN

-- Does a_group exist?

require
group_not_void: a_group /= Void
group_exists_by_name (a_group_name: STRING): BOOLEAN

-- Does group with a_group_name exist?

require
group_name_not_void: a_group_name /= Void
last_created_group: EM_NET_GROUP

-- Last group that was created by create_group

remove_group (a_group: EM_NET_GROUP)

-- Remove a_group.

require
group_exists: group_exists(a_group)
ensure
group_deleted: not group_exists(a_group)
remove_group_by_name (a_group_name: STRING)

-- Remove group with a_group_name.

require
group_exists_by_name: group_exists_by_name(a_group_name)
ensure
group_deleted: not group_exists_by_name(a_group_name)
set_standard_group (a_group_name: STRING)

-- Set standard_group to the group with a_group_name.

require
group_name_not_void: a_group_name /= Void
group_exists_by_name: group_exists_by_name (a_group_name)
ensure
standard_group_set: standard_group = group(a_group_name)
standard_group:EM_NET_GROUP

-- Standard group
-- Many convenience features use this group

feature {EM_NET_GROUP} -- Internal object management implementation

add_object_internal (an_object: EM_NET_OBJECT)

-- Add an_object to the list of synchronized objects.

require
object_not_void: an_object /= Void
object_not_yet_added: not has_object (an_object)
ensure
object_added: has_object(an_object)
remove_object_internal (an_object: EM_NET_OBJECT)

-- Remove an_object from the list of synchronized objects.

require
object_not_void: an_object /= Void
has_object: has_object(an_object)
ensure
object_removed: not has_object (an_object)

feature {EM_NET_2PC_EVENT_OBJECT} -- Two phase commit

enable_2pc_timed_resend (an_event: EM_NET_2PC_EVENT_OBJECT)

-- Enable timed resend of an_event.

require
event_not_void: an_event /= Void
on_2pc_ack (an_ack: EM_NET_2PC_ACK)

-- Handle client ACK messages.

require
ack_not_void: an_ack /= Void
ensure
event_not_pending_for_connection: not pending_2pc_acks.has (an_ack.event_id) or else not pending_2pc_acks.item (an_ack.event_id).second.has (an_ack.updating_connection)
pending_2pc_acks: DS_HASH_TABLE[EM_PAIR[EM_NET_2PC_EVENT_OBJECT,DS_LINKED_LIST[EM_NET_CONNECTION]],INTEGER]

-- List of pending 2PC events
-- The key of the hashtable is the event_id.
-- The value of the hashtable is the EVENT itself and the list of connections whose ACKs are not received yet.

resend_2pc (an_event: EM_NET_2PC_EVENT_OBJECT)

-- Send an_event to clients that have not sent an acknowledgement yet.

require
event_not_void: an_event /= Void

feature -- Publishing

local_publish (an_event: EM_NET_EVENT_OBJECT)

-- Publish an_event to the event queue.

-- (From EM_NET_EVENT_PROCESSOR)

feature -- Setters

set_are_timed_procedures_paused (a_value: BOOLEAN)

-- Set are_timed_procedures_paused to a_value
-- Use this to pause the procedure callbacks. This is really usefull if you want to pause a game!
-- However, this will not pause functions added with add_timed_callback.

-- (From EM_DELAYED_PROCEDURES)

ensure
are_timed_procedures_paused_set: are_timed_procedures_paused = a_value

feature -- ID management

id_manager: EM_NET_OBJECT_ID_MANAGER

-- ID manager for net objects

net_object_factory: EM_NET_OBJECT_FACTORY

-- Object creation factory

feature -- Status information

game_identifier: STRING

-- Game identifier

is_auto_transmit_enabled: BOOLEAN

-- Is auto transmit enabled?
-- Use this to automate the sending of outgoing data.
-- This relies on the net_fps frequency.

net_fps: INTEGER

-- Network updates per second

object_types: TYPES

-- Object types which can be used in this implementation

feature {EM_NET_GROUP} -- Group implementation

groups: DS_HASH_TABLE[EM_NET_GROUP, STRING]

-- List of groups

objects: DS_HASH_TABLE[EM_NET_OBJECT,INTEGER]

-- List of objects which are synchronized over the network

updating_connection: EM_NET_CONNECTION

-- Connection which sent the last update

feature -- Status

is_open: BOOLEAN

-- Is socket open?

-- (From EM_SOCKET)

feature -- Attributes

are_timed_procedures_paused: BOOLEAN

-- Are timed prcedures paused?

-- (From EM_DELAYED_PROCEDURES)

feature {EM_SOCKETS} -- Implementation

handle_data_received

-- Handle data reveived event.

-- (From EM_SOCKET)

handle_send_failed (an_error: INTEGER)

-- Handle send failed.

-- (From EM_UDP_SOCKET)

net2_socket_id: INTEGER

-- Socket id of Net2

-- (From EM_SOCKET)

feature {NONE} -- Implementation

pause_start_ticks: INTEGER

-- pause started ticks
-- Used to compute a timedifference to implement pause mechanism.

-- (From EM_DELAYED_PROCEDURES)

procedure_list: DS_LINKED_LIST[EM_PAIR[PROCEDURE[ANY,TUPLE],INTEGER]]

-- list of procedures

-- (From EM_DELAYED_PROCEDURES)

invariant

event_container_correct: event_container = objects.item (object_types.em_net_event_container_object)

-- From ANY
reflexive_equality: standard_is_equal (Current)
reflexive_conformance: conforms_to (Current)

end