mirror of
https://github.com/cyberarm/i-mic-fps.git
synced 2025-12-12 22:32:35 +00:00
Added rubocop config, more work on CyberarmEngine Netcode; basic sending and receiving of packets is now functional
This commit is contained in:
8
.rubocop.yml
Normal file
8
.rubocop.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
Style/StringLiterals:
|
||||
EnforcedStyle: double_quotes
|
||||
|
||||
Metrics/MethodLength:
|
||||
Max: 40
|
||||
|
||||
Style/EmptyMethod:
|
||||
EnforcedStyle: expanded
|
||||
@@ -1,4 +1,4 @@
|
||||
class IMICFPS
|
||||
module CyberarmEngine
|
||||
module Networking
|
||||
MULTICAST_ADDRESS = "224.0.0.1"
|
||||
MULTICAST_PORT = 30_000
|
||||
@@ -6,7 +6,7 @@ class IMICFPS
|
||||
REMOTE_GAMEHUB = "i-mic.cyberarm.dev"
|
||||
REMOTE_GAMEHUB_PORT = 98765
|
||||
|
||||
DEFAULT_SERVER_HOST = "0.0.0.0"
|
||||
DEFAULT_SERVER_HOSTNAME = "0.0.0.0"
|
||||
DEFAULT_SERVER_PORT = 56789
|
||||
DEFAULT_SERVER_QUERY_PORT = 28900
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
module CyberarmEngine
|
||||
module Networking
|
||||
class Channel
|
||||
def initialize(id:, mode:)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,31 +1,78 @@
|
||||
module CyberarmEngine
|
||||
module Networking
|
||||
class Connection
|
||||
def initialize(hostname:, port:, max_clients:, channels: 1)
|
||||
def initialize(hostname:, port:, channels: 3)
|
||||
@hostname = hostname
|
||||
@port = port
|
||||
|
||||
@channels = Array(0..channels).map { |id| Channel.new(id: id, mode: :default) }
|
||||
|
||||
@peer = Peer.new(id: 0, hostname: "", port: "")
|
||||
|
||||
@last_read_time = Networking.milliseconds
|
||||
@last_write_time = Networking.milliseconds
|
||||
|
||||
@total_packets_sent = 0
|
||||
@total_packets_received = 0
|
||||
@total_data_sent = 0
|
||||
@total_data_received = 0
|
||||
end
|
||||
|
||||
# Callbacks #
|
||||
def connected
|
||||
end
|
||||
|
||||
def disconnected(reason)
|
||||
def disconnected(reason:)
|
||||
end
|
||||
|
||||
def reconnected
|
||||
end
|
||||
|
||||
def packet_received(message, channel)
|
||||
def packet_received(message:, channel:)
|
||||
end
|
||||
|
||||
# Functions #
|
||||
def send_packet(message, reliable, channel = 0)
|
||||
def send_packet(message:, reliable: false, channel: 0)
|
||||
end
|
||||
|
||||
def broadcast_packet(message, reliable, channel = 0)
|
||||
def connect(timeout: Protocol::TIMEOUT_PERIOD)
|
||||
@socket = UDPSocket.new
|
||||
|
||||
write(PacketHandler.create_control_packet(peer: @peer, control_type: Protocol::CONTROL_CONNECT))
|
||||
end
|
||||
|
||||
def disconnect(reason = "")
|
||||
def disconnect(timeout: Protocol::TIMEOUT_PERIOD)
|
||||
end
|
||||
|
||||
def update
|
||||
while read
|
||||
end
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def read
|
||||
data, addr = @socket.recvfrom_nonblock(Protocol::MAX_PACKET_SIZE)
|
||||
pkt = PacketHandler.handle(host: self, raw: data, peer: @peer)
|
||||
packet_received(message: pkt.message, channel: -1) if pkt.is_a?(RawPacket)
|
||||
|
||||
@total_packets_received += 1
|
||||
@total_data_received += data.length
|
||||
@last_read_time = Networking.milliseconds
|
||||
|
||||
return true
|
||||
rescue IO::WaitReadable
|
||||
return false
|
||||
end
|
||||
|
||||
def write(packet)
|
||||
raw = packet.encode
|
||||
@socket.send(raw, 0, @hostname, @port)
|
||||
|
||||
@total_packets_sent += 1
|
||||
@total_data_sent += raw.length
|
||||
@last_write_time = Networking.milliseconds
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,19 +1,30 @@
|
||||
module CyberarmEngine
|
||||
module Networking
|
||||
class Packet
|
||||
attr_reader :protocol_version, :type, :peer_id, :message
|
||||
attr_reader :protocol_version, :peer_id, :channel, :message
|
||||
|
||||
def self.type
|
||||
raise NotImplementedError, "#{self.class}.type must be defined!"
|
||||
def self.decode(raw)
|
||||
header = raw.unpack(CyberarmEngine::Networking::Protocol::PACKET_BASE_HEADER)
|
||||
|
||||
Packet.new(protocol_version: header[0], peer_id: header[1], channel: header[2], message: raw[Protocol::PACKET_BASE_HEADER_LENGTH...raw.length])
|
||||
end
|
||||
|
||||
def self.decode(packet)
|
||||
raise NotImplementedError, "#{self.class}.decode must be defined!"
|
||||
def initialize(protocol_version:, peer_id:, channel:, message:)
|
||||
@protocol_version = protocol_version
|
||||
@peer_id = peer_id
|
||||
@channel = channel
|
||||
@message = message
|
||||
end
|
||||
|
||||
def encode
|
||||
raise NotImplementedError, "#{self.class}#encode must be defined!"
|
||||
header = [
|
||||
@protocol_version,
|
||||
@peer_id,
|
||||
@channel
|
||||
].pack(CyberarmEngine::Networking::Protocol::PACKET_BASE_HEADER)
|
||||
|
||||
"#{header}#{@message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,6 +1,108 @@
|
||||
module CyberarmEngine
|
||||
module Networking
|
||||
module PacketHandler
|
||||
def self.type_to_name(type:)
|
||||
Protocol.constants.select { |const| const.to_s.start_with?("PACKET_") }
|
||||
.find { |const| Protocol.const_get(const) == type }
|
||||
end
|
||||
|
||||
def self.handle(host:, raw:, peer:)
|
||||
packet = Packet.decode(raw)
|
||||
type = packet.message.unpack1("C")
|
||||
|
||||
puts "#{host.class} received #{type_to_name(type: type)}"
|
||||
|
||||
case type
|
||||
when Protocol::PACKET_CONTROL
|
||||
handle_control_packet(host, packet, peer)
|
||||
when Protocol::PACKET_RAW
|
||||
handle_raw_packet(packet)
|
||||
else
|
||||
raise NotImplementedError, "A Packet handler for #{type} is not implmented!"
|
||||
end
|
||||
end
|
||||
|
||||
def self.handle_control_packet(host, packet, peer)
|
||||
pkt = ControlPacket.decode(packet.message)
|
||||
|
||||
case pkt.control_type
|
||||
when Protocol::CONTROL_CONNECT # TOSERVER only
|
||||
if (peer_id = host.available_peer_id)
|
||||
peer.id = peer_id
|
||||
host.clients << peer
|
||||
peer.write_queue << create_control_packet(peer: peer, control_type: Protocol::CONTROL_SET_PEER_ID, message: [peer_id].pack("n"))
|
||||
host.client_connected(peer: peer)
|
||||
else
|
||||
host.write(
|
||||
peer: peer,
|
||||
packet: PacketHandler.create_control_packet(
|
||||
peer: peer,
|
||||
control_type: Protocol::CONTROL_DISCONNECT,
|
||||
message: "ERROR: max number of clients already connected"
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
when Protocol::CONTROL_SET_PEER_ID # TOCLIENT only
|
||||
peer.id = pkt.message.unpack1("n")
|
||||
host.connected
|
||||
|
||||
when Protocol::CONTROL_DISCONNECT
|
||||
if host.is_a?(Server)
|
||||
host.client_disconnected(peer: peer)
|
||||
else
|
||||
host.disconnected(reason: pkt.message)
|
||||
end
|
||||
|
||||
when Protocol::CONTROL_HEARTBEAT
|
||||
when Protocol::CONTROL_PING
|
||||
when Protocol::CONTROL_PONG
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def self.handle_raw_packet(packet)
|
||||
RawPacket.decode(packet.message)
|
||||
end
|
||||
|
||||
def self.create_control_packet(peer:, control_type:, message: nil, reliable: false, channel: 0)
|
||||
message_packet = nil
|
||||
|
||||
if reliable
|
||||
warn "Reliable packets are not yet implemented!"
|
||||
packet = ControlPacket.new(control_type: control_type, message: message)
|
||||
message_packet = ReliablePacket.new(sequence_number: peer.next_reliable_sequence_number, message: packet.encode)
|
||||
else
|
||||
message_packet = ControlPacket.new(control_type: control_type, message: message)
|
||||
end
|
||||
|
||||
Packet.new(
|
||||
protocol_version: Protocol::PROTOCOL_VERSION,
|
||||
peer_id: peer.id,
|
||||
channel: channel,
|
||||
message: message_packet.encode
|
||||
)
|
||||
end
|
||||
|
||||
def self.create_raw_packet(peer:, message:, reliable: false, channel: 0)
|
||||
message_packet = nil
|
||||
|
||||
if reliable
|
||||
warn "Reliable packets are not yet implemented!"
|
||||
packet = RawPacket.new(message: message)
|
||||
message_packet = ReliablePacket.new(sequence_number: peer.next_reliable_sequence_number, message: packet.encode)
|
||||
else
|
||||
message_packet = RawPacket.new(message: message)
|
||||
end
|
||||
|
||||
Packet.new(
|
||||
protocol_version: Protocol::PROTOCOL_VERSION,
|
||||
peer_id: peer.id,
|
||||
channel: channel,
|
||||
message: message_packet.encode
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
32
lib/networking/backend/packets/control_packet.rb
Normal file
32
lib/networking/backend/packets/control_packet.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
module CyberarmEngine
|
||||
module Networking
|
||||
class ControlPacket
|
||||
attr_reader :message, :type, :control_type
|
||||
|
||||
HEADER_PACKER = "CC"
|
||||
HEADER_LENGTH = 1 + 1 # bytes
|
||||
|
||||
def self.decode(raw_message)
|
||||
header = raw_message.unpack(HEADER_PACKER)
|
||||
message = raw_message[HEADER_LENGTH..raw_message.length - 1]
|
||||
|
||||
ControlPacket.new(type: header[0], control_type: header[1], message: message)
|
||||
end
|
||||
|
||||
def initialize(control_type:, message: nil, type: Protocol::PACKET_CONTROL)
|
||||
@type = type
|
||||
@control_type = control_type
|
||||
@message = message
|
||||
end
|
||||
|
||||
def encode
|
||||
header = [
|
||||
@type,
|
||||
@control_type
|
||||
].pack(HEADER_PACKER)
|
||||
|
||||
"#{header}#{@message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,37 +0,0 @@
|
||||
module CyberarmEngine
|
||||
module Networking
|
||||
class DataPacket < Packet
|
||||
HEADER_PACKER = "CCn"
|
||||
HEADER_LENGTH = 1 + 1 + 4 # bytes
|
||||
|
||||
def self.type
|
||||
Protocol::DATA
|
||||
end
|
||||
|
||||
def self.decode(raw_message)
|
||||
header = raw_message.unpack(HEADER_PACKER)
|
||||
message = raw_message[HEADER_LENGTH..raw_message.length - 1]
|
||||
|
||||
DataPacket.new(protocol_version: header[0], type: header[1], message: message)
|
||||
end
|
||||
|
||||
def initialize(protocol_version:, type:, peer_id:, message:)
|
||||
@protocol_version = protocol_version
|
||||
@type = type
|
||||
@peer_id = peer_id
|
||||
|
||||
@message = message
|
||||
end
|
||||
|
||||
def encode
|
||||
header = [
|
||||
Protocol::PROTOCOL_VERSION,
|
||||
@type,
|
||||
@peer_id,
|
||||
].pack(HEADER_PACKER)
|
||||
|
||||
"#{header}#{message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
0
lib/networking/backend/packets/frament_packet.rb
Normal file
0
lib/networking/backend/packets/frament_packet.rb
Normal file
30
lib/networking/backend/packets/raw_packet.rb
Normal file
30
lib/networking/backend/packets/raw_packet.rb
Normal file
@@ -0,0 +1,30 @@
|
||||
module CyberarmEngine
|
||||
module Networking
|
||||
class RawPacket
|
||||
attr_reader :message, :type
|
||||
|
||||
HEADER_PACKER = "C"
|
||||
HEADER_LENGTH = 1 # bytes
|
||||
|
||||
def self.decode(raw_message)
|
||||
header = raw_message.unpack(HEADER_PACKER)
|
||||
message = raw_message[HEADER_LENGTH..raw_message.length - 1]
|
||||
|
||||
RawPacket.new(type: header[0], message: message)
|
||||
end
|
||||
|
||||
def initialize(message:, type: Protocol::PACKET_RAW)
|
||||
@type = type
|
||||
@message = message
|
||||
end
|
||||
|
||||
def encode
|
||||
header = [
|
||||
@type
|
||||
].pack(HEADER_PACKER)
|
||||
|
||||
"#{header}#{@message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
32
lib/networking/backend/packets/reliable_packet.rb
Normal file
32
lib/networking/backend/packets/reliable_packet.rb
Normal file
@@ -0,0 +1,32 @@
|
||||
module CyberarmEngine
|
||||
module Networking
|
||||
class ReliablePacket
|
||||
attr_reader :message, :type, :control_type
|
||||
|
||||
HEADER_PACKER = "Cn"
|
||||
HEADER_LENGTH = 1 + 2 # bytes
|
||||
|
||||
def self.decode(raw_message)
|
||||
header = raw_message.unpack(HEADER_PACKER)
|
||||
message = raw_message[HEADER_LENGTH..raw_message.length - 1]
|
||||
|
||||
ReliablePacket.new(type: header[0], control_type: header[1], message: message)
|
||||
end
|
||||
|
||||
def initialize(sequence_number:, message:, type: Protocol::PACKET_RELIABLE)
|
||||
@type = type
|
||||
@sequence_number = sequence_number
|
||||
@message = message
|
||||
end
|
||||
|
||||
def encode
|
||||
header = [
|
||||
@type,
|
||||
@control_type
|
||||
].pack(HEADER_PACKER)
|
||||
|
||||
"#{header}#{@message}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,14 +1,34 @@
|
||||
module CyberarmEngine
|
||||
module Networking
|
||||
class Peer
|
||||
attr_reader :id, :hostname, :port, :data
|
||||
attr_reader :id, :hostname, :port, :data, :read_queue, :write_queue
|
||||
attr_accessor :total_packets_sent, :total_packets_received,
|
||||
:total_data_sent, :total_data_received,
|
||||
:last_read_time, :last_write_time
|
||||
|
||||
def initialize(id:, hostname:, port:)
|
||||
@id = id
|
||||
@hostname = hostname
|
||||
@port = port
|
||||
|
||||
@data = {}
|
||||
@read_queue = []
|
||||
@write_queue = []
|
||||
|
||||
@last_read_time = Networking.milliseconds
|
||||
@last_write_time = Networking.milliseconds
|
||||
|
||||
@total_packets_sent = 0
|
||||
@total_packets_received = 0
|
||||
@total_data_sent = 0
|
||||
@total_data_received = 0
|
||||
end
|
||||
|
||||
def id=(n)
|
||||
raise "Peer id must be an integer" unless n.is_a?(Integer)
|
||||
|
||||
@id = n
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,30 +1,29 @@
|
||||
module CyberarmEngine
|
||||
module Networking
|
||||
module Protocol
|
||||
MAX_PACKET_SIZE = 1024
|
||||
PROTOCOL_VERSION = 0 # int
|
||||
MAX_PACKET_SIZE = 1024 # bytes
|
||||
PROTOCOL_VERSION = 0 # u32
|
||||
HEARTBEAT_INTERVAL = 5_000 # ms
|
||||
TIMEOUT_PERIOD = 30_000 # ms
|
||||
|
||||
packet_types = %w{
|
||||
# protocol packets
|
||||
reliable
|
||||
multipart
|
||||
control
|
||||
data
|
||||
PACKET_BASE_HEADER = "NnC" # protocol version (u32), sender peer id (u16), channel (u8)
|
||||
PACKET_BASE_HEADER_LENGTH = 4 + 2 + 1 # bytes
|
||||
|
||||
# control packet types
|
||||
disconnect
|
||||
acknowledge
|
||||
heartbeat
|
||||
ping
|
||||
}
|
||||
# protocol packets
|
||||
PACKET_RELIABLE = 0
|
||||
PACKET_FRAGMENT = 1
|
||||
PACKET_CONTROL = 2
|
||||
PACKET_RAW = 3
|
||||
|
||||
# emulate c-like enum
|
||||
packet_types.each_with_index do |type, i|
|
||||
next if type.start_with?("#")
|
||||
self.const_set(:"#{type.upcase}", i)
|
||||
end
|
||||
# control packet types
|
||||
CONTROL_CONNECT = 30
|
||||
CONTROL_SET_PEER_ID = 31
|
||||
CONTROL_DISCONNECT = 32
|
||||
CONTROL_ACKNOWLEDGE = 33
|
||||
CONTROL_HEARTBEAT = 34
|
||||
CONTROL_PING = 35
|
||||
CONTROL_PONG = 36
|
||||
CONTROL_SET_PEER_MTU = 37 # In future
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1,59 +1,180 @@
|
||||
class Server
|
||||
attr_reader :hostname, :port, :max_clients
|
||||
def initialize(hostname: "0.0.0.0", port: 56789, max_clients: 32, channels: 1)
|
||||
@hostname = hostname
|
||||
@port = port
|
||||
@max_clients = max_clients
|
||||
module CyberarmEngine
|
||||
module Networking
|
||||
class Server
|
||||
attr_reader :hostname, :port, :max_clients
|
||||
attr_accessor :total_packets_sent, :total_packets_received,
|
||||
:total_data_sent, :total_data_received,
|
||||
:last_read_time, :last_write_time
|
||||
|
||||
@socket = UDPSocket.new
|
||||
@socket.bind(@hostname, @port)
|
||||
def initialize(
|
||||
hostname: CyberarmEngine::Networking::DEFAULT_SERVER_HOSTNAME,
|
||||
port: CyberarmEngine::Networking::DEFAULT_SERVER_PORT,
|
||||
max_clients: CyberarmEngine::Networking::DEFAULT_PEER_LIMIT,
|
||||
channels: 3
|
||||
)
|
||||
@hostname = hostname
|
||||
@port = port
|
||||
@max_clients = max_clients
|
||||
|
||||
@channels = Array(0..channels).map { |id| Channel.new(id: id, server: self) }
|
||||
@peers = []
|
||||
@channels = Array(0..channels).map { |id| Channel.new(id: id, mode: :default) }
|
||||
@peers = []
|
||||
|
||||
@last_read_time = Networking.milliseconds
|
||||
@last_write_time = Networking.milliseconds
|
||||
|
||||
@total_packets_sent = 0
|
||||
@total_packets_received = 0
|
||||
@total_data_sent = 0
|
||||
@total_data_received = 0
|
||||
end
|
||||
|
||||
# Helpers #
|
||||
def connected_clients
|
||||
@peers.size
|
||||
end
|
||||
|
||||
def clients
|
||||
@peers
|
||||
end
|
||||
|
||||
# Callbacks #
|
||||
|
||||
# Called when client connects
|
||||
def client_connected(peer:)
|
||||
end
|
||||
|
||||
# Called when client times out or explicitly disconnects
|
||||
def client_disconnected(peer:, reason:)
|
||||
end
|
||||
|
||||
### REMOVE? ###
|
||||
# Called when client was not sending heartbeats or regular packets for a
|
||||
# period of time, but was not logically disconnected and removed, and started
|
||||
# send packets again.
|
||||
#
|
||||
# TLDR: Client was temporarily unreachable but did not timeout.
|
||||
def client_reconnected(peer:)
|
||||
end
|
||||
|
||||
# Called when a (logical) packet is received from client
|
||||
def packet_received(peer:, message:, channel:)
|
||||
end
|
||||
|
||||
# Functions #
|
||||
# Bind server
|
||||
def bind
|
||||
# TODO: Handle socket errors
|
||||
@socket = UDPSocket.new
|
||||
@socket.bind(@hostname, @port)
|
||||
end
|
||||
|
||||
# Send packet to specified peer
|
||||
def send_packet(peer:, message:, reliable: false, channel: 0)
|
||||
if (peer = @peers.get(peer))
|
||||
packet = PacketHandler.create_raw_packet(message, reliable, channel)
|
||||
peer.write_queue << packet
|
||||
else
|
||||
# TODO: Handle no such peer error
|
||||
end
|
||||
end
|
||||
|
||||
# Send packet to all connected peer
|
||||
def broadcast_packet(message:, reliable: false, channel: 0)
|
||||
@peers.each { |peer| send_packet(peer.id, message, reliable, channel) }
|
||||
end
|
||||
|
||||
# Disconnect peer
|
||||
def disconnect_client(peer:, reason: "")
|
||||
if (peer = @peers.get(peer))
|
||||
packet = PacketHandler.create_disconnect_packet(peer.id, reason)
|
||||
peer.write_now!(packet)
|
||||
@peers.delete(peer)
|
||||
end
|
||||
end
|
||||
|
||||
def update
|
||||
while(read)
|
||||
end
|
||||
|
||||
# handle write queue
|
||||
# TODO: handle reliable packets differently
|
||||
@peers.each do |peer|
|
||||
if Networking.milliseconds - peer.last_read_time > Protocol::TIMEOUT_PERIOD
|
||||
message = "ERROR: connection timed out"
|
||||
|
||||
write(
|
||||
peer: peer,
|
||||
packet: PacketHandler.create_control_packet(
|
||||
peer: peer,
|
||||
control_type: Protocol::CONTROL_DISCONNECT,
|
||||
message: message
|
||||
)
|
||||
)
|
||||
client_disconnected(peer: peer, reason: message)
|
||||
@peers.delete(peer)
|
||||
next
|
||||
end
|
||||
|
||||
while(packet = peer.write_queue.shift)
|
||||
write(peer: peer, packet: packet)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# !--- this following functions are meant for internal use only ---! #
|
||||
|
||||
def available_peer_id
|
||||
peer_ids = @peers.map { ||peer| peer.id }
|
||||
ids = (1..@max_clients).to_a - peer_ids
|
||||
|
||||
ids.size.positive? ? ids.first : nil
|
||||
end
|
||||
|
||||
def read
|
||||
data, addr = @socket.recvfrom_nonblock(Protocol::MAX_PACKET_SIZE)
|
||||
peer = nil
|
||||
|
||||
if (peer = @peers.find { |pr| pr.hostname == addr[2] && pr.port == addr[1] })
|
||||
pkt = PacketHandler.handle(host: self, raw: data, peer: peer)
|
||||
packet_received(peer: peer, message: pkt.message, channel: 0) if pkt.is_a?(RawPacket)
|
||||
else
|
||||
# TODO: Reject packet unless it's a connection request
|
||||
peer = Peer.new(id: 0, hostname: addr[2], port: addr[1])
|
||||
pkt = PacketHandler.handle(host: self, raw: data, peer: peer)
|
||||
|
||||
if pkt && !pkt.is_a?(ControlPacket) && pkt.control_type != Protocol::CONTROL_CONNECT
|
||||
write(
|
||||
peer: peer,
|
||||
packet: PacketHandler.create_control_packet(
|
||||
peer: peer,
|
||||
control_type: Protocol::CONTROL_DISCONNECT,
|
||||
message: "ERROR: client not connected"
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
@total_packets_received += 1
|
||||
@total_data_received += data.length
|
||||
@last_read_time = Networking.milliseconds
|
||||
|
||||
true
|
||||
rescue IO::WaitReadable
|
||||
false
|
||||
end
|
||||
|
||||
def write(peer:, packet:)
|
||||
raw = packet.encode
|
||||
@socket.send(raw, 0, peer.hostname, peer.port)
|
||||
|
||||
@total_packets_sent += 1
|
||||
@total_data_sent += raw.length
|
||||
@last_write_time = Networking.milliseconds
|
||||
|
||||
peer.total_packets_sent += 1
|
||||
peer.total_data_sent += raw.length
|
||||
peer.last_write_time = Networking.milliseconds
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Helpers #
|
||||
def connected_clients
|
||||
@peers.size
|
||||
end
|
||||
|
||||
def clients
|
||||
@peers
|
||||
end
|
||||
|
||||
# Callbacks #
|
||||
|
||||
# Called when client connects
|
||||
def client_connected(peer)
|
||||
end
|
||||
|
||||
# Called when client times out or explicitly disconnects
|
||||
def client_disconnected(peer, reason)
|
||||
end
|
||||
|
||||
### REMOVE? ###
|
||||
# Called when client was not sending heartbeats or regular packets for a
|
||||
# period of time, but was not logically disconnected and removed, and started
|
||||
# send packets again.
|
||||
#
|
||||
# TLDR: Client was temporarily unreachable but did not timeout.
|
||||
def client_reconnected(peer)
|
||||
end
|
||||
|
||||
# Called when a (logical) packet is received from client
|
||||
def packet_received(peer, message, channel = 0)
|
||||
end
|
||||
|
||||
# Functions #
|
||||
# Send packet to specified peer
|
||||
def send_packet(peer, message, reliable, channel = 0)
|
||||
end
|
||||
|
||||
# Send packet to all connected peer
|
||||
def broadcast_packet(message, reliable, channel = 0)
|
||||
end
|
||||
|
||||
# Disconnect peer
|
||||
def disconnect_client(peer, reason = "")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
66
new_server_test.rb
Normal file
66
new_server_test.rb
Normal file
@@ -0,0 +1,66 @@
|
||||
def require_all(directory)
|
||||
files = Dir["#{directory}/**/*.rb"].sort!
|
||||
|
||||
begin
|
||||
failed = []
|
||||
first_name_error = nil
|
||||
|
||||
files.each do |file|
|
||||
begin
|
||||
require_relative file
|
||||
rescue NameError => name_error
|
||||
failed << file
|
||||
first_name_error ||= name_error
|
||||
end
|
||||
end
|
||||
|
||||
if failed.size == files.size
|
||||
raise first_name_error
|
||||
else
|
||||
files = failed
|
||||
end
|
||||
end until( failed.empty? )
|
||||
end
|
||||
|
||||
require "socket"
|
||||
require_relative "lib/networking"
|
||||
require_all "lib/networking/backend"
|
||||
|
||||
server = CyberarmEngine::Networking::Server.new
|
||||
def server.client_connected(peer:)
|
||||
puts "Client connected as peer: #{peer.id}"
|
||||
end
|
||||
|
||||
def server.packet_received(peer:, message:, channel:)
|
||||
pp "Server received: #{message} [on channel: #{channel} from peer: #{peer&.id}]"
|
||||
end
|
||||
|
||||
Thread.new do
|
||||
server.bind
|
||||
|
||||
loop do
|
||||
server.update
|
||||
sleep (1000.0 / 60.0) / 10.0
|
||||
end
|
||||
end
|
||||
|
||||
connection = CyberarmEngine::Networking::Connection.new(hostname: "localhost", port: CyberarmEngine::Networking::DEFAULT_SERVER_PORT, channels: 3)
|
||||
def connection.connected
|
||||
puts "Connection: Connected!"
|
||||
end
|
||||
|
||||
def connection.disconnected(reason:)
|
||||
puts "Connection: disconnected: #{reason}"
|
||||
end
|
||||
|
||||
def connection.packet_received(message:, channel:)
|
||||
pp "Connection received: #{message} [on channel: #{channel} from peer: SERVER]"
|
||||
end
|
||||
connection.connect(timeout: 1_000)
|
||||
|
||||
loop do
|
||||
connection.update
|
||||
sleep (1000.0 / 60.0) / 10.0
|
||||
end
|
||||
|
||||
sleep
|
||||
Reference in New Issue
Block a user