| 
									
										
										
										
											2018-11-10 21:13:06 +05:00
										 |  |  | // URTrator - Urban Terror server browser and game launcher, written in | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | // Go. | 
					
						
							|  |  |  | // | 
					
						
							| 
									
										
										
										
											2019-12-29 13:21:05 +05:00
										 |  |  | // Copyright (c) 2016-2020, Stanslav N. a.k.a pztrn (or p0z1tr0n) and | 
					
						
							| 
									
										
										
										
											2018-11-10 21:13:06 +05:00
										 |  |  | // URTrator contributors. | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | // | 
					
						
							| 
									
										
										
										
											2018-11-10 21:13:06 +05:00
										 |  |  | // Permission is hereby granted, free of charge, to any person obtaining | 
					
						
							|  |  |  | // a copy of this software and associated documentation files (the | 
					
						
							|  |  |  | // "Software"), to deal in the Software without restriction, including | 
					
						
							|  |  |  | // without limitation the rights to use, copy, modify, merge, publish, | 
					
						
							|  |  |  | // distribute, sublicense, and/or sell copies of the Software, and to | 
					
						
							|  |  |  | // permit persons to whom the Software is furnished to do so, subject | 
					
						
							|  |  |  | // to the following conditions: | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // The above copyright notice and this permission notice shall be | 
					
						
							|  |  |  | // included in all copies or substantial portions of the Software. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, | 
					
						
							|  |  |  | // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | 
					
						
							|  |  |  | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | 
					
						
							|  |  |  | // IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY | 
					
						
							|  |  |  | // CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, | 
					
						
							|  |  |  | // TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE | 
					
						
							|  |  |  | // OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | package requester | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	// stdlib | 
					
						
							|  |  |  | 	"errors" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"net" | 
					
						
							|  |  |  | 	"runtime" | 
					
						
							|  |  |  | 	"strconv" | 
					
						
							|  |  |  | 	"strings" | 
					
						
							|  |  |  | 	"sync" | 
					
						
							|  |  |  | 	"time" | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	// local | 
					
						
							| 
									
										
										
										
											2019-12-29 13:21:05 +05:00
										 |  |  | 	"go.dev.pztrn.name/urtrator/datamodels" | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | type Pooler struct { | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	// Maximum number of simultaneous requests running. | 
					
						
							|  |  |  | 	maxrequests int | 
					
						
							|  |  |  | 	// Packet prefix. | 
					
						
							|  |  |  | 	pp string | 
					
						
							|  |  |  | 	// Current requests counter mutex. | 
					
						
							|  |  |  | 	cur_requests_mutex sync.Mutex | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Pooler) Initialize() { | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	fmt.Println("Initializing requester goroutine pooler...") | 
					
						
							|  |  |  | 	// ToDo: figure out how to make this work nice. | 
					
						
							|  |  |  | 	p.maxrequests = 150 | 
					
						
							|  |  |  | 	_ = runtime.GOMAXPROCS(runtime.NumCPU() * 4) | 
					
						
							|  |  |  | 	p.pp = "\377\377\377\377" | 
					
						
							|  |  |  | 	fmt.Println("Pooler initialized") | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-07 14:28:44 +05:00
										 |  |  | func (p *Pooler) PingOneServer(server_address string) { | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	var wait sync.WaitGroup | 
					
						
							| 
									
										
										
										
											2016-10-07 14:28:44 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	Cache.ServersMutex.Lock() | 
					
						
							|  |  |  | 	server := Cache.Servers[server_address].Server | 
					
						
							|  |  |  | 	Cache.ServersMutex.Unlock() | 
					
						
							| 
									
										
										
										
											2016-10-07 14:28:44 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	wait.Add(1) | 
					
						
							|  |  |  | 	go func(srv *datamodels.Server) { | 
					
						
							|  |  |  | 		defer wait.Done() | 
					
						
							|  |  |  | 		p.pingServersExecutor(srv) | 
					
						
							|  |  |  | 	}(server) | 
					
						
							|  |  |  | 	wait.Wait() | 
					
						
							| 
									
										
										
										
											2016-10-07 14:28:44 +05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | // Servers pinging pooler. Should be started as goroutine to prevent | 
					
						
							|  |  |  | // UI blocking. | 
					
						
							|  |  |  | func (p *Pooler) PingServers(servers_type string) { | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	fmt.Println("About to ping " + servers_type + " servers...") | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	cur_requests := 0 | 
					
						
							|  |  |  | 	var wait sync.WaitGroup | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	Cache.ServersMutex.Lock() | 
					
						
							|  |  |  | 	for _, server_to_ping := range Cache.Servers { | 
					
						
							|  |  |  | 		if servers_type == "favorites" && server_to_ping.Server.Favorite != "1" { | 
					
						
							|  |  |  | 			continue | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		for { | 
					
						
							|  |  |  | 			p.cur_requests_mutex.Lock() | 
					
						
							|  |  |  | 			if cur_requests == p.maxrequests { | 
					
						
							|  |  |  | 				p.cur_requests_mutex.Unlock() | 
					
						
							|  |  |  | 				time.Sleep(time.Second * 1) | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				p.cur_requests_mutex.Unlock() | 
					
						
							|  |  |  | 				break | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		wait.Add(1) | 
					
						
							|  |  |  | 		p.cur_requests_mutex.Lock() | 
					
						
							|  |  |  | 		cur_requests += 1 | 
					
						
							|  |  |  | 		p.cur_requests_mutex.Unlock() | 
					
						
							|  |  |  | 		go func(srv *datamodels.Server) { | 
					
						
							|  |  |  | 			defer wait.Done() | 
					
						
							|  |  |  | 			p.pingServersExecutor(srv) | 
					
						
							|  |  |  | 			p.cur_requests_mutex.Lock() | 
					
						
							|  |  |  | 			cur_requests -= 1 | 
					
						
							|  |  |  | 			p.cur_requests_mutex.Unlock() | 
					
						
							|  |  |  | 		}(server_to_ping.Server) | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	wait.Wait() | 
					
						
							|  |  |  | 	Cache.ServersMutex.Unlock() | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (p *Pooler) pingServersExecutor(server *datamodels.Server) error { | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	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) | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	msg_ping := []byte(p.pp + "getinfo") | 
					
						
							|  |  |  | 	conn_ping.Write(msg_ping) | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	// 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() | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	delta := strconv.Itoa(int(time.Since(start_p).Nanoseconds()) / 1000000) | 
					
						
							|  |  |  | 	server.Ping = delta | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-07 14:28:44 +05:00
										 |  |  | func (p *Pooler) UpdateOneServer(server_address string) { | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	var wait sync.WaitGroup | 
					
						
							| 
									
										
										
										
											2016-10-07 14:28:44 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	Cache.ServersMutex.Lock() | 
					
						
							|  |  |  | 	server := Cache.Servers[server_address].Server | 
					
						
							|  |  |  | 	Cache.ServersMutex.Unlock() | 
					
						
							| 
									
										
										
										
											2016-10-07 14:28:44 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	wait.Add(1) | 
					
						
							|  |  |  | 	go func(server *datamodels.Server) { | 
					
						
							|  |  |  | 		defer wait.Done() | 
					
						
							|  |  |  | 		p.UpdateSpecificServer(server) | 
					
						
							|  |  |  | 	}(server) | 
					
						
							|  |  |  | 	wait.Wait() | 
					
						
							|  |  |  | 	p.PingOneServer(server_address) | 
					
						
							|  |  |  | 	Eventer.LaunchEvent("flushServers", map[string]string{}) | 
					
						
							| 
									
										
										
										
											2016-10-07 14:28:44 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	Eventer.LaunchEvent("loadAllServers", map[string]string{}) | 
					
						
							|  |  |  | 	Eventer.LaunchEvent("loadFavoriteServers", map[string]string{}) | 
					
						
							|  |  |  | 	Eventer.LaunchEvent("serversUpdateCompleted", map[string]string{}) | 
					
						
							| 
									
										
										
										
											2016-10-07 14:28:44 +05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | func (p *Pooler) UpdateServers(servers_type string) { | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	var wait sync.WaitGroup | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	Cache.ServersMutex.Lock() | 
					
						
							|  |  |  | 	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() | 
					
						
							|  |  |  | 	Cache.ServersMutex.Unlock() | 
					
						
							|  |  |  | 	p.PingServers(servers_type) | 
					
						
							|  |  |  | 	Eventer.LaunchEvent("flushServers", map[string]string{}) | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	Eventer.LaunchEvent("loadAllServers", map[string]string{}) | 
					
						
							|  |  |  | 	Eventer.LaunchEvent("loadFavoriteServers", map[string]string{}) | 
					
						
							|  |  |  | 	Eventer.LaunchEvent("serversUpdateCompleted", map[string]string{}) | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Updates information about specific server. | 
					
						
							| 
									
										
										
										
											2016-10-08 00:29:46 +05:00
										 |  |  | func (p *Pooler) UpdateSpecificServer(server *datamodels.Server) error { | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	server_addr := server.Ip + ":" + server.Port | 
					
						
							|  |  |  | 	fmt.Println("Updating server: " + server_addr) | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	// 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 + "!") | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	// 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) | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	msg := []byte(p.pp + "getstatus") | 
					
						
							|  |  |  | 	conn.Write(msg) | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	// 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() | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	// 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 srv_config[i] == "g_needpass" { | 
					
						
							|  |  |  | 				if srv_config[i+1] == "0" { | 
					
						
							|  |  |  | 					server.IsPrivate = "0" | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					server.IsPrivate = "1" | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			server.ExtendedConfig = received_lines[1] | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if len(received_lines) >= 2 { | 
					
						
							|  |  |  | 			// Here we go, players information. | 
					
						
							|  |  |  | 			players := received_lines[2:] | 
					
						
							|  |  |  | 			var real_players int = 0 | 
					
						
							|  |  |  | 			var bots int = 0 | 
					
						
							|  |  |  | 			// Calculate players! | 
					
						
							|  |  |  | 			if len(players) == 1 && len(players[0]) > 255 { | 
					
						
							|  |  |  | 				server.Players = "0" | 
					
						
							|  |  |  | 				server.Bots = "0" | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				// Looks like we have last element to be empty, due to | 
					
						
							|  |  |  | 				// strings.Split() call before. | 
					
						
							|  |  |  | 				for i := range players { | 
					
						
							|  |  |  | 					// Get slice with data for bots-humans parsing. | 
					
						
							|  |  |  | 					player_data := strings.Split(string(players[i]), " ") | 
					
						
							|  |  |  | 					// If slice length isn't equal 3 - this is not what | 
					
						
							|  |  |  | 					// we want. | 
					
						
							|  |  |  | 					if len(player_data) != 3 { | 
					
						
							|  |  |  | 						continue | 
					
						
							|  |  |  | 					} | 
					
						
							| 
									
										
										
										
											2016-11-26 07:38:26 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 					if player_data[1] == "0" { | 
					
						
							|  |  |  | 						bots++ | 
					
						
							|  |  |  | 					} else { | 
					
						
							|  |  |  | 						real_players++ | 
					
						
							|  |  |  | 					} | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 				//server.Players = strconv.Itoa(len(players) - 1) | 
					
						
							|  |  |  | 				server.Players = strconv.Itoa(real_players) | 
					
						
							|  |  |  | 				server.Bots = strconv.Itoa(bots) | 
					
						
							|  |  |  | 				fmt.Println(server.Players, server.Bots) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			server.PlayersInfo = strings.Join(received_lines[2:], "\\") | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-11-10 13:21:53 +05:00
										 |  |  | 	// ToDo: Calculate ping. 0 for now. | 
					
						
							|  |  |  | 	server.Ping = "0" | 
					
						
							|  |  |  | 	return nil | 
					
						
							| 
									
										
										
										
											2016-10-06 13:55:03 +05:00
										 |  |  | } |