Merge pull request #15 from Ellie42/improve-chat

Improve chat and additional small fixes for party interaction
This commit is contained in:
Andrew Gutekanst
2020-03-10 18:30:16 -04:00
committed by GitHub
7 changed files with 242 additions and 53 deletions

1
.gitignore vendored
View File

@@ -1,6 +1,7 @@
www/tw/
www/jp/
vendor/
bin/*.bin
bin/quests/*.bin
bin/questlists/*.bin

View File

@@ -14,18 +14,19 @@ type Config struct {
DevMode bool
DevModeOptions DevModeOptions
Database Database
Launcher Launcher
Sign Sign
Channel Channel
Entrance Entrance
Database Database
Launcher Launcher
Sign Sign
Channel Channel
Entrance Entrance
}
// DevModeOptions holds various debug/temporary options for use while developing Erupe.
type DevModeOptions struct {
CleanDB bool // Automatically wipes the DB on server reset.
MaxLauncherHR bool // Sets the HR returned in the launcher to HR9 so that you can join non-beginner worlds.
FixedStageID bool // Causes all move_stage to use the ID sl1Ns200p0a0u0 to get you into all stages
CleanDB bool // Automatically wipes the DB on server reset.
MaxLauncherHR bool // Sets the HR returned in the launcher to HR9 so that you can join non-beginner worlds.
FixedStageID bool // Causes all move_stage to use the ID sl1Ns200p0a0u0 to get you into all stages
LogOutboundMessages bool // Log all messages sent to the clients
}
// Database holds the postgres database config.

View File

@@ -0,0 +1,48 @@
package binpacket
import (
"github.com/Andoryuuta/Erupe/network"
"github.com/Andoryuuta/byteframe"
)
type ChatTargetType uint16
const (
CHAT_TARGET_PRIVATE = 0x05
CHAT_TARGET_PARTY = 0x04
)
type MsgBinTargetedChatMessage struct {
// I can't see a reason if this is indeed the number of targets, that
// it should use 2 bytes
TargetCount uint16
TargetCharIDs []uint32
TargetType uint16
RawDataPayload []byte
}
// Opcode returns the ID associated with this packet type.
func (m *MsgBinTargetedChatMessage) Opcode() network.PacketID {
return network.MSG_SYS_CAST_BINARY
}
func (m *MsgBinTargetedChatMessage) Parse(bf *byteframe.ByteFrame) error {
m.TargetCount = bf.ReadUint16()
i := uint16(0)
m.TargetCharIDs = make([]uint32, m.TargetCount)
for ; i < m.TargetCount; i++ {
m.TargetCharIDs[i] = bf.ReadUint32()
}
m.TargetType = bf.ReadUint16()
m.RawDataPayload = bf.DataFromCurrent()
return nil
}
// Build builds a binary packet from the current data.
func (m *MsgBinTargetedChatMessage) Build(bf *byteframe.ByteFrame) error {
panic("Not implemented")
}

View File

@@ -6,7 +6,9 @@ import (
)
// MsgSysDeleteUser represents the MSG_SYS_DELETE_USER
type MsgSysDeleteUser struct{}
type MsgSysDeleteUser struct {
CharID uint32
}
// Opcode returns the ID associated with this packet type.
func (m *MsgSysDeleteUser) Opcode() network.PacketID {
@@ -20,5 +22,7 @@ func (m *MsgSysDeleteUser) Parse(bf *byteframe.ByteFrame) error {
// Build builds a binary packet from the current data.
func (m *MsgSysDeleteUser) Build(bf *byteframe.ByteFrame) error {
panic("Not implemented")
bf.WriteUint32(m.CharID)
return nil
}

View File

@@ -167,3 +167,20 @@ func (s *Server) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session)
session.QueueSendNonBlocking(bf.Data())
}
}
func (s *Server) FindSessionByCharID(charID uint32) *Session {
s.stagesLock.RLock()
defer s.stagesLock.RUnlock()
for _, stage := range s.stages {
stage.RLock()
for client := range stage.clients {
if client.charID == charID {
stage.RUnlock()
return client
}
}
stage.RUnlock()
}
return nil
}

View File

@@ -6,6 +6,8 @@ import (
"encoding/base64"
"encoding/hex"
"fmt"
"github.com/Andoryuuta/Erupe/network/binpacket"
"io"
"io/ioutil"
"os"
"path/filepath"
@@ -177,7 +179,9 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
s.QueueAck(pkt.AckHandle, bf.Data())
}
func handleMsgSysLogout(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysLogout(s *Session, p mhfpacket.MHFPacket) {
logoutPlayer(s)
}
func handleMsgSysSetStatus(s *Session, p mhfpacket.MHFPacket) {}
@@ -190,21 +194,71 @@ func handleMsgSysPing(s *Session, p mhfpacket.MHFPacket) {
s.QueueAck(pkt.AckHandle, bf.Data())
}
const (
BINARY_MESSAGE_TYPE_CHAT = 1
BINARY_MESSAGE_TYPE_EMOTE = 6
)
const (
CHAT_TYPE_WORLD = 0x0a
CHAT_TYPE_STAGE = 0x03
CHAT_TYPE_TARGETED = 0x01
)
func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysCastBinary)
// Simply forward the packet to all the other clients.
// (The client never uses Type0 upon receiving)
// TODO(Andoryuuta): Does this broadcast need to be limited? (world, stage, guild, etc).
resp := &mhfpacket.MsgSysCastedBinary{
CharID: s.charID,
Type0: pkt.Type0,
Type1: pkt.Type1,
RawDataPayload: pkt.RawDataPayload,
}
s.server.BroadcastMHF(resp, s)
if pkt.Type0 == 3 && pkt.Type1 == 1 {
if pkt.Type1 == BINARY_MESSAGE_TYPE_CHAT {
bf := byteframe.NewByteFrame()
bf.WriteBytes(pkt.RawDataPayload)
bf.Seek(0, io.SeekStart)
fmt.Println("Got chat message!")
switch pkt.Type0 {
case CHAT_TYPE_WORLD:
s.server.BroadcastMHF(resp, s)
case CHAT_TYPE_STAGE:
s.stage.BroadcastMHF(resp, s)
case CHAT_TYPE_TARGETED:
chatMessage := &binpacket.MsgBinTargetedChatMessage{}
err := chatMessage.Parse(bf)
if err != nil {
s.logger.Warn("failed to parse chat message")
break
}
chatBf := byteframe.NewByteFrame()
chatBf.WriteUint16(chatMessage.TargetType)
chatBf.WriteBytes(chatMessage.RawDataPayload)
resp = &mhfpacket.MsgSysCastedBinary{
CharID: s.charID,
Type0: pkt.Type0,
Type1: pkt.Type1,
RawDataPayload: chatBf.Data(),
}
for _, targetID := range chatMessage.TargetCharIDs {
char := s.server.FindSessionByCharID(targetID)
if char != nil {
char.QueueSendMHF(resp)
}
}
default:
s.stage.BroadcastMHF(resp, s)
}
/*
// Made the inside of the casted binary
payload := byteframe.NewByteFrame()
@@ -237,6 +291,11 @@ func handleMsgSysCastBinary(s *Session, p mhfpacket.MHFPacket) {
bfw.WriteUint16(uint16(len(payloadBytes)))
bfw.WriteBytes(payloadBytes)
*/
} else {
// Simply forward the packet to all the other clients.
// (The client never uses Type0 upon receiving)
// TODO(Andoryuuta): Does this broadcast need to be limited? (world, stage, guild, etc).
s.server.BroadcastMHF(resp, s)
}
}
@@ -348,6 +407,7 @@ func handleMsgSysCreateStage(s *Session, p mhfpacket.MHFPacket) {
s.server.stagesLock.Lock()
stage := NewStage(stripNullTerminator(pkt.StageID))
stage.maxPlayers = uint16(pkt.PlayerCount)
s.server.stages[stage.id] = stage
s.server.stagesLock.Unlock()
@@ -364,27 +424,7 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
s.server.stagesLock.Unlock()
if s.stage != nil {
s.stage.Lock()
// Remove client from old stage.
delete(s.stage.clients, s)
// Delete old stage objects owned by the client.
s.logger.Info("Sending MsgSysDeleteObject to old stage clients")
for objID, stageObject := range s.stage.objects {
if stageObject.ownerCharID == s.charID {
// Broadcast the deletion to clients in the stage.
s.stage.BroadcastMHF(&mhfpacket.MsgSysDeleteObject{
ObjID: stageObject.id,
}, s)
// TODO(Andoryuuta): Should this be sent to the owner's client as well? it currently isn't.
// Actually delete it form the objects map.
delete(s.stage.objects, objID)
}
}
s.stage.Unlock()
removeSessionFromStage(s)
}
// Add the new stage.
@@ -407,25 +447,28 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
s.QueueAck(ackHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
// Notify existing stage clients that this new client has entered.
s.logger.Info("Sending MsgSysInsertUser & MsgSysNotifyUserBinary")
s.logger.Info("Sending MsgSysInsertUser")
s.stage.BroadcastMHF(&mhfpacket.MsgSysInsertUser{
CharID: s.charID,
}, s)
s.stage.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{
CharID: s.charID,
BinaryType: 1,
}, s)
s.stage.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{
CharID: s.charID,
BinaryType: 2,
}, s)
s.stage.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{
CharID: s.charID,
BinaryType: 3,
}, s)
// It seems to be acceptable to recast all MSG_SYS_SET_USER_BINARY messages so far,
// players are still notified when a new player has joined the stage.
// These extra messages may not be needed
//s.stage.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{
// CharID: s.charID,
// BinaryType: 1,
//}, s)
//s.stage.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{
// CharID: s.charID,
// BinaryType: 2,
//}, s)
//s.stage.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{
// CharID: s.charID,
// BinaryType: 3,
//}, s)
// Notify the entree client about all of the existing clients in the stage.
//Notify the entree client about all of the existing clients in the stage.
s.logger.Info("Notifying entree about existing stage clients")
s.stage.RLock()
clientNotif := byteframe.NewByteFrame()
@@ -483,6 +526,54 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
s.QueueSend(clientDupObjNotif.Data())
}
func removeSessionFromStage(s *Session) {
s.stage.Lock()
defer s.stage.Unlock()
// Remove client from old stage.
delete(s.stage.clients, s)
// Delete old stage objects owned by the client.
s.logger.Info("Sending MsgSysDeleteObject to old stage clients")
for objID, stageObject := range s.stage.objects {
if stageObject.ownerCharID == s.charID {
// Broadcast the deletion to clients in the stage.
s.stage.BroadcastMHF(&mhfpacket.MsgSysDeleteObject{
ObjID: stageObject.id,
}, s)
// TODO(Andoryuuta): Should this be sent to the owner's client as well? it currently isn't.
// Actually delete it form the objects map.
delete(s.stage.objects, objID)
}
}
}
func stageContainsSession(stage *Stage, s *Session) bool {
stage.RLock()
defer stage.RUnlock()
for session := range stage.clients {
if session == s {
return true
}
}
return false
}
func logoutPlayer(s *Session) {
s.stage.RLock()
for client := range s.stage.clients {
client.QueueSendMHF(&mhfpacket.MsgSysDeleteUser{
CharID: s.charID,
})
}
s.stage.RUnlock()
removeSessionFromStage(s)
}
func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysEnterStage)
@@ -540,7 +631,7 @@ func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysReserveStage)
stageID := stripNullTerminator(pkt.StageID)
fmt.Printf("Got reserve stage req, Unk0:%v, StageID:%v\n", pkt.Unk0, stageID)
fmt.Printf("Got reserve stage req, TargetCount:%v, StageID:%v\n", pkt.Unk0, stageID)
// Try to get the stage
s.server.stagesLock.Lock()
@@ -568,6 +659,7 @@ func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) {
} else {
s.QueueAck(pkt.AckHandle, []byte{0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
}
}
func handleMsgSysUnreserveStage(s *Session, p mhfpacket.MHFPacket) {
@@ -691,6 +783,11 @@ func handleMsgSysGetStageBinary(s *Session, p mhfpacket.MHFPacket) {
if gotBinary {
doSizedAckResp(s, pkt.AckHandle, stageBinary)
} else if pkt.BinaryType1 == 4 {
// This particular type seems to be expecting data that isn't set
// is it required before the party joining can be completed
s.QueueAck(pkt.AckHandle, []byte{0x01, 0x00, 0x00, 0x00, 0x10})
} else {
s.logger.Warn("Failed to get stage binary", zap.Uint8("BinaryType0", pkt.BinaryType0), zap.Uint8("pkt.BinaryType1", pkt.BinaryType1))
s.logger.Warn("Sending blank stage binary")
@@ -846,7 +943,7 @@ func handleMsgSysCreateObject(s *Session, p mhfpacket.MHFPacket) {
// Response to our requesting client.
resp := byteframe.NewByteFrame()
resp.WriteUint32(0) // Unk, is this echoed back from pkt.Unk0?
resp.WriteUint32(0) // Unk, is this echoed back from pkt.TargetCount?
resp.WriteUint32(objID) // New local obj handle.
s.QueueAck(pkt.AckHandle, resp.Data())
@@ -909,6 +1006,13 @@ func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) {
s.server.userBinaryPartsLock.Lock()
s.server.userBinaryParts[userBinaryPartID{charID: s.charID, index: pkt.BinaryType}] = pkt.RawDataPayload
s.server.userBinaryPartsLock.Unlock()
msg := &mhfpacket.MsgSysNotifyUserBinary{
CharID: s.charID,
BinaryType: pkt.BinaryType,
}
s.stage.BroadcastMHF(msg, s)
}
func handleMsgSysGetUserBinary(s *Session, p mhfpacket.MHFPacket) {

View File

@@ -3,6 +3,7 @@ package channelserver
import (
"encoding/hex"
"fmt"
"io"
"net"
"sync"
@@ -59,6 +60,11 @@ func (s *Session) Start() {
// QueueSend queues a packet (raw []byte) to be sent.
func (s *Session) QueueSend(data []byte) {
if s.server.erupeConfig.DevMode && s.server.erupeConfig.DevModeOptions.LogOutboundMessages {
fmt.Printf("Sending To CharID: '%x'\n", s.charID)
fmt.Printf("Sent Data:\n%s\n", hex.Dump(data))
}
s.sendPackets <- data
}
@@ -119,6 +125,13 @@ func (s *Session) sendLoop() {
func (s *Session) recvLoop() {
for {
pkt, err := s.cryptConn.ReadPacket()
if err == io.EOF {
s.logger.Info(fmt.Sprintf("Character(%d) disconnected", s.charID))
logoutPlayer(s)
return
}
if err != nil {
s.logger.Warn("Error on ReadPacket, exiting recv loop", zap.Error(err))
return
@@ -145,6 +158,7 @@ func (s *Session) handlePacketGroup(pktGroup []byte) {
opcode != network.MSG_SYS_NOP &&
opcode != network.MSG_SYS_TIME &&
opcode != network.MSG_SYS_EXTEND_THRESHOLD {
fmt.Printf("CharID: '%x'\n", s.charID)
fmt.Printf("Opcode: %s\n", opcode)
fmt.Printf("Data:\n%s\n", hex.Dump(pktGroup))
}