Lua -> Go: Chat

master
HimbeerserverDE 2021-01-14 13:40:31 +01:00
parent 32f4185a0f
commit 80f2589332
6 changed files with 315 additions and 263 deletions

143
chat.go Normal file
View File

@ -0,0 +1,143 @@
package multiserver
import (
"encoding/binary"
"log"
"strings"
"time"
"unicode/utf16"
)
type chatCommand struct {
privs map[string]bool
function func(*Peer, string)
}
var chatCommands map[string]chatCommand
var onChatMsg []func(*Peer, string) bool
func registerChatCommand(name string, privs map[string]bool, function func(*Peer, string)) {
chatCommands[name] = chatCommand{privs: privs, function: function}
}
func registerOnChatMessage(function func(*Peer, string) bool) {
onChatMsg = append(onChatMsg, function)
}
func processChatMessage(p *Peer, pkt Pkt) bool {
s := string(narrow(pkt.Data[4:]))
if strings.HasPrefix(s, "#") {
// Chat command
s = strings.Replace(s, "#", "", 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(Pkt{Data: data})
if err != nil {
log.Print(err)
}
<-ack
return true
}
// Callback
chatCommands[params[0]].function(p, strings.Join(params[1:], " "))
return true
} else {
// Regular message
forward := true
for i := range onChatMsg {
if onChatMsg[i](p, s) {
forward = false
}
}
return forward
}
}
func (p *Peer) sendChatMsg(msg string) {
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(Pkt{Data: data})
if err != nil {
log.Print(err)
}
<-ack
}
func chatSendAll(msg string) {
l := GetListener()
l.mu.Lock()
defer l.mu.Unlock()
for i := range l.addr2peer {
l.addr2peer[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
}

123
command.go Normal file
View File

@ -0,0 +1,123 @@
package multiserver
import (
"encoding/binary"
)
const (
ToClientHello = 0x02
ToClientAuthAccept = 0x03
ToClientAcceptSudoMode = 0x04
ToClientDenySudoMode = 0x05
ToClientAccessDenied = 0x0A
ToClientBlockdata = 0x20
ToClientAddNode = 0x21
ToClientRemoveNode = 0x22
ToClientInventory = 0x27
ToClientTimeOfDay = 0x29
ToClientCsmRestrictionFlags = 0x2A
ToClientPlayerSpeed = 0x2B
ToClientMediaPush = 0x2C
ToClientChatMessage = 0x2F
ToClientActiveObjectRemoveAdd = 0x31
ToClientActiveObjectMessages = 0x32
ToClientHp = 0x33
ToClientMovePlayer = 0x34
ToClientFov = 0x36
ToClientDeathscreen = 0x37
ToClientMedia = 0x38
ToClientTooldef = 0x39
ToClientNodedef = 0x3A
ToClientCraftitemdef = 0x3B
ToClientAnnounceMedia = 0x3C
ToClientItemdef = 0x3D
ToClientPlaySound = 0x3F
ToClientStopSound = 0x40
ToClientPrivileges = 0x41
ToClientInventoryFormspec = 0x42
ToClientDetachedInventory = 0x43
ToClientShowFormspec = 0x44
ToClientMovement = 0x45
ToClientSpawnParticle = 0x46
ToClientAddParticlespawner = 0x47
ToClientHudAdd = 0x49
ToClientHudRm = 0x4A
ToClientHudChange = 0x4B
ToClientHudSetFlags = 0x4C
ToClientHudSetParam = 0x4D
ToClientBreath = 0x4E
ToClientSetSky = 0x4F
ToClientOverrideDayNightRatio = 0x50
ToClientLocalPlayerAnimations = 0x51
ToClientEyeOffset = 0x52
ToClientDeleteParticlespawner = 0x53
ToClientCloudParams = 0x54
ToClientFadeSound = 0x55
ToClientUpdatePlayerList = 0x56
ToClientModchannelMsg = 0x57
ToClientModchannelSignal = 0x58
ToClientNodeMetaChanged = 0x59
ToClientSetSun = 0x5A
ToClientSetMoon = 0x5B
ToClientSetStars = 0x5C
ToClientSrpBytesSB = 0x60
ToClientFormspecPrepend = 0x61
ToClientMinimapModes = 0x62
)
const (
ToServerInit = 0x02
ToServerInit2 = 0x11
ToServerModchannelJoin = 0x17
ToServerModchannelLeave = 0x18
ToServerModchannelMsg = 0x19
ToServerPlayerPos = 0x23
ToServerGotblocks = 0x24
ToServerDeletedblocks = 0x25
ToServerInventoryAction = 0x31
ToServerChatMessage = 0x32
ToServerDamage = 0x35
ToServerPlayerItem = 0x37
ToServerRespawn = 0x38
ToServerInteract = 0x39
ToServerRemovedSounds = 0x3A
ToServerNodeMetaFields = 0x3B
ToServerInventoryFields = 0x3C
ToServerRequestMedia = 0x40
ToServerClientReady = 0x43
ToServerFirstSrp = 0x50
ToServerSrpBytesA = 0x51
ToServerSrpBytesM = 0x52
)
const (
AccessDeniedWrongPassword = iota
AccessDeniedUnexpectedData
AccessDeniedSingleplayer
AccessDeniedWrongVersion
AccessDeniedWrongCharsInName
AccessDeniedWrongName
AccessDeniedTooManyUsers
AccessDeniedEmptyPassword
AccessDeniedAlreadyConnected
AccessDeniedServerFail
AccessDeniedCustomString
AccessDeniedShutdown
AccessDeniedCrash
)
func processPktCommand(p *Peer, pkt Pkt) bool {
if p.IsSrv() {
switch cmd := binary.BigEndian.Uint16(pkt.Data[0:2]); cmd {
default:
return false
}
} else {
switch cmd := binary.BigEndian.Uint16(pkt.Data[0:2]); cmd {
case ToServerChatMessage:
return processChatMessage(p, pkt)
default:
return false
}
}
}

View File

@ -1,253 +0,0 @@
package multiserver
import (
"encoding/binary"
"github.com/yuin/gopher-lua"
"log"
"strings"
"time"
"unicode/utf16"
)
type chatCommand struct {
name string
privs map[string]bool
function *lua.LFunction
}
var chatCommands []chatCommand
var chatMessageHandlers []*lua.LFunction
func registerChatCommand(L *lua.LState) int {
name := L.ToString(1)
cmddef := L.ToTable(2)
privs := cmddef.RawGet(lua.LString("privs")).(*lua.LTable)
pmap := make(map[string]bool)
privs.ForEach(func(k, v lua.LValue) {
if lua.LVAsBool(v) {
pmap[k.String()] = true
}
})
f := cmddef.RawGet(lua.LString("func")).(*lua.LFunction)
chatCommands = append(chatCommands, chatCommand{name: name, privs: pmap, function: f})
return 0
}
func registerOnChatMessage(L *lua.LState) int {
f := L.ToFunction(1)
chatMessageHandlers = append(chatMessageHandlers, f)
return 0
}
func processChatMessage(peerid PeerID, msg []byte) bool {
s := string(narrow(msg[4:]))
if strings.HasPrefix(s, "/") {
// Chat command
s = strings.Replace(s, "/", "", 1)
params := strings.Split(s, " ")
for i := range chatCommands {
if chatCommands[i].name == params[0] {
// Priv check
db, err := initAuthDB()
if err != nil {
log.Print(err)
return true
}
eprivs, err := readPrivItem(db, string(GetListener().GetPeerByID(peerid).username))
if err != nil {
log.Print(err)
return true
}
db.Close()
privs := decodePrivs(eprivs)
allowAccess := true
for priv := range chatCommands[i].privs {
if chatCommands[i].privs[priv] && !privs[priv] {
allowAccess = false
}
}
if !allowAccess {
str := "You do not have permission to run this command! Required privileges: " + strings.Replace(encodePrivs(chatCommands[i].privs), "|", " ", -1)
wstr := wider([]byte(str))
data := make([]byte, 16+len(wstr))
data[0] = uint8(0x00)
data[1] = uint8(0x2F)
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 := GetListener().GetPeerByID(peerid).Send(Pkt{Data: data, ChNo: 0, Unrel: false})
if err != nil {
log.Print(err)
}
<-ack
return true
}
// Callback
if err := l.CallByParam(lua.P{Fn: chatCommands[i].function, NRet: 1, Protect: true}, lua.LNumber(peerid), lua.LString(strings.Join(params[1:], " "))); err != nil {
log.Print(err)
go func() {
End(true, true)
}()
}
if str, ok := l.Get(-1).(lua.LString); ok {
wstr := wider([]byte(str.String()))
data := make([]byte, 16+len(wstr))
data[0] = uint8(0x00)
data[1] = uint8(0x2F)
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.String())))
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 := GetListener().GetPeerByID(peerid).Send(Pkt{Data: data})
if err != nil {
log.Print(err)
}
<-ack
}
return true
}
}
} else {
// Regular message
for i := range chatMessageHandlers {
if err := l.CallByParam(lua.P{Fn: chatMessageHandlers[i], NRet: 1, Protect: true}, lua.LNumber(peerid), lua.LString(s)); err != nil {
log.Print(err)
End(true, true)
}
if b, ok := l.Get(-1).(lua.LBool); ok {
if lua.LVAsBool(b) {
return true
}
}
}
}
return false
}
func chatSendPlayer(L *lua.LState) int {
id := PeerID(L.ToInt(1))
msg := L.ToString(2)
l := GetListener()
p := l.GetPeerByID(id)
wstr := wider([]byte(msg))
data := make([]byte, 16+len(wstr))
data[0] = uint8(0x00)
data[1] = uint8(0x2F)
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(Pkt{Data: data})
if err != nil {
log.Print(err)
return 0
}
<-ack
return 0
}
func chatSendAll(L *lua.LState) int {
msg := L.ToString(1)
l := GetListener()
wstr := wider([]byte(msg))
data := make([]byte, 16+len(wstr))
data[0] = uint8(0x00)
data[1] = uint8(0x2F)
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()))
i := PeerIDCltMin
for l.id2peer[i].Peer != nil {
ack, err := l.id2peer[i].Send(Pkt{Data: data})
if err != nil {
log.Print(err)
return 0
}
<-ack
i++
}
return 0
}
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
}

5
lua.go
View File

@ -15,11 +15,6 @@ func InitLua() {
addLuaFunc(redirect, "redirect")
addLuaFunc(getServers, "get_servers")
addLuaFunc(registerOnRedirectDone, "register_on_redirect_done")
// chatmessage
addLuaFunc(registerChatCommand, "register_chatcommand")
addLuaFunc(registerOnChatMessage, "register_on_chatmessage")
addLuaFunc(chatSendPlayer, "chat_send_player")
addLuaFunc(chatSendAll, "chat_send_all")
// player
addLuaFunc(getPlayerName, "get_player_name")
addLuaFunc(luaGetPeerID, "get_peer_id")

View File

@ -138,3 +138,50 @@ func init() {
modPrivItem(db, admin.(string), newprivs)
}
}
func (p *Peer) getPrivs() (map[string]bool, error) {
db, err := initAuthDB()
if err != nil {
return nil, err
}
defer db.Close()
eprivs, err := readPrivItem(db, string(p.username))
if err != nil {
return nil, err
}
return decodePrivs(eprivs), nil
}
func (p *Peer) setPrivs(privs map[string]bool) error {
db, err := initAuthDB()
if err != nil {
return err
}
defer db.Close()
err = modPrivItem(db, string(p.username), encodePrivs(privs))
if err != nil {
return err
}
return nil
}
func (p *Peer) checkPrivs(req map[string]bool) (bool, error) {
privs, err := p.getPrivs()
if err != nil {
return false, err
}
allow := true
for priv := range req {
if req[priv] && !privs[priv] {
allow = false
break
}
}
return allow, nil
}

View File

@ -34,11 +34,8 @@ func Proxy(src, dst *Peer) {
}
// Process
// Chat message
if pkt.Data[0] == uint8(0x00) && pkt.Data[1] == uint8(0x32) && !src.IsSrv() {
if processChatMessage(src.ID(), pkt.Data) {
continue
}
if processPktCommand(src, pkt) {
continue
}
// Active object remove add
if pkt.Data[0] == uint8(0x00) && pkt.Data[1] == uint8(0x31) && src.IsSrv() {