mirror of
https://github.com/cyberarm/w3d_hub_linux_launcher.git
synced 2025-12-16 01:02:34 +00:00
Added support for pinging servers, server list now reorders containers instead of recreating all of them for every refresh, server list updater should restart on crash
This commit is contained in:
1
Gemfile
1
Gemfile
@@ -11,6 +11,7 @@ gem "async-http"
|
||||
gem "async-websocket"
|
||||
gem "thread-local"
|
||||
gem "ircparser"
|
||||
gem "net-ping"
|
||||
|
||||
# group :windows_packaging do
|
||||
# gem "rake"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
class W3DHub
|
||||
class Api
|
||||
class ServerListServer
|
||||
attr_reader :id, :game, :address, :port, :region, :channel, :status
|
||||
attr_reader :id, :game, :address, :port, :region, :channel, :ping, :status
|
||||
|
||||
def initialize(hash)
|
||||
@data = hash
|
||||
@@ -12,8 +12,12 @@ class W3DHub
|
||||
@port = @data[:port]
|
||||
@region = @data[:region]
|
||||
@channel = @data[:channel] || "release"
|
||||
@ping = -1
|
||||
|
||||
@status = @data[:status] ? Status.new(@data[:status]) : nil
|
||||
|
||||
@last_pinged = -1
|
||||
@ping_interval = 30
|
||||
end
|
||||
|
||||
def update(hash)
|
||||
@@ -29,6 +33,17 @@ class W3DHub
|
||||
@status.instance_variable_set(:@teams, hash[:teams]&.map { |t| Team.new(t) }) if hash[:teams]
|
||||
@status.instance_variable_set(:@players, hash[:players]&.select { |t| t[:nick] != "Nod" && t[:nick] != "GDI" }&.map { |t| Player.new(t) }) if hash[:players]
|
||||
|
||||
if Gosu.milliseconds - @last_pinged >= @ping_interval
|
||||
@last_pinged = Gosu.milliseconds
|
||||
|
||||
Thread.new do
|
||||
ping = Net::Ping::External.new(@address)
|
||||
@ping = (ping.duration * 1000.0).round if ping.ping?
|
||||
|
||||
States::Interface.instance&.update_server_ping(self)
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
@@ -77,53 +77,65 @@ class W3DHub
|
||||
|
||||
def run
|
||||
Thread.new do
|
||||
Async do |task|
|
||||
internet = Async::HTTP::Internet.instance
|
||||
begin
|
||||
connect
|
||||
rescue => e
|
||||
puts e
|
||||
puts e.backtrace
|
||||
|
||||
logger.debug(LOG_TAG) { "Requesting connection token..." }
|
||||
response = internet.post("https://gsh.w3dhub.com/listings/push/v2/negotiate?negotiateVersion=1", Api::DEFAULT_HEADERS, [""])
|
||||
data = JSON.parse(response.read, symbolize_names: true)
|
||||
sleep 10
|
||||
retry
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
id = data[:connectionToken]
|
||||
endpoint = Async::HTTP::Endpoint.parse("https://gsh.w3dhub.com/listings/push/v2?id=#{id}", alpn_protocols: Async::HTTP::Protocol::HTTP11.names)
|
||||
def connect
|
||||
Async do |task|
|
||||
internet = Async::HTTP::Internet.instance
|
||||
|
||||
logger.debug(LOG_TAG) { "Connecting to websocket..." }
|
||||
Async::WebSocket::Client.connect(endpoint, headers: Api::DEFAULT_HEADERS, handler: PatchedConnection) do |connection|
|
||||
logger.debug(LOG_TAG) { "Requesting json protocol, v1..." }
|
||||
connection.write({ protocol: "json", version: 1 })
|
||||
connection.flush
|
||||
logger.debug(LOG_TAG) { "Received: #{connection.read}" }
|
||||
logger.debug(LOG_TAG) { "Sending \"PING\"(?)" }
|
||||
connection.write({ "type": 6 })
|
||||
logger.debug(LOG_TAG) { "Requesting connection token..." }
|
||||
response = internet.post("https://gsh.w3dhub.com/listings/push/v2/negotiate?negotiateVersion=1", Api::DEFAULT_HEADERS, [""])
|
||||
data = JSON.parse(response.read, symbolize_names: true)
|
||||
|
||||
logger.debug(LOG_TAG) { "Subscribing to server changes..." }
|
||||
Store.server_list.each_with_index do |server, i|
|
||||
i += 1
|
||||
mode = 1 # 2 full details, 1 basic details
|
||||
out = { "type": 1, "invocationId": "#{i}", "target": "SubscribeToServerStatusUpdates", "arguments": [server.id, mode] }
|
||||
connection.write(out)
|
||||
end
|
||||
id = data[:connectionToken]
|
||||
endpoint = Async::HTTP::Endpoint.parse("https://gsh.w3dhub.com/listings/push/v2?id=#{id}", alpn_protocols: Async::HTTP::Protocol::HTTP11.names)
|
||||
|
||||
logger.debug(LOG_TAG) { "Waiting for data..." }
|
||||
while (message = connection.read)
|
||||
connection.write({ type: 6 }) if message.first[:type] == 6
|
||||
logger.debug(LOG_TAG) { "Connecting to websocket..." }
|
||||
Async::WebSocket::Client.connect(endpoint, headers: Api::DEFAULT_HEADERS, handler: PatchedConnection) do |connection|
|
||||
logger.debug(LOG_TAG) { "Requesting json protocol, v1..." }
|
||||
connection.write({ protocol: "json", version: 1 })
|
||||
connection.flush
|
||||
logger.debug(LOG_TAG) { "Received: #{connection.read}" }
|
||||
logger.debug(LOG_TAG) { "Sending \"PING\"(?)" }
|
||||
connection.write({ "type": 6 })
|
||||
|
||||
if message&.first&.fetch(:type) == 1
|
||||
message.each do |rpc|
|
||||
next unless rpc[:target] == "ServerStatusChanged"
|
||||
logger.debug(LOG_TAG) { "Subscribing to server changes..." }
|
||||
Store.server_list.each_with_index do |server, i|
|
||||
i += 1
|
||||
mode = 1 # 2 full details, 1 basic details
|
||||
out = { "type": 1, "invocationId": "#{i}", "target": "SubscribeToServerStatusUpdates", "arguments": [server.id, mode] }
|
||||
connection.write(out)
|
||||
end
|
||||
|
||||
id, data = rpc[:arguments]
|
||||
server = Store.server_list.find { |s| s.id == id }
|
||||
server_updated = server&.update(data)
|
||||
States::Interface.instance&.update_server_browser(server) if server_updated
|
||||
end
|
||||
logger.debug(LOG_TAG) { "Waiting for data..." }
|
||||
while (message = connection.read)
|
||||
connection.write({ type: 6 }) if message.first[:type] == 6
|
||||
|
||||
if message&.first&.fetch(:type) == 1
|
||||
message.each do |rpc|
|
||||
next unless rpc[:target] == "ServerStatusChanged"
|
||||
|
||||
id, data = rpc[:arguments]
|
||||
server = Store.server_list.find { |s| s.id == id }
|
||||
server_updated = server&.update(data)
|
||||
States::Interface.instance&.update_server_browser(server) if server_updated
|
||||
end
|
||||
end
|
||||
end
|
||||
ensure
|
||||
logger.debug(LOG_TAG) { "Cleaning up..." }
|
||||
@@instance = nil
|
||||
end
|
||||
ensure
|
||||
logger.debug(LOG_TAG) { "Cleaning up..." }
|
||||
@@instance = nil
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -15,6 +15,9 @@ class W3DHub
|
||||
|
||||
Store.applications.games.each { |game| @filters[game.id.to_sym] = true if @filters[game.id.to_sym].nil? }
|
||||
|
||||
@ping_icons = {}
|
||||
generate_ping_icons
|
||||
|
||||
body.clear do
|
||||
stack(width: 1.0, height: 1.0, padding: 8) do
|
||||
stack(width: 1.0, height: 18) do
|
||||
@@ -142,7 +145,8 @@ class W3DHub
|
||||
if @refresh_server_list && Gosu.milliseconds >= @refresh_server_list
|
||||
@refresh_server_list = nil
|
||||
|
||||
populate_server_list
|
||||
# populate_server_list
|
||||
reorder_server_list
|
||||
|
||||
if @selected_server&.id == @refresh_server&.id
|
||||
if @refresh_server
|
||||
@@ -158,11 +162,79 @@ class W3DHub
|
||||
end
|
||||
end
|
||||
|
||||
def generate_ping_icons
|
||||
signal3 = get_image("#{GAME_ROOT_PATH}/media/ui_icons/signal3.png")
|
||||
signal2 = get_image("#{GAME_ROOT_PATH}/media/ui_icons/signal2.png")
|
||||
signal1 = get_image("#{GAME_ROOT_PATH}/media/ui_icons/signal1.png")
|
||||
question = get_image("#{GAME_ROOT_PATH}/media/ui_icons/question.png")
|
||||
|
||||
good = Gosu.render(signal3.width, signal3.height) do
|
||||
signal3.draw(0, 0, 0, 1, 1, 0xff_008000)
|
||||
end
|
||||
|
||||
fair = Gosu.render(signal3.width, signal3.height) do
|
||||
signal3.draw(0, 0, 0, 1, 1, 0xff_444444)
|
||||
signal2.draw(0, 0, 0, 1, 1, 0xff_804000)
|
||||
end
|
||||
|
||||
poor = Gosu.render(signal3.width, signal3.height) do
|
||||
signal3.draw(0, 0, 0, 1, 1, 0xff_444444)
|
||||
signal1.draw(0, 0, 0, 1, 1, 0xff_800000)
|
||||
end
|
||||
|
||||
bad = Gosu.render(signal3.width, signal3.height) do
|
||||
signal3.draw(0, 0, 0, 1, 1, 0xff_444444)
|
||||
end
|
||||
|
||||
unknown = Gosu.render(signal3.width, signal3.height) do
|
||||
signal3.draw(0, 0, 0, 1, 1, 0xff_222222)
|
||||
question.draw(0, 0, 0, 1, 1, 0xff_888888)
|
||||
end
|
||||
|
||||
@ping_icons[:good] = good
|
||||
@ping_icons[:fair] = fair
|
||||
@ping_icons[:poor] = poor
|
||||
@ping_icons[:bad] = bad
|
||||
@ping_icons[:unknown] = unknown
|
||||
end
|
||||
|
||||
def ping_icon(ping)
|
||||
case ping
|
||||
when 0..160
|
||||
@ping_icons[:good]
|
||||
when 161..250
|
||||
@ping_icons[:fair]
|
||||
when 251..1_000
|
||||
@ping_icons[:poor]
|
||||
when 1_001..5_000
|
||||
@ping_icons[:bad]
|
||||
else
|
||||
@ping_icons[:unknown]
|
||||
end
|
||||
end
|
||||
|
||||
def refresh_server_list(server)
|
||||
@refresh_server_list = Gosu.milliseconds + 3_000
|
||||
@refresh_server = server if @selected_server&.id == server.id
|
||||
end
|
||||
|
||||
def update_server_ping(server)
|
||||
container = @server_list_container.children.find do |child|
|
||||
child.style.tag == server.id
|
||||
end
|
||||
|
||||
if container
|
||||
ping_image = container.children.map { |c| c.children }.flatten.find do |child|
|
||||
child.style.tag == :ping
|
||||
end
|
||||
|
||||
if ping_image
|
||||
ping_image.value = ping_icon(server.ping)
|
||||
ping_image.tip = "#{server.ping}ms"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def stylize_selected_server(server_container)
|
||||
server_container.style.server_item_background = server_container.style.default[:background]
|
||||
server_container.style.server_item_hover_background = server_container.style.hover[:background]
|
||||
@@ -175,6 +247,19 @@ class W3DHub
|
||||
server_container.style.active[:background] = @selected_color
|
||||
end
|
||||
|
||||
def reorder_server_list
|
||||
@server_list_container.children.sort_by! do |child|
|
||||
s = Store.server_list.find { |s| s.id == child.style.tag }
|
||||
|
||||
[s&.status&.player_count, s&.id]
|
||||
end.reverse!.each_with_index do |child, i|
|
||||
child.style.background = 0xff_333333 if i.even?
|
||||
child.style.background = 0 if i.odd?
|
||||
end
|
||||
|
||||
@server_list_container.recalculate
|
||||
end
|
||||
|
||||
def populate_server_list
|
||||
Store.server_list = Store.server_list.sort_by! { |s| [s&.status&.player_count, s&.id] }.reverse if Store.server_list
|
||||
|
||||
@@ -188,41 +273,34 @@ class W3DHub
|
||||
|
||||
i += 1
|
||||
|
||||
server_container = flow(width: 1.0, height: 48, hover: { background: 0xff_555566 }, active: { background: 0xff_555588 }) do
|
||||
server_container = flow(width: 1.0, height: 48, hover: { background: 0xff_555566 }, active: { background: 0xff_555588 }, tag: server.id) do
|
||||
background 0xff_333333 if i.even?
|
||||
|
||||
flow(width: 48, height: 1.0, padding: 4) do
|
||||
image game_icon(server), height: 1.0
|
||||
image game_icon(server), height: 1.0, tag: :game_icon
|
||||
end
|
||||
|
||||
stack(width: 0.45, height: 1.0) do
|
||||
inscription "<b>#{server&.status&.name}</b>"
|
||||
inscription "<b>#{server&.status&.name}</b>", tag: :server_name
|
||||
|
||||
flow(width: 1.0, height: 1.0) do
|
||||
inscription server.channel, margin_right: 64, text_size: 14
|
||||
inscription server.region, text_size: 14
|
||||
inscription server.channel, margin_right: 64, text_size: 14, tag: :server_channel
|
||||
inscription server.region, text_size: 14, tag: :server_region
|
||||
end
|
||||
end
|
||||
|
||||
flow(fill: true, height: 1.0) do
|
||||
inscription "#{server&.status&.map}"
|
||||
inscription "#{server&.status&.map}", tag: :map_name
|
||||
end
|
||||
|
||||
flow(width: 0.11, height: 1.0) do
|
||||
inscription "#{server&.status&.player_count}/#{server&.status&.max_players}"
|
||||
inscription "#{server&.status&.player_count}/#{server&.status&.max_players}", tag: :player_count
|
||||
end
|
||||
|
||||
# case rand(0..478)
|
||||
# when 0..60
|
||||
# image "#{GAME_ROOT_PATH}/media/ui_icons/signal3.png", width: 0.05, color: 0xff_008000
|
||||
# when 61..160
|
||||
# image "#{GAME_ROOT_PATH}/media/ui_icons/signal2.png", width: 0.05, color: 0xff_804000
|
||||
# else
|
||||
# image "#{GAME_ROOT_PATH}/media/ui_icons/signal1.png", width: 0.05, color: 0xff_800000
|
||||
# end
|
||||
|
||||
flow(width: 48, height: 1.0, padding: 4) do
|
||||
image "#{GAME_ROOT_PATH}/media/ui_icons/question.png", height: 1.0, color: 0xff_444444
|
||||
puts "#{server&.status&.name}#{server.ping}"
|
||||
|
||||
image ping_icon(server.ping), height: 1.0, tip: "#{server.ping}ms", tag: :ping
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -165,6 +165,12 @@ class W3DHub
|
||||
@page.refresh_server_list(server)
|
||||
end
|
||||
|
||||
def update_server_ping(server)
|
||||
return unless @page.is_a?(Pages::ServerBrowser)
|
||||
|
||||
@page.update_server_ping(server)
|
||||
end
|
||||
|
||||
def show_application_taskbar
|
||||
@application_taskbar_container.show
|
||||
end
|
||||
|
||||
@@ -50,6 +50,7 @@ require "async/http/internet/instance"
|
||||
require "async/http/endpoint"
|
||||
require "async/websocket/client"
|
||||
require "protocol/websocket/connection"
|
||||
require "net/ping"
|
||||
|
||||
I18n.load_path << Dir["#{W3DHub::GAME_ROOT_PATH}/locales/*.yml"]
|
||||
I18n.default_locale = :en
|
||||
|
||||
Reference in New Issue
Block a user