mirror of
https://github.com/Mezeporta/Erupe.git
synced 2025-12-16 08:55:31 +01:00
Stage/objects test
This commit is contained in:
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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,10 +21,15 @@ 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.
|
||||||
func (m *MsgSysEnumerateClient) Build(bf *byteframe.ByteFrame) error {
|
func (m *MsgSysEnumerateClient) Build(bf *byteframe.ByteFrame) error {
|
||||||
panic("Not implemented")
|
panic("Not implemented")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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) {}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ type Session struct {
|
|||||||
sendPackets chan []byte
|
sendPackets chan []byte
|
||||||
|
|
||||||
stageID string
|
stageID string
|
||||||
|
stage *Stage
|
||||||
charID uint32
|
charID uint32
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user