2016-10-06 13:55:03 +05:00
|
|
|
// URTator - Urban Terror server browser and game launcher, written in
|
|
|
|
// Go.
|
|
|
|
//
|
|
|
|
// Copyright (c) 2016, Stanslav N. a.k.a pztrn (or p0z1tr0n)
|
|
|
|
// All rights reserved.
|
|
|
|
//
|
|
|
|
// Licensed under Terms and Conditions of GNU General Public License
|
|
|
|
// version 3 or any higher.
|
|
|
|
// ToDo: put full text of license here.
|
|
|
|
package requester
|
|
|
|
|
|
|
|
import (
|
|
|
|
// stdlib
|
|
|
|
"errors"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
|
|
|
"runtime"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
// local
|
|
|
|
"github.com/pztrn/urtrator/datamodels"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Pooler struct {
|
|
|
|
// Maximum number of simultaneous requests running.
|
|
|
|
maxrequests int
|
|
|
|
// Packet prefix.
|
|
|
|
pp string
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Pooler) Initialize() {
|
|
|
|
fmt.Println("Initializing requester goroutine pooler...")
|
2016-10-06 17:11:36 +05:00
|
|
|
// ToDo: figure out how to make this work nice.
|
|
|
|
p.maxrequests = runtime.NumCPU() * 2000
|
2016-10-06 13:55:03 +05:00
|
|
|
p.pp = "\377\377\377\377"
|
|
|
|
}
|
|
|
|
|
|
|
|
// Servers pinging pooler. Should be started as goroutine to prevent
|
|
|
|
// UI blocking.
|
|
|
|
func (p *Pooler) PingServers(servers_type string) {
|
|
|
|
fmt.Println("About to ping " + servers_type + " servers...")
|
|
|
|
|
|
|
|
cur_requests := 0
|
|
|
|
var wait sync.WaitGroup
|
|
|
|
|
|
|
|
for _, server_to_ping := range Cache.Servers {
|
|
|
|
if servers_type == "favorites" && server_to_ping.Server.Favorite != "1" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
for {
|
|
|
|
if cur_requests == p.maxrequests {
|
|
|
|
time.Sleep(time.Second * 1)
|
|
|
|
} else {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
wait.Add(1)
|
|
|
|
cur_requests += 1
|
|
|
|
go func(srv *datamodels.Server) {
|
|
|
|
defer wait.Done()
|
|
|
|
p.pingServersExecutor(srv)
|
|
|
|
cur_requests -= 1
|
|
|
|
}(server_to_ping.Server)
|
|
|
|
}
|
|
|
|
wait.Wait()
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Pooler) pingServersExecutor(server *datamodels.Server) error {
|
|
|
|
srv := server.Ip + ":" + server.Port
|
|
|
|
fmt.Println("Pinging " + srv)
|
|
|
|
// Dial to server.
|
|
|
|
start_p := time.Now()
|
|
|
|
conn_ping, err2 := net.Dial("udp", srv)
|
|
|
|
if err2 != nil {
|
|
|
|
fmt.Println("Error dialing to server " + srv + "!")
|
|
|
|
return errors.New("Error dialing to server " + srv + "!")
|
|
|
|
}
|
|
|
|
// Set deadline, so we won't wait forever.
|
|
|
|
ddl_ping := time.Now()
|
|
|
|
// This should be enough. Maybe, you should'n run URTrator on modem
|
|
|
|
// connections? :)
|
|
|
|
ddl_ping = ddl_ping.Add(time.Second * 10)
|
|
|
|
conn_ping.SetDeadline(ddl_ping)
|
|
|
|
|
|
|
|
msg_ping := []byte(p.pp + "getinfo")
|
|
|
|
conn_ping.Write(msg_ping)
|
|
|
|
|
|
|
|
// UDP Buffer.
|
|
|
|
var received_buf_ping []byte = make([]byte, 128)
|
|
|
|
// Received buffer.
|
|
|
|
var raw_received_ping []byte
|
|
|
|
_, err := conn_ping.Read(received_buf_ping)
|
|
|
|
if err != nil {
|
|
|
|
fmt.Println("PING ERROR")
|
|
|
|
}
|
|
|
|
raw_received_ping = append(raw_received_ping, received_buf_ping...)
|
|
|
|
conn_ping.Close()
|
|
|
|
|
|
|
|
delta := strconv.Itoa(int(time.Since(start_p).Nanoseconds()) / 1000000)
|
|
|
|
server.Ping = delta
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (p *Pooler) UpdateServers(servers_type string) {
|
|
|
|
var wait sync.WaitGroup
|
|
|
|
|
|
|
|
for _, server := range Cache.Servers {
|
|
|
|
if servers_type == "favorites" && server.Server.Favorite != "1" {
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
wait.Add(1)
|
|
|
|
go func(server *datamodels.Server) {
|
|
|
|
defer wait.Done()
|
|
|
|
p.updateSpecificServer(server)
|
|
|
|
}(server.Server)
|
|
|
|
}
|
|
|
|
wait.Wait()
|
|
|
|
Eventer.LaunchEvent("flushServers")
|
|
|
|
p.PingServers(servers_type)
|
|
|
|
|
|
|
|
if servers_type == "all" {
|
|
|
|
Eventer.LaunchEvent("loadAllServers")
|
2016-10-06 17:50:08 +05:00
|
|
|
Eventer.LaunchEvent("serversUpdateCompleted")
|
2016-10-06 13:55:03 +05:00
|
|
|
} else if servers_type == "favorites" {
|
|
|
|
Eventer.LaunchEvent("loadFavoriteServers")
|
2016-10-06 17:50:08 +05:00
|
|
|
Eventer.LaunchEvent("serversUpdateCompleted")
|
2016-10-06 13:55:03 +05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Updates information about specific server.
|
|
|
|
func (p *Pooler) updateSpecificServer(server *datamodels.Server) error {
|
|
|
|
server_addr := server.Ip + ":" + server.Port
|
|
|
|
fmt.Println("Updating server: " + server_addr)
|
|
|
|
|
|
|
|
// Dial to server.
|
|
|
|
conn, err1 := net.Dial("udp", server_addr)
|
|
|
|
if err1 != nil {
|
|
|
|
fmt.Println("Error dialing to server " + server_addr + "!")
|
|
|
|
return errors.New("Error dialing to server " + server_addr + "!")
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set deadline, so we won't wait forever.
|
|
|
|
ddl := time.Now()
|
|
|
|
// This should be enough. Maybe, you should'n run URTrator on modem
|
|
|
|
// connections? :)
|
|
|
|
ddl = ddl.Add(time.Second * 2)
|
|
|
|
conn.SetDeadline(ddl)
|
|
|
|
|
|
|
|
msg := []byte(p.pp + "getstatus")
|
|
|
|
conn.Write(msg)
|
|
|
|
|
|
|
|
// UDP Buffer.
|
|
|
|
var received_buf []byte = make([]byte, 4096)
|
|
|
|
// Received buffer.
|
|
|
|
var raw_received []byte
|
|
|
|
for {
|
|
|
|
_, err := conn.Read(received_buf)
|
|
|
|
if err != nil {
|
|
|
|
break
|
|
|
|
}
|
|
|
|
raw_received = append(raw_received, received_buf...)
|
|
|
|
}
|
|
|
|
conn.Close()
|
|
|
|
|
|
|
|
// First line is "infoResponse" string, which we should skip by
|
|
|
|
// splitting response by "\n".
|
|
|
|
received_lines := strings.Split(string(raw_received), "\n")
|
|
|
|
// We have server's data!
|
|
|
|
if len(received_lines) > 1 {
|
|
|
|
srv_config := strings.Split(received_lines[1], "\\")
|
|
|
|
// Parse server configuration into passed server's datamodel.
|
|
|
|
for i := 0; i < len(srv_config); i = i + 1 {
|
|
|
|
if srv_config[i] == "g_modversion" {
|
|
|
|
server.Version = srv_config[i + 1]
|
|
|
|
}
|
|
|
|
if srv_config[i] == "g_gametype" {
|
|
|
|
server.Gamemode = srv_config[i + 1]
|
|
|
|
}
|
|
|
|
if srv_config[i] == "sv_maxclients" {
|
|
|
|
server.Maxplayers = srv_config[i + 1]
|
|
|
|
}
|
|
|
|
if srv_config[i] == "clients" {
|
|
|
|
server.Players = srv_config[i + 1]
|
|
|
|
}
|
|
|
|
if srv_config[i] == "mapname" {
|
|
|
|
server.Map = srv_config[i + 1]
|
|
|
|
}
|
|
|
|
if srv_config[i] == "sv_hostname" {
|
|
|
|
server.Name = srv_config[i + 1]
|
|
|
|
}
|
|
|
|
server.ExtendedConfig = received_lines[1]
|
|
|
|
}
|
|
|
|
if len(received_lines) >= 2 {
|
|
|
|
// Here we go, players information.
|
|
|
|
players := received_lines[2:]
|
2016-10-06 21:34:14 +05:00
|
|
|
fmt.Println(players, len(players), len(players[0]))
|
|
|
|
// Calculate players!
|
|
|
|
if len(players) == 1 && len(players[0]) > 255 {
|
|
|
|
server.Players = "0"
|
|
|
|
} else {
|
|
|
|
server.Players = strconv.Itoa(len(players))
|
|
|
|
}
|
|
|
|
server.PlayersInfo = strings.Join(received_lines[2:], "\\")
|
2016-10-06 13:55:03 +05:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ToDo: Calculate ping. 0 for now.
|
|
|
|
server.Ping = "0"
|
|
|
|
return nil
|
|
|
|
}
|