stage as a /internal/system

This commit is contained in:
stratic-dev
2024-10-17 20:22:53 +01:00
parent 674ac9bd47
commit 094855b984
10 changed files with 177 additions and 158 deletions

View File

@@ -1,4 +1,4 @@
package channelserver
package system
import (
"sync"
@@ -6,18 +6,24 @@ import (
"erupe-ce/network/mhfpacket"
)
type SessionStage interface {
QueueSendMHF(packet mhfpacket.MHFPacket)
GetCharID() uint32
GetName() string
}
// Object holds infomation about a specific object.
type Object struct {
sync.RWMutex
id uint32
ownerCharID uint32
x, y, z float32
Id uint32
OwnerCharID uint32
X, Y, Z float32
}
// stageBinaryKey is a struct used as a map key for identifying a stage binary part.
type stageBinaryKey struct {
id0 uint8
id1 uint8
type StageBinaryKey struct {
Id0 uint8
Id1 uint8
}
// Stage holds stage-specific information
@@ -25,49 +31,49 @@ type Stage struct {
sync.RWMutex
// Stage ID string
id string
Id string
// Objects
objects map[uint32]*Object
Objects map[uint32]*Object
objectIndex uint8
// Map of session -> charID.
// These are clients that are CURRENTLY in the stage
clients map[*Session]uint32
Clients map[SessionStage]uint32
// Map of charID -> bool, key represents whether they are ready
// These are clients that aren't in the stage, but have reserved a slot (for quests, etc).
reservedClientSlots map[uint32]bool
ReservedClientSlots map[uint32]bool
// These are raw binary blobs that the stage owner sets,
// other clients expect the server to echo them back in the exact same format.
rawBinaryData map[stageBinaryKey][]byte
RawBinaryData map[StageBinaryKey][]byte
host *Session
maxPlayers uint16
password string
locked bool
Host SessionStage
MaxPlayers uint16
Password string
Locked bool
}
// NewStage creates a new stage with intialized values.
func NewStage(ID string) *Stage {
s := &Stage{
id: ID,
clients: make(map[*Session]uint32),
reservedClientSlots: make(map[uint32]bool),
objects: make(map[uint32]*Object),
Id: ID,
Clients: make(map[SessionStage]uint32),
ReservedClientSlots: make(map[uint32]bool),
Objects: make(map[uint32]*Object),
objectIndex: 0,
rawBinaryData: make(map[stageBinaryKey][]byte),
maxPlayers: 127,
RawBinaryData: make(map[StageBinaryKey][]byte),
MaxPlayers: 127,
}
return s
}
// BroadcastMHF queues a MHFPacket to be sent to all sessions in the stage.
func (s *Stage) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) {
func (s *Stage) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession SessionStage) {
s.Lock()
defer s.Unlock()
for session := range s.clients {
for session := range s.Clients {
if session == ignoredSession {
continue
}
@@ -76,13 +82,13 @@ func (s *Stage) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) {
}
func (s *Stage) isCharInQuestByID(charID uint32) bool {
if _, exists := s.reservedClientSlots[charID]; exists {
if _, exists := s.ReservedClientSlots[charID]; exists {
return exists
}
return false
}
func (s *Stage) isQuest() bool {
return len(s.reservedClientSlots) > 0
func (s *Stage) IsQuest() bool {
return len(s.ReservedClientSlots) > 0
}

View File

@@ -146,11 +146,11 @@ func psn(s *Session, args []string) error {
func reload(s *Session, _ []string) error {
s.sendMessage(t("commands.reload", v{}))
var temp mhfpacket.MHFPacket
for _, object := range s.stage.objects {
if object.ownerCharID == s.CharID {
for _, object := range s.stage.Objects {
if object.OwnerCharID == s.CharID {
continue
}
temp = &mhfpacket.MsgSysDeleteObject{ObjID: object.id}
temp = &mhfpacket.MsgSysDeleteObject{ObjID: object.Id}
s.QueueSendMHF(temp)
}
for _, session := range s.Server.sessions {
@@ -175,17 +175,17 @@ func reload(s *Session, _ []string) error {
s.QueueSendMHF(temp)
}
}
for _, obj := range s.stage.objects {
if obj.ownerCharID == s.CharID {
for _, obj := range s.stage.Objects {
if obj.OwnerCharID == s.CharID {
continue
}
temp = &mhfpacket.MsgSysDuplicateObject{
ObjID: obj.id,
X: obj.x,
Y: obj.y,
Z: obj.z,
ObjID: obj.Id,
X: obj.X,
Y: obj.Y,
Z: obj.Z,
Unk0: 0,
OwnerCharID: obj.ownerCharID,
OwnerCharID: obj.OwnerCharID,
}
s.QueueSendMHF(temp)
}

View File

@@ -4,6 +4,7 @@ import (
"encoding/binary"
"erupe-ce/config"
"erupe-ce/internal/model"
"erupe-ce/internal/system"
"erupe-ce/utils/db"
"erupe-ce/utils/gametime"
"erupe-ce/utils/mhfcourse"
@@ -155,18 +156,18 @@ func logoutPlayer(s *Session) {
for _, stage := range s.Server.stages {
// Tell sessions registered to disconnecting players quest to unregister
if stage.host != nil && stage.host.CharID == s.CharID {
if stage.Host != nil && stage.Host.GetCharID() == s.CharID {
for _, sess := range s.Server.sessions {
for rSlot := range stage.reservedClientSlots {
if sess.CharID == rSlot && sess.stage != nil && sess.stage.id[3:5] != "Qs" {
for rSlot := range stage.ReservedClientSlots {
if sess.CharID == rSlot && sess.stage != nil && sess.stage.Id[3:5] != "Qs" {
sess.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{})
}
}
}
}
for session := range stage.clients {
if session.CharID == s.CharID {
delete(stage.clients, session)
for session := range stage.Clients {
if session.GetCharID() == s.CharID {
delete(stage.Clients, session)
}
}
}
@@ -211,8 +212,8 @@ func logoutPlayer(s *Session) {
s.Server.Lock()
for _, stage := range s.Server.stages {
if _, exists := stage.reservedClientSlots[s.CharID]; exists {
delete(stage.reservedClientSlots, s.CharID)
if _, exists := stage.ReservedClientSlots[s.CharID]; exists {
delete(stage.ReservedClientSlots, s.CharID)
}
}
s.Server.Unlock()
@@ -285,7 +286,7 @@ func handleMsgSysRecordLog(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
}
}
// remove a client returning to town from reserved slots to make sure the stage is hidden from board
delete(s.stage.reservedClientSlots, s.CharID)
delete(s.stage.ReservedClientSlots, s.CharID)
s.DoAckSimpleSucceed(pkt.AckHandle, make([]byte, 4))
}
@@ -377,12 +378,12 @@ func handleMsgMhfTransitMessage(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket)
if pkt.SearchType == 2 && !strings.Contains(session.Name, term) {
continue
}
if pkt.SearchType == 3 && session.Server.IP != ip && session.Server.Port != port && session.stage.id != term {
if pkt.SearchType == 3 && session.Server.IP != ip && session.Server.Port != port && session.stage.Id != term {
continue
}
count++
sessionName := stringsupport.UTF8ToSJIS(session.Name)
sessionStage := stringsupport.UTF8ToSJIS(session.stage.id)
sessionStage := stringsupport.UTF8ToSJIS(session.stage.Id)
if !local {
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
} else {
@@ -497,8 +498,8 @@ func handleMsgMhfTransitMessage(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket)
if count == maxResults {
break
}
if strings.HasPrefix(stage.id, findPartyParams.StagePrefix) {
sb3 := byteframe.NewByteFrameFromBytes(stage.rawBinaryData[stageBinaryKey{1, 3}])
if strings.HasPrefix(stage.Id, findPartyParams.StagePrefix) {
sb3 := byteframe.NewByteFrameFromBytes(stage.RawBinaryData[system.StageBinaryKey{1, 3}])
sb3.Seek(4, 0)
stageDataParams := 7
@@ -546,17 +547,17 @@ func handleMsgMhfTransitMessage(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket)
resp.WriteUint16(0) // Static?
resp.WriteUint16(0) // Unk, [0 1 2]
resp.WriteUint16(uint16(len(stage.clients) + len(stage.reservedClientSlots)))
resp.WriteUint16(stage.maxPlayers)
resp.WriteUint16(uint16(len(stage.Clients) + len(stage.ReservedClientSlots)))
resp.WriteUint16(stage.MaxPlayers)
// TODO: Retail returned the number of clients in quests, not workshop/my series
resp.WriteUint16(uint16(len(stage.reservedClientSlots)))
resp.WriteUint16(uint16(len(stage.ReservedClientSlots)))
resp.WriteUint8(0) // Static?
resp.WriteUint8(uint8(stage.maxPlayers))
resp.WriteUint8(uint8(stage.MaxPlayers))
resp.WriteUint8(1) // Static?
resp.WriteUint8(uint8(len(stage.id) + 1))
resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 0}])))
resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 1}])))
resp.WriteUint8(uint8(len(stage.Id) + 1))
resp.WriteUint8(uint8(len(stage.RawBinaryData[system.StageBinaryKey{1, 0}])))
resp.WriteUint8(uint8(len(stage.RawBinaryData[system.StageBinaryKey{1, 1}])))
for i := range stageData {
if config.GetConfig().ClientID >= config.Z1 {
@@ -568,9 +569,9 @@ func handleMsgMhfTransitMessage(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket)
resp.WriteUint8(0) // Unk
resp.WriteUint8(0) // Unk
resp.WriteNullTerminatedBytes([]byte(stage.id))
resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 0}])
resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 1}])
resp.WriteNullTerminatedBytes([]byte(stage.Id))
resp.WriteBytes(stage.RawBinaryData[system.StageBinaryKey{1, 0}])
resp.WriteBytes(stage.RawBinaryData[system.StageBinaryKey{1, 1}])
}
}
}

View File

@@ -147,7 +147,7 @@ func handleMsgSysCastBinary(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
}
return
}
if (pkt.BroadcastType == constant.BroadcastTypeStage && s.stage.id == "sl1Ns200p0a0u0") || pkt.BroadcastType == constant.BroadcastTypeWorld {
if (pkt.BroadcastType == constant.BroadcastTypeStage && s.stage.Id == "sl1Ns200p0a0u0") || pkt.BroadcastType == constant.BroadcastTypeWorld {
s.Server.DiscordChannelSend(chatMessage.SenderName, chatMessage.Message)
}
}

View File

@@ -28,20 +28,20 @@ func handleMsgSysEnumerateClient(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket)
var clients []uint32
switch pkt.Get {
case 0: // All
for _, cid := range stage.clients {
for _, cid := range stage.Clients {
clients = append(clients, cid)
}
for cid := range stage.reservedClientSlots {
for cid := range stage.ReservedClientSlots {
clients = append(clients, cid)
}
case 1: // Not ready
for cid, ready := range stage.reservedClientSlots {
for cid, ready := range stage.ReservedClientSlots {
if !ready {
clients = append(clients, cid)
}
}
case 2: // Ready
for cid, ready := range stage.reservedClientSlots {
for cid, ready := range stage.ReservedClientSlots {
if ready {
clients = append(clients, cid)
}

View File

@@ -4,6 +4,7 @@ import (
"fmt"
"erupe-ce/config"
"erupe-ce/internal/system"
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
@@ -14,30 +15,30 @@ func handleMsgSysCreateObject(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysCreateObject)
s.stage.Lock()
newObj := &Object{
id: s.NextObjectID(),
ownerCharID: s.CharID,
x: pkt.X,
y: pkt.Y,
z: pkt.Z,
newObj := &system.Object{
Id: s.NextObjectID(),
OwnerCharID: s.CharID,
X: pkt.X,
Y: pkt.Y,
Z: pkt.Z,
}
s.stage.objects[s.CharID] = newObj
s.stage.Objects[s.CharID] = newObj
s.stage.Unlock()
// Response to our requesting client.
resp := byteframe.NewByteFrame()
resp.WriteUint32(newObj.id) // New local obj handle.
resp.WriteUint32(newObj.Id) // New local obj handle.
s.DoAckSimpleSucceed(pkt.AckHandle, resp.Data())
// Duplicate the object creation to all sessions in the same stage.
dupObjUpdate := &mhfpacket.MsgSysDuplicateObject{
ObjID: newObj.id,
X: newObj.x,
Y: newObj.y,
Z: newObj.z,
OwnerCharID: newObj.ownerCharID,
ObjID: newObj.Id,
X: newObj.X,
Y: newObj.Y,
Z: newObj.Z,
OwnerCharID: newObj.OwnerCharID,
}
s.Logger.Info(fmt.Sprintf("Broadcasting new object: %s (%d)", s.Name, newObj.id))
s.Logger.Info(fmt.Sprintf("Broadcasting new object: %s (%d)", s.Name, newObj.Id))
s.stage.BroadcastMHF(dupObjUpdate, s)
}
@@ -49,11 +50,11 @@ func handleMsgSysPositionObject(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket)
fmt.Printf("[%s] with objectID [%d] move to (%f,%f,%f)\n\n", s.Name, pkt.ObjID, pkt.X, pkt.Y, pkt.Z)
}
s.stage.Lock()
object, ok := s.stage.objects[s.CharID]
object, ok := s.stage.Objects[s.CharID]
if ok {
object.x = pkt.X
object.y = pkt.Y
object.z = pkt.Z
object.X = pkt.X
object.Y = pkt.Y
object.Z = pkt.Z
}
s.stage.Unlock()
// One of the few packets we can just re-broadcast directly.

View File

@@ -5,8 +5,10 @@ import (
"strings"
"time"
"erupe-ce/internal/system"
"erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe"
ps "erupe-ce/utils/pascalstring"
"github.com/jmoiron/sqlx"
@@ -20,10 +22,10 @@ func handleMsgSysCreateStage(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
if _, exists := s.Server.stages[pkt.StageID]; exists {
s.DoAckSimpleFail(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
} else {
stage := NewStage(pkt.StageID)
stage.host = s
stage.maxPlayers = uint16(pkt.PlayerCount)
s.Server.stages[stage.id] = stage
stage := system.NewStage(pkt.StageID)
stage.Host = s
stage.MaxPlayers = uint16(pkt.PlayerCount)
s.Server.stages[stage.Id] = stage
s.DoAckSimpleSucceed(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00})
}
}
@@ -37,16 +39,16 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
if exists {
stage.Lock()
stage.clients[s] = s.CharID
stage.Clients[s] = s.CharID
stage.Unlock()
} else { // Create new stage object
s.Server.Lock()
s.Server.stages[stageID] = NewStage(stageID)
s.Server.stages[stageID] = system.NewStage(stageID)
stage = s.Server.stages[stageID]
s.Server.Unlock()
stage.Lock()
stage.host = s
stage.clients[s] = s.CharID
stage.Host = s
stage.Clients[s] = s.CharID
stage.Unlock()
}
@@ -92,17 +94,17 @@ func doStageTransfer(s *Session, ackHandle uint32, stageID string) {
// Notify the client to duplicate the existing objects.
s.Logger.Info(fmt.Sprintf("Sending existing stage objects to %s", s.Name))
s.stage.RLock()
for _, obj := range s.stage.objects {
if obj.ownerCharID == s.CharID {
for _, obj := range s.stage.Objects {
if obj.OwnerCharID == s.CharID {
continue
}
temp = &mhfpacket.MsgSysDuplicateObject{
ObjID: obj.id,
X: obj.x,
Y: obj.y,
Z: obj.z,
ObjID: obj.Id,
X: obj.X,
Y: obj.Y,
Z: obj.Z,
Unk0: 0,
OwnerCharID: obj.ownerCharID,
OwnerCharID: obj.OwnerCharID,
}
s.QueueSendMHF(temp)
}
@@ -115,10 +117,10 @@ func destructEmptyStages(s *Session) {
defer s.Server.Unlock()
for _, stage := range s.Server.stages {
// Destroy empty Quest/My series/Guild stages.
if stage.id[3:5] == "Qs" || stage.id[3:5] == "Ms" || stage.id[3:5] == "Gs" || stage.id[3:5] == "Ls" {
if len(stage.reservedClientSlots) == 0 && len(stage.clients) == 0 {
delete(s.Server.stages, stage.id)
s.Logger.Debug("Destructed stage", zap.String("stage.id", stage.id))
if stage.Id[3:5] == "Qs" || stage.Id[3:5] == "Ms" || stage.Id[3:5] == "Gs" || stage.Id[3:5] == "Ls" {
if len(stage.ReservedClientSlots) == 0 && len(stage.Clients) == 0 {
delete(s.Server.stages, stage.Id)
s.Logger.Debug("Destructed stage", zap.String("stage.id", stage.Id))
}
}
}
@@ -126,14 +128,14 @@ func destructEmptyStages(s *Session) {
func removeSessionFromStage(s *Session) {
// Remove client from old stage.
delete(s.stage.clients, s)
delete(s.stage.Clients, s)
// Delete old stage objects owned by the client.
s.Logger.Info("Sending notification to old stage clients")
for _, object := range s.stage.objects {
if object.ownerCharID == s.CharID {
s.stage.BroadcastMHF(&mhfpacket.MsgSysDeleteObject{ObjID: object.id}, s)
delete(s.stage.objects, object.ownerCharID)
for _, object := range s.stage.Objects {
if object.OwnerCharID == s.CharID {
s.stage.BroadcastMHF(&mhfpacket.MsgSysDeleteObject{ObjID: object.Id}, s)
delete(s.stage.Objects, object.OwnerCharID)
}
}
destructEmptyStages(s)
@@ -142,10 +144,10 @@ func removeSessionFromStage(s *Session) {
func isStageFull(s *Session, StageID string) bool {
if stage, exists := s.Server.stages[StageID]; exists {
if _, exists := stage.reservedClientSlots[s.CharID]; exists {
if _, exists := stage.ReservedClientSlots[s.CharID]; exists {
return false
}
return len(stage.reservedClientSlots)+len(stage.clients) >= int(stage.maxPlayers)
return len(stage.ReservedClientSlots)+len(stage.Clients) >= int(stage.MaxPlayers)
}
return false
}
@@ -161,9 +163,9 @@ func handleMsgSysEnterStage(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
// Push our current stage ID to the movement stack before entering another one.
if s.stage != nil {
s.stage.Lock()
s.stage.reservedClientSlots[s.CharID] = false
s.stage.ReservedClientSlots[s.CharID] = false
s.stage.Unlock()
s.stageMoveStack.Push(s.stage.id)
s.stageMoveStack.Push(s.stage.Id)
}
if s.reservationStage != nil {
@@ -188,12 +190,12 @@ func handleMsgSysBackStage(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
return
}
if _, exists := s.stage.reservedClientSlots[s.CharID]; exists {
delete(s.stage.reservedClientSlots, s.CharID)
if _, exists := s.stage.ReservedClientSlots[s.CharID]; exists {
delete(s.stage.ReservedClientSlots, s.CharID)
}
if _, exists := s.Server.stages[backStage].reservedClientSlots[s.CharID]; exists {
delete(s.Server.stages[backStage].reservedClientSlots, s.CharID)
if _, exists := s.Server.stages[backStage].ReservedClientSlots[s.CharID]; exists {
delete(s.Server.stages[backStage].ReservedClientSlots, s.CharID)
}
doStageTransfer(s, pkt.AckHandle, backStage)
@@ -216,7 +218,7 @@ func handleMsgSysLockStage(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysLockStage)
if stage, exists := s.Server.stages[pkt.StageID]; exists {
stage.Lock()
stage.locked = true
stage.Locked = true
stage.Unlock()
}
s.DoAckSimpleSucceed(pkt.AckHandle, make([]byte, 4))
@@ -227,14 +229,14 @@ func handleMsgSysUnlockStage(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
s.reservationStage.RLock()
defer s.reservationStage.RUnlock()
for charID := range s.reservationStage.reservedClientSlots {
for charID := range s.reservationStage.ReservedClientSlots {
session := s.Server.FindSessionByCharID(charID)
if session != nil {
session.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{})
}
}
delete(s.Server.stages, s.reservationStage.id)
delete(s.Server.stages, s.reservationStage.Id)
}
destructEmptyStages(s)
@@ -245,26 +247,26 @@ func handleMsgSysReserveStage(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
if stage, exists := s.Server.stages[pkt.StageID]; exists {
stage.Lock()
defer stage.Unlock()
if _, exists := stage.reservedClientSlots[s.CharID]; exists {
if _, exists := stage.ReservedClientSlots[s.CharID]; exists {
switch pkt.Ready {
case 1: // 0x01
stage.reservedClientSlots[s.CharID] = false
stage.ReservedClientSlots[s.CharID] = false
case 17: // 0x11
stage.reservedClientSlots[s.CharID] = true
stage.ReservedClientSlots[s.CharID] = true
}
s.DoAckSimpleSucceed(pkt.AckHandle, make([]byte, 4))
} else if uint16(len(stage.reservedClientSlots)) < stage.maxPlayers {
if stage.locked {
} else if uint16(len(stage.ReservedClientSlots)) < stage.MaxPlayers {
if stage.Locked {
s.DoAckSimpleFail(pkt.AckHandle, make([]byte, 4))
return
}
if len(stage.password) > 0 {
if stage.password != s.stagePass {
if len(stage.Password) > 0 {
if stage.Password != s.stagePass {
s.DoAckSimpleFail(pkt.AckHandle, make([]byte, 4))
return
}
}
stage.reservedClientSlots[s.CharID] = false
stage.ReservedClientSlots[s.CharID] = false
// Save the reservation stage in the session for later use in MsgSysUnreserveStage.
s.Lock()
s.reservationStage = stage
@@ -286,8 +288,8 @@ func handleMsgSysUnreserveStage(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket)
s.Unlock()
if stage != nil {
stage.Lock()
if _, exists := stage.reservedClientSlots[s.CharID]; exists {
delete(stage.reservedClientSlots, s.CharID)
if _, exists := stage.ReservedClientSlots[s.CharID]; exists {
delete(stage.ReservedClientSlots, s.CharID)
}
stage.Unlock()
}
@@ -301,8 +303,8 @@ func handleMsgSysSetStagePass(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
if stage != nil {
stage.Lock()
// Will only exist if host.
if _, exists := stage.reservedClientSlots[s.CharID]; exists {
stage.password = pkt.Password
if _, exists := stage.ReservedClientSlots[s.CharID]; exists {
stage.Password = pkt.Password
}
stage.Unlock()
} else {
@@ -317,7 +319,7 @@ func handleMsgSysSetStageBinary(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket)
pkt := p.(*mhfpacket.MsgSysSetStageBinary)
if stage, exists := s.Server.stages[pkt.StageID]; exists {
stage.Lock()
stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}] = pkt.RawDataPayload
stage.RawBinaryData[system.StageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}] = pkt.RawDataPayload
stage.Unlock()
} else {
s.Logger.Warn("Failed to get stage", zap.String("StageID", pkt.StageID))
@@ -328,7 +330,7 @@ func handleMsgSysGetStageBinary(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket)
pkt := p.(*mhfpacket.MsgSysGetStageBinary)
if stage, exists := s.Server.stages[pkt.StageID]; exists {
stage.Lock()
if binaryData, exists := stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}]; exists {
if binaryData, exists := stage.RawBinaryData[system.StageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}]; exists {
s.DoAckBufSucceed(pkt.AckHandle, binaryData)
} else if pkt.BinaryType1 == 4 {
// Unknown binary type that is supposedly generated server side
@@ -357,7 +359,7 @@ func handleMsgSysWaitStageBinary(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket)
for {
s.Logger.Debug("MsgSysWaitStageBinary before lock and get stage")
stage.Lock()
stageBinary, gotBinary := stage.rawBinaryData[stageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}]
stageBinary, gotBinary := stage.RawBinaryData[system.StageBinaryKey{pkt.BinaryType0, pkt.BinaryType1}]
stage.Unlock()
s.Logger.Debug("MsgSysWaitStageBinary after lock and get stage")
if gotBinary {
@@ -389,29 +391,29 @@ func handleMsgSysEnumerateStage(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket)
for sid, stage := range s.Server.stages {
stage.RLock()
if len(stage.reservedClientSlots) == 0 && len(stage.clients) == 0 {
if len(stage.ReservedClientSlots) == 0 && len(stage.Clients) == 0 {
stage.RUnlock()
continue
}
if !strings.Contains(stage.id, pkt.StagePrefix) {
if !strings.Contains(stage.Id, pkt.StagePrefix) {
stage.RUnlock()
continue
}
joinable++
bf.WriteUint16(uint16(len(stage.reservedClientSlots)))
bf.WriteUint16(uint16(len(stage.clients)))
if strings.HasPrefix(stage.id, "sl2Ls") {
bf.WriteUint16(uint16(len(stage.clients) + len(stage.reservedClientSlots)))
bf.WriteUint16(uint16(len(stage.ReservedClientSlots)))
bf.WriteUint16(uint16(len(stage.Clients)))
if strings.HasPrefix(stage.Id, "sl2Ls") {
bf.WriteUint16(uint16(len(stage.Clients) + len(stage.ReservedClientSlots)))
} else {
bf.WriteUint16(uint16(len(stage.clients)))
bf.WriteUint16(uint16(len(stage.Clients)))
}
bf.WriteUint16(stage.maxPlayers)
bf.WriteUint16(stage.MaxPlayers)
var flags uint8
if stage.locked {
if stage.Locked {
flags |= 1
}
if len(stage.password) > 0 {
if len(stage.Password) > 0 {
flags |= 2
}
bf.WriteUint8(flags)

View File

@@ -7,8 +7,10 @@ import (
"time"
"erupe-ce/config"
"erupe-ce/internal/system"
"erupe-ce/server/discordbot"
"erupe-ce/utils/db"
"erupe-ce/utils/gametime"
"erupe-ce/utils/logger"
@@ -47,7 +49,7 @@ type ChannelServer struct {
isShuttingDown bool
stagesLock sync.RWMutex
stages map[string]*Stage
stages map[string]*system.Stage
// Used to map different languages
i18n i18n
@@ -82,9 +84,9 @@ func NewServer(config *Config) *ChannelServer {
"sl2Ns379p0a0u0", // Diva fountain
"sl1Ns462p0a0u0", // MezFes
}
stages := make(map[string]*Stage)
stages := make(map[string]*system.Stage)
for _, name := range stageNames {
stages[name] = NewStage(name)
stages[name] = system.NewStage(name)
}
server := &ChannelServer{
ID: config.ID,
@@ -228,14 +230,14 @@ func (server *ChannelServer) DisconnectUser(uid uint32) {
}
}
func (server *ChannelServer) FindObjectByChar(charID uint32) *Object {
func (server *ChannelServer) FindObjectByChar(charID uint32) *system.Object {
server.stagesLock.RLock()
defer server.stagesLock.RUnlock()
for _, stage := range server.stages {
stage.RLock()
for objId := range stage.objects {
obj := stage.objects[objId]
if obj.ownerCharID == charID {
for objId := range stage.Objects {
obj := stage.Objects[objId]
if obj.OwnerCharID == charID {
stage.RUnlock()
return obj
}

View File

@@ -22,17 +22,17 @@ func getPlayerSlice(server *ChannelServer) []Player {
for _, channel := range server.Channels {
for _, stage := range channel.stages {
if len(stage.clients) == 0 {
if len(stage.Clients) == 0 {
continue
}
questID := 0
if stage.isQuest() {
if stage.IsQuest() {
questIndex++
questID = questIndex
}
for client := range stage.clients {
for client := range stage.Clients {
p = append(p, Player{
CharName: client.Name,
CharName: client.GetName(),
QuestID: questID,
})
}

View File

@@ -5,6 +5,7 @@ import (
"encoding/hex"
"erupe-ce/config"
"erupe-ce/internal/constant"
"erupe-ce/internal/system"
"erupe-ce/network"
"erupe-ce/network/binpacket"
"erupe-ce/network/mhfpacket"
@@ -36,10 +37,10 @@ type Session struct {
objectIndex uint16
userEnteredStage bool // If the user has entered a stage before
stage *Stage
reservationStage *Stage // Required for the stateful MsgSysUnreserveStage packet.
stagePass string // Temporary storage
prevGuildID uint32 // Stores the last GuildID used in InfoGuild
stage *system.Stage
reservationStage *system.Stage // Required for the stateful MsgSysUnreserveStage packet.
stagePass string // Temporary storage
prevGuildID uint32 // Stores the last GuildID used in InfoGuild
CharID uint32
logKey []byte
sessionStart int64
@@ -371,3 +372,9 @@ func (s *Session) DoAckSimpleFail(ackHandle uint32, data []byte) {
AckData: data,
})
}
func (s *Session) GetCharID() uint32 {
return s.CharID // Assuming `Session` has a field `CharID`
}
func (s *Session) GetName() string {
return s.Name // Assuming `Session` has a field `CharID`
}