Huge refactor regarding concurrency.
Now we have pooler which will pool connections (like pinging), so there will be no timeouts due to "we have launched 100000 goroutines". Reworked all code to use events (see Eventer). Still more work about this to go. Maybe more fixes I forgot.
This commit is contained in:
parent
dd41537106
commit
f37dd3adfd
132
cache/cache_object.go
vendored
Normal file
132
cache/cache_object.go
vendored
Normal file
@ -0,0 +1,132 @@
|
|||||||
|
// 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 cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
// stdlib
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
// local
|
||||||
|
"github.com/pztrn/urtrator/cachemodels"
|
||||||
|
"github.com/pztrn/urtrator/datamodels"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Cache struct {
|
||||||
|
// Servers cache.
|
||||||
|
Servers map[string]*cachemodels.Server
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) CreateServer(addr string) {
|
||||||
|
_, ok := c.Servers[addr]
|
||||||
|
if !ok {
|
||||||
|
fmt.Println("Creating cached server " + addr)
|
||||||
|
c.Servers[addr] = &cachemodels.Server{}
|
||||||
|
c.Servers[addr].Server = &datamodels.Server{}
|
||||||
|
} else {
|
||||||
|
fmt.Println("Server " + addr + " already exist.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Flush servers to database.
|
||||||
|
func (c *Cache) FlushServers() {
|
||||||
|
fmt.Println("Updating servers information in database...")
|
||||||
|
raw_cached := []datamodels.Server{}
|
||||||
|
Database.Db.Select(&raw_cached, "SELECT * FROM servers")
|
||||||
|
|
||||||
|
// Create map[string]*datamodels.Server once, so we won't iterate
|
||||||
|
// over slice of datamodels.Server everytime.
|
||||||
|
cached_servers := make(map[string]*datamodels.Server)
|
||||||
|
for s := range raw_cached {
|
||||||
|
mapping_item_name := raw_cached[s].Ip + ":" + raw_cached[s].Port
|
||||||
|
cached_servers[mapping_item_name] = &raw_cached[s]
|
||||||
|
}
|
||||||
|
|
||||||
|
new_servers := make(map[string]*datamodels.Server)
|
||||||
|
|
||||||
|
// Update our cached mapping.
|
||||||
|
for _, s := range c.Servers {
|
||||||
|
mapping_item_name := s.Server.Ip + ":" + s.Server.Port
|
||||||
|
_, ok := cached_servers[mapping_item_name]
|
||||||
|
if !ok {
|
||||||
|
fmt.Println(mapping_item_name + " not found!")
|
||||||
|
new_servers[mapping_item_name] = s.Server
|
||||||
|
} else {
|
||||||
|
cached_servers[mapping_item_name].Ip = s.Server.Ip
|
||||||
|
cached_servers[mapping_item_name].Port = s.Server.Port
|
||||||
|
cached_servers[mapping_item_name].Name = s.Server.Name
|
||||||
|
cached_servers[mapping_item_name].Players = s.Server.Players
|
||||||
|
cached_servers[mapping_item_name].Maxplayers = s.Server.Maxplayers
|
||||||
|
cached_servers[mapping_item_name].Ping = s.Server.Ping
|
||||||
|
cached_servers[mapping_item_name].Map = s.Server.Map
|
||||||
|
cached_servers[mapping_item_name].Gamemode = s.Server.Gamemode
|
||||||
|
cached_servers[mapping_item_name].Version = s.Server.Version
|
||||||
|
cached_servers[mapping_item_name].ExtendedConfig = s.Server.ExtendedConfig
|
||||||
|
cached_servers[mapping_item_name].PlayersInfo = s.Server.PlayersInfo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tx := Database.Db.MustBegin()
|
||||||
|
fmt.Println("Adding new servers...")
|
||||||
|
for _, srv := range new_servers {
|
||||||
|
tx.NamedExec("INSERT INTO servers (ip, port, name, ping, players, maxplayers, gamemode, map, version, extended_config, players_info) VALUES (:ip, :port, :name, :ping, :players, :maxplayers, :gamemode, :map, :version, :extended_config, :players_info)", srv)
|
||||||
|
}
|
||||||
|
fmt.Println("Updating cached servers...")
|
||||||
|
for _, srv := range cached_servers {
|
||||||
|
tx.NamedExec("UPDATE servers SET name=:name, players=:players, maxplayers=:maxplayers, gamemode=:gamemode, map=:map, ping=:ping, version=:version, extended_config=:extended_config, players_info=:players_info WHERE ip=:ip AND port=:port", &srv)
|
||||||
|
}
|
||||||
|
|
||||||
|
tx.Commit()
|
||||||
|
fmt.Println("Done")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) Initialize() {
|
||||||
|
fmt.Println("Initializing cache...")
|
||||||
|
c.initializeStorages()
|
||||||
|
c.LoadServers()
|
||||||
|
|
||||||
|
Eventer.AddEventHandler("flushServers", c.FlushServers)
|
||||||
|
Eventer.AddEventHandler("loadServersIntoCache", c.LoadServers)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) initializeStorages() {
|
||||||
|
// Servers cache.
|
||||||
|
c.Servers = make(map[string]*cachemodels.Server)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Cache) LoadServers() {
|
||||||
|
c.Servers = make(map[string]*cachemodels.Server)
|
||||||
|
// Getting servers from database.
|
||||||
|
raw_servers := []datamodels.Server{}
|
||||||
|
err := Database.Db.Select(&raw_servers, "SELECT * FROM servers")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Due to nature of pointers and goroutines thing (?) this should
|
||||||
|
// be done in this way.
|
||||||
|
for _, server := range raw_servers {
|
||||||
|
key := server.Ip + ":" + server.Port
|
||||||
|
c.CreateServer(key)
|
||||||
|
c.Servers[key].Server.Name = server.Name
|
||||||
|
c.Servers[key].Server.Ip = server.Ip
|
||||||
|
c.Servers[key].Server.Port = server.Port
|
||||||
|
c.Servers[key].Server.Players = server.Players
|
||||||
|
c.Servers[key].Server.Maxplayers = server.Maxplayers
|
||||||
|
c.Servers[key].Server.Ping = server.Ping
|
||||||
|
c.Servers[key].Server.Gamemode = server.Gamemode
|
||||||
|
c.Servers[key].Server.Map = server.Map
|
||||||
|
c.Servers[key].Server.Version = server.Version
|
||||||
|
c.Servers[key].Server.Favorite = server.Favorite
|
||||||
|
c.Servers[key].Server.Password = server.Password
|
||||||
|
c.Servers[key].Server.ProfileToUse = server.ProfileToUse
|
||||||
|
c.Servers[key].Server.ExtendedConfig = server.ExtendedConfig
|
||||||
|
c.Servers[key].Server.PlayersInfo = server.PlayersInfo
|
||||||
|
}
|
||||||
|
}
|
28
cache/exported.go
vendored
Normal file
28
cache/exported.go
vendored
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
// 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 cache
|
||||||
|
|
||||||
|
import (
|
||||||
|
// local
|
||||||
|
event "github.com/pztrn/urtrator/eventer"
|
||||||
|
"github.com/pztrn/urtrator/database"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Database *database.Database
|
||||||
|
Eventer *event.Eventer
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(d *database.Database, e *event.Eventer) *Cache {
|
||||||
|
Database = d
|
||||||
|
Eventer = e
|
||||||
|
c := Cache{}
|
||||||
|
return &c
|
||||||
|
}
|
24
cachemodels/server.go
Normal file
24
cachemodels/server.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// 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 cachemodels
|
||||||
|
|
||||||
|
import (
|
||||||
|
// local
|
||||||
|
"github.com/pztrn/urtrator/datamodels"
|
||||||
|
|
||||||
|
// Other
|
||||||
|
"github.com/mattn/go-gtk/gtk"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Server struct {
|
||||||
|
Server *datamodels.Server
|
||||||
|
TreeIter gtk.TreeIter
|
||||||
|
IterSet bool
|
||||||
|
}
|
@ -14,6 +14,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
// local
|
// local
|
||||||
|
"github.com/pztrn/urtrator/cache"
|
||||||
"github.com/pztrn/urtrator/colorizer"
|
"github.com/pztrn/urtrator/colorizer"
|
||||||
"github.com/pztrn/urtrator/configuration"
|
"github.com/pztrn/urtrator/configuration"
|
||||||
"github.com/pztrn/urtrator/database"
|
"github.com/pztrn/urtrator/database"
|
||||||
@ -26,6 +27,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Context struct {
|
type Context struct {
|
||||||
|
// Caching.
|
||||||
|
Cache *cache.Cache
|
||||||
// Colors parser and prettifier.
|
// Colors parser and prettifier.
|
||||||
Colorizer *colorizer.Colorizer
|
Colorizer *colorizer.Colorizer
|
||||||
// Configuration.
|
// Configuration.
|
||||||
@ -43,12 +46,18 @@ type Context struct {
|
|||||||
func (ctx *Context) Close() {
|
func (ctx *Context) Close() {
|
||||||
fmt.Println("Closing URTrator...")
|
fmt.Println("Closing URTrator...")
|
||||||
|
|
||||||
|
ctx.Cache.FlushServers()
|
||||||
ctx.Database.Close()
|
ctx.Database.Close()
|
||||||
|
|
||||||
// At last, close main window.
|
// At last, close main window.
|
||||||
gtk.MainQuit()
|
gtk.MainQuit()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (ctx *Context) initializeCache() {
|
||||||
|
ctx.Cache = cache.New(ctx.Database, ctx.Eventer)
|
||||||
|
ctx.Cache.Initialize()
|
||||||
|
}
|
||||||
|
|
||||||
func (ctx *Context) initializeColorizer() {
|
func (ctx *Context) initializeColorizer() {
|
||||||
ctx.Colorizer = colorizer.New()
|
ctx.Colorizer = colorizer.New()
|
||||||
ctx.Colorizer.Initialize()
|
ctx.Colorizer.Initialize()
|
||||||
@ -76,7 +85,7 @@ func (ctx *Context) initializeLauncher() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ctx *Context) initializeRequester() {
|
func (ctx *Context) initializeRequester() {
|
||||||
ctx.Requester = requester.New()
|
ctx.Requester = requester.New(ctx.Cache, ctx.Eventer)
|
||||||
ctx.Requester.Initialize()
|
ctx.Requester.Initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,6 +95,7 @@ func (ctx *Context) Initialize() {
|
|||||||
ctx.initializeConfig()
|
ctx.initializeConfig()
|
||||||
ctx.initializeDatabase()
|
ctx.initializeDatabase()
|
||||||
ctx.initializeEventer()
|
ctx.initializeEventer()
|
||||||
|
ctx.initializeCache()
|
||||||
ctx.initializeLauncher()
|
ctx.initializeLauncher()
|
||||||
ctx.initializeRequester()
|
ctx.initializeRequester()
|
||||||
}
|
}
|
||||||
|
@ -12,9 +12,20 @@ package requester
|
|||||||
import (
|
import (
|
||||||
// stdlib
|
// stdlib
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
|
// local
|
||||||
|
"github.com/pztrn/urtrator/cache"
|
||||||
|
"github.com/pztrn/urtrator/eventer"
|
||||||
)
|
)
|
||||||
|
|
||||||
func New() *Requester {
|
var (
|
||||||
|
Cache *cache.Cache
|
||||||
|
Eventer *eventer.Eventer
|
||||||
|
)
|
||||||
|
|
||||||
|
func New(c *cache.Cache, e *eventer.Eventer) *Requester {
|
||||||
|
Cache = c
|
||||||
|
Eventer = e
|
||||||
fmt.Println("Creating Requester object...")
|
fmt.Println("Creating Requester object...")
|
||||||
r := Requester{}
|
r := Requester{}
|
||||||
return &r
|
return &r
|
||||||
|
209
requester/pooler.go
Normal file
209
requester/pooler.go
Normal file
@ -0,0 +1,209 @@
|
|||||||
|
// 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...")
|
||||||
|
p.maxrequests = runtime.NumCPU() * 2
|
||||||
|
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)
|
||||||
|
fmt.Println(delta)
|
||||||
|
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")
|
||||||
|
} else if servers_type == "favorites" {
|
||||||
|
Eventer.LaunchEvent("loadFavoriteServers")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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:]
|
||||||
|
server.Players = strconv.Itoa(len(players))
|
||||||
|
//server.PlayersInfo = received_lines[2:]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ToDo: Calculate ping. 0 for now.
|
||||||
|
server.Ping = "0"
|
||||||
|
|
||||||
|
fmt.Println(server)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -12,19 +12,15 @@ package requester
|
|||||||
import (
|
import (
|
||||||
// stdlib
|
// stdlib
|
||||||
"bytes"
|
"bytes"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
// local
|
|
||||||
"github.com/pztrn/urtrator/datamodels"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type Requester struct {
|
type Requester struct {
|
||||||
|
// Pooler.
|
||||||
|
pooler *Pooler
|
||||||
// Master server address
|
// Master server address
|
||||||
master_server string
|
master_server string
|
||||||
// Master server port
|
// Master server port
|
||||||
@ -44,12 +40,14 @@ func (r *Requester) Initialize() {
|
|||||||
r.master_server_port = "27900"
|
r.master_server_port = "27900"
|
||||||
r.pp = "\377\377\377\377"
|
r.pp = "\377\377\377\377"
|
||||||
r.ip_delimiter = 92
|
r.ip_delimiter = 92
|
||||||
|
r.pooler = &Pooler{}
|
||||||
|
r.pooler.Initialize()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Gets all available servers from master server.
|
// Gets all available servers from master server.
|
||||||
func (r *Requester) getServers(callback chan [][]string) {
|
// This isn't in pooler, because it have no need to be pooled.
|
||||||
|
func (r *Requester) getServers() {
|
||||||
// IP addresses we will compose to return.
|
// IP addresses we will compose to return.
|
||||||
var received_ips [][]string
|
|
||||||
conn, err1 := net.Dial("udp", r.master_server + ":" + r.master_server_port)
|
conn, err1 := net.Dial("udp", r.master_server + ":" + r.master_server_port)
|
||||||
if err1 != nil {
|
if err1 != nil {
|
||||||
fmt.Println("Error dialing to master server!")
|
fmt.Println("Error dialing to master server!")
|
||||||
@ -109,146 +107,29 @@ func (r *Requester) getServers(callback chan [][]string) {
|
|||||||
// second byte.
|
// second byte.
|
||||||
p1 := int(slice[4]) * 256
|
p1 := int(slice[4]) * 256
|
||||||
port := strconv.Itoa(p1 + int(slice[5]))
|
port := strconv.Itoa(p1 + int(slice[5]))
|
||||||
|
addr := ip + ":" + port
|
||||||
|
|
||||||
// Create a slice with IP and port.
|
// Check if we already have this server added previously. If so - do nothing.
|
||||||
ip_and_port := []string{ip, port}
|
_, ok := Cache.Servers[addr]
|
||||||
// Add it to received_ips.
|
if !ok {
|
||||||
received_ips = append(received_ips, ip_and_port)
|
// Create cached server.
|
||||||
|
Cache.CreateServer(addr)
|
||||||
|
Cache.Servers[addr].Server.Ip = ip
|
||||||
|
Cache.Servers[addr].Server.Port = port
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println("Parsed " + strconv.Itoa(len(received_ips)) + " addresses")
|
|
||||||
callback <- received_ips
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Updates information about all available servers from master server and
|
// Updates information about all available servers from master server and
|
||||||
// parses it to usable format.
|
// parses it to usable format.
|
||||||
func (r *Requester) UpdateAllServers(done_chan chan map[string]*datamodels.Server, error_chan chan bool) {
|
func (r *Requester) UpdateAllServers() {
|
||||||
fmt.Println("Starting all servers updating procedure...")
|
fmt.Println("Starting all servers updating procedure...")
|
||||||
|
r.getServers()
|
||||||
callback := make(chan [][]string)
|
r.pooler.UpdateServers("all")
|
||||||
go r.getServers(callback)
|
|
||||||
|
|
||||||
servers := make(map[string]*datamodels.Server)
|
|
||||||
|
|
||||||
select {
|
|
||||||
case data := <- callback:
|
|
||||||
// Yay, we got data! :)
|
|
||||||
fmt.Println("Received " + strconv.Itoa(len(data)) + " servers")
|
|
||||||
servers = r.updateServerGoroutineDispatcher(data)
|
|
||||||
break
|
|
||||||
case <- time.After(time.Second * 10):
|
|
||||||
// Timeouted? Okay, push error back.
|
|
||||||
error_chan <- true
|
|
||||||
}
|
|
||||||
|
|
||||||
done_chan <- servers
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Requester) UpdateFavoriteServers(servers [][]string, done_chan chan map[string]*datamodels.Server, error_chan chan bool) {
|
func (r *Requester) UpdateFavoriteServers() {
|
||||||
fmt.Println("Updating favorites servers...")
|
fmt.Println("Updating favorites servers...")
|
||||||
updated_servers := r.updateServerGoroutineDispatcher(servers)
|
r.pooler.UpdateServers("favorites")
|
||||||
done_chan <- updated_servers
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Requester) updateServerGoroutineDispatcher(data [][]string) map[string]*datamodels.Server {
|
|
||||||
var wait sync.WaitGroup
|
|
||||||
var lock = sync.RWMutex{}
|
|
||||||
done_updating := 0
|
|
||||||
servers := make(map[string]*datamodels.Server)
|
|
||||||
|
|
||||||
for _, s := range data {
|
|
||||||
s := datamodels.Server{
|
|
||||||
Ip: s[0],
|
|
||||||
Port: s[1],
|
|
||||||
}
|
|
||||||
go func(s *datamodels.Server, servers map[string]*datamodels.Server) {
|
|
||||||
wait.Add(1)
|
|
||||||
defer wait.Done()
|
|
||||||
r.UpdateServer(s)
|
|
||||||
done_updating = done_updating + 1
|
|
||||||
lock.Lock()
|
|
||||||
servers[s.Ip + ":" + s.Port] = s
|
|
||||||
lock.Unlock()
|
|
||||||
}(&s, servers)
|
|
||||||
}
|
|
||||||
wait.Wait()
|
|
||||||
return servers
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updates information about specific server.
|
|
||||||
func (r *Requester) UpdateServer(server *datamodels.Server) error {
|
|
||||||
srv := server.Ip + ":" + server.Port
|
|
||||||
fmt.Println("Updating server: " + srv)
|
|
||||||
|
|
||||||
// Dial to server.
|
|
||||||
conn, err1 := net.Dial("udp", srv)
|
|
||||||
if err1 != nil {
|
|
||||||
fmt.Println("Error dialing to server " + srv + "!")
|
|
||||||
return errors.New("Error dialing to server " + srv + "!")
|
|
||||||
}
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
// 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(r.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...)
|
|
||||||
}
|
|
||||||
|
|
||||||
// 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]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if len(received_lines) >= 2 {
|
|
||||||
// Here we go, players information.
|
|
||||||
players := received_lines[2:]
|
|
||||||
server.Players = strconv.Itoa(len(players))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ToDo: Calculate ping. 0 for now.
|
|
||||||
server.Ping = "0"
|
|
||||||
// ToDo: put this info.
|
|
||||||
server.ExtendedConfig = ""
|
|
||||||
server.PlayersInfo = ""
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
153
ui/mainwindow.go
153
ui/mainwindow.go
@ -14,6 +14,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"reflect"
|
||||||
"runtime"
|
"runtime"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@ -148,10 +149,15 @@ func (m *MainWindow) addToFavorites() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Executes when delimiter for two panes is moved, to calculate VALID
|
||||||
|
// position.
|
||||||
func (m *MainWindow) checkMainPanePosition() {
|
func (m *MainWindow) checkMainPanePosition() {
|
||||||
m.pane_negative_position = m.window_width - m.hpane.GetPosition()
|
m.pane_negative_position = m.window_width - m.hpane.GetPosition()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Executes when main window is moved or resized.
|
||||||
|
// Also calculating pane delimiter position and set it to avoid
|
||||||
|
// widgets hell :).
|
||||||
func (m *MainWindow) checkPositionAndSize() {
|
func (m *MainWindow) checkPositionAndSize() {
|
||||||
m.window.GetPosition(&m.window_pos_x, &m.window_pos_y)
|
m.window.GetPosition(&m.window_pos_x, &m.window_pos_y)
|
||||||
m.window.GetSize(&m.window_width, &m.window_height)
|
m.window.GetSize(&m.window_width, &m.window_height)
|
||||||
@ -159,6 +165,7 @@ func (m *MainWindow) checkPositionAndSize() {
|
|||||||
m.hpane.SetPosition(m.window_width - m.pane_negative_position)
|
m.hpane.SetPosition(m.window_width - m.pane_negative_position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Executes on URTrator shutdown.
|
||||||
func (m *MainWindow) Close() {
|
func (m *MainWindow) Close() {
|
||||||
// Save window parameters.
|
// Save window parameters.
|
||||||
ctx.Cfg.Cfg["/mainwindow/width"] = strconv.Itoa(m.window_width)
|
ctx.Cfg.Cfg["/mainwindow/width"] = strconv.Itoa(m.window_width)
|
||||||
@ -170,6 +177,7 @@ func (m *MainWindow) Close() {
|
|||||||
ctx.Close()
|
ctx.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Deleting server from favorites.
|
||||||
func (m *MainWindow) deleteFromFavorites() {
|
func (m *MainWindow) deleteFromFavorites() {
|
||||||
fmt.Println("Removing server from favorites...")
|
fmt.Println("Removing server from favorites...")
|
||||||
current_tab := m.tab_widget.GetTabLabelText(m.tab_widget.GetNthPage(m.tab_widget.GetCurrentPage()))
|
current_tab := m.tab_widget.GetTabLabelText(m.tab_widget.GetNthPage(m.tab_widget.GetCurrentPage()))
|
||||||
@ -221,6 +229,8 @@ func (m *MainWindow) deleteFromFavorites() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Drop database data.
|
||||||
|
// ToDo: extend so we should have an ability to decide what to drop.
|
||||||
func (m *MainWindow) dropDatabasesData() {
|
func (m *MainWindow) dropDatabasesData() {
|
||||||
fmt.Println("Dropping database data...")
|
fmt.Println("Dropping database data...")
|
||||||
var will_continue bool = false
|
var will_continue bool = false
|
||||||
@ -249,6 +259,7 @@ func (m *MainWindow) dropDatabasesData() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Executes on "Edit favorite server" click.
|
||||||
func (m *MainWindow) editFavorite() {
|
func (m *MainWindow) editFavorite() {
|
||||||
fmt.Println("Editing favorite server...")
|
fmt.Println("Editing favorite server...")
|
||||||
|
|
||||||
@ -276,6 +287,8 @@ func (m *MainWindow) editFavorite() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Executes when "Hide offline servers" checkbox changed it's state on
|
||||||
|
// "Servers" tab.
|
||||||
func (m *MainWindow) hideOfflineAllServers() {
|
func (m *MainWindow) hideOfflineAllServers() {
|
||||||
fmt.Println("(Un)Hiding offline servers in 'Servers' tab...")
|
fmt.Println("(Un)Hiding offline servers in 'Servers' tab...")
|
||||||
if m.all_servers_hide_offline.GetActive() {
|
if m.all_servers_hide_offline.GetActive() {
|
||||||
@ -286,6 +299,8 @@ func (m *MainWindow) hideOfflineAllServers() {
|
|||||||
ctx.Eventer.LaunchEvent("loadAllServers")
|
ctx.Eventer.LaunchEvent("loadAllServers")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Executes when "Hide offline servers" checkbox changed it's state on
|
||||||
|
// "Favorites" tab.
|
||||||
func (m *MainWindow) hideOfflineFavoriteServers() {
|
func (m *MainWindow) hideOfflineFavoriteServers() {
|
||||||
fmt.Println("(Un)Hiding offline servers in 'Favorite' tab...")
|
fmt.Println("(Un)Hiding offline servers in 'Favorite' tab...")
|
||||||
if m.fav_servers_hide_offline.GetActive() {
|
if m.fav_servers_hide_offline.GetActive() {
|
||||||
@ -296,6 +311,7 @@ func (m *MainWindow) hideOfflineFavoriteServers() {
|
|||||||
ctx.Eventer.LaunchEvent("loadFavoriteServers")
|
ctx.Eventer.LaunchEvent("loadFavoriteServers")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Main window initialization.
|
||||||
func (m *MainWindow) Initialize() {
|
func (m *MainWindow) Initialize() {
|
||||||
m.initializeStorages()
|
m.initializeStorages()
|
||||||
|
|
||||||
@ -381,6 +397,9 @@ func (m *MainWindow) Initialize() {
|
|||||||
m.initializeTrayIcon()
|
m.initializeTrayIcon()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Events.
|
||||||
|
m.initializeEvents()
|
||||||
|
|
||||||
// Game profiles and launch button.
|
// Game profiles and launch button.
|
||||||
profile_and_launch_hbox := gtk.NewHBox(false, 0)
|
profile_and_launch_hbox := gtk.NewHBox(false, 0)
|
||||||
m.vbox.PackStart(profile_and_launch_hbox, false, true, 5)
|
m.vbox.PackStart(profile_and_launch_hbox, false, true, 5)
|
||||||
@ -421,12 +440,22 @@ func (m *MainWindow) Initialize() {
|
|||||||
|
|
||||||
// Launch events.
|
// Launch events.
|
||||||
ctx.Eventer.LaunchEvent("loadProfiles")
|
ctx.Eventer.LaunchEvent("loadProfiles")
|
||||||
|
ctx.Eventer.LaunchEvent("loadServersIntoCache")
|
||||||
ctx.Eventer.LaunchEvent("loadAllServers")
|
ctx.Eventer.LaunchEvent("loadAllServers")
|
||||||
ctx.Eventer.LaunchEvent("loadFavoriteServers")
|
ctx.Eventer.LaunchEvent("loadFavoriteServers")
|
||||||
|
|
||||||
gtk.Main()
|
gtk.Main()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Events initialization.
|
||||||
|
func (m *MainWindow) initializeEvents() {
|
||||||
|
fmt.Println("Initializing events...")
|
||||||
|
ctx.Eventer.AddEventHandler("loadAllServers", m.loadAllServers)
|
||||||
|
ctx.Eventer.AddEventHandler("loadFavoriteServers", m.loadFavoriteServers)
|
||||||
|
ctx.Eventer.AddEventHandler("serversUpdateCompleted", m.serversUpdateCompleted)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Main menu initialization.
|
||||||
func (m *MainWindow) InitializeMainMenu() {
|
func (m *MainWindow) InitializeMainMenu() {
|
||||||
m.menubar = gtk.NewMenuBar()
|
m.menubar = gtk.NewMenuBar()
|
||||||
m.vbox.PackStart(m.menubar, false, false, 0)
|
m.vbox.PackStart(m.menubar, false, false, 0)
|
||||||
@ -472,6 +501,7 @@ func (m *MainWindow) InitializeMainMenu() {
|
|||||||
about_menu_drop_database_data_item.Connect("activate", m.dropDatabasesData)
|
about_menu_drop_database_data_item.Connect("activate", m.dropDatabasesData)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Sidebar (with quick connect and server's information) initialization.
|
||||||
func (m *MainWindow) initializeSidebar() {
|
func (m *MainWindow) initializeSidebar() {
|
||||||
sidebar_vbox := gtk.NewVBox(false, 0)
|
sidebar_vbox := gtk.NewVBox(false, 0)
|
||||||
|
|
||||||
@ -544,6 +574,7 @@ func (m *MainWindow) initializeStorages() {
|
|||||||
m.hidden = false
|
m.hidden = false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tabs widget initialization, including all child widgets.
|
||||||
func (m *MainWindow) InitializeTabs() {
|
func (m *MainWindow) InitializeTabs() {
|
||||||
// Create tabs widget.
|
// Create tabs widget.
|
||||||
m.tab_widget = gtk.NewNotebook()
|
m.tab_widget = gtk.NewNotebook()
|
||||||
@ -667,11 +698,9 @@ func (m *MainWindow) InitializeTabs() {
|
|||||||
|
|
||||||
// Add tab_widget widget to window.
|
// Add tab_widget widget to window.
|
||||||
m.hpane.Add1(m.tab_widget)
|
m.hpane.Add1(m.tab_widget)
|
||||||
|
|
||||||
ctx.Eventer.AddEventHandler("loadAllServers", m.loadAllServers)
|
|
||||||
ctx.Eventer.AddEventHandler("loadFavoriteServers", m.loadFavoriteServers)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Toolbar initialization.
|
||||||
func (m *MainWindow) InitializeToolbar() {
|
func (m *MainWindow) InitializeToolbar() {
|
||||||
m.toolbar = gtk.NewToolbar()
|
m.toolbar = gtk.NewToolbar()
|
||||||
m.vbox.PackStart(m.toolbar, false, false, 5)
|
m.vbox.PackStart(m.toolbar, false, false, 5)
|
||||||
@ -708,6 +737,7 @@ func (m *MainWindow) InitializeToolbar() {
|
|||||||
m.toolbar.Insert(fav_delete_button, 4)
|
m.toolbar.Insert(fav_delete_button, 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Tray icon initialization.
|
||||||
func (m *MainWindow) initializeTrayIcon() {
|
func (m *MainWindow) initializeTrayIcon() {
|
||||||
fmt.Println("Initializing tray icon...")
|
fmt.Println("Initializing tray icon...")
|
||||||
|
|
||||||
@ -859,67 +889,68 @@ func (m *MainWindow) launchGame() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *MainWindow) loadAllServers() {
|
func (m *MainWindow) loadAllServers() {
|
||||||
fmt.Println("Loading servers lists into widgets...")
|
fmt.Println("Loading all servers...")
|
||||||
servers := []datamodels.Server{}
|
|
||||||
err := ctx.Database.Db.Select(&servers, "SELECT * FROM servers")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
}
|
|
||||||
// ToDo: do it without clearing.
|
// ToDo: do it without clearing.
|
||||||
m.all_servers_store.Clear()
|
srv_addrs := reflect.ValueOf(ctx.Cache.Servers).MapKeys()
|
||||||
for _, srv := range servers {
|
fmt.Println(srv_addrs)
|
||||||
if m.all_servers_hide_offline.GetActive() && srv.Name == "" && srv.Players == "" {
|
for _, server := range ctx.Cache.Servers {
|
||||||
|
if m.all_servers_hide_offline.GetActive() && server.Server.Name == "" && server.Server.Players == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var iter gtk.TreeIter
|
var iter gtk.TreeIter
|
||||||
|
if !server.IterSet {
|
||||||
|
server.TreeIter = iter
|
||||||
m.all_servers_store.Append(&iter)
|
m.all_servers_store.Append(&iter)
|
||||||
if srv.Name == "" && srv.Players == "" {
|
server.IterSet = true
|
||||||
|
} else {
|
||||||
|
iter = server.TreeIter
|
||||||
|
}
|
||||||
|
if server.Server.Name == "" && server.Server.Players == "" {
|
||||||
m.all_servers_store.Set(&iter, 0, gtk.NewImage().RenderIcon(gtk.STOCK_NO, gtk.ICON_SIZE_SMALL_TOOLBAR, "").GPixbuf)
|
m.all_servers_store.Set(&iter, 0, gtk.NewImage().RenderIcon(gtk.STOCK_NO, gtk.ICON_SIZE_SMALL_TOOLBAR, "").GPixbuf)
|
||||||
} else {
|
} else {
|
||||||
m.all_servers_store.Set(&iter, 0, gtk.NewImage().RenderIcon(gtk.STOCK_OK, gtk.ICON_SIZE_SMALL_TOOLBAR, "").GPixbuf)
|
m.all_servers_store.Set(&iter, 0, gtk.NewImage().RenderIcon(gtk.STOCK_OK, gtk.ICON_SIZE_SMALL_TOOLBAR, "").GPixbuf)
|
||||||
}
|
}
|
||||||
srv_name := ctx.Colorizer.Fix(srv.Name)
|
server_name := ctx.Colorizer.Fix(server.Server.Name)
|
||||||
m.all_servers_store.Set(&iter, 1, srv_name)
|
m.all_servers_store.Set(&iter, 1, server_name)
|
||||||
m.all_servers_store.Set(&iter, 2, m.gamemodes[srv.Gamemode])
|
m.all_servers_store.Set(&iter, 2, m.gamemodes[server.Server.Gamemode])
|
||||||
m.all_servers_store.Set(&iter, 3, srv.Map)
|
m.all_servers_store.Set(&iter, 3, server.Server.Map)
|
||||||
m.all_servers_store.Set(&iter, 4, srv.Players + "/" + srv.Maxplayers)
|
m.all_servers_store.Set(&iter, 4, server.Server.Players + "/" + server.Server.Maxplayers)
|
||||||
m.all_servers_store.Set(&iter, 5, srv.Ping)
|
m.all_servers_store.Set(&iter, 5, server.Server.Ping)
|
||||||
m.all_servers_store.Set(&iter, 6, srv.Version)
|
m.all_servers_store.Set(&iter, 6, server.Server.Version)
|
||||||
m.all_servers_store.Set(&iter, 7, srv.Ip + ":" + srv.Port)
|
m.all_servers_store.Set(&iter, 7, server.Server.Ip + ":" + server.Server.Port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MainWindow) loadFavoriteServers() {
|
func (m *MainWindow) loadFavoriteServers() {
|
||||||
fmt.Println("Loading favorite servers...")
|
fmt.Println("Loading favorite servers...")
|
||||||
servers := []datamodels.Server{}
|
for _, server := range ctx.Cache.Servers {
|
||||||
err := ctx.Database.Db.Select(&servers, "SELECT * FROM servers WHERE favorite='1'")
|
if server.Server.Favorite != "1" {
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
}
|
|
||||||
// ToDo: do it without clearing.
|
|
||||||
m.fav_servers_store.Clear()
|
|
||||||
for _, srv := range servers {
|
|
||||||
if srv.Favorite != "1" {
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if m.fav_servers_hide_offline.GetActive() && srv.Name == "" && srv.Players == "" {
|
if m.fav_servers_hide_offline.GetActive() && server.Server.Name == "" && server.Server.Players == "" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
var iter gtk.TreeIter
|
var iter gtk.TreeIter
|
||||||
|
if !server.IterSet {
|
||||||
|
server.TreeIter = iter
|
||||||
m.fav_servers_store.Append(&iter)
|
m.fav_servers_store.Append(&iter)
|
||||||
if srv.Name == "" && srv.Players == "" {
|
server.IterSet = true
|
||||||
|
} else {
|
||||||
|
iter = server.TreeIter
|
||||||
|
}
|
||||||
|
if server.Server.Name == "" && server.Server.Players == "" {
|
||||||
m.fav_servers_store.Set(&iter, 0, gtk.NewImage().RenderIcon(gtk.STOCK_NO, gtk.ICON_SIZE_SMALL_TOOLBAR, "").GPixbuf)
|
m.fav_servers_store.Set(&iter, 0, gtk.NewImage().RenderIcon(gtk.STOCK_NO, gtk.ICON_SIZE_SMALL_TOOLBAR, "").GPixbuf)
|
||||||
} else {
|
} else {
|
||||||
m.fav_servers_store.Set(&iter, 0, gtk.NewImage().RenderIcon(gtk.STOCK_OK, gtk.ICON_SIZE_SMALL_TOOLBAR, "").GPixbuf)
|
m.fav_servers_store.Set(&iter, 0, gtk.NewImage().RenderIcon(gtk.STOCK_OK, gtk.ICON_SIZE_SMALL_TOOLBAR, "").GPixbuf)
|
||||||
}
|
}
|
||||||
srv_name := ctx.Colorizer.Fix(srv.Name)
|
server_name := ctx.Colorizer.Fix(server.Server.Name)
|
||||||
m.fav_servers_store.Set(&iter, 1, srv_name)
|
m.fav_servers_store.Set(&iter, 1, server_name)
|
||||||
m.fav_servers_store.Set(&iter, 2, m.gamemodes[srv.Gamemode])
|
m.fav_servers_store.Set(&iter, 2, m.gamemodes[server.Server.Gamemode])
|
||||||
m.fav_servers_store.Set(&iter, 3, srv.Map)
|
m.fav_servers_store.Set(&iter, 3, server.Server.Map)
|
||||||
m.fav_servers_store.Set(&iter, 4, srv.Players + "/" + srv.Maxplayers)
|
m.fav_servers_store.Set(&iter, 4, server.Server.Players + "/" + server.Server.Maxplayers)
|
||||||
m.fav_servers_store.Set(&iter, 5, srv.Ping)
|
m.fav_servers_store.Set(&iter, 5, server.Server.Ping)
|
||||||
m.fav_servers_store.Set(&iter, 6, srv.Version)
|
m.fav_servers_store.Set(&iter, 6, server.Server.Version)
|
||||||
m.fav_servers_store.Set(&iter, 7, srv.Ip + ":" + srv.Port)
|
m.fav_servers_store.Set(&iter, 7, server.Server.Ip + ":" + server.Server.Port)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -944,6 +975,10 @@ func (m *MainWindow) loadProfiles() {
|
|||||||
fmt.Println("Added " + strconv.Itoa(m.old_profiles_count) + " profiles")
|
fmt.Println("Added " + strconv.Itoa(m.old_profiles_count) + " profiles")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (m *MainWindow) serversUpdateCompleted() {
|
||||||
|
m.statusbar.Push(m.statusbar_context_id, "Servers updated.")
|
||||||
|
}
|
||||||
|
|
||||||
func (m *MainWindow) showHide() {
|
func (m *MainWindow) showHide() {
|
||||||
if m.hidden {
|
if m.hidden {
|
||||||
m.window.Show()
|
m.window.Show()
|
||||||
@ -966,47 +1001,15 @@ func (m *MainWindow) unlockInterface() {
|
|||||||
m.statusbar.Push(m.statusbar_context_id, "URTrator is ready.")
|
m.statusbar.Push(m.statusbar_context_id, "URTrator is ready.")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *MainWindow) updateFavorites(done_chan chan map[string]*datamodels.Server, error_chan chan bool) {
|
// Triggered when "Update all servers" button is clicked.
|
||||||
m.fav_servers_store.Clear()
|
|
||||||
servers := []datamodels.Server{}
|
|
||||||
err := ctx.Database.Db.Select(&servers, "SELECT * FROM servers WHERE favorite='1'")
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println(err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
var servers_from_db [][]string
|
|
||||||
|
|
||||||
for s := range servers {
|
|
||||||
servers_from_db = append(servers_from_db, []string{servers[s].Ip, servers[s].Port})
|
|
||||||
}
|
|
||||||
|
|
||||||
go ctx.Requester.UpdateFavoriteServers(servers_from_db, done_chan, error_chan)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MainWindow) UpdateServers() {
|
func (m *MainWindow) UpdateServers() {
|
||||||
m.statusbar.Push(m.statusbar_context_id, "Updating servers...")
|
m.statusbar.Push(m.statusbar_context_id, "Updating servers...")
|
||||||
current_tab := m.tab_widget.GetTabLabelText(m.tab_widget.GetNthPage(m.tab_widget.GetCurrentPage()))
|
current_tab := m.tab_widget.GetTabLabelText(m.tab_widget.GetNthPage(m.tab_widget.GetCurrentPage()))
|
||||||
fmt.Println("Updating servers on tab '" + current_tab + "'...")
|
fmt.Println("Updating servers on tab '" + current_tab + "'...")
|
||||||
done_chan := make(chan map[string]*datamodels.Server, 1)
|
|
||||||
error_chan := make(chan bool, 1)
|
|
||||||
if strings.Contains(current_tab, "Servers") {
|
|
||||||
go ctx.Requester.UpdateAllServers(done_chan, error_chan)
|
|
||||||
} else if strings.Contains(current_tab, "Favorites") {
|
|
||||||
m.updateFavorites(done_chan, error_chan)
|
|
||||||
}
|
|
||||||
|
|
||||||
select {
|
|
||||||
case data := <- done_chan:
|
|
||||||
fmt.Println("Information about servers successfully gathered")
|
|
||||||
ctx.Database.UpdateServers(data)
|
|
||||||
if strings.Contains(current_tab, "Servers") {
|
if strings.Contains(current_tab, "Servers") {
|
||||||
ctx.Eventer.LaunchEvent("loadAllServers")
|
go ctx.Requester.UpdateAllServers()
|
||||||
} else if strings.Contains(current_tab, "Favorites") {
|
} else if strings.Contains(current_tab, "Favorites") {
|
||||||
ctx.Eventer.LaunchEvent("loadFavoriteServers")
|
go ctx.Requester.UpdateFavoriteServers()
|
||||||
}
|
}
|
||||||
case <- error_chan:
|
|
||||||
fmt.Println("Error occured")
|
|
||||||
}
|
|
||||||
|
|
||||||
m.statusbar.Push(m.statusbar_context_id, "Servers updated.")
|
|
||||||
}
|
}
|
||||||
|
@ -16,11 +16,15 @@ import (
|
|||||||
|
|
||||||
// stdlib
|
// stdlib
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"runtime"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Println("This is URTrator, version 0.1")
|
fmt.Println("This is URTrator, version 0.1")
|
||||||
|
|
||||||
|
numCPUs := runtime.NumCPU()
|
||||||
|
runtime.GOMAXPROCS(numCPUs)
|
||||||
|
|
||||||
ctx := context.New()
|
ctx := context.New()
|
||||||
ctx.Initialize()
|
ctx.Initialize()
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user