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

View File

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

View File

@@ -4,6 +4,7 @@ import (
"encoding/binary" "encoding/binary"
"erupe-ce/config" "erupe-ce/config"
"erupe-ce/internal/model" "erupe-ce/internal/model"
"erupe-ce/internal/system"
"erupe-ce/utils/db" "erupe-ce/utils/db"
"erupe-ce/utils/gametime" "erupe-ce/utils/gametime"
"erupe-ce/utils/mhfcourse" "erupe-ce/utils/mhfcourse"
@@ -155,18 +156,18 @@ func logoutPlayer(s *Session) {
for _, stage := range s.Server.stages { for _, stage := range s.Server.stages {
// Tell sessions registered to disconnecting players quest to unregister // 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 _, sess := range s.Server.sessions {
for rSlot := range stage.reservedClientSlots { for rSlot := range stage.ReservedClientSlots {
if sess.CharID == rSlot && sess.stage != nil && sess.stage.id[3:5] != "Qs" { if sess.CharID == rSlot && sess.stage != nil && sess.stage.Id[3:5] != "Qs" {
sess.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{}) sess.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{})
} }
} }
} }
} }
for session := range stage.clients { for session := range stage.Clients {
if session.CharID == s.CharID { if session.GetCharID() == s.CharID {
delete(stage.clients, session) delete(stage.Clients, session)
} }
} }
} }
@@ -211,8 +212,8 @@ func logoutPlayer(s *Session) {
s.Server.Lock() s.Server.Lock()
for _, stage := range s.Server.stages { for _, stage := range s.Server.stages {
if _, exists := stage.reservedClientSlots[s.CharID]; exists { if _, exists := stage.ReservedClientSlots[s.CharID]; exists {
delete(stage.reservedClientSlots, s.CharID) delete(stage.ReservedClientSlots, s.CharID)
} }
} }
s.Server.Unlock() 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 // 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)) 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) { if pkt.SearchType == 2 && !strings.Contains(session.Name, term) {
continue 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 continue
} }
count++ count++
sessionName := stringsupport.UTF8ToSJIS(session.Name) sessionName := stringsupport.UTF8ToSJIS(session.Name)
sessionStage := stringsupport.UTF8ToSJIS(session.stage.id) sessionStage := stringsupport.UTF8ToSJIS(session.stage.Id)
if !local { if !local {
resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4())) resp.WriteUint32(binary.LittleEndian.Uint32(net.ParseIP(c.IP).To4()))
} else { } else {
@@ -497,8 +498,8 @@ func handleMsgMhfTransitMessage(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket)
if count == maxResults { if count == maxResults {
break break
} }
if strings.HasPrefix(stage.id, findPartyParams.StagePrefix) { if strings.HasPrefix(stage.Id, findPartyParams.StagePrefix) {
sb3 := byteframe.NewByteFrameFromBytes(stage.rawBinaryData[stageBinaryKey{1, 3}]) sb3 := byteframe.NewByteFrameFromBytes(stage.RawBinaryData[system.StageBinaryKey{1, 3}])
sb3.Seek(4, 0) sb3.Seek(4, 0)
stageDataParams := 7 stageDataParams := 7
@@ -546,17 +547,17 @@ func handleMsgMhfTransitMessage(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket)
resp.WriteUint16(0) // Static? resp.WriteUint16(0) // Static?
resp.WriteUint16(0) // Unk, [0 1 2] resp.WriteUint16(0) // Unk, [0 1 2]
resp.WriteUint16(uint16(len(stage.clients) + len(stage.reservedClientSlots))) resp.WriteUint16(uint16(len(stage.Clients) + len(stage.ReservedClientSlots)))
resp.WriteUint16(stage.maxPlayers) resp.WriteUint16(stage.MaxPlayers)
// TODO: Retail returned the number of clients in quests, not workshop/my series // 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(0) // Static?
resp.WriteUint8(uint8(stage.maxPlayers)) resp.WriteUint8(uint8(stage.MaxPlayers))
resp.WriteUint8(1) // Static? resp.WriteUint8(1) // Static?
resp.WriteUint8(uint8(len(stage.id) + 1)) resp.WriteUint8(uint8(len(stage.Id) + 1))
resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 0}]))) resp.WriteUint8(uint8(len(stage.RawBinaryData[system.StageBinaryKey{1, 0}])))
resp.WriteUint8(uint8(len(stage.rawBinaryData[stageBinaryKey{1, 1}]))) resp.WriteUint8(uint8(len(stage.RawBinaryData[system.StageBinaryKey{1, 1}])))
for i := range stageData { for i := range stageData {
if config.GetConfig().ClientID >= config.Z1 { 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.WriteUint8(0) // Unk resp.WriteUint8(0) // Unk
resp.WriteNullTerminatedBytes([]byte(stage.id)) resp.WriteNullTerminatedBytes([]byte(stage.Id))
resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 0}]) resp.WriteBytes(stage.RawBinaryData[system.StageBinaryKey{1, 0}])
resp.WriteBytes(stage.rawBinaryData[stageBinaryKey{1, 1}]) 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 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) 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 var clients []uint32
switch pkt.Get { switch pkt.Get {
case 0: // All case 0: // All
for _, cid := range stage.clients { for _, cid := range stage.Clients {
clients = append(clients, cid) clients = append(clients, cid)
} }
for cid := range stage.reservedClientSlots { for cid := range stage.ReservedClientSlots {
clients = append(clients, cid) clients = append(clients, cid)
} }
case 1: // Not ready case 1: // Not ready
for cid, ready := range stage.reservedClientSlots { for cid, ready := range stage.ReservedClientSlots {
if !ready { if !ready {
clients = append(clients, cid) clients = append(clients, cid)
} }
} }
case 2: // Ready case 2: // Ready
for cid, ready := range stage.reservedClientSlots { for cid, ready := range stage.ReservedClientSlots {
if ready { if ready {
clients = append(clients, cid) clients = append(clients, cid)
} }

View File

@@ -4,6 +4,7 @@ import (
"fmt" "fmt"
"erupe-ce/config" "erupe-ce/config"
"erupe-ce/internal/system"
"erupe-ce/network/mhfpacket" "erupe-ce/network/mhfpacket"
"erupe-ce/utils/byteframe" "erupe-ce/utils/byteframe"
@@ -14,30 +15,30 @@ func handleMsgSysCreateObject(s *Session, db *sqlx.DB, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysCreateObject) pkt := p.(*mhfpacket.MsgSysCreateObject)
s.stage.Lock() s.stage.Lock()
newObj := &Object{ newObj := &system.Object{
id: s.NextObjectID(), Id: s.NextObjectID(),
ownerCharID: s.CharID, OwnerCharID: s.CharID,
x: pkt.X, X: pkt.X,
y: pkt.Y, Y: pkt.Y,
z: pkt.Z, Z: pkt.Z,
} }
s.stage.objects[s.CharID] = newObj s.stage.Objects[s.CharID] = newObj
s.stage.Unlock() s.stage.Unlock()
// Response to our requesting client. // Response to our requesting client.
resp := byteframe.NewByteFrame() 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()) s.DoAckSimpleSucceed(pkt.AckHandle, resp.Data())
// Duplicate the object creation to all sessions in the same stage. // Duplicate the object creation to all sessions in the same stage.
dupObjUpdate := &mhfpacket.MsgSysDuplicateObject{ dupObjUpdate := &mhfpacket.MsgSysDuplicateObject{
ObjID: newObj.id, ObjID: newObj.Id,
X: newObj.x, X: newObj.X,
Y: newObj.y, Y: newObj.Y,
Z: newObj.z, Z: newObj.Z,
OwnerCharID: newObj.ownerCharID, 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) 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) 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() s.stage.Lock()
object, ok := s.stage.objects[s.CharID] object, ok := s.stage.Objects[s.CharID]
if ok { if ok {
object.x = pkt.X object.X = pkt.X
object.y = pkt.Y object.Y = pkt.Y
object.z = pkt.Z object.Z = pkt.Z
} }
s.stage.Unlock() s.stage.Unlock()
// One of the few packets we can just re-broadcast directly. // One of the few packets we can just re-broadcast directly.

View File

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

View File

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

View File

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

View File

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