mirror of
https://github.com/cyberarm/i-mic-fps.git
synced 2025-12-13 06:42:35 +00:00
Initial work on locales, more work on netcode
This commit is contained in:
1
Gemfile
1
Gemfile
@@ -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"
|
||||
|
||||
13
Gemfile.lock
13
Gemfile.lock
@@ -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
|
||||
|
||||
@@ -10,6 +10,7 @@ require "securerandom"
|
||||
require "opengl"
|
||||
require "glu"
|
||||
require "nokogiri"
|
||||
require "i18n"
|
||||
|
||||
begin
|
||||
require_relative "../cyberarm_engine/lib/cyberarm_engine"
|
||||
|
||||
@@ -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
|
||||
|
||||
13
locales/de.yml
Normal file
13
locales/de.yml
Normal 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
13
locales/en.yml
Normal 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
|
||||
@@ -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)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user