big revamp
This commit is contained in:
parent
90e584fcff
commit
5fad9f085e
227
bot.go
227
bot.go
@ -2,60 +2,212 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
tb "gopkg.in/tucnak/telebot.v2"
|
tb "gopkg.in/tucnak/telebot.v2"
|
||||||
)
|
)
|
||||||
|
|
||||||
func BotHandlers(b *tb.Bot) {
|
type Bot struct {
|
||||||
|
bot *tb.Bot
|
||||||
|
Config *TelegramConfig
|
||||||
|
}
|
||||||
|
|
||||||
b.Handle("/pause", botPause)
|
func (b *Bot) Start() {
|
||||||
b.Handle("/unpause", botUnpause)
|
var err error
|
||||||
|
b.bot, err = tb.NewBot(tb.Settings{
|
||||||
|
Token: b.Config.Token,
|
||||||
|
URL: b.Config.URL,
|
||||||
|
Poller: &tb.LongPoller{Timeout: 10 * time.Second},
|
||||||
|
})
|
||||||
|
failError(err, "Bot.Start() : registering bot")
|
||||||
|
|
||||||
b.Handle(tb.OnPhoto, botPhoto)
|
b.BotHandlers()
|
||||||
b.Handle(tb.OnChannelPost, botChannelPost)
|
}
|
||||||
b.Handle(tb.OnQuery, botQuery)
|
|
||||||
b.Handle(tb.OnText, botText)
|
|
||||||
b.Handle(tb.OnDocument, botDocument)
|
|
||||||
|
|
||||||
b.Start()
|
func (b *Bot) SendUser(id int64, msg string) {
|
||||||
|
u := tb.User{
|
||||||
|
ID: int(id),
|
||||||
|
}
|
||||||
|
_, err := b.bot.Send(&u, msg)
|
||||||
|
logErrorDebug(err, "Bot.SendUser()")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) SendChat(chatID int64, text string) {
|
||||||
|
opt := tb.SendOptions{
|
||||||
|
ParseMode: tb.ModeDefault,
|
||||||
|
}
|
||||||
|
|
||||||
|
ch := tb.Chat{
|
||||||
|
ID: chatID,
|
||||||
|
}
|
||||||
|
_, err := b.bot.Send(&ch, text, &opt)
|
||||||
|
logErrorDebug(err, "Bot.SendChat()")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b *Bot) BotHandlers() {
|
||||||
|
|
||||||
|
b.bot.Handle("/pause", botPause)
|
||||||
|
b.bot.Handle("/unpause", botUnpause)
|
||||||
|
b.bot.Handle("/register", botRegister)
|
||||||
|
b.bot.Handle("/delete", botDelete)
|
||||||
|
b.bot.Handle("/companies", botCompanies)
|
||||||
|
b.bot.Handle("/clients", botClients)
|
||||||
|
b.bot.Handle("/players", botPlayers)
|
||||||
|
|
||||||
|
b.bot.Handle(tb.OnPhoto, botPhoto)
|
||||||
|
b.bot.Handle(tb.OnChannelPost, botChannelPost)
|
||||||
|
b.bot.Handle(tb.OnQuery, botQuery)
|
||||||
|
b.bot.Handle(tb.OnText, botText)
|
||||||
|
b.bot.Handle(tb.OnDocument, botDocument)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
b.SendUser(b.Config.AdminID, fmt.Sprintf("Started (%s)", version))
|
||||||
|
}()
|
||||||
|
|
||||||
|
b.bot.Start()
|
||||||
}
|
}
|
||||||
|
|
||||||
func botPause(m *tb.Message) {
|
func botPause(m *tb.Message) {
|
||||||
forcePaused = true
|
PrintText(m)
|
||||||
if !paused {
|
for userID, cc := range cfg.Clients {
|
||||||
paused = true
|
if userID == m.Sender.ID {
|
||||||
px := PacketAdminRCon{
|
if co, ok := srv.Status.Companies[cc.CompanyID]; ok {
|
||||||
Packet: Packet{PType: AdminPacketAdminRCon},
|
if clt, ok2 := srv.Status.Clients[co.ClientID]; ok2 {
|
||||||
Command: "pause",
|
clt.Paused = true
|
||||||
}
|
}
|
||||||
_, err := conn.Write(px.Bytes())
|
}
|
||||||
logErrorDebug(err, "botPause : conn.Write")
|
}
|
||||||
sendChat(-436055948, "Game paused.")
|
}
|
||||||
|
if !srv.Status.Paused {
|
||||||
|
srv.Pause()
|
||||||
|
bot.SendChat(bot.Config.ChatID, "Game paused.")
|
||||||
} else {
|
} else {
|
||||||
sendChat(-436055948, "Game already paused.")
|
bot.SendChat(bot.Config.ChatID, "Game already paused.")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func botUnpause(m *tb.Message) {
|
func botUnpause(m *tb.Message) {
|
||||||
forcePaused = false
|
PrintText(m)
|
||||||
if paused && len(clients) > 1 {
|
for userID, cc := range cfg.Clients {
|
||||||
paused = false
|
if userID == m.Sender.ID {
|
||||||
px := PacketAdminRCon{
|
if co, ok := srv.Status.Companies[cc.CompanyID]; ok {
|
||||||
Packet: Packet{PType: AdminPacketAdminRCon},
|
if clt, ok2 := srv.Status.Clients[co.ClientID]; ok2 {
|
||||||
Command: "unpause",
|
clt.Paused = false
|
||||||
}
|
}
|
||||||
_, err := conn.Write(px.Bytes())
|
}
|
||||||
logErrorDebug(err, "botUnpause : conn.Write")
|
}
|
||||||
sendChat(-436055948, "Game unpaused.")
|
}
|
||||||
} else if len(clients) == 1 {
|
if !srv.NeedPause() {
|
||||||
sendChat(-436055948, "No players, cannot unpause.")
|
srv.Unpause()
|
||||||
|
bot.SendChat(bot.Config.ChatID, "Game unpaused.")
|
||||||
} else {
|
} else {
|
||||||
sendChat(-436055948, "Game already unpaused.")
|
bot.SendChat(bot.Config.ChatID, fmt.Sprintf("Cannot unpause : %s", srv.NeedPauseReason()))
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func botDelete(m *tb.Message) {
|
||||||
|
logInfoDebug("[%d] %s(%d) | %s(%d) : delete : %s\n", m.ID, m.Chat.Title, m.Chat.ID, m.Sender.Username, m.Sender.ID, m.Text)
|
||||||
|
if m.Sender.ID == int(bot.Config.AdminID) {
|
||||||
|
r := regexp.MustCompile("/delete (?P<CompanyID>[0-9]+)")
|
||||||
|
ID64, _ := strconv.ParseInt(r.ReplaceAllString(m.Text, "${CompanyID}"), 10, 64)
|
||||||
|
srv.DeleteCompany(uint8(ID64))
|
||||||
|
bot.SendChat(m.Chat.ID, "Deleting")
|
||||||
|
} else {
|
||||||
|
bot.SendChat(m.Chat.ID, "Not authorized to delete")
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func botCompanies(m *tb.Message) {
|
||||||
|
str := "Companies :"
|
||||||
|
for k, v := range srv.Status.Companies {
|
||||||
|
str = str + "\r\n" + fmt.Sprintf(" - %s (%d)", v.Name, k)
|
||||||
|
}
|
||||||
|
bot.SendChat(m.Chat.ID, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func botClients(m *tb.Message) {
|
||||||
|
str := "Clients :"
|
||||||
|
for k, v := range srv.Status.Clients {
|
||||||
|
str = str + "\r\n" + fmt.Sprintf(" - %s (%d) : company #%d", v.Name, k, v.CompanyID)
|
||||||
|
}
|
||||||
|
bot.SendChat(m.Chat.ID, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func botPlayers(m *tb.Message) {
|
||||||
|
online := ""
|
||||||
|
for _, cc := range cfg.Clients {
|
||||||
|
if cc.Online {
|
||||||
|
online = online + fmt.Sprintf(" - @%s (%s) : %s", cc.Username, cc.TimeLeft, srv.Status.Companies[cc.CompanyID].Name) + "\r\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
offline := ""
|
||||||
|
for _, cc := range cfg.Clients {
|
||||||
|
if !cc.Online {
|
||||||
|
offline = offline + fmt.Sprintf(" - @%s (%s) : %s", cc.Username, cc.TimeLeft, srv.Status.Companies[cc.CompanyID].Name) + "\r\n"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
str := ""
|
||||||
|
if len(online) > 0 {
|
||||||
|
str = str + "Players online :\r\n" + online
|
||||||
|
}
|
||||||
|
if len(offline) > 0 {
|
||||||
|
str = str + "Players offline :\r\n" + offline
|
||||||
|
}
|
||||||
|
if str == "" {
|
||||||
|
str = "No players."
|
||||||
|
}
|
||||||
|
bot.SendChat(m.Chat.ID, str)
|
||||||
|
}
|
||||||
|
|
||||||
|
func botRegister(m *tb.Message) {
|
||||||
|
cc, ok := cfg.Clients[m.Sender.ID]
|
||||||
|
if !ok {
|
||||||
|
cc = &ClientConfig{
|
||||||
|
UserID: m.Sender.ID,
|
||||||
|
Username: m.Sender.Username,
|
||||||
|
CompanyID: 255,
|
||||||
|
}
|
||||||
|
cfg.Clients[m.Sender.ID] = cc
|
||||||
|
}
|
||||||
|
coList := make(map[uint8]struct{})
|
||||||
|
for coID, _ := range srv.Status.Companies {
|
||||||
|
coList[coID] = struct{}{}
|
||||||
|
}
|
||||||
|
for _, c := range cfg.Clients {
|
||||||
|
if c.CompanyID != 255 {
|
||||||
|
if _, ok := coList[c.CompanyID]; !ok {
|
||||||
|
logInfoAlert("botRegister : %s : no such CompanyID : %d", c.Username, c.CompanyID)
|
||||||
|
c.CompanyID = 255
|
||||||
|
} else {
|
||||||
|
delete(coList, c.CompanyID)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(coList) == 0 {
|
||||||
|
bot.SendChat(m.Chat.ID, "No company to register")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(coList) == 1 {
|
||||||
|
for id, _ := range coList {
|
||||||
|
cc.CompanyID = id
|
||||||
|
days := int(time.Now().Sub(cfg.Game.StartDate).Hours() / 24)
|
||||||
|
cc.TimeLeft = cfg.Game.StartingAllotment + cfg.Game.DailyAllotment*time.Duration(days)
|
||||||
|
bot.SendChat(m.Chat.ID, fmt.Sprintf("@%s registered %s (with %s playable)", cc.Username, srv.Status.Companies[cc.CompanyID].Name, cc.TimeLeft))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bot.SendChat(m.Chat.ID, "More than one company unregistered. Wait for bot update (poke @tiennou)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
func PrintText(m *tb.Message) {
|
func PrintText(m *tb.Message) {
|
||||||
logInfoDebug("[%d] %s(%d) | %s(%d) : %s\n", m.ID, m.Chat.Title, m.Chat.ID, m.Sender.Username, m.Sender.ID, m.Text)
|
logInfoDebug("[%d] %s(%d) | %s(%d) : %s\n", m.ID, m.Chat.Title, m.Chat.ID, m.Sender.Username, m.Sender.ID, m.Text)
|
||||||
return
|
return
|
||||||
@ -75,7 +227,6 @@ func botChannelPost(m *tb.Message) {
|
|||||||
PrintText(m)
|
PrintText(m)
|
||||||
b, _ := json.Marshal(m)
|
b, _ := json.Marshal(m)
|
||||||
logInfoDebug("botChannelPost : %s\n", string(b))
|
logInfoDebug("botChannelPost : %s\n", string(b))
|
||||||
|
|
||||||
// channel posts only
|
// channel posts only
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,15 +238,3 @@ func botQuery(q *tb.Query) {
|
|||||||
func botText(m *tb.Message) {
|
func botText(m *tb.Message) {
|
||||||
PrintText(m)
|
PrintText(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
func sendChat(chatID int64, text string) {
|
|
||||||
opt := tb.SendOptions{
|
|
||||||
ParseMode: tb.ModeDefault,
|
|
||||||
}
|
|
||||||
|
|
||||||
ch := tb.Chat{
|
|
||||||
ID: chatID,
|
|
||||||
}
|
|
||||||
_, err := bot.Send(&ch, text, &opt)
|
|
||||||
logErrorDebug(err, "sendChat")
|
|
||||||
}
|
|
||||||
|
118
config.go
Normal file
118
config.go
Normal file
@ -0,0 +1,118 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
_ "embed"
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/tidwall/pretty"
|
||||||
|
)
|
||||||
|
|
||||||
|
//go:embed config.sample.json
|
||||||
|
var cfgSample []byte
|
||||||
|
|
||||||
|
type ServerConfig struct {
|
||||||
|
Addr string `json:"addr"`
|
||||||
|
Passwd string `json:"passwd"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type TelegramConfig struct {
|
||||||
|
AdminID int64 `json:"admin_id"`
|
||||||
|
ChatID int64 `json:"chat_id"`
|
||||||
|
URL string `json:"url"`
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GameConfig struct {
|
||||||
|
TimeZone string `json:"timezone"`
|
||||||
|
DailyAllotment time.Duration `json:"daily_allotment"`
|
||||||
|
StartingAllotment time.Duration `json:"starting_allotment"`
|
||||||
|
Threshold time.Duration `json:"threshold"`
|
||||||
|
StartDate time.Time `json:"start_date"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ClientConfig struct {
|
||||||
|
UserID int `json:"user_id"`
|
||||||
|
Username string `json:"username"`
|
||||||
|
Online bool `json:"online"`
|
||||||
|
TimeLeft time.Duration `json:"time_left"`
|
||||||
|
CompanyID uint8 `json:"company_id`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
Server *ServerConfig `json:"server"`
|
||||||
|
Telegram *TelegramConfig `json:"telegram"`
|
||||||
|
Game *GameConfig `json:"game"`
|
||||||
|
Clients map[int]*ClientConfig `json:"clients"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init values for a config based on package defaults
|
||||||
|
func (c *Config) Init() error {
|
||||||
|
err := json.Unmarshal(cfgSample, &c)
|
||||||
|
c.Clients = make(map[int]*ClientConfig)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load config values from a given file
|
||||||
|
func (c *Config) Load(path string) error {
|
||||||
|
err := c.Init()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
b, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(b, &c)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save config values to a given file
|
||||||
|
func (c *Config) Save(path string) error {
|
||||||
|
b, err := c.Export()
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(path, b, 0644)
|
||||||
|
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Export config values as a bytestream
|
||||||
|
func (c *Config) Export() ([]byte, error) {
|
||||||
|
b, err := json.Marshal(c)
|
||||||
|
if err != nil {
|
||||||
|
return b, err
|
||||||
|
}
|
||||||
|
return pretty.Pretty(b), nil
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) CompanyIsRegistered(id uint8) bool {
|
||||||
|
for _, cc := range c.Clients {
|
||||||
|
if cc.CompanyID == id {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *Config) GetCompanyClient(id uint8) *ClientConfig {
|
||||||
|
for _, cc := range c.Clients {
|
||||||
|
if cc.CompanyID == id {
|
||||||
|
return cc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
18
config.sample.json
Normal file
18
config.sample.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"server":{
|
||||||
|
"addr":"heidi.siteop.biz:3977",
|
||||||
|
"passwd":"plop"
|
||||||
|
},
|
||||||
|
"telegram":{
|
||||||
|
"admin_id":32173684,
|
||||||
|
"chat_id":-436055948,
|
||||||
|
"url":"https://api.telegram.org",
|
||||||
|
"token":"954090437:AAEMYeUWGluKZRwXi_K3-T23ZVpFoqQAmu0"
|
||||||
|
},
|
||||||
|
"game":{
|
||||||
|
"daily_allotment":1200000000000,
|
||||||
|
"starting_allotment":6000000000000,
|
||||||
|
"threshold":600000000000,
|
||||||
|
"start_date":"2021-11-01T17:22:28+02:00"
|
||||||
|
}
|
||||||
|
}
|
3
generate.go
Normal file
3
generate.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
//go:generate bash ./version.sh
|
5
go.mod
5
go.mod
@ -2,4 +2,7 @@ module git.siteop.biz/shoopea/gottdad
|
|||||||
|
|
||||||
go 1.16
|
go 1.16
|
||||||
|
|
||||||
require gopkg.in/tucnak/telebot.v2 v2.4.0
|
require (
|
||||||
|
github.com/tidwall/pretty v1.2.0
|
||||||
|
gopkg.in/tucnak/telebot.v2 v2.4.0
|
||||||
|
)
|
||||||
|
2
go.sum
2
go.sum
@ -7,6 +7,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
|
|||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||||
|
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
|
||||||
|
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/tucnak/telebot.v2 v2.4.0 h1:nOeqOWnOAD3dzbKW+NRumd8zjj5vrWwSa0WRTxvgfag=
|
gopkg.in/tucnak/telebot.v2 v2.4.0 h1:nOeqOWnOAD3dzbKW+NRumd8zjj5vrWwSa0WRTxvgfag=
|
||||||
gopkg.in/tucnak/telebot.v2 v2.4.0/go.mod h1:BgaIIx50PSRS9pG59JH+geT82cfvoJU/IaI5TJdN3v8=
|
gopkg.in/tucnak/telebot.v2 v2.4.0/go.mod h1:BgaIIx50PSRS9pG59JH+geT82cfvoJU/IaI5TJdN3v8=
|
||||||
|
337
main.go
337
main.go
@ -1,328 +1,55 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"flag"
|
||||||
"encoding/binary"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"regexp"
|
|
||||||
"strconv"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
tb "gopkg.in/tucnak/telebot.v2"
|
_ "embed"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
|
||||||
ClientID uint32
|
|
||||||
Name string
|
|
||||||
Address string
|
|
||||||
CompanyID uint8
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
var (
|
||||||
bot *tb.Bot
|
cfg *Config
|
||||||
clients map[uint32]*Client
|
srv *ServerTTD
|
||||||
paused bool = true
|
bot *Bot
|
||||||
forcePaused bool = true
|
|
||||||
pausedBy string
|
|
||||||
|
|
||||||
githash string
|
configFlag = flag.String("config", "config.json", "config file")
|
||||||
buildstamp string
|
initFlag = flag.Bool("init", false, "init config")
|
||||||
commits string
|
|
||||||
|
|
||||||
conn net.Conn
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
clients = make(map[uint32]*Client)
|
flag.Parse()
|
||||||
|
|
||||||
conn, err = net.Dial("tcp", "poop.siteop.biz:3977")
|
cfg = &Config{}
|
||||||
failError(err, "net.Dial")
|
if *initFlag {
|
||||||
logInfoDebug("Connected to poop.siteop.biz:3977")
|
err = cfg.Init()
|
||||||
|
failError(err, "Cannot init config")
|
||||||
//send auth
|
err = cfg.Save(*configFlag)
|
||||||
p := PacketAdminJoin{
|
failError(err, "Cannot save config")
|
||||||
Packet: Packet{PType: AdminPacketAdminJoin},
|
} else {
|
||||||
Password: "plop",
|
err = cfg.Load(*configFlag)
|
||||||
AppName: "gottdad",
|
failError(err, "Cannot open config")
|
||||||
AppVersion: "alpha",
|
|
||||||
}
|
}
|
||||||
_, err = conn.Write(p.Bytes())
|
|
||||||
failError(err, "conn.Write")
|
logInfoWarn("Starting up (%s) ...", version)
|
||||||
r := bufio.NewReader(conn)
|
|
||||||
b := make([]byte, 0xFFFF)
|
|
||||||
read := 0
|
|
||||||
n := 0
|
|
||||||
|
|
||||||
// Registering bot
|
// Registering bot
|
||||||
bot, err = tb.NewBot(tb.Settings{
|
bot = &Bot{
|
||||||
Token: "954090437:AAEMYeUWGluKZRwXi_K3-T23ZVpFoqQAmu0",
|
Config: cfg.Telegram,
|
||||||
URL: "https://api.telegram.org",
|
|
||||||
Poller: &tb.LongPoller{Timeout: 10 * time.Second},
|
|
||||||
})
|
|
||||||
failError(err, "Registering bot")
|
|
||||||
logInfoDebug("Connected to Telegram")
|
|
||||||
|
|
||||||
go BotHandlers(bot)
|
|
||||||
|
|
||||||
u := tb.User{
|
|
||||||
ID: int(32173684),
|
|
||||||
}
|
}
|
||||||
bot.Send(&u, fmt.Sprintf("Started (%s-b%s - %s)", githash, commits, buildstamp))
|
go bot.Start()
|
||||||
|
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
|
srv = &ServerTTD{
|
||||||
|
Config: cfg.Server,
|
||||||
|
Data: &ServerDataTTD{},
|
||||||
|
Status: &ServerStatusTTD{},
|
||||||
|
}
|
||||||
|
go srv.Start()
|
||||||
|
|
||||||
for {
|
for {
|
||||||
p := Packet{}
|
time.Sleep(1 * time.Second)
|
||||||
|
|
||||||
for {
|
|
||||||
if read >= 3 {
|
|
||||||
//logInfoDebug("Packet read")
|
|
||||||
break
|
|
||||||
}
|
}
|
||||||
n, err = r.Read(b[read:])
|
|
||||||
logErrorDebug(err, "r.Read")
|
|
||||||
read += n
|
|
||||||
//logInfoDebug("Waiting for packet, read %d bytes.", read)
|
|
||||||
|
|
||||||
}
|
|
||||||
p.PLength = binary.LittleEndian.Uint16(b[0:])
|
|
||||||
p.PType = b[2]
|
|
||||||
if p.PLength <= 3 {
|
|
||||||
logInfoAlert("Wrong packet length")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
//logInfoDebug("Waiting for packet data : len : %d / type : %d", p.PLength, p.PType)
|
|
||||||
|
|
||||||
for {
|
|
||||||
if read >= int(p.PLength) {
|
|
||||||
//logInfoDebug("Data read")
|
|
||||||
break
|
|
||||||
}
|
|
||||||
n, err = r.Read(b[read:])
|
|
||||||
logErrorDebug(err, "r.Read")
|
|
||||||
read += n
|
|
||||||
//logInfoDebug("Waiting for data, read %d/%d bytes.", read, p.PLength)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
switch p.PType {
|
|
||||||
case AdminPacketServerProtocol:
|
|
||||||
sp := PacketServerProtocol{
|
|
||||||
Packet: p,
|
|
||||||
}
|
|
||||||
sp.Read(b[:p.PLength])
|
|
||||||
logInfoDebug("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(b[:p.PLength])
|
|
||||||
logInfoDebug("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)
|
|
||||||
px := PacketAdminUpdateFrequency{
|
|
||||||
Packet: Packet{PType: AdminPacketAdminUpdateFrequency},
|
|
||||||
UpdateType: AdminUpdateDate,
|
|
||||||
UpdateFrequency: AdminFrequencyDaily,
|
|
||||||
}
|
|
||||||
_, err = conn.Write(px.Bytes())
|
|
||||||
|
|
||||||
px = PacketAdminUpdateFrequency{
|
|
||||||
Packet: Packet{PType: AdminPacketAdminUpdateFrequency},
|
|
||||||
UpdateType: AdminUpdateClientInfo,
|
|
||||||
UpdateFrequency: AdminFrequencyAutomatic,
|
|
||||||
}
|
|
||||||
_, err = conn.Write(px.Bytes())
|
|
||||||
|
|
||||||
px = PacketAdminUpdateFrequency{
|
|
||||||
Packet: Packet{PType: AdminPacketAdminUpdateFrequency},
|
|
||||||
UpdateType: AdminUpdateCompanyInfo,
|
|
||||||
UpdateFrequency: AdminFrequencyAutomatic,
|
|
||||||
}
|
|
||||||
_, err = conn.Write(px.Bytes())
|
|
||||||
|
|
||||||
px = PacketAdminUpdateFrequency{
|
|
||||||
Packet: Packet{PType: AdminPacketAdminUpdateFrequency},
|
|
||||||
UpdateType: AdminUpdateChat,
|
|
||||||
UpdateFrequency: AdminFrequencyAutomatic,
|
|
||||||
}
|
|
||||||
_, err = conn.Write(px.Bytes())
|
|
||||||
|
|
||||||
px = PacketAdminUpdateFrequency{
|
|
||||||
Packet: Packet{PType: AdminPacketAdminUpdateFrequency},
|
|
||||||
UpdateType: AdminUpdateConsole,
|
|
||||||
UpdateFrequency: AdminFrequencyAutomatic,
|
|
||||||
}
|
|
||||||
_, err = conn.Write(px.Bytes())
|
|
||||||
case AdminPacketServerDate:
|
|
||||||
sp := PacketServerDate{
|
|
||||||
Packet: p,
|
|
||||||
}
|
|
||||||
sp.Read(b[:p.PLength])
|
|
||||||
logInfoDebug("AdminPacketServerDate :\n- Date: %d\n- RealDate : %v", sp.Date, toDate(sp.Date))
|
|
||||||
t := toDate(sp.Date)
|
|
||||||
if t.Day() == 1 && t.Month() == 1 {
|
|
||||||
sendChat(-436055948, fmt.Sprintf("Year %d.", t.Year()))
|
|
||||||
}
|
|
||||||
paused = false
|
|
||||||
case AdminPacketServerClientJoin:
|
|
||||||
sp := PacketServerClientJoin{
|
|
||||||
Packet: p,
|
|
||||||
}
|
|
||||||
sp.Read(b[:p.PLength])
|
|
||||||
logInfoDebug("AdminPacketServerClientJoin :\n- ClientID: %d", sp.ClientID)
|
|
||||||
sendChat(-436055948, fmt.Sprintf("%s joining.", clients[sp.ClientID].Name))
|
|
||||||
case AdminPacketServerClientInfo:
|
|
||||||
sp := PacketServerClientInfo{
|
|
||||||
Packet: p,
|
|
||||||
}
|
|
||||||
sp.Read(b[:p.PLength])
|
|
||||||
//logInfoDebug("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 := Client{
|
|
||||||
ClientID: sp.ClientID,
|
|
||||||
Name: sp.Name,
|
|
||||||
Address: sp.Address,
|
|
||||||
CompanyID: sp.CompanyID,
|
|
||||||
}
|
|
||||||
clients[sp.ClientID] = &clt
|
|
||||||
logInfoDebug("Clients : %v", clients)
|
|
||||||
case AdminPacketServerClientError:
|
|
||||||
sp := PacketServerClientError{
|
|
||||||
Packet: p,
|
|
||||||
}
|
|
||||||
sp.Read(b[:p.PLength])
|
|
||||||
logInfoDebug("AdminPacketServerClientError :\n- ClientID: %d\n- ErrorID: %d", sp.ClientID, sp.ErrorID)
|
|
||||||
case AdminPacketServerClientQuit:
|
|
||||||
sp := PacketServerClientQuit{
|
|
||||||
Packet: p,
|
|
||||||
}
|
|
||||||
sp.Read(b[:p.PLength])
|
|
||||||
logInfoDebug("AdminPacketServerClientQuit :\n- ClientID: %d", sp.ClientID)
|
|
||||||
if len(clients) == 2 && !paused {
|
|
||||||
sendChat(-436055948, fmt.Sprintf("%s leaving. Game paused.", clients[sp.ClientID].Name))
|
|
||||||
} else {
|
|
||||||
sendChat(-436055948, fmt.Sprintf("%s leaving.", clients[sp.ClientID].Name))
|
|
||||||
}
|
|
||||||
delete(clients, sp.ClientID)
|
|
||||||
case AdminPacketServerChat:
|
|
||||||
sp := PacketServerChat{
|
|
||||||
Packet: p,
|
|
||||||
}
|
|
||||||
sp.Read(b[:p.PLength])
|
|
||||||
|
|
||||||
if sp.Message == "!unpause" {
|
|
||||||
logInfoDebug("AdminPacketServerChat : Unpausing")
|
|
||||||
forcePaused = false
|
|
||||||
pausedBy = clients[sp.ClientID].Name
|
|
||||||
} else if sp.Message == "!pause" {
|
|
||||||
logInfoDebug("AdminPacketServerChat : Pausing")
|
|
||||||
forcePaused = true
|
|
||||||
pausedBy = clients[sp.ClientID].Name
|
|
||||||
} else {
|
|
||||||
logInfoDebug("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(b[:p.PLength])
|
|
||||||
|
|
||||||
ok, err := regexp.MatchString("\\[udp\\] queried from .*", sp.Text)
|
|
||||||
logErrorDebug(err, "queried from")
|
|
||||||
if sp.Origin != "net" || ok == false {
|
|
||||||
logInfoDebug("AdminPacketServerConsole :\n- Origin: %q\n- Text: %s", sp.Origin, sp.Text)
|
|
||||||
}
|
|
||||||
case AdminPacketServerRCon:
|
|
||||||
sp := PacketServerRCon{
|
|
||||||
Packet: p,
|
|
||||||
}
|
|
||||||
sp.Read(b[:p.PLength])
|
|
||||||
|
|
||||||
ok, _ := regexp.MatchString("Client #[0-9]+ name: '.*' company: [0-9]+ IP: .*", sp.Output)
|
|
||||||
if ok {
|
|
||||||
clt := Client{}
|
|
||||||
r, _ := regexp.Compile("Client #(?P<ClientID>[0-9]+) name: '(?P<Name>.*)' company: (?P<CompanyID>[0-9]+) IP: (?P<Address>.*)")
|
|
||||||
ID64, _ := strconv.ParseInt(r.ReplaceAllString(sp.Output, "${ClientID}"), 10, 32)
|
|
||||||
clt.ClientID = uint32(ID64)
|
|
||||||
clt.Name = r.ReplaceAllString(sp.Output, "${Name}")
|
|
||||||
if clt.Name == "" {
|
|
||||||
clt.Name = "server"
|
|
||||||
}
|
|
||||||
ID64, _ = strconv.ParseInt(r.ReplaceAllString(sp.Output, "${CompanyID}"), 10, 8)
|
|
||||||
clt.CompanyID = uint8(ID64)
|
|
||||||
clt.Address = r.ReplaceAllString(sp.Output, "${Address}")
|
|
||||||
clients[clt.ClientID] = &clt
|
|
||||||
} else {
|
|
||||||
logInfoDebug("AdminPacketServerRCon :\n- ColorID: %d\n- Output: %s", sp.ColorID, sp.Output)
|
|
||||||
}
|
|
||||||
case AdminPacketServerRConEnd:
|
|
||||||
sp := PacketServerRConEnd{
|
|
||||||
Packet: p,
|
|
||||||
}
|
|
||||||
sp.Read(b[:p.PLength])
|
|
||||||
|
|
||||||
if sp.Command == "clients" {
|
|
||||||
for k, v := range clients {
|
|
||||||
logInfoDebug("Client[%d] : %s - %d (%s)", k, v.Name, v.CompanyID, v.Address)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logInfoDebug("AdminPacketServerRConEnd :\n- Command: %s", sp.Command)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
logInfoDebug("Packet fully read : len : %d / type : %d", p.PLength, p.PType)
|
|
||||||
}
|
|
||||||
|
|
||||||
c := make([]byte, 0xFFFF)
|
|
||||||
copy(c, b[p.PLength:])
|
|
||||||
b = c
|
|
||||||
read -= int(p.PLength)
|
|
||||||
|
|
||||||
if len(clients) == 0 {
|
|
||||||
px := PacketAdminRCon{
|
|
||||||
Packet: Packet{PType: AdminPacketAdminRCon},
|
|
||||||
Command: "clients",
|
|
||||||
}
|
|
||||||
_, err = conn.Write(px.Bytes())
|
|
||||||
}
|
|
||||||
|
|
||||||
if !paused && forcePaused {
|
|
||||||
paused = true
|
|
||||||
px := PacketAdminRCon{
|
|
||||||
Packet: Packet{PType: AdminPacketAdminRCon},
|
|
||||||
Command: "pause",
|
|
||||||
}
|
|
||||||
_, err = conn.Write(px.Bytes())
|
|
||||||
logInfoDebug("Force pausing")
|
|
||||||
if pausedBy != "" {
|
|
||||||
sendChat(-436055948, fmt.Sprintf("Game paused by %s", pausedBy))
|
|
||||||
pausedBy = ""
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if paused && !forcePaused && len(clients) > 1 { // server is client #1
|
|
||||||
paused = false
|
|
||||||
px := PacketAdminRCon{
|
|
||||||
Packet: Packet{PType: AdminPacketAdminRCon},
|
|
||||||
Command: "unpause",
|
|
||||||
}
|
|
||||||
_, err = conn.Write(px.Bytes())
|
|
||||||
logInfoDebug("Force unpausing")
|
|
||||||
if pausedBy != "" {
|
|
||||||
sendChat(-436055948, fmt.Sprintf("Game unpaused by %s", pausedBy))
|
|
||||||
pausedBy = ""
|
|
||||||
} else {
|
|
||||||
sendChat(-436055948, "Game unpaused")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !paused && len(clients) == 1 { // server is client #1
|
|
||||||
paused = true
|
|
||||||
px := PacketAdminRCon{
|
|
||||||
Packet: Packet{PType: AdminPacketAdminRCon},
|
|
||||||
Command: "pause",
|
|
||||||
}
|
|
||||||
_, err = conn.Write(px.Bytes())
|
|
||||||
logInfoDebug("Pausing")
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
136
packet.go
136
packet.go
@ -109,6 +109,11 @@ type PacketAdminUpdateFrequency struct {
|
|||||||
UpdateFrequency uint16
|
UpdateFrequency uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PacketAdminPoll struct {
|
||||||
|
Packet
|
||||||
|
UpdateType uint8
|
||||||
|
UpdateID uint32
|
||||||
|
}
|
||||||
type PacketServerDate struct {
|
type PacketServerDate struct {
|
||||||
Packet
|
Packet
|
||||||
Date uint32
|
Date uint32
|
||||||
@ -134,12 +139,52 @@ type PacketServerClientInfo struct {
|
|||||||
CompanyID uint8
|
CompanyID uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PacketServerClientUpdate struct {
|
||||||
|
Packet
|
||||||
|
ClientID uint32
|
||||||
|
Name string
|
||||||
|
CompanyID uint8
|
||||||
|
}
|
||||||
|
|
||||||
type PacketServerClientError struct {
|
type PacketServerClientError struct {
|
||||||
Packet
|
Packet
|
||||||
ClientID uint32
|
ClientID uint32
|
||||||
ErrorID uint8
|
ErrorID uint8
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type PacketServerCompanyNew struct {
|
||||||
|
Packet
|
||||||
|
CompanyID uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type PacketServerCompanyInfo struct {
|
||||||
|
Packet
|
||||||
|
CompanyID uint8
|
||||||
|
Name string
|
||||||
|
President string
|
||||||
|
Color uint8
|
||||||
|
Inauguration uint32
|
||||||
|
AI bool
|
||||||
|
Protected bool
|
||||||
|
Bankruptcies uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type PacketServerCompanyUpdate struct {
|
||||||
|
Packet
|
||||||
|
CompanyID uint8
|
||||||
|
Name string
|
||||||
|
President string
|
||||||
|
Color uint8
|
||||||
|
Protected bool
|
||||||
|
Bankruptcies uint8
|
||||||
|
}
|
||||||
|
|
||||||
|
type PacketServerCompanyRemove struct {
|
||||||
|
Packet
|
||||||
|
CompanyID uint8
|
||||||
|
Reason uint8
|
||||||
|
}
|
||||||
|
|
||||||
type PacketServerChat struct {
|
type PacketServerChat struct {
|
||||||
Packet
|
Packet
|
||||||
ActionID uint8
|
ActionID uint8
|
||||||
@ -206,6 +251,18 @@ func (p *PacketAdminUpdateFrequency) Bytes() []byte {
|
|||||||
return buf.Bytes()
|
return buf.Bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PacketAdminPoll) Bytes() []byte {
|
||||||
|
buf := new(bytes.Buffer)
|
||||||
|
p.PLength = 8
|
||||||
|
|
||||||
|
binary.Write(buf, binary.LittleEndian, p.PLength)
|
||||||
|
binary.Write(buf, binary.LittleEndian, p.PType)
|
||||||
|
binary.Write(buf, binary.LittleEndian, p.UpdateType)
|
||||||
|
binary.Write(buf, binary.LittleEndian, p.UpdateID)
|
||||||
|
|
||||||
|
return buf.Bytes()
|
||||||
|
}
|
||||||
|
|
||||||
func (p *PacketServerWelcome) Read(b []byte) {
|
func (p *PacketServerWelcome) Read(b []byte) {
|
||||||
r := bufio.NewReader(bytes.NewReader(b))
|
r := bufio.NewReader(bytes.NewReader(b))
|
||||||
r.Discard(3)
|
r.Discard(3)
|
||||||
@ -285,6 +342,18 @@ func (p *PacketServerClientInfo) Read(b []byte) {
|
|||||||
p.CompanyID = uint8(c)
|
p.CompanyID = uint8(c)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PacketServerClientUpdate) Read(b []byte) {
|
||||||
|
r := bufio.NewReader(bytes.NewReader(b))
|
||||||
|
r.Discard(3)
|
||||||
|
bs := make([]byte, 4)
|
||||||
|
_, _ = r.Read(bs)
|
||||||
|
p.ClientID = binary.LittleEndian.Uint32(bs[0:])
|
||||||
|
p.Name, _ = r.ReadString(0)
|
||||||
|
p.Name = p.Name[:len(p.Name)-1]
|
||||||
|
c, _ := r.ReadByte()
|
||||||
|
p.CompanyID = uint8(c)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *PacketServerClientError) Read(b []byte) {
|
func (p *PacketServerClientError) Read(b []byte) {
|
||||||
r := bufio.NewReader(bytes.NewReader(b))
|
r := bufio.NewReader(bytes.NewReader(b))
|
||||||
r.Discard(3)
|
r.Discard(3)
|
||||||
@ -303,6 +372,73 @@ func (p *PacketServerClientQuit) Read(b []byte) {
|
|||||||
p.ClientID = binary.LittleEndian.Uint32(bs[0:])
|
p.ClientID = binary.LittleEndian.Uint32(bs[0:])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (p *PacketServerCompanyNew) Read(b []byte) {
|
||||||
|
r := bufio.NewReader(bytes.NewReader(b))
|
||||||
|
r.Discard(3)
|
||||||
|
c, _ := r.ReadByte()
|
||||||
|
p.CompanyID = uint8(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PacketServerCompanyInfo) Read(b []byte) {
|
||||||
|
r := bufio.NewReader(bytes.NewReader(b))
|
||||||
|
r.Discard(3)
|
||||||
|
c, _ := r.ReadByte()
|
||||||
|
p.CompanyID = uint8(c)
|
||||||
|
p.Name, _ = r.ReadString(0)
|
||||||
|
p.Name = p.Name[:len(p.Name)-1]
|
||||||
|
p.President, _ = r.ReadString(0)
|
||||||
|
p.President = p.Name[:len(p.Name)-1]
|
||||||
|
c, _ = r.ReadByte()
|
||||||
|
p.Color = uint8(c)
|
||||||
|
c, _ = r.ReadByte()
|
||||||
|
if c > 0 {
|
||||||
|
p.Protected = true
|
||||||
|
} else {
|
||||||
|
p.Protected = false
|
||||||
|
}
|
||||||
|
bs := make([]byte, 4)
|
||||||
|
_, _ = r.Read(bs)
|
||||||
|
p.Inauguration = binary.LittleEndian.Uint32(bs[0:])
|
||||||
|
c, _ = r.ReadByte()
|
||||||
|
if c > 0 {
|
||||||
|
p.AI = true
|
||||||
|
} else {
|
||||||
|
p.AI = false
|
||||||
|
}
|
||||||
|
c, _ = r.ReadByte()
|
||||||
|
p.Bankruptcies = uint8(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PacketServerCompanyUpdate) Read(b []byte) {
|
||||||
|
r := bufio.NewReader(bytes.NewReader(b))
|
||||||
|
r.Discard(3)
|
||||||
|
c, _ := r.ReadByte()
|
||||||
|
p.CompanyID = uint8(c)
|
||||||
|
p.Name, _ = r.ReadString(0)
|
||||||
|
p.Name = p.Name[:len(p.Name)-1]
|
||||||
|
p.President, _ = r.ReadString(0)
|
||||||
|
p.President = p.Name[:len(p.Name)-1]
|
||||||
|
c, _ = r.ReadByte()
|
||||||
|
p.Color = uint8(c)
|
||||||
|
c, _ = r.ReadByte()
|
||||||
|
if c > 0 {
|
||||||
|
p.Protected = true
|
||||||
|
} else {
|
||||||
|
p.Protected = false
|
||||||
|
}
|
||||||
|
c, _ = r.ReadByte()
|
||||||
|
p.Bankruptcies = uint8(c)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *PacketServerCompanyRemove) Read(b []byte) {
|
||||||
|
r := bufio.NewReader(bytes.NewReader(b))
|
||||||
|
r.Discard(3)
|
||||||
|
c, _ := r.ReadByte()
|
||||||
|
p.CompanyID = uint8(c)
|
||||||
|
c, _ = r.ReadByte()
|
||||||
|
p.Reason = uint8(c)
|
||||||
|
}
|
||||||
|
|
||||||
func (p *PacketServerChat) Read(b []byte) {
|
func (p *PacketServerChat) Read(b []byte) {
|
||||||
r := bufio.NewReader(bytes.NewReader(b))
|
r := bufio.NewReader(bytes.NewReader(b))
|
||||||
r.Discard(3)
|
r.Discard(3)
|
||||||
|
729
ttd.go
Normal file
729
ttd.go
Normal file
@ -0,0 +1,729 @@
|
|||||||
|
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,
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
bot.SendChat(cfg.Telegram.ChatID, fmt.Sprintf("%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 len(s.Status.Clients) == 2 && !srv.Status.Paused {
|
||||||
|
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. Game paused.", cc.Username))
|
||||||
|
cc.Online = false
|
||||||
|
} else {
|
||||||
|
bot.SendChat(cfg.Telegram.ChatID, fmt.Sprintf("%s leaving. Game paused.", s.Status.Clients[sp.ClientID].Name))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
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(),
|
||||||
|
}
|
||||||
|
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(),
|
||||||
|
}
|
||||||
|
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()
|
||||||
|
|
||||||
|
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()
|
||||||
|
|
||||||
|
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()
|
||||||
|
}
|
6
version.go
Normal file
6
version.go
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
// Code generated by version.sh (@generated) DO NOT EDIT.
|
||||||
|
package main
|
||||||
|
var githash = "90e584f"
|
||||||
|
var buildstamp = "2021-11-06_15:32:44"
|
||||||
|
var commits = "156"
|
||||||
|
var version = "90e584f-b156 - 2021-11-06_15:32:44"
|
13
version.sh
Normal file
13
version.sh
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
# Get the version.
|
||||||
|
githash=`git rev-parse --short HEAD`
|
||||||
|
buildstamp=`date -u '+%Y-%m-%d_%H:%M:%S'`
|
||||||
|
commits=`git rev-list --count master`
|
||||||
|
# Write out the package.
|
||||||
|
cat << EOF > version.go
|
||||||
|
// Code generated by version.sh (@generated) DO NOT EDIT.
|
||||||
|
package main
|
||||||
|
var githash = "$githash"
|
||||||
|
var buildstamp = "$buildstamp"
|
||||||
|
var commits = "$commits"
|
||||||
|
var version = "$githash-b$commits - $buildstamp"
|
||||||
|
EOF
|
Loading…
Reference in New Issue
Block a user