gottdad/ttd.go
2021-11-08 14:19:52 +08:00

729 lines
20 KiB
Go

package main
import (
"bufio"
"encoding/binary"
"fmt"
"net"
"os"
"regexp"
"strconv"
"time"
)
type ClientTTD struct {
ClientID uint32
Name string
Address string
CompanyID uint8
Paused bool
LastSeen time.Time
}
type CompanyTTD struct {
CompanyID uint8
Name string
Protected bool
ClientID uint32
FirstSeen time.Time
LastSeen time.Time
}
type ServerTTD struct {
Config *ServerConfig
Status *ServerStatusTTD
Data *ServerDataTTD
}
type ServerStatusTTD struct {
Connected bool
Paused bool
UpdateCompanies time.Time
UpdateClients time.Time
UpdateDate time.Time
Initialized bool
GameDate time.Time
Clients map[uint32]*ClientTTD
Companies map[uint8]*CompanyTTD
}
type ServerDataTTD struct {
LastClientCompute time.Time
Conn net.Conn
Stop chan struct{}
}
var updateHeartBeat = 5 * time.Second
func (s *ServerTTD) Connect() (err error) {
t := time.Now()
s.Status.Connected = false
s.Data.Conn, err = net.Dial("tcp", s.Config.Addr)
logErrorDebug(err, "Server.Connect() : net.Dial")
if err != nil {
return err
}
logInfoDebug("Server.Connect() : Connected to " + s.Config.Addr)
s.Data.LastClientCompute = time.Now()
s.Status.Connected = true
s.Status.Initialized = false
s.Status.UpdateDate = t
s.Status.UpdateClients = t
s.Status.UpdateCompanies = t
s.Status.Clients = make(map[uint32]*ClientTTD)
s.Status.Companies = make(map[uint8]*CompanyTTD)
return err
}
func (s *ServerTTD) Send(b []byte) (err error) {
if !s.Status.Connected {
return fmt.Errorf("not connected")
}
_, err = s.Data.Conn.Write(b)
return err
}
func (s *ServerTTD) Start() {
for {
err := s.Connect()
if err != nil {
time.Sleep(5 * time.Second)
}
break
}
//send auth
p := PacketAdminJoin{
Packet: Packet{PType: AdminPacketAdminJoin},
Password: s.Config.Passwd,
AppName: "gottdad",
AppVersion: version,
}
err := s.Send(p.Bytes())
failError(err, "Server.Start() : Cannot send authentication packet")
stopPoll := make(chan struct{})
go s.Poll(stopPoll)
stopHeartBeat := make(chan struct{})
go s.HeartBeat(stopHeartBeat)
for {
select {
// call to stop polling
case <-s.Data.Stop:
close(stopPoll)
close(stopHeartBeat)
return
}
}
}
// Stop gracefully shuts the poller down.
func (s *ServerTTD) Stop() {
s.Data.Stop <- struct{}{}
}
func (s *ServerTTD) HeartBeat(stop chan struct{}) {
for {
select {
case <-stop:
return
default:
}
s.UpdateDate()
s.UpdateClients()
s.UpdateCompanies()
s.PruneClients()
s.PruneCompanies()
s.ComputeClientTime()
if !s.Status.Paused && s.NeedPause() {
s.Pause()
} else if s.Status.Paused && !s.NeedPause() {
s.Unpause()
}
cfg.Save(*configFlag)
time.Sleep(updateHeartBeat)
}
}
func (s *ServerTTD) Poll(stop chan struct{}) {
var err error
reader := bufio.NewReader(s.Data.Conn)
buffer := make([]byte, 0xFFFF)
var n, read int
for {
select {
case <-stop:
return
default:
}
p := Packet{}
for {
if read >= 3 {
//logInfoDebug("Server.Poll() : packet read")
break
}
n, err = reader.Read(buffer[read:])
logErrorDebug(err, "Server.Poll() : reader.Read")
read += n
//logInfoDebug("Server.Poll() : waiting for packet, read %d bytes.", read)
}
p.PLength = binary.LittleEndian.Uint16(buffer[0:])
p.PType = buffer[2]
if p.PLength <= 3 {
logInfoAlert("Server.Poll() : wrong packet length")
break
}
//logInfoDebug("Server.Poll() : waiting for packet data : len : %d / type : %d", p.PLength, p.PType)
for {
if read >= int(p.PLength) {
//logInfoDebug("Server.Poll() : data read")
break
}
n, err = reader.Read(buffer[read:])
logErrorDebug(err, "Server.Poll() : reader.Read")
read += n
//logInfoDebug("Server.Poll() : waiting for data, read %d/%d bytes.", read, p.PLength)
}
switch p.PType {
case AdminPacketServerProtocol:
sp := PacketServerProtocol{
Packet: p,
}
sp.Read(buffer[:p.PLength])
//logInfoDebug("Server.Poll() : AdminPacketServerProtocol :\n- ProtocolVersion: %v\n- FurtherData: %v\n- UpdatePacketType: %v\n- FrequenciesAllowed: %b", sp.ProtocolVersion, sp.FurtherData, sp.UpdatePacketType, sp.FrequenciesAllowed)
case AdminPacketServerWelcome:
sp := PacketServerWelcome{
Packet: p,
}
sp.Read(buffer[:p.PLength])
//logInfoDebug("Server.Poll() : AdminPacketServerWelcome :\n- ServerName: %v\n- OpenTTDVersion: %v\n- Dedicated: %v\n- MapSeed: %x\n- MapLandscape: %v\n- MapStartDate: %v\n- Size: %v x %v", sp.ServerName, sp.OpenTTDVersion, sp.Dedicated, sp.MapSeed, sp.MapLandscape, sp.MapStartDate, sp.MapX, sp.MapY)
s.Initialize()
case AdminPacketServerDate:
sp := PacketServerDate{
Packet: p,
}
sp.Read(buffer[:p.PLength])
//logInfoDebug("Server.Poll() : AdminPacketServerDate :\n- Date: %d\n- RealDate : %v", sp.Date, toDate(sp.Date))
gameDate := toDate(sp.Date)
if gameDate.Day() == 1 && gameDate.Month() == 1 && gameDate != s.Status.GameDate {
bot.SendChat(cfg.Telegram.ChatID, fmt.Sprintf("Year %d.", gameDate.Year()))
}
s.Status.GameDate = gameDate
s.Status.UpdateDate = time.Now()
case AdminPacketServerClientJoin:
sp := PacketServerClientJoin{
Packet: p,
}
sp.Read(buffer[:p.PLength])
logInfoDebug("Server.Poll() : AdminPacketServerClientJoin :\n- ClientID: %d", sp.ClientID)
case AdminPacketServerClientInfo:
sp := PacketServerClientInfo{
Packet: p,
}
sp.Read(buffer[:p.PLength])
//logInfoDebug("Server.Poll() : AdminPacketServerClientInfo :\n- ClientID: %d\n- Address: %s\n- Name: %s\n- Lang: %d\n- Date: %d\n- CompanyID: %d", sp.ClientID, sp.Address, sp.Name, sp.Lang, sp.Date, sp.CompanyID)
clt := &ClientTTD{
ClientID: sp.ClientID,
CompanyID: 255,
}
if _, ok := s.Status.Clients[sp.ClientID]; ok {
clt = s.Status.Clients[sp.ClientID]
} else {
s.Status.Clients[sp.ClientID] = clt
}
clt.Address = sp.Address
clt.Name = sp.Name
clt.CompanyID = sp.CompanyID
clt.LastSeen = time.Now()
if clt.CompanyID != 255 {
if co, ok := s.Status.Companies[clt.CompanyID]; ok {
if co.ClientID == 0 {
co.ClientID = clt.ClientID
}
} else {
//FIXME company doesn't exist ?
}
if cfg.CompanyIsRegistered(clt.CompanyID) {
cc := cfg.GetCompanyClient(clt.CompanyID)
if !cc.Online {
cc.Online = true
bot.SendChat(cfg.Telegram.ChatID, fmt.Sprintf("@%s now playing.", cc.Username))
}
}
}
case AdminPacketServerClientUpdate:
sp := PacketServerClientUpdate{
Packet: p,
}
sp.Read(buffer[:p.PLength])
logInfoDebug("Server.Poll() : AdminPacketServerClientUpdate :\n- ClientID: %d\n- Name: %s\n- CompanyID: %d", sp.ClientID, sp.Name, sp.CompanyID)
clt := s.Status.Clients[sp.ClientID]
clt.Name = sp.Name
if sp.CompanyID != 255 {
if cfg.CompanyIsRegistered(sp.CompanyID) {
cc := cfg.GetCompanyClient(sp.CompanyID)
cc.Online = true
bot.SendChat(cfg.Telegram.ChatID, fmt.Sprintf("@%s playing.", cc.Username))
} else {
if co, ok := s.Status.Companies[sp.CompanyID]; ok {
if co.ClientID == 0 {
bot.SendChat(cfg.Telegram.ChatID, fmt.Sprintf("Company '%s' unclaimed. Please /register to claim the company", s.Status.Companies[sp.CompanyID].Name))
}
}
}
} else if clt.CompanyID != 255 && cfg.CompanyIsRegistered(clt.CompanyID) {
cc := cfg.GetCompanyClient(clt.CompanyID)
bot.SendChat(cfg.Telegram.ChatID, fmt.Sprintf("@%s taking a break.", cc.Username))
cc.Online = false
}
clt.LastSeen = time.Now()
clt.CompanyID = sp.CompanyID
s.Unpause()
s.Pause()
case AdminPacketServerClientError:
sp := PacketServerClientError{
Packet: p,
}
sp.Read(buffer[:p.PLength])
logInfoDebug("Server.Poll() : AdminPacketServerClientError :\n- ClientID: %d\n- ErrorID: %d", sp.ClientID, sp.ErrorID)
case AdminPacketServerClientQuit:
sp := PacketServerClientQuit{
Packet: p,
}
sp.Read(buffer[:p.PLength])
logInfoDebug("Server.Poll() : AdminPacketServerClientQuit :\n- ClientID: %d", sp.ClientID)
if cfg.CompanyIsRegistered(s.Status.Clients[sp.ClientID].CompanyID) {
cc := cfg.GetCompanyClient(s.Status.Clients[sp.ClientID].CompanyID)
bot.SendChat(cfg.Telegram.ChatID, fmt.Sprintf("@%s leaving.", cc.Username))
cc.Online = false
} else {
bot.SendChat(cfg.Telegram.ChatID, fmt.Sprintf("%s leaving.", s.Status.Clients[sp.ClientID].Name))
}
delete(s.Status.Clients, sp.ClientID)
case AdminPacketServerCompanyNew:
sp := PacketServerCompanyNew{
Packet: p,
}
sp.Read(buffer[:p.PLength])
logInfoDebug("Server.Poll() : PacketServerCompanyNew :\n- CompanyID: %d", sp.CompanyID)
c := &CompanyTTD{
CompanyID: sp.CompanyID,
FirstSeen: time.Now(),
ClientID: 0,
}
s.Status.Companies[sp.CompanyID] = c
case AdminPacketServerCompanyInfo:
sp := PacketServerCompanyInfo{
Packet: p,
}
sp.Read(buffer[:p.PLength])
//logInfoDebug("Server.Poll() : PacketServerCompanyInfo :\n- CompanyID: %d\n- Name: %s\n- President: %s\n- Protected: %t\n- Inauguration: %d\n- AI: %t", sp.CompanyID, sp.Name, sp.President, sp.Protected, sp.Inauguration, sp.AI)
c := &CompanyTTD{
CompanyID: sp.CompanyID,
FirstSeen: time.Now(),
ClientID: 0,
}
if _, ok := s.Status.Companies[sp.CompanyID]; !ok {
s.Status.Companies[sp.CompanyID] = c
} else {
c = s.Status.Companies[sp.CompanyID]
}
c.Name = sp.Name
c.LastSeen = time.Now()
c.Protected = sp.Protected
case AdminPacketServerCompanyUpdate:
sp := PacketServerCompanyUpdate{
Packet: p,
}
sp.Read(buffer[:p.PLength])
logInfoDebug("Server.Poll() : PacketServerCompanyUpdate :\n- CompanyID: %d\n- Name: %s\n- President: %s\n- Protected: %t", sp.CompanyID, sp.Name, sp.President, sp.Protected)
c := s.Status.Companies[sp.CompanyID]
c.Name = sp.Name
c.FirstSeen = time.Now()
c.LastSeen = time.Now()
c.Protected = sp.Protected
case AdminPacketServerCompanyRemove:
sp := PacketServerCompanyRemove{
Packet: p,
}
sp.Read(buffer[:p.PLength])
logInfoDebug("Server.Poll() : PacketServerCompanyRemove :\n- CompanyID: %d\n- Reason: %d", sp.CompanyID, sp.Reason)
bot.SendChat(cfg.Telegram.ChatID, fmt.Sprintf("Company #%d deleted (%s)", sp.CompanyID, s.Status.Companies[sp.CompanyID].Name))
delete(s.Status.Companies, sp.CompanyID)
case AdminPacketServerChat:
sp := PacketServerChat{
Packet: p,
}
sp.Read(buffer[:p.PLength])
if sp.Message == "!unpause" {
logInfoDebug("Server.Poll() : AdminPacketServerChat : Unpausing")
clt := s.Status.Clients[sp.ClientID]
clt.Paused = false
s.Unpause()
} else if sp.Message == "!pause" {
logInfoDebug("Server.Poll() : AdminPacketServerChat : Pausing")
clt := s.Status.Clients[sp.ClientID]
clt.Paused = true
s.Pause()
} else {
logInfoDebug("Server.Poll() : AdminPacketServerChat :\n- ActionID: %d\n- DestinationID: %d\n- ClientID: %d\n- Message: %s\n- Amount: %d", sp.ActionID, sp.DestinationID, sp.ClientID, sp.Message, sp.Amount)
}
case AdminPacketServerConsole:
sp := PacketServerConsole{
Packet: p,
}
sp.Read(buffer[:p.PLength])
udp_queried_from, err := regexp.MatchString("\\[udp\\] queried from .*", sp.Text)
logErrorDebug(err, "Server.Poll() : queried from")
if udp_queried_from && sp.Origin == "net" {
break
}
joined_company, err := regexp.MatchString("\\*\\*\\* .* has joined company #[0-9]+", sp.Text)
logErrorDebug(err, "Server.Poll() : joined company")
if joined_company && sp.Origin == "console" {
r := regexp.MustCompile("\\*\\*\\* (?P<Client>.*) has joined company #(?P<CompanyID>[0-9]+)")
clientName := r.ReplaceAllString(sp.Text, "${Client}")
ID8, _ := strconv.ParseInt(r.ReplaceAllString(sp.Text, "${CompanyID}"), 10, 8)
companyID := uint8(ID8) - 1
for _, clt := range s.Status.Clients {
if clt.Name == clientName {
clt.CompanyID = companyID
}
}
break
}
joined_spectators, err := regexp.MatchString("\\*\\*\\* .* has joined spectators", sp.Text)
if joined_spectators && sp.Origin == "console" {
r := regexp.MustCompile("\\*\\*\\* (?P<Client>.*) has joined spectators")
clientName := r.ReplaceAllString(sp.Text, "${Client}")
for _, clt := range s.Status.Clients {
if clt.Name == clientName {
clt.CompanyID = 255
}
}
break
}
command, err := regexp.MatchString("\\[admin\\] Rcon command from 'gottdad' \\(.*\\): .*", sp.Text)
if command && sp.Origin == "net" {
break
}
logInfoDebug("Server.Poll() : AdminPacketServerConsole :\n- Origin: %q\n- Text: %s", sp.Origin, sp.Text)
case AdminPacketServerRCon:
sp := PacketServerRCon{
Packet: p,
}
sp.Read(buffer[:p.PLength])
logInfoDebug("Server.Poll() : AdminPacketServerRCon :\n- ColorID: %d\n- Output: %s", sp.ColorID, sp.Output)
case AdminPacketServerRConEnd:
sp := PacketServerRConEnd{
Packet: p,
}
sp.Read(buffer[:p.PLength])
switch sp.Command {
case "clients":
for k, v := range s.Status.Clients {
logInfoDebug("Server.Poll() : Client[%d] : %s - %d (%s)", k, v.Name, v.CompanyID, v.Address)
}
case "companies":
for k, v := range s.Status.Companies {
logInfoDebug("Server.Poll() : Company[%d] : %s", k, v.Name)
}
case "pause":
default:
logInfoDebug("Server.Poll() : AdminPacketServerRConEnd :\n- Command: %s", sp.Command)
}
default:
logInfoDebug("Server.Poll() : Packet fully read : len : %d / type : %d", p.PLength, p.PType)
os.Exit(0)
}
c := make([]byte, 0xFFFF)
copy(c, buffer[p.PLength:])
buffer = c
read -= int(p.PLength)
}
}
func (s *ServerTTD) UpdateDate() {
//logInfoDebug("Server.UpdateDate")
px := PacketAdminPoll{
Packet: Packet{PType: AdminPacketAdminPoll},
UpdateType: AdminUpdateDate,
UpdateID: 0,
}
err := s.Send(px.Bytes())
logErrorDebug(err, "Server.UpdateDate() : Send(PacketAdminPoll) : AdminUpdateDate")
s.Status.UpdateDate = time.Now()
}
func (s *ServerTTD) UpdateClients() {
//logInfoDebug("Server.UpdateClients")
px := PacketAdminPoll{
Packet: Packet{PType: AdminPacketAdminPoll},
UpdateType: AdminUpdateClientInfo,
UpdateID: uint32(4294967295),
}
err := s.Send(px.Bytes())
logErrorDebug(err, "Server.UpdateClients() : Send(PacketAdminPoll) : AdminUpdateClientInfo")
s.Status.UpdateClients = time.Now()
}
func (s *ServerTTD) UpdateCompanies() {
//logInfoDebug("Server.UpdateCompanies")
px := PacketAdminPoll{
Packet: Packet{PType: AdminPacketAdminPoll},
UpdateType: AdminUpdateCompanyInfo,
UpdateID: uint32(4294967295),
}
err := s.Send(px.Bytes())
logErrorDebug(err, "Server.UpdateCompanies() : Send(UpdateCompanies) : AdminUpdateClientInfo")
s.Status.UpdateCompanies = time.Now()
}
func (s *ServerTTD) PruneClients() {
for cltID, clt := range s.Status.Clients {
if clt.LastSeen.Add(2 * updateHeartBeat).Before(time.Now()) {
logInfoDebug("ServerTTD.PruneClients : deleting client #%d", cltID)
delete(s.Status.Clients, cltID)
}
}
}
func (s *ServerTTD) PruneCompanies() {
for coID, co := range s.Status.Companies {
if co.LastSeen.Add(2 * updateHeartBeat).Before(time.Now()) {
logInfoDebug("ServerTTD.PruneCompanies : deleting company #%d", coID)
delete(s.Status.Companies, coID)
}
}
}
func (s *ServerTTD) ComputeClientTime() {
t := time.Now()
daysNow := int(t.Sub(cfg.Game.StartDate).Hours() / 24)
daysLast := int(s.Data.LastClientCompute.Sub(cfg.Game.StartDate).Hours() / 24)
if daysLast != daysNow {
bot.SendChat(cfg.Telegram.ChatID, "New daily allotment available.")
logInfoDebug("Server.ComputeClientTime : newDay : now : %d vs %d : last", daysNow, daysLast)
for _, cc := range cfg.Clients {
cc.TimeLeft = cc.TimeLeft + cfg.Game.DailyAllotment
}
}
if !s.Status.Paused {
diff := t.Sub(s.Data.LastClientCompute)
for _, cc := range cfg.Clients {
if cc.Online {
if cc.TimeLeft > 0 && cc.TimeLeft < diff {
bot.SendChat(cfg.Telegram.ChatID, fmt.Sprintf("@%s got not time left. Grace period of %s.", cc.Username, cfg.Game.Threshold))
}
if (cc.TimeLeft+cfg.Game.Threshold) > 0 && (cc.TimeLeft+cfg.Game.Threshold) < diff {
bot.SendChat(cfg.Telegram.ChatID, fmt.Sprintf("@%s got not time left. Grace period over.", cc.Username))
}
cc.TimeLeft = cc.TimeLeft - diff
}
}
}
s.Data.LastClientCompute = t
return
}
func (s *ServerTTD) DeleteCompany(id uint8) {
if _, ok := s.Status.Companies[id]; ok {
logInfoDebug("Server.DeleteCompany : deleting #%d", id)
px := PacketAdminRCon{
Packet: Packet{PType: AdminPacketAdminRCon},
Command: fmt.Sprintf("reset_company %d", id),
}
s.Send(px.Bytes())
px = PacketAdminRCon{
Packet: Packet{PType: AdminPacketAdminRCon},
Command: "companies",
}
s.Send(px.Bytes())
} else {
logInfoDebug("Server.DeleteCompany : cannot find company #%d", id)
px := PacketAdminRCon{
Packet: Packet{PType: AdminPacketAdminRCon},
Command: "companies",
}
s.Send(px.Bytes())
}
}
func (s *ServerTTD) Pause() {
if s.Status.Paused {
return
}
if !s.NeedPause() {
return
}
px := PacketAdminRCon{
Packet: Packet{PType: AdminPacketAdminRCon},
Command: "pause",
}
err := s.Send(px.Bytes())
s.ComputeClientTime()
bot.SendChat(cfg.Telegram.ChatID, "Game is paused.")
s.Status.Paused = true
logErrorDebug(err, "Server.Pause : Send()")
logInfoDebug("Server.Pause : pausing")
}
func (s *ServerTTD) Unpause() {
if !s.Status.Paused {
return
}
if s.NeedPause() {
return
}
px := PacketAdminRCon{
Packet: Packet{PType: AdminPacketAdminRCon},
Command: "unpause",
}
err := s.Send(px.Bytes())
s.ComputeClientTime()
bot.SendChat(cfg.Telegram.ChatID, "Game is unpaused.")
s.Status.Paused = false
logErrorDebug(err, "Server.Unpause : Send()")
logInfoDebug("Server.Unpause : unpausing")
}
func (s *ServerTTD) NeedPause() bool {
if !s.Status.Initialized {
logInfoDebug("Server.NeedPause : not initialized yet")
return false
}
reason := s.NeedPauseReason()
if reason == "" {
return false
} else {
//logInfoDebug("Server.NeedPause : %s", reason)
return true
}
}
func (s *ServerTTD) NeedPauseReason() string {
if !s.Status.Initialized {
return ""
}
dupl := make(map[uint8]struct{})
online := 0
for cltID, clt := range s.Status.Clients {
if cltID != 1 && clt.CompanyID != 255 {
online++
if _, ok := s.Status.Companies[clt.CompanyID]; !ok {
return "company unidentified"
}
}
if clt.Paused {
return "user paused"
}
if _, ok := dupl[clt.CompanyID]; ok && clt.CompanyID != 255 {
return "more than one user in company"
}
dupl[clt.CompanyID] = struct{}{}
}
for coID, co := range s.Status.Companies {
if !co.Protected {
return "company unprotected"
}
if !cfg.CompanyIsRegistered(coID) {
return "company unregistered"
}
}
for _, cc := range cfg.Clients {
if cc.Online && (cc.TimeLeft+cfg.Game.Threshold) < 0 {
return "no time left"
}
}
if online == 0 {
return "no players online"
}
return ""
}
func (s *ServerTTD) Initialize() {
var err error
if s.Status.Initialized {
return
}
px := PacketAdminUpdateFrequency{
Packet: Packet{PType: AdminPacketAdminUpdateFrequency},
UpdateType: AdminUpdateDate,
UpdateFrequency: AdminFrequencyDaily,
}
err = s.Send(px.Bytes())
logErrorDebug(err, "Server.Initialize() : Send(AdminUpdateDate)")
px = PacketAdminUpdateFrequency{
Packet: Packet{PType: AdminPacketAdminUpdateFrequency},
UpdateType: AdminUpdateClientInfo,
UpdateFrequency: AdminFrequencyAutomatic,
}
err = s.Send(px.Bytes())
logErrorDebug(err, "Server.Initialize() : Send(AdminUpdateClientInfo)")
px = PacketAdminUpdateFrequency{
Packet: Packet{PType: AdminPacketAdminUpdateFrequency},
UpdateType: AdminUpdateCompanyInfo,
UpdateFrequency: AdminFrequencyAutomatic,
}
err = s.Send(px.Bytes())
logErrorDebug(err, "Server.Initialize() : Send(AdminUpdateCompanyInfo)")
px = PacketAdminUpdateFrequency{
Packet: Packet{PType: AdminPacketAdminUpdateFrequency},
UpdateType: AdminUpdateChat,
UpdateFrequency: AdminFrequencyAutomatic,
}
err = s.Send(px.Bytes())
logErrorDebug(err, "Server.Initialize() : Send(AdminUpdateChat)")
px = PacketAdminUpdateFrequency{
Packet: Packet{PType: AdminPacketAdminUpdateFrequency},
UpdateType: AdminUpdateConsole,
UpdateFrequency: AdminFrequencyAutomatic,
}
err = s.Send(px.Bytes())
logErrorDebug(err, "Server.Initialize() : Send(AdminUpdateConsole)")
s.Status.Initialized = true
s.UpdateCompanies()
s.UpdateClients()
}