Initial work on locales, more work on netcode

This commit is contained in:
2020-12-01 13:02:22 -06:00
parent b9c30ade80
commit c7590366a6
20 changed files with 131 additions and 47 deletions

View File

@@ -3,6 +3,7 @@ gem "rake"
gem "opengl-bindings", require: "opengl"
gem "cyberarm_engine", git: "https://github.com/cyberarm/cyberarm_engine"
gem "nokogiri", ">= 1.11.0.rc1"
gem "i18n"
group(:packaging) do
gem "releasy", github: "gosu/releasy"

View File

@@ -1,9 +1,10 @@
GIT
remote: https://github.com/cyberarm/cyberarm_engine
revision: da4188764ce8ac6060068e09f607d1ec904fe165
revision: d02c001989ce967e2b3184d1a0f01cd5b20ac241
specs:
cyberarm_engine (0.14.0)
clipboard (~> 1.3.4)
excon (~> 0.76.0)
gosu (~> 0.15.0)
gosu_more_drawables (~> 0.3)
@@ -20,16 +21,19 @@ GIT
GEM
remote: https://rubygems.org/
specs:
clipboard (1.3.4)
clipboard (1.3.5)
concurrent-ruby (1.1.7)
cri (2.1.0)
excon (0.76.0)
gosu (0.15.2)
gosu (0.15.2-x64-mingw32)
gosu_more_drawables (0.3.1)
i18n (1.8.5)
concurrent-ruby (~> 1.0)
mini_portile2 (2.5.0)
nokogiri (1.11.0.rc2)
nokogiri (1.11.0.rc3)
mini_portile2 (~> 2.5.0)
nokogiri (1.11.0.rc2-x64-mingw32)
nokogiri (1.11.0.rc3-x64-mingw32)
ocra (1.3.11)
opengl-bindings (1.6.10)
rake (13.0.1)
@@ -42,6 +46,7 @@ PLATFORMS
DEPENDENCIES
cyberarm_engine!
excon
i18n
nokogiri (>= 1.11.0.rc1)
ocra
opengl-bindings

View File

@@ -10,6 +10,7 @@ require "securerandom"
require "opengl"
require "glu"
require "nokogiri"
require "i18n"
begin
require_relative "../cyberarm_engine/lib/cyberarm_engine"

View File

@@ -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

View File

@@ -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)

View File

@@ -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}"

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -12,7 +12,7 @@ class IMICFPS
end
end
link "Back" do
link I18n.t("menus.back") do
pop_state
end
end

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

13
locales/de.yml Normal file
View File

@@ -0,0 +1,13 @@
de:
game:
name: I-MIC FPS
menus:
singleplayer: Einzelspieler
multiplayer: Multiplayer
settings: Optionen
extras: Mehr
quit: Fortgehen
back: Zurückbringen
leave: Verlassen
paused: Pausieren
resume: Fortsetzen

13
locales/en.yml Normal file
View File

@@ -0,0 +1,13 @@
en:
game:
name: I-MIC FPS
menus:
singleplayer: Single Player
multiplayer: Multiplayer
settings: Settings
extras: Extras
quit: Quit
back: Back
leave: Leave
paused: Paused
resume: Resume

View File

@@ -19,13 +19,15 @@ def require_all(directory)
else
files = failed
end
end until( failed.empty? )
end until(failed.empty?)
end
require "socket"
require_relative "lib/networking"
require_all "lib/networking/backend"
Thread.abort_on_exception = true
server = CyberarmEngine::Networking::Server.new
def server.client_connected(peer:)
puts "Client connected as peer: #{peer.id}"
@@ -33,6 +35,7 @@ end
def server.packet_received(peer:, message:, channel:)
pp "Server received: #{message} [on channel: #{channel} from peer: #{peer&.id}]"
broadcast_packet(message: "Broadcasting...")
end
Thread.new do
@@ -47,6 +50,7 @@ end
connection = CyberarmEngine::Networking::Connection.new(hostname: "localhost", port: CyberarmEngine::Networking::DEFAULT_SERVER_PORT, channels: 3)
def connection.connected
puts "Connection: Connected!"
send_packet(message: "I be connected!")
end
def connection.disconnected(reason:)
@@ -55,6 +59,7 @@ end
def connection.packet_received(message:, channel:)
pp "Connection received: #{message} [on channel: #{channel} from peer: SERVER]"
send_packet(message: "ECHO: #{message}")
end
connection.connect(timeout: 1_000)