You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
216 lines
5.3 KiB
216 lines
5.3 KiB
package main |
|
|
|
import ( |
|
"bufio" |
|
"fmt" |
|
"io" |
|
"os/exec" |
|
"regexp" |
|
"strings" |
|
"time" |
|
) |
|
|
|
var ( |
|
mcOut io.Reader |
|
mcIn io.Writer |
|
mcStop = false |
|
mcLoaded = false |
|
mcRebooting = false |
|
mcWBRunning = false |
|
) |
|
|
|
func (u User) McWhitelist() { |
|
mcCommand(fmt.Sprintf("whitelist add %+v", u.McUser)) |
|
mcCommand(fmt.Sprintf("nicknames %+v %+v", u.McUser, u.Email)) |
|
} |
|
|
|
func configureMinecraft() { |
|
log.LogInfo("Starting MinecraftServer") |
|
server := exec.Command("screen", "-r", "1234") |
|
mcIn, _ = server.StdinPipe() |
|
mcOut, _ = server.StdoutPipe() |
|
mcErr, _ := server.StderrPipe() |
|
mcErrScan := bufio.NewScanner(mcErr) |
|
server.Start() |
|
defer server.Wait() |
|
go listenOut() |
|
for mcErrScan.Scan() { |
|
if mcStop { |
|
break |
|
} |
|
log.LogError(mcErrScan.Text()) |
|
} |
|
} |
|
|
|
func handleMcOnline(s string) { |
|
log.LogDebug("A user has come online or gone offline.") |
|
s = strings.Replace(s, "&e", "", -1) |
|
var u User |
|
parts := strings.Split(s, " ") |
|
log.LogDebug(fmt.Sprintf("%+v", parts)) |
|
if parts[2] == "UUID" { |
|
log.LogDebug(fmt.Sprintf("User %+v detected.", parts[5])) |
|
if serverRebooting { |
|
mcCommand(fmt.Sprintf("kick %+v Server reboot pending.", parts[5])) |
|
} |
|
//mcCommand(fmt.Sprintf("kick %+v Server currently unavailable.", parts[5])) |
|
u.McUser = parts[5] |
|
u.Retrieve() |
|
if u.Email == "" { |
|
u.Email = u.McUser |
|
u.McUser = "" |
|
u.Retrieve() |
|
if u.McUser == "" { |
|
sendAdminDiscordMessage(fmt.Sprintf("Unable to verify %+v", parts[5])) |
|
mcCommand(fmt.Sprintf("kick %+v Verified SITNET not found. Admins have been notified.", parts[5])) |
|
return |
|
} |
|
} else if u.Email != parts[5] { |
|
mcCommand(fmt.Sprintf("lp user %+v parent add player", u.McUser)) |
|
mcCommand(fmt.Sprintf("nicknames %+v %+v", u.McUser, u.Email)) |
|
} |
|
for _, v := range onlineUsers { |
|
if v.Email == u.Email { |
|
return |
|
} |
|
} |
|
onlineUsers = append(onlineUsers, u) |
|
} else { |
|
log.LogDebug(fmt.Sprintf("User %+v detected.", parts[2])) |
|
u.McUser = parts[2] |
|
u.Retrieve() |
|
for k, v := range onlineUsers { |
|
log.LogDebug(fmt.Sprintf("v.McUser: %+v, parts[2]: %+v", v.McUser, parts[2])) |
|
if v.McUser == parts[2] || v.Email == parts[2] { |
|
onlineUsers[k] = onlineUsers[len(onlineUsers)-1] // Copy last element to index i. |
|
onlineUsers = onlineUsers[:len(onlineUsers)-1] // Truncate slice. |
|
} |
|
} |
|
} |
|
u.Write() |
|
log.LogDebug(fmt.Sprintf("Online users: %+v", onlineUsers)) |
|
if len(onlineUsers) == 0 && !config.WbFinished { |
|
log.LogInfo("The last user has logged out.") |
|
mcWorldBuilderToggle(true) |
|
} else { |
|
mcWorldBuilderToggle(false) |
|
} |
|
} |
|
|
|
func listenOut() { |
|
outScan := bufio.NewScanner(mcOut) |
|
var re = regexp.MustCompile(`INFO\]: <(.*?)> (.*)`) |
|
for outScan.Scan() { |
|
if mcStop { |
|
break |
|
} |
|
output := outScan.Text() |
|
go log.LogInfo(fmt.Sprintf("server.jar: %+v", output)) |
|
if !mcLoaded { |
|
if strings.Contains(output, "For help, type \"help\"") { |
|
mcLoaded = true |
|
sendDiscordMessage("Minecraft server is now available.") |
|
mcWorldBuilderToggle(true) |
|
} |
|
continue |
|
} |
|
if strings.Contains(output, "UUID of player") || strings.Contains(output, "left the game") { |
|
go handleMcOnline(output) |
|
} |
|
match := re.FindStringSubmatch(output) |
|
if match != nil { |
|
if match[1] != "Server" { |
|
var u User |
|
u.McUser = match[1] |
|
u.Retrieve() |
|
if u.Email == "" { |
|
u.McUser = "" |
|
u.Email = match[1] |
|
u.Retrieve() |
|
u.Write() |
|
} else if u.Email != match[1] { |
|
mcCommand(fmt.Sprintf("nicknames %+v %+v", u.McUser, u.Email)) |
|
} |
|
log.LogDebug(fmt.Sprintf("Minecraft message from retrieved user %+v", u)) |
|
go sendDiscordMessage(fmt.Sprintf("[%s]: %s", u.Email, match[2])) |
|
continue |
|
} |
|
} else if mcLoaded && !strings.Contains(output, " INFO]: [Server] [") && !strings.Contains(output, "[WorldBorder]") { |
|
go sendAdminDiscordMessage(output) |
|
} |
|
|
|
} |
|
|
|
} |
|
func mcWorldBuilderToggle(b bool) { |
|
log.LogDebug("World builder toggled") |
|
if b && !mcWBRunning { |
|
log.LogDebug("Building world!") |
|
mcCommand("wb world fill 500 300 false") |
|
mcCommand("wb fill confirm") |
|
mcWBRunning = true |
|
return |
|
} else if mcWBRunning { |
|
log.LogDebug("Not building world!") |
|
mcCommand("wb fill cancel") |
|
mcWBRunning = false |
|
} |
|
|
|
} |
|
func mcSay(message string) { |
|
mcCommand(fmt.Sprintf("say %+v", mcMention(message))) |
|
} |
|
func mcMention(s string) string { |
|
var ret string |
|
if !strings.Contains(s, "@") { |
|
return s |
|
} |
|
parts := strings.Split(s, " ") |
|
for _, part := range parts { |
|
if strings.HasPrefix(part, "<@!") { |
|
var u User |
|
u.DiscordID = strings.Replace(strings.Replace(part, "<@!", "", -1), ">", "", -1) |
|
u.Retrieve() |
|
ret += "@" |
|
ret += u.Email |
|
} else { |
|
ret += part |
|
} |
|
ret += " " |
|
log.LogDebug(ret) |
|
} |
|
return ret |
|
} |
|
func mcCommand(message string) { |
|
mcIn.Write([]byte(message)) |
|
mcIn.Write([]byte("\n")) |
|
} |
|
func mcReboot(when int, reminders int) { |
|
if mcRebooting { |
|
return |
|
} |
|
mcRebooting = true |
|
untilReboot := time.Duration(when) * time.Minute |
|
rebootTime := time.Now().Add(untilReboot) |
|
var untilReminder time.Duration |
|
if reminders != 0 { |
|
untilReminder = time.Duration(when / reminders) |
|
} |
|
for { |
|
mcSay(fmt.Sprintf("A reboot has been scheduled for %+v", untilReboot)) |
|
if reminders > 0 { |
|
reminders-- |
|
time.Sleep(untilReminder) |
|
continue |
|
} |
|
time.Sleep(time.Until(rebootTime)) |
|
mcExit() |
|
discordExit() |
|
} |
|
} |
|
func mcExit() { |
|
log.LogCritical("Closing Minecraft (issuing command `stop`)") |
|
mcCommand("stop") |
|
mcStop = true |
|
mcLoaded = false |
|
}
|
|
|