mirror of
https://github.com/cyberarm/i-mic-fps.git
synced 2025-12-15 15:42:35 +00:00
Initial work on locales, more work on netcode
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
module CyberarmEngine
|
||||
module Networking
|
||||
class Connection
|
||||
attr_reader :hostname, :port, :peer
|
||||
def initialize(hostname:, port:, channels: 3)
|
||||
@hostname = hostname
|
||||
@port = port
|
||||
@@ -8,14 +9,6 @@ module CyberarmEngine
|
||||
@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 #
|
||||
@@ -33,12 +26,13 @@ module CyberarmEngine
|
||||
|
||||
# Functions #
|
||||
def send_packet(message:, reliable: false, channel: 0)
|
||||
@peer.write_queue << PacketHandler.create_raw_packet(peer: @peer, message: message, reliable: reliable, channel: channel)
|
||||
end
|
||||
|
||||
def connect(timeout: Protocol::TIMEOUT_PERIOD)
|
||||
@socket = UDPSocket.new
|
||||
|
||||
write(PacketHandler.create_control_packet(peer: @peer, control_type: Protocol::CONTROL_CONNECT))
|
||||
write(packet: PacketHandler.create_control_packet(peer: @peer, control_type: Protocol::CONTROL_CONNECT))
|
||||
end
|
||||
|
||||
def disconnect(timeout: Protocol::TIMEOUT_PERIOD)
|
||||
@@ -49,38 +43,36 @@ module CyberarmEngine
|
||||
end
|
||||
|
||||
@peer.write_queue.reverse.each do |packet|
|
||||
write(packet)
|
||||
write(packet: packet)
|
||||
@peer.write_queue.delete(packet)
|
||||
end
|
||||
|
||||
if Networking.milliseconds - @last_write_time > Protocol::HEARTBEAT_INTERVAL
|
||||
if Networking.milliseconds - @peer.last_write_time > Protocol::HEARTBEAT_INTERVAL
|
||||
@peer.write_queue << PacketHandler.create_control_packet(peer: @peer, control_type: Protocol::CONTROL_HEARTBEAT)
|
||||
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
|
||||
@peer.total_packets_received += 1
|
||||
@peer.total_data_received += data.length
|
||||
@peer.last_read_time = Networking.milliseconds
|
||||
|
||||
return true
|
||||
rescue IO::WaitReadable
|
||||
return false
|
||||
end
|
||||
|
||||
def write(packet)
|
||||
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
|
||||
@peer.total_packets_sent += 1
|
||||
@peer.total_data_sent += raw.length
|
||||
@peer.last_write_time = Networking.milliseconds
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -11,14 +11,17 @@ module CyberarmEngine
|
||||
type = packet.message.unpack1("C")
|
||||
|
||||
puts "#{host.class} received #{type_to_name(type: type)}"
|
||||
pp raw
|
||||
|
||||
case type
|
||||
when Protocol::PACKET_CONTROL
|
||||
handle_control_packet(host, packet, peer)
|
||||
when Protocol::PACKET_RAW
|
||||
handle_raw_packet(packet)
|
||||
when Protocol::PACKET_RELIABLE
|
||||
handle_reliable_packet(host, packet, peer)
|
||||
else
|
||||
raise NotImplementedError, "A Packet handler for #{type} is not implmented!"
|
||||
raise NotImplementedError, "A Packet handler for #{type_to_name(type: type)}[#{type}] is not implemented!"
|
||||
end
|
||||
end
|
||||
|
||||
@@ -56,7 +59,16 @@ module CyberarmEngine
|
||||
|
||||
when Protocol::CONTROL_HEARTBEAT
|
||||
when Protocol::CONTROL_PING
|
||||
peer.write_queue << PacketHandler.create_control_packet(
|
||||
peer: peer, control_type: Protocol::CONTROL_PONG,
|
||||
reliable: true,
|
||||
message: [Networking.milliseconds].pack("Q") # Uint64, native endian
|
||||
)
|
||||
when Protocol::CONTROL_PONG
|
||||
sent_time = pkt.message.unpack1("Q")
|
||||
difference = Networking.milliseconds - sent_time
|
||||
|
||||
peer.ping = difference
|
||||
end
|
||||
|
||||
nil
|
||||
@@ -66,12 +78,30 @@ module CyberarmEngine
|
||||
RawPacket.decode(packet.message)
|
||||
end
|
||||
|
||||
def self.handle_reliable_packet(host, packet, peer)
|
||||
# TODO: Preserve delivery order of reliable packets
|
||||
|
||||
pkt = ReliablePacket.decode(packet.message)
|
||||
peer.write_queue << create_control_packet(
|
||||
peer: peer,
|
||||
control_type: Protocol::CONTROL_ACKNOWLEDGE,
|
||||
message: [pkt.sequence_number].pack("n")
|
||||
)
|
||||
|
||||
handle(host: host, raw: pkt.message, peer: peer)
|
||||
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)
|
||||
packet = Packet.new(
|
||||
protocol_version: Protocol::PROTOCOL_VERSION,
|
||||
peer_id: peer.id,
|
||||
channel: channel,
|
||||
message: ControlPacket.new(control_type: control_type, message: message).encode
|
||||
)
|
||||
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)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
module CyberarmEngine
|
||||
module Networking
|
||||
class ReliablePacket
|
||||
attr_reader :message, :type, :control_type
|
||||
attr_reader :message, :type, :sequence_number
|
||||
|
||||
HEADER_PACKER = "Cn"
|
||||
HEADER_LENGTH = 1 + 2 # bytes
|
||||
@@ -10,7 +10,7 @@ module CyberarmEngine
|
||||
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)
|
||||
ReliablePacket.new(type: header[0], sequence_number: header[1], message: message)
|
||||
end
|
||||
|
||||
def initialize(sequence_number:, message:, type: Protocol::PACKET_RELIABLE)
|
||||
@@ -22,7 +22,7 @@ module CyberarmEngine
|
||||
def encode
|
||||
header = [
|
||||
@type,
|
||||
@control_type
|
||||
@sequence_number
|
||||
].pack(HEADER_PACKER)
|
||||
|
||||
"#{header}#{@message}"
|
||||
|
||||
@@ -4,7 +4,8 @@ module CyberarmEngine
|
||||
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
|
||||
:last_read_time, :last_write_time,
|
||||
:ping
|
||||
|
||||
def initialize(id:, hostname:, port:)
|
||||
@id = id
|
||||
@@ -22,6 +23,10 @@ module CyberarmEngine
|
||||
@total_packets_received = 0
|
||||
@total_data_sent = 0
|
||||
@total_data_received = 0
|
||||
|
||||
@ping = 0
|
||||
|
||||
@reliable_sequence_number = 65_500
|
||||
end
|
||||
|
||||
def id=(n)
|
||||
@@ -29,6 +34,10 @@ module CyberarmEngine
|
||||
|
||||
@id = n
|
||||
end
|
||||
|
||||
def next_reliable_sequence_number
|
||||
@reliable_sequence_number = (@reliable_sequence_number + 1) % 65_535
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -70,7 +70,7 @@ module CyberarmEngine
|
||||
|
||||
# Send packet to specified peer
|
||||
def send_packet(peer:, message:, reliable: false, channel: 0)
|
||||
if (peer = @peers.get(peer))
|
||||
if (peer = @peers[peer])
|
||||
packet = PacketHandler.create_raw_packet(message, reliable, channel)
|
||||
peer.write_queue << packet
|
||||
else
|
||||
@@ -80,12 +80,12 @@ module CyberarmEngine
|
||||
|
||||
# Send packet to all connected peer
|
||||
def broadcast_packet(message:, reliable: false, channel: 0)
|
||||
@peers.each { |peer| send_packet(peer.id, message, reliable, channel) }
|
||||
@peers.each { |peer| send_packet(peer: peer.id, message: message, reliable: reliable, channel: channel) }
|
||||
end
|
||||
|
||||
# Disconnect peer
|
||||
def disconnect_client(peer:, reason: "")
|
||||
if (peer = @peers.get(peer))
|
||||
if (peer = @peers[peer])
|
||||
packet = PacketHandler.create_disconnect_packet(peer.id, reason)
|
||||
peer.write_now!(packet)
|
||||
@peers.delete(peer)
|
||||
@@ -115,6 +115,16 @@ module CyberarmEngine
|
||||
next
|
||||
end
|
||||
|
||||
if Networking.milliseconds - peer.last_write_time > Protocol::HEARTBEAT_INTERVAL
|
||||
write(
|
||||
peer: peer,
|
||||
packet: PacketHandler.create_control_packet(
|
||||
peer: peer,
|
||||
control_type: Protocol::CONTROL_PING
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
while(packet = peer.write_queue.shift)
|
||||
write(peer: peer, packet: packet)
|
||||
end
|
||||
|
||||
@@ -12,7 +12,7 @@ class IMICFPS
|
||||
push_state(IMICFPS::MapEditorTool::MainMenu)
|
||||
end
|
||||
|
||||
link "Back" do
|
||||
link I18n.t("menus.back") do
|
||||
pop_state
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,11 +9,11 @@ class IMICFPS
|
||||
pop_state
|
||||
end
|
||||
|
||||
link "Settings" do
|
||||
link I18n.t("menus.settings") do
|
||||
push_state(SettingsMenu)
|
||||
end
|
||||
|
||||
link "Leave" do
|
||||
link I18n.t("menus.leave") do
|
||||
push_state(MainMenu)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,7 +12,7 @@ class IMICFPS
|
||||
end
|
||||
end
|
||||
|
||||
link "Back" do
|
||||
link I18n.t("menus.back") do
|
||||
pop_state
|
||||
end
|
||||
end
|
||||
|
||||
@@ -3,23 +3,23 @@ class IMICFPS
|
||||
def setup
|
||||
title IMICFPS::NAME
|
||||
|
||||
link "Single Player" do
|
||||
link I18n.t("menus.singleplayer") do
|
||||
push_state(LevelSelectMenu)
|
||||
end
|
||||
|
||||
link "Multiplayer" do
|
||||
link I18n.t("menus.multiplayer") do
|
||||
push_state(MultiplayerMenu)
|
||||
end
|
||||
|
||||
link "Settings" do
|
||||
link I18n.t("menus.settings") do
|
||||
push_state(SettingsMenu)
|
||||
end
|
||||
|
||||
link "Extras" do
|
||||
link I18n.t("menus.extras") do
|
||||
push_state(ExtrasMenu)
|
||||
end
|
||||
|
||||
link "Quit" do
|
||||
link I18n.t("menus.quit") do
|
||||
window.close
|
||||
end
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ class IMICFPS
|
||||
link "Profile" do
|
||||
push_state(MultiplayerProfileMenu)
|
||||
end
|
||||
link "Back" do
|
||||
link I18n.t("menus.back") do
|
||||
pop_state
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,7 +7,7 @@ class IMICFPS
|
||||
stack(width: 0.25, height: 1.0) do
|
||||
button "Edit Profile", width: 1.0
|
||||
button "Log Out", width: 1.0
|
||||
button "Back", width: 1.0, margin_top: 64 do
|
||||
button I18n.t("menus.back"), width: 1.0, margin_top: 64 do
|
||||
pop_state
|
||||
end
|
||||
end
|
||||
|
||||
@@ -34,7 +34,7 @@ class IMICFPS
|
||||
button "Host Game", width: 1.0
|
||||
button "Direct Connect", width: 1.0
|
||||
|
||||
button "Back", width: 1.0, margin_top: 64 do
|
||||
button I18n.t("menus.back"), width: 1.0, margin_top: 64 do
|
||||
pop_state
|
||||
end
|
||||
end
|
||||
|
||||
@@ -29,7 +29,7 @@ class IMICFPS
|
||||
end
|
||||
end
|
||||
|
||||
button "Back", width: 1.0, margin_top: 64 do
|
||||
button I18n.t("menus.back"), width: 1.0, margin_top: 64 do
|
||||
pop_state
|
||||
end
|
||||
end
|
||||
|
||||
@@ -12,6 +12,11 @@ class IMICFPS
|
||||
super(width: window_width, height: window_height, fullscreen: fullscreen, resizable: true, update_interval: 1000.0/fps_target)
|
||||
end
|
||||
$window = self
|
||||
I18n.load_path << Dir["#{GAME_ROOT_PATH}/locales/*.yml"]
|
||||
I18n.default_locale = :en
|
||||
language = Gosu.language.split("_").first.to_sym
|
||||
I18n.locale = language if I18n.available_locales.include?(language)
|
||||
|
||||
@needs_cursor = false
|
||||
@cursor = Gosu::Image.new(IMICFPS::GAME_ROOT_PATH + "/static/cursors/pointer.png")
|
||||
@number_of_vertices = 0
|
||||
|
||||
Reference in New Issue
Block a user