up3
Build and deploy / Build (push) Has been cancelled
Build and deploy / Update Contributors (push) Has been cancelled
Build and deploy / Deploy (push) Has been cancelled
Build and deploy / Build (push) Has been cancelled
Build and deploy / Update Contributors (push) Has been cancelled
Build and deploy / Deploy (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,89 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"github.com/google/uuid"
|
||||
"sync"
|
||||
)
|
||||
|
||||
// Global represents a global chat. Players will write in this chat by default
|
||||
// when they send any message in the chat.
|
||||
var Global = New()
|
||||
|
||||
// Chat represents the in-game chat. Messages may be written to it to send a
|
||||
// message to all subscribers.
|
||||
// Methods on Chat may be called from multiple goroutines concurrently. Chat
|
||||
// implements the io.Writer and io.StringWriter interfaces. fmt.Fprintf and
|
||||
// fmt.Fprint may be used to write formatted messages to the chat.
|
||||
type Chat struct {
|
||||
m sync.Mutex
|
||||
subscribers map[uuid.UUID]Subscriber
|
||||
}
|
||||
|
||||
// New returns a new chat.
|
||||
func New() *Chat {
|
||||
return &Chat{subscribers: map[uuid.UUID]Subscriber{}}
|
||||
}
|
||||
|
||||
// Write writes the byte slice p as a string to the chat. It is equivalent to
|
||||
// calling Chat.WriteString(string(p)).
|
||||
func (chat *Chat) Write(p []byte) (n int, err error) {
|
||||
return chat.WriteString(string(p))
|
||||
}
|
||||
|
||||
// WriteString writes a string s to the chat.
|
||||
func (chat *Chat) WriteString(s string) (n int, err error) {
|
||||
chat.m.Lock()
|
||||
defer chat.m.Unlock()
|
||||
for _, subscriber := range chat.subscribers {
|
||||
subscriber.Message(s)
|
||||
}
|
||||
return len(s), nil
|
||||
}
|
||||
|
||||
// Writet writes a Translation message to a Chat, parameterising the message
|
||||
// using the arguments passed. Messages are translated according to the locale
|
||||
// of subscribers if they implement Translator. Subscribers that do not
|
||||
// implement Translator have the fallback message sent.
|
||||
func (chat *Chat) Writet(t Translation, a ...any) {
|
||||
chat.m.Lock()
|
||||
defer chat.m.Unlock()
|
||||
for _, subscriber := range chat.subscribers {
|
||||
if translator, ok := subscriber.(Translator); ok {
|
||||
translator.Messaget(t, a...)
|
||||
continue
|
||||
}
|
||||
subscriber.Message(t.F(a...).String())
|
||||
}
|
||||
}
|
||||
|
||||
// Subscribe adds a subscriber to the chat, sending it every message written to
|
||||
// the chat. In order to remove it again, use Chat.Unsubscribe().
|
||||
func (chat *Chat) Subscribe(s Subscriber) {
|
||||
chat.m.Lock()
|
||||
defer chat.m.Unlock()
|
||||
chat.subscribers[s.UUID()] = s
|
||||
}
|
||||
|
||||
// Subscribed checks if a subscriber is currently subscribed to the chat.
|
||||
func (chat *Chat) Subscribed(s Subscriber) bool {
|
||||
chat.m.Lock()
|
||||
defer chat.m.Unlock()
|
||||
_, ok := chat.subscribers[s.UUID()]
|
||||
return ok
|
||||
}
|
||||
|
||||
// Unsubscribe removes a subscriber from the chat, so that messages written to
|
||||
// the chat will no longer be sent to it.
|
||||
func (chat *Chat) Unsubscribe(s Subscriber) {
|
||||
chat.m.Lock()
|
||||
defer chat.m.Unlock()
|
||||
delete(chat.subscribers, s.UUID())
|
||||
}
|
||||
|
||||
// Close closes the chat, removing all subscribers from it.
|
||||
func (chat *Chat) Close() error {
|
||||
chat.m.Lock()
|
||||
chat.subscribers = nil
|
||||
chat.m.Unlock()
|
||||
return nil
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/google/uuid"
|
||||
"github.com/sandertv/gophertunnel/minecraft/text"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Subscriber represents an entity that may subscribe to a Chat. In order to do
|
||||
// so, the Subscriber must implement methods to send messages to it.
|
||||
type Subscriber interface {
|
||||
// UUID returns a unique ID for the Subscriber.
|
||||
UUID() uuid.UUID
|
||||
// Message sends a formatted message to the subscriber. The message is
|
||||
// formatted as it would when using fmt.Println.
|
||||
Message(a ...any)
|
||||
}
|
||||
|
||||
// Translator is a Subscriber that is able to translate messages to their own
|
||||
// locale.
|
||||
type Translator interface {
|
||||
// Messaget sends a Translation message to the Translator, using the
|
||||
// arguments passed to fill out any translation parameters.
|
||||
Messaget(t Translation, a ...any)
|
||||
}
|
||||
|
||||
// StdoutSubscriber is an implementation of Subscriber that forwards messages
|
||||
// sent to the chat to the stdout.
|
||||
type StdoutSubscriber struct{}
|
||||
|
||||
var id = uuid.New()
|
||||
|
||||
// UUID ...
|
||||
func (c StdoutSubscriber) UUID() uuid.UUID {
|
||||
return id
|
||||
}
|
||||
|
||||
// Message ...
|
||||
func (c StdoutSubscriber) Message(a ...any) {
|
||||
s := make([]string, len(a))
|
||||
for i, b := range a {
|
||||
s[i] = fmt.Sprint(b)
|
||||
}
|
||||
t := text.ANSI(strings.Join(s, " "))
|
||||
if !strings.HasSuffix(t, "\n") {
|
||||
fmt.Println(t)
|
||||
return
|
||||
}
|
||||
fmt.Print(t)
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package chat
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/sandertv/gophertunnel/minecraft/text"
|
||||
"golang.org/x/text/language"
|
||||
)
|
||||
|
||||
// https://github.com/Mojang/bedrock-samples/blob/main/resource_pack/texts/en_GB.lang
|
||||
|
||||
var MessageJoin = Translate(str("%multiplayer.player.joined"), 1, `%v joined the game`).Enc("<yellow>%v</yellow>")
|
||||
var MessageQuit = Translate(str("%multiplayer.player.left"), 1, `%v left the game`).Enc("<yellow>%v</yellow>")
|
||||
var MessageServerDisconnect = Translate(str("%disconnect.disconnected"), 0, `Disconnected by Server`).Enc("<yellow>%v</yellow>")
|
||||
|
||||
var MessageBedTooFar = Translate(str("%tile.bed.tooFar"), 0, `Bed is too far away`).Enc("<grey>%v</grey>")
|
||||
var MessageBedObstructed = Translate(str("%tile.bed.obstructed"), 0, `Bed is obstructed`).Enc("<grey>%v</grey>")
|
||||
var MessageRespawnPointSet = Translate(str("%tile.bed.respawnSet"), 0, `Respawn point set`).Enc("<grey>%v</grey>")
|
||||
var MessageNoSleep = Translate(str("%tile.bed.noSleep"), 0, `You can only sleep at night and during thunderstorms`).Enc("<grey>%v</grey>")
|
||||
var MessageBedIsOccupied = Translate(str("%tile.bed.occupied"), 0, `This bed is occupied`).Enc("<grey>%v</grey>")
|
||||
var MessageSleeping = Translate(str("%chat.type.sleeping"), 2, `%v is sleeping in a bed. To skip to dawn, %v more users need to sleep in beds at the same time.`)
|
||||
var MessageBedNotValid = Translate(str("%tile.bed.notValid"), 0, `Your home bed was missing or obstructed`)
|
||||
|
||||
type str string
|
||||
|
||||
// Resolve returns the translation identifier as a string.
|
||||
func (s str) Resolve(language.Tag) string { return string(s) }
|
||||
|
||||
// TranslationString is a value that can resolve a translated version of itself
|
||||
// for a language.Tag passed.
|
||||
type TranslationString interface {
|
||||
// Resolve finds a suitable translated version for a translation string for
|
||||
// a specific language.Tag.
|
||||
Resolve(l language.Tag) string
|
||||
}
|
||||
|
||||
// Translate returns a Translation for a TranslationString. The required number
|
||||
// of parameters specifies how many arguments may be passed to Translation.F.
|
||||
// The fallback string should be a 'standard' translation of the string, which
|
||||
// is used when translation.String is called on the translation that results
|
||||
// from a call to Translation.F. This fallback string should have as many
|
||||
// formatting identifiers (like in fmt.Sprintf) as the number of params.
|
||||
func Translate(str TranslationString, params int, fallback string) Translation {
|
||||
return Translation{str: str, params: params, fallback: fallback, format: "%v"}
|
||||
}
|
||||
|
||||
// Translation represents a TranslationString with additional formatting, that
|
||||
// may be filled out by calling F on it with a list of arguments for the
|
||||
// translation.
|
||||
type Translation struct {
|
||||
str TranslationString
|
||||
format string
|
||||
params int
|
||||
fallback string
|
||||
}
|
||||
|
||||
// Zero returns false if a Translation was not created using Translate or
|
||||
// Untranslated.
|
||||
func (t Translation) Zero() bool {
|
||||
return t.format == ""
|
||||
}
|
||||
|
||||
// Enc encapsulates the translation string into the format passed. This format
|
||||
// should have exactly one formatting identifier, %v, to specify where the
|
||||
// translation string should go, such as 'translation: %v'.
|
||||
// Enc accepts colouring formats parsed by text.Colourf.
|
||||
func (t Translation) Enc(format string) Translation {
|
||||
t.format = format
|
||||
return t
|
||||
}
|
||||
|
||||
// Resolve passes 0 arguments to the translation and resolves the translation
|
||||
// string for the language passed. It is equal to calling t.F().Resolve(l).
|
||||
// Resolve panics if the Translation requires at least 1 argument.
|
||||
func (t Translation) Resolve(l language.Tag) string {
|
||||
return t.F().Resolve(l)
|
||||
}
|
||||
|
||||
// F takes arguments for a translation string passed and returns a filled out
|
||||
// translation that may be sent to players. The number of arguments passed must
|
||||
// be exactly equal to the number specified in Translate. If not, F will panic.
|
||||
// Arguments passed are converted to strings using fmt.Sprint(). Exceptions are
|
||||
// made for argument values of the type TranslationString, Translation and
|
||||
// translation, which are resolved based on the Translator's language.
|
||||
// Translations used as arguments should not require any parameters.
|
||||
func (t Translation) F(a ...any) translation {
|
||||
if len(a) != t.params {
|
||||
panic(fmt.Sprintf("translation '%v' requires exactly %v parameters, got %v", t.format, t.params, len(a)))
|
||||
}
|
||||
return translation{t: t, params: a}
|
||||
}
|
||||
|
||||
// translation is a translation string with its arguments filled out. Resolve may
|
||||
// be called to obtain the translated version of the translation string and
|
||||
// Params may be called to obtain the parameters passed in Translation.F.
|
||||
// translation implements the fmt.Stringer and error interfaces.
|
||||
type translation struct {
|
||||
t Translation
|
||||
params []any
|
||||
}
|
||||
|
||||
// Resolve translates the TranslationString of the translation to the language
|
||||
// passed and returns it.
|
||||
func (t translation) Resolve(l language.Tag) string {
|
||||
return text.Colourf(t.t.format, t.t.str.Resolve(l))
|
||||
}
|
||||
|
||||
// Params returns a slice of values that are used to parameterise the
|
||||
// translation returned by Resolve.
|
||||
func (t translation) Params(l language.Tag) []string {
|
||||
params := make([]string, len(t.params))
|
||||
for i, arg := range t.params {
|
||||
if str, ok := arg.(TranslationString); ok {
|
||||
params[i] = str.Resolve(l)
|
||||
continue
|
||||
}
|
||||
params[i] = fmt.Sprint(arg)
|
||||
}
|
||||
return params
|
||||
}
|
||||
|
||||
// String formats and returns the fallback value of the translation.
|
||||
func (t translation) String() string {
|
||||
return fmt.Sprintf(text.Colourf(t.t.format, t.t.fallback), t.params...)
|
||||
}
|
||||
|
||||
// Error formats and returns the fallback value of the translation.
|
||||
func (t translation) Error() string {
|
||||
return t.String()
|
||||
}
|
||||
Reference in New Issue
Block a user