multiserver/chat.go

214 lines
5.2 KiB
Go

package main
import (
"encoding/binary"
"log"
"strings"
"time"
"unicode/utf16"
"github.com/anon55555/mt/rudp"
)
const (
ChatCommandPrefix = "#"
ServerChatCommandPrefix = ":"
)
type chatCommand struct {
privs map[string]bool
function func(*Peer, string)
}
var chatCommands map[string]chatCommand
var onChatMsg []func(*Peer, string) bool
var onServerChatMsg []func(*Peer, string) bool
// RegisterChatCommand registers a callback function that is called
// when a client executes the command and has the required privileges
func RegisterChatCommand(name string, privs map[string]bool, function func(*Peer, string)) {
chatCommands[name] = chatCommand{privs: privs, function: function}
}
// RegisterOnChatMessage registers a callback function that is called
// when a client sends a chat message
// If a callback function returns true the message is not forwarded
// to the minetest server
func RegisterOnChatMessage(function func(*Peer, string) bool) {
onChatMsg = append(onChatMsg, function)
}
// RegisterOnServerChatMessage registers a callback function
// that is called when a server sends a chat message
// If a callback function returns true the message is not forwarded
// to the minetest clients
func RegisterOnServerChatMessage(function func(*Peer, string) bool) {
onServerChatMsg = append(onServerChatMsg, function)
}
func processChatMessage(p *Peer, pkt rudp.Pkt) bool {
s := string(narrow(pkt.Data[4:]))
if strings.HasPrefix(s, ChatCommandPrefix) {
// Chat command
s = strings.Replace(s, ChatCommandPrefix, "", 1)
params := strings.Split(s, " ")
// Priv check
allow, err := p.CheckPrivs(chatCommands[params[0]].privs)
if err != nil {
log.Print(err)
return true
}
if !allow {
str := "You do not have permission to run this command! Required privileges: " + strings.Replace(encodePrivs(chatCommands[params[0]].privs), "|", " ", -1)
wstr := wider([]byte(str))
data := make([]byte, 16+len(wstr))
data[0] = uint8(0x00)
data[1] = uint8(ToClientChatMessage)
data[2] = uint8(0x01)
data[3] = uint8(0x00)
data[4] = uint8(0x00)
data[5] = uint8(0x00)
binary.BigEndian.PutUint16(data[6:8], uint16(len(str)))
copy(data[8:8+len(wstr)], wstr)
data[8+len(wstr)] = uint8(0x00)
data[9+len(wstr)] = uint8(0x00)
data[10+len(wstr)] = uint8(0x00)
data[11+len(wstr)] = uint8(0x00)
binary.BigEndian.PutUint32(data[12+len(wstr):16+len(wstr)], uint32(time.Now().Unix()))
ack, err := p.Send(rudp.Pkt{Data: data})
if err != nil {
log.Print(err)
}
<-ack
return true
}
// Callback
// Existance check
if chatCommands[params[0]].function == nil {
str := "Unknown command " + params[0] + "."
wstr := wider([]byte(str))
data := make([]byte, 16+len(wstr))
data[0] = uint8(0x00)
data[1] = uint8(ToClientChatMessage)
data[2] = uint8(0x01)
data[3] = uint8(0x00)
data[4] = uint8(0x00)
data[5] = uint8(0x00)
binary.BigEndian.PutUint16(data[6:8], uint16(len(str)))
copy(data[8:8+len(wstr)], wstr)
data[8+len(wstr)] = uint8(0x00)
data[9+len(wstr)] = uint8(0x00)
data[10+len(wstr)] = uint8(0x00)
data[11+len(wstr)] = uint8(0x00)
binary.BigEndian.PutUint32(data[12+len(wstr):16+len(wstr)], uint32(time.Now().Unix()))
ack, err := p.Send(rudp.Pkt{Data: data})
if err != nil {
log.Print(err)
}
<-ack
return true
}
chatCommands[params[0]].function(p, strings.Join(params[1:], " "))
return true
} else {
// Regular message
noforward := false
for i := range onChatMsg {
if onChatMsg[i](p, s) {
noforward = true
}
}
return noforward
}
}
func processServerChatMessage(p *Peer, pkt rudp.Pkt) bool {
s := string(narrow(pkt.Data[4:]))
noforward := false
for i := range onServerChatMsg {
if onServerChatMsg[i](p, s) {
noforward = true
}
}
return noforward
}
// SendChatMsg sends a chat message to the Peer if it isn't a server
func (p *Peer) SendChatMsg(msg string) {
if p.IsSrv() {
return
}
wstr := wider([]byte(msg))
data := make([]byte, 16+len(wstr))
data[0] = uint8(0x00)
data[1] = uint8(ToClientChatMessage)
data[2] = uint8(0x01)
data[3] = uint8(0x00)
data[4] = uint8(0x00)
data[5] = uint8(0x00)
binary.BigEndian.PutUint16(data[6:8], uint16(len(msg)))
copy(data[8:8+len(wstr)], wstr)
data[8+len(wstr)] = uint8(0x00)
data[9+len(wstr)] = uint8(0x00)
data[10+len(wstr)] = uint8(0x00)
data[11+len(wstr)] = uint8(0x00)
binary.BigEndian.PutUint32(data[12+len(wstr):16+len(wstr)], uint32(time.Now().Unix()))
ack, err := p.Send(rudp.Pkt{Data: data})
if err != nil {
log.Print(err)
}
<-ack
}
// ChatSendAll sends a chat message to all connected client Peers
func ChatSendAll(msg string) {
peers := GetListener().GetPeers()
for i := range peers {
go peers[i].SendChatMsg(msg)
}
}
func narrow(b []byte) []byte {
if len(b)%2 != 0 {
return nil
}
e := make([]uint16, len(b)/2)
for i := 0; i < len(b); i += 2 {
e[i/2] = binary.BigEndian.Uint16(b[i : 2+i])
}
return []byte(string(utf16.Decode(e)))
}
func wider(b []byte) []byte {
r := make([]byte, len(b)*2)
e := utf16.Encode([]rune(string(b)))
for i := range e {
binary.BigEndian.PutUint16(r[i*2:2+i*2], e[i])
}
return r
}
func init() {
chatCommands = make(map[string]chatCommand)
}