This commit is contained in:
Vincent Schweiger
2022-07-25 21:26:46 +02:00
commit 91ef246bcc
6 changed files with 1260 additions and 0 deletions
+193
View File
@@ -0,0 +1,193 @@
package main
import (
"fmt"
"log"
"net"
"time"
"github.com/hashicorp/consul/api"
"go.minekube.com/brigodier"
. "go.minekube.com/common/minecraft/component"
"go.minekube.com/common/minecraft/component/codec/legacy"
"go.minekube.com/gate/cmd/gate"
"go.minekube.com/gate/pkg/command"
"go.minekube.com/gate/pkg/edition/java/proxy"
"go.minekube.com/gate/pkg/runtime/event"
)
func main() {
// Add our "plug-in" to be initialized on Gate start.
proxy.Plugins = append(proxy.Plugins, proxy.Plugin{
Name: "FenceProxy",
Init: func(proxy *proxy.Proxy) error {
return newFenceProxy(proxy).init()
},
})
// Execute Gate entrypoint and block until shutdown.
// We could also run gate.Start if we don't need Gate's command-line.
gate.Execute()
}
// FenceProxy is a simple proxy that adds a `/broadcast` command
// and sends a message on server switch.
type FenceProxy struct {
*proxy.Proxy
legacyCodec *legacy.Legacy
}
func newFenceProxy(proxy *proxy.Proxy) *FenceProxy {
return &FenceProxy{
Proxy: proxy,
legacyCodec: &legacy.Legacy{Char: legacy.AmpersandChar},
}
}
// initialize our sample proxy
func (p *FenceProxy) init() error {
p.registerCommands()
go p.registerServers()
return p.registerSubscribers()
}
func (p *FenceProxy) registerServers() {
// Get a new client
config := api.DefaultConfig()
config.Address = "consul1.pawott.de:8500"
client, err := api.NewClient(config)
if err != nil {
panic(err)
}
// Get handle to catalog API
catalog := client.Catalog()
q := &api.QueryOptions{RequireConsistent: true}
var lastServers []proxy.ServerInfo
for {
allSvc, _, err := catalog.Services(q)
if err != nil {
log.Printf("Error querying Consul: %v", err)
}
var minecraftServices []string
for svc, tags := range allSvc {
for _, tag := range tags {
if tag == "consulity.enabled" {
minecraftServices = append(minecraftServices, svc)
break
}
}
}
var servers []proxy.ServerInfo
for _, name := range minecraftServices {
svcs, _, err := catalog.Service(name, "", q)
if err != nil {
log.Printf("Error querying Consul: %v", err)
continue
}
for _, svc := range svcs {
adr := fmt.Sprintf("%v:%d", svc.ServiceAddress, svc.ServicePort)
ip, err := net.ResolveTCPAddr("tcp", adr)
if err != nil {
log.Printf("Error converting address: %s", adr)
continue
}
servers = append(servers, proxy.NewServerInfo(svc.ID, ip))
}
}
// servers in lastServers that don't exist in servers now
var notExistingServers []proxy.ServerInfo
for _, server := range lastServers {
stillExists := false
for _, curServer := range servers {
if curServer.Name() == server.Name() {
stillExists = true
}
}
if !stillExists {
fmt.Printf("Server doesn't exist anymore: %v", server.Name())
notExistingServers = append(notExistingServers, server)
}
}
for _, server := range notExistingServers {
p.Unregister(server)
}
for _, server := range servers {
alreadyExists := false
for _, lastServer := range lastServers {
if lastServer.Name() == server.Name() {
alreadyExists = true
}
}
if !alreadyExists {
p.Register(server)
}
}
lastServers = servers
time.Sleep(5 * time.Second)
}
}
// Register a proxy-wide commands (can be run while being on any server)
func (p *FenceProxy) registerCommands() {
// Registers the "/broadcast" command
p.Command().Register(brigodier.Literal("broadcast").Then(
// Adds message argument as in "/broadcast <message>"
brigodier.Argument("message", brigodier.StringPhrase).
// Adds completion suggestions as in "/broadcast [suggestions]"
Suggests(command.SuggestFunc(func(
c *command.Context,
b *brigodier.SuggestionsBuilder,
) *brigodier.Suggestions {
player, ok := c.Source.(proxy.Player)
if ok {
b.Suggest("&oI am &6&l" + player.Username())
}
b.Suggest("Hello world!")
return b.Build()
})).
// Executed when running "/broadcast <message>"
Executes(command.Command(func(c *command.Context) error {
// Colorize/format message
message, err := p.legacyCodec.Unmarshal([]byte(c.String("message")))
if err != nil {
return c.Source.SendMessage(&Text{
Content: fmt.Sprintf("Error formatting message: %v", err)})
}
// Send to all players on this proxy
for _, player := range p.Players() {
// Send message in new goroutine,
// to not halt loop on slow connections.
go func(p proxy.Player) { _ = p.SendMessage(message) }(player)
}
return nil
})),
))
}
// Register event subscribers
func (p *FenceProxy) registerSubscribers() error {
p.Event().Subscribe(&proxy.PingEvent{}, 0, func(ev event.Event) {
e := ev.(*proxy.PingEvent)
p := e.Ping()
p.Players.Max = p.Players.Online + 1
})
p.Event().Subscribe(&proxy.LoginEvent{}, 0, func(ev event.Event) {
//e := ev.(*proxy.LoginEvent)
//e.Deny(&Text{Content: "&cMaintenance mode"})
})
p.Event().Subscribe(&proxy.PlayerChooseInitialServerEvent{}, 0, func(ev event.Event) {
e := ev.(*proxy.PlayerChooseInitialServerEvent)
e.SetInitialServer(p.Servers()[0])
})
return nil
}
func contains(list []proxy.ServerInfo, b proxy.ServerInfo) bool {
for _, as := range list {
if as == b {
return true
}
}
return false
}