Stage/objects test

This commit is contained in:
Andrew Gutekanst
2020-01-23 19:29:19 -05:00
parent 10c80322af
commit 28e0dafd54
8 changed files with 174 additions and 28 deletions

View File

@@ -20,5 +20,6 @@ func (m *MsgSysCleanupObject) Parse(bf *byteframe.ByteFrame) error {
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.
func (m *MsgSysCleanupObject) Build(bf *byteframe.ByteFrame) error { func (m *MsgSysCleanupObject) Build(bf *byteframe.ByteFrame) error {
panic("Not implemented") // This packet has no data.
return nil
} }

View File

@@ -6,7 +6,12 @@ import (
) )
// MsgSysDuplicateObject represents the MSG_SYS_DUPLICATE_OBJECT // MsgSysDuplicateObject represents the MSG_SYS_DUPLICATE_OBJECT
type MsgSysDuplicateObject struct{} type MsgSysDuplicateObject struct {
ObjID uint32
X, Y, Z float32
Unk0 uint32
OwnerCharID uint32
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgSysDuplicateObject) Opcode() network.PacketID { func (m *MsgSysDuplicateObject) Opcode() network.PacketID {
@@ -20,5 +25,11 @@ func (m *MsgSysDuplicateObject) Parse(bf *byteframe.ByteFrame) error {
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.
func (m *MsgSysDuplicateObject) Build(bf *byteframe.ByteFrame) error { func (m *MsgSysDuplicateObject) Build(bf *byteframe.ByteFrame) error {
panic("Not implemented") bf.WriteUint32(m.ObjID)
bf.WriteFloat32(m.X)
bf.WriteFloat32(m.Y)
bf.WriteFloat32(m.Z)
bf.WriteUint32(m.Unk0)
bf.WriteUint32(m.OwnerCharID)
return nil
} }

View File

@@ -10,7 +10,7 @@ type MsgSysEnterStage struct {
AckHandle uint32 AckHandle uint32
UnkBool uint8 UnkBool uint8
StageIDLength uint8 StageIDLength uint8
StageID []byte StageID string
} }
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
@@ -23,7 +23,7 @@ func (m *MsgSysEnterStage) Parse(bf *byteframe.ByteFrame) error {
m.AckHandle = bf.ReadUint32() m.AckHandle = bf.ReadUint32()
m.UnkBool = bf.ReadUint8() m.UnkBool = bf.ReadUint8()
m.StageIDLength = bf.ReadUint8() m.StageIDLength = bf.ReadUint8()
m.StageID = bf.ReadBytes(uint(m.StageIDLength)) m.StageID = string(bf.ReadBytes(uint(m.StageIDLength)))
return nil return nil
} }

View File

@@ -6,7 +6,13 @@ import (
) )
// MsgSysEnumerateClient represents the MSG_SYS_ENUMERATE_CLIENT // MsgSysEnumerateClient represents the MSG_SYS_ENUMERATE_CLIENT
type MsgSysEnumerateClient struct{} type MsgSysEnumerateClient struct {
AckHandle uint32
Unk0 uint8 // Hardcoded 1 in the client
Unk1 uint8
StageIDLength uint8
StageID string
}
// Opcode returns the ID associated with this packet type. // Opcode returns the ID associated with this packet type.
func (m *MsgSysEnumerateClient) Opcode() network.PacketID { func (m *MsgSysEnumerateClient) Opcode() network.PacketID {
@@ -15,7 +21,12 @@ func (m *MsgSysEnumerateClient) Opcode() network.PacketID {
// Parse parses the packet from binary // Parse parses the packet from binary
func (m *MsgSysEnumerateClient) Parse(bf *byteframe.ByteFrame) error { func (m *MsgSysEnumerateClient) Parse(bf *byteframe.ByteFrame) error {
panic("Not implemented") m.AckHandle = bf.ReadUint32()
m.Unk0 = bf.ReadUint8()
m.Unk1 = bf.ReadUint8()
m.StageIDLength = bf.ReadUint8()
m.StageID = string(bf.ReadBytes(uint(m.StageIDLength)))
return nil
} }
// Build builds a binary packet from the current data. // Build builds a binary packet from the current data.

View File

@@ -49,7 +49,8 @@ func NewServer(config *Config) *Server {
} }
// Default town stage that clients try to enter without creating. // Default town stage that clients try to enter without creating.
s.stages["sl1Ns200p0a0u0"] = &Stage{} stage := NewStage("sl1Ns200p0a0u0")
s.stages[stage.id] = stage
return s return s
} }

View File

@@ -256,7 +256,8 @@ func handleMsgSysCreateStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysCreateStage) pkt := p.(*mhfpacket.MsgSysCreateStage)
s.server.stagesLock.Lock() s.server.stagesLock.Lock()
s.server.stages[stripNullTerminator(s.stageID)] = &Stage{} stage := NewStage(stripNullTerminator(pkt.StageID))
s.server.stages[stage.id] = stage
s.server.stagesLock.Unlock() s.server.stagesLock.Unlock()
resp := make([]byte, 8) // Unk resp. resp := make([]byte, 8) // Unk resp.
@@ -268,13 +269,40 @@ func handleMsgSysStageDestruct(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysEnterStage) pkt := p.(*mhfpacket.MsgSysEnterStage)
// Remove this session from old stage clients list and put myself in the new one.
s.server.stagesLock.Lock()
newStage, gotNewStage := s.server.stages[stripNullTerminator(pkt.StageID)]
s.server.stagesLock.Unlock()
// Remove from old stage.
if s.stage != nil {
s.stage.Lock()
delete(s.stage.clients, s)
s.stage.Unlock()
}
// Add the new stage.
if gotNewStage {
newStage.Lock()
newStage.clients[s] = s.charID
newStage.Unlock()
}
// Save our new stage ID and pointer to the new stage itself.
s.Lock() s.Lock()
s.stageID = string(pkt.StageID) s.stageID = string(stripNullTerminator(pkt.StageID))
s.stage = newStage
s.Unlock() s.Unlock()
//TODO: Send MSG_SYS_CLEANUP_OBJECT here before the client changes stages. // Tell the client to cleanup it's current stage objects.
s.QueueSendMHF(&mhfpacket.MsgSysCleanupObject{})
// Confirm the stage entry.
s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
// TODO(Andoryuuta): Notify existing stage clients that this new client has entered.
// TODO(Andoryuuta): Notify this client about all of the existing clients in the stage.
} }
func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {}
@@ -292,6 +320,8 @@ func handleMsgSysReserveStage(s *Session, p mhfpacket.MHFPacket) {
fmt.Printf("Got reserve stage req, Unk0:%v, StageID:%q\n", pkt.Unk0, pkt.StageID) fmt.Printf("Got reserve stage req, Unk0:%v, StageID:%q\n", pkt.Unk0, pkt.StageID)
// TODO(Andoryuuta): Add proper player-slot reservations for stages.
s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) s.QueueAck(pkt.AckHandle, []byte{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
} }
@@ -305,7 +335,31 @@ func handleMsgSysSetStageBinary(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysGetStageBinary(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysGetStageBinary(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysEnumerateClient(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysEnumerateClient(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysEnumerateClient)
// Read-lock the stages map.
s.server.stagesLock.RLock()
stage, ok := s.server.stages[stripNullTerminator(pkt.StageID)]
if !ok {
s.logger.Fatal("Can't enumerate clients for stage that doesn't exist!", zap.String("stageID", pkt.StageID))
}
// Unlock the stages map.
s.server.stagesLock.RUnlock()
// Read-lock the stage and make the response with all of the charID's in the stage.
resp := byteframe.NewByteFrame()
stage.RLock()
resp.WriteUint16(uint16(len(stage.clients))) // Client count
for session := range stage.clients {
resp.WriteUint32(session.charID) // Client represented by charID
}
stage.RUnlock()
doSizedAckResp(s, pkt.AckHandle, resp.Data())
}
func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysEnumerateStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysEnumerateStage) pkt := p.(*mhfpacket.MsgSysEnumerateStage)
@@ -362,27 +416,47 @@ func handleMsgSysNotifyRegister(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysCreateObject(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysCreateObject(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysCreateObject) pkt := p.(*mhfpacket.MsgSysCreateObject)
// Get the current stage. // Make sure we have a stage.
s.server.stagesLock.RLock() if s.stage == nil {
defer s.server.stagesLock.RUnlock()
stage, ok := s.server.stages[stripNullTerminator(s.stageID)]
if !ok {
s.logger.Fatal("StageID not in the stages map!", zap.String("stageID", s.stageID)) s.logger.Fatal("StageID not in the stages map!", zap.String("stageID", s.stageID))
} }
// Lock the stage. // Lock the stage.
stage.Lock() s.stage.Lock()
defer stage.Unlock()
// Make a new object ID. // Make a new stage object and insert it into the stage.
objID := stage.gameObjectCount objID := s.stage.gameObjectCount
stage.gameObjectCount++ s.stage.gameObjectCount++
newObj := &StageObject{
id: objID,
ownerCharID: s.charID,
x: pkt.X,
y: pkt.Y,
z: pkt.Z,
}
s.stage.objects[objID] = newObj
// Unlock the stage.
s.stage.Unlock()
// Response to our requesting client.
resp := byteframe.NewByteFrame() resp := byteframe.NewByteFrame()
resp.WriteUint32(0) // Unk, is this echoed back from pkt.Unk0? resp.WriteUint32(0) // Unk, is this echoed back from pkt.Unk0?
resp.WriteUint32(objID) // New local obj handle. resp.WriteUint32(objID) // New local obj handle.
s.QueueAck(pkt.AckHandle, resp.Data()) s.QueueAck(pkt.AckHandle, resp.Data())
// Duplicate the object creation to all sessions in the same stage.
dupObjUpdate := &mhfpacket.MsgSysDuplicateObject{
ObjID: objID,
X: pkt.X,
Y: pkt.Y,
Z: pkt.Z,
Unk0: 0,
OwnerCharID: s.charID,
}
s.stage.BroadcastMHF(dupObjUpdate, s)
} }
func handleMsgSysDeleteObject(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysDeleteObject(s *Session, p mhfpacket.MHFPacket) {}
@@ -391,6 +465,8 @@ func handleMsgSysPositionObject(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysPositionObject) pkt := p.(*mhfpacket.MsgSysPositionObject)
fmt.Printf("Moved object %v to (%f,%f,%f)\n", pkt.ObjID, pkt.X, pkt.Y, pkt.Z) fmt.Printf("Moved object %v to (%f,%f,%f)\n", pkt.ObjID, pkt.X, pkt.Y, pkt.Z)
// One of the few packets we can just re-broadcast directly.
s.stage.BroadcastMHF(pkt, s)
} }
func handleMsgSysRotateObject(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysRotateObject(s *Session, p mhfpacket.MHFPacket) {}

View File

@@ -22,6 +22,7 @@ type Session struct {
sendPackets chan []byte sendPackets chan []byte
stageID string stageID string
stage *Stage
charID uint32 charID uint32
} }

View File

@@ -1,9 +1,54 @@
package channelserver package channelserver
import "sync" import (
"sync"
"github.com/Andoryuuta/Erupe/network/mhfpacket"
"github.com/Andoryuuta/byteframe"
)
// StageObject holds infomation about a specific stage object.
type StageObject struct {
sync.RWMutex
id uint32
ownerCharID uint32
x, y, z float32
}
// Stage holds stage-specific information // Stage holds stage-specific information
type Stage struct { type Stage struct {
sync.RWMutex sync.RWMutex
gameObjectCount uint32 id string // Stage ID string
gameObjectCount uint32 // Total count of objects ever created for this stage. Used for ObjID generation.
objects map[uint32]*StageObject // Map of ObjID -> StageObject
clients map[*Session]uint32 // Map of session -> charID
}
// NewStage creates a new stage with intialized values.
func NewStage(ID string) *Stage {
s := &Stage{
objects: make(map[uint32]*StageObject),
clients: make(map[*Session]uint32),
}
return s
}
// BroadcastMHF queues a MHFPacket to be sent to all sessions in the stage.
func (s *Stage) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) {
// Make the header
bf := byteframe.NewByteFrame()
bf.WriteUint16(uint16(pkt.Opcode()))
// Build the packet onto the byteframe.
pkt.Build(bf)
// Broadcast the data.
for session := range s.clients {
if session == ignoredSession {
continue
}
// Enqueue in a non-blocking way that drops the packet if the connections send buffer channel is full.
session.QueueSendNonBlocking(bf.Data())
}
} }