Merge pull request #7 from ZeruLight/feature/obj-indexing

rewrite object indexing, stringstack and bug fixes
This commit is contained in:
wish
2022-07-26 20:08:06 +10:00
committed by GitHub
12 changed files with 360 additions and 387 deletions

View File

@@ -2,33 +2,45 @@ package stringstack
import ( import (
"errors" "errors"
"sync"
) )
// StringStack is a basic LIFO "stack" for storing strings. // StringStack is a basic LIFO "stack" for storing strings.
type StringStack struct { type StringStack struct {
sync.Mutex Locked bool
stack []string stack []string
} }
// New creates a new instance of StringStack // New creates a new instance of StringStack
func New() *StringStack { func New() *StringStack {
return &StringStack{} return &StringStack{Locked: false}
}
// Set sets up a new StringStack
func (s *StringStack) Set(v string) {
s.stack = []string{v}
}
// Lock freezes the StringStack
func (s *StringStack) Lock() {
if !s.Locked {
s.Locked = true
}
}
// Unlock unfreezes the StringStack
func (s *StringStack) Unlock() {
if s.Locked {
s.Locked = false
}
} }
// Push pushes a string onto the stack. // Push pushes a string onto the stack.
func (s *StringStack) Push(v string) { func (s *StringStack) Push(v string) {
s.Lock()
defer s.Unlock()
s.stack = append(s.stack, v) s.stack = append(s.stack, v)
} }
// Pop pops a string from the stack. // Pop pops a string from the stack.
func (s *StringStack) Pop() (string, error) { func (s *StringStack) Pop() (string, error) {
s.Lock()
defer s.Unlock()
if len(s.stack) == 0 { if len(s.stack) == 0 {
return "", errors.New("no items on stack") return "", errors.New("no items on stack")
} }

View File

@@ -8,6 +8,7 @@
"maxlauncherhr": true, "maxlauncherhr": true,
"LogInboundMessages": false, "LogInboundMessages": false,
"LogOutboundMessages": false, "LogOutboundMessages": false,
"MaxHexdumpLength": 256,
"Event": 0, "Event": 0,
"DivaEvent": 0, "DivaEvent": 0,
"FestaEvent": 0, "FestaEvent": 0,

View File

@@ -29,6 +29,7 @@ type DevModeOptions struct {
FixedStageID bool // Causes all move_stage to use the ID sl1Ns200p0a0u0 to get you into all stages FixedStageID bool // Causes all move_stage to use the ID sl1Ns200p0a0u0 to get you into all stages
LogInboundMessages bool // Log all messages sent to the server LogInboundMessages bool // Log all messages sent to the server
LogOutboundMessages bool // Log all messages sent to the clients LogOutboundMessages bool // Log all messages sent to the clients
MaxHexdumpLength int // Maximum number of bytes printed when logs are enabled
DivaEvent int // Diva Defense event status DivaEvent int // Diva Defense event status
FestaEvent int // Hunter's Festa event status FestaEvent int // Hunter's Festa event status
TournamentEvent int // VS Tournament event status TournamentEvent int // VS Tournament event status

View File

@@ -181,6 +181,8 @@ func handleMsgSysLogin(s *Session, p mhfpacket.MHFPacket) {
} }
doAckSimpleSucceed(s, pkt.AckHandle, bf.Data()) doAckSimpleSucceed(s, pkt.AckHandle, bf.Data())
updateRights(s)
} }
func handleMsgSysLogout(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysLogout(s *Session, p mhfpacket.MHFPacket) {

View File

@@ -263,7 +263,7 @@ func getCharInfo(server *Server, charName string) string {
objInfo := "" objInfo := ""
obj := server.FindStageObjectByChar(c.charID) obj := server.FindObjectByChar(c.charID)
// server.logger.Info("Found object: %+v", zap.Object("obj", obj)) // server.logger.Info("Found object: %+v", zap.Object("obj", obj))
if obj != nil { if obj != nil {

View File

@@ -10,44 +10,31 @@ import (
func handleMsgSysCreateObject(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysCreateObject(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysCreateObject) pkt := p.(*mhfpacket.MsgSysCreateObject)
// Lock the stage. s.stage.Lock()
s.server.Lock() newObj := &Object{
id: s.stage.NextObjectID(),
// Make a new stage object and insert it into the stage.
objID := s.stage.GetNewObjectID(s.charID)
newObj := &StageObject{
id: objID,
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()
// Unlock the stage.
s.server.Unlock()
// Response to our requesting client. // Response to our requesting client.
resp := byteframe.NewByteFrame() resp := byteframe.NewByteFrame()
resp.WriteUint32(objID) // New local obj handle. resp.WriteUint32(newObj.id) // New local obj handle.
doAckSimpleSucceed(s, pkt.AckHandle, resp.Data()) doAckSimpleSucceed(s, 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: objID, ObjID: newObj.id,
X: pkt.X, X: newObj.x,
Y: pkt.Y, Y: newObj.y,
Z: pkt.Z, Z: newObj.z,
OwnerCharID: s.charID, OwnerCharID: newObj.ownerCharID,
} }
for i := 1; i <= 3; i++ { s.logger.Info(fmt.Sprintf("Broadcasting new object: %s (%d)", s.Name, s.charID))
s.server.BroadcastMHF(&mhfpacket.MsgSysNotifyUserBinary{
CharID: s.charID,
BinaryType: uint8(i),
}, s)
}
s.logger.Info("Duplicate a new characters to others clients")
s.stage.BroadcastMHF(dupObjUpdate, s) s.stage.BroadcastMHF(dupObjUpdate, s)
} }
@@ -74,7 +61,14 @@ func handleMsgSysRotateObject(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysDuplicateObject(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysDuplicateObject(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysSetObjectBinary(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysSetObjectBinary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysSetObjectBinary)
for _, object := range s.stage.objects {
if object.id == pkt.ObjID {
object.binary = pkt.RawDataPayload
}
}
}
func handleMsgSysGetObjectBinary(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysGetObjectBinary(s *Session, p mhfpacket.MHFPacket) {}

View File

@@ -34,7 +34,7 @@ func handleMsgSysGetFile(s *Session, p mhfpacket.MHFPacket) {
// Get quest file. // Get quest file.
data, err := ioutil.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%s.bin", pkt.Filename))) data, err := ioutil.ReadFile(filepath.Join(s.server.erupeConfig.BinPath, fmt.Sprintf("quests/%s.bin", pkt.Filename)))
if err != nil { if err != nil {
panic(err) s.logger.Fatal(fmt.Sprintf("Failed to open quest file: quests/%s.bin", pkt.Filename))
} }
doAckBufSucceed(s, pkt.AckHandle, data) doAckBufSucceed(s, pkt.AckHandle, data)
} }
@@ -45,7 +45,7 @@ func handleMsgMhfLoadFavoriteQuest(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgMhfLoadFavoriteQuest) pkt := p.(*mhfpacket.MsgMhfLoadFavoriteQuest)
var data []byte var data []byte
err := s.server.db.QueryRow("SELECT savefavoritequest FROM characters WHERE id = $1", s.charID).Scan(&data) err := s.server.db.QueryRow("SELECT savefavoritequest FROM characters WHERE id = $1", s.charID).Scan(&data)
if err == nil { if err == nil && len(data) > 0 {
doAckBufSucceed(s, pkt.AckHandle, data) doAckBufSucceed(s, pkt.AckHandle, data)
} else { } else {
doAckBufSucceed(s, pkt.AckHandle, []byte{0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}) doAckBufSucceed(s, pkt.AckHandle, []byte{0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00})
@@ -68,8 +68,6 @@ func handleMsgMhfEnumerateQuest(s *Session, p mhfpacket.MHFPacket) {
} else { } else {
doAckBufSucceed(s, pkt.AckHandle, data) doAckBufSucceed(s, pkt.AckHandle, data)
} }
// Update the client's rights as well:
updateRights(s)
} }
func handleMsgMhfEnterTournamentQuest(s *Session, p mhfpacket.MHFPacket) {} func handleMsgMhfEnterTournamentQuest(s *Session, p mhfpacket.MHFPacket) {}

View File

@@ -108,29 +108,10 @@ func removeSessionFromStage(s *Session) {
// 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 objID, stageObject := range s.stage.objects { for _, object := range s.stage.objects {
if stageObject.ownerCharID == s.charID { if object.ownerCharID == s.charID {
clientNotif := byteframe.NewByteFrame() s.stage.BroadcastMHF(&mhfpacket.MsgSysDeleteObject{ObjID: object.id}, s)
var pkt mhfpacket.MHFPacket delete(s.stage.objects, object.ownerCharID)
pkt = &mhfpacket.MsgSysDeleteObject{
ObjID: stageObject.id,
}
clientNotif.WriteUint16(uint16(pkt.Opcode()))
pkt.Build(clientNotif, s.clientContext)
clientNotif.WriteUint16(0x0010)
for client, _ := range s.stage.clients {
client.QueueSend(clientNotif.Data())
}
// TODO(Andoryuuta): Should this be sent to the owner's client as well? it currently isn't.
// Actually delete it from the objects map.
delete(s.stage.objects, objID)
}
}
for objListID, stageObjectList := range s.stage.objectList {
if stageObjectList.charid == s.charID {
// Added to prevent duplicates from flooding ObjectMap and causing server hangs
s.stage.objectList[objListID].status = false
s.stage.objectList[objListID].charid = 0
} }
} }
s.stage.Unlock() s.stage.Unlock()
@@ -140,44 +121,20 @@ func removeSessionFromStage(s *Session) {
func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysEnterStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysEnterStage) pkt := p.(*mhfpacket.MsgSysEnterStage)
// 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.
s.Lock() if s.stageID == "" {
s.stageMoveStack.Push(s.stageID) s.stageMoveStack.Set(pkt.StageID)
s.Unlock() } else {
s.stageMoveStack.Push(s.stageID)
s.stageMoveStack.Lock()
}
s.QueueSendMHF(&mhfpacket.MsgSysCleanupObject{}) s.QueueSendMHF(&mhfpacket.MsgSysCleanupObject{})
if s.reservationStage != nil { if s.reservationStage != nil {
s.reservationStage = nil s.reservationStage = nil
} }
if pkt.StageID == "sl1Ns200p0a0u0" { // First entry
var temp mhfpacket.MHFPacket
loginNotif := byteframe.NewByteFrame()
s.server.Lock()
for _, session := range s.server.sessions {
if s == session || !session.binariesDone {
continue
}
temp = &mhfpacket.MsgSysInsertUser{
CharID: session.charID,
}
loginNotif.WriteUint16(uint16(temp.Opcode()))
temp.Build(loginNotif, s.clientContext)
for i := 1; i <= 3; i++ {
temp = &mhfpacket.MsgSysNotifyUserBinary{
CharID: session.charID,
BinaryType: uint8(i),
}
loginNotif.WriteUint16(uint16(temp.Opcode()))
temp.Build(loginNotif, s.clientContext)
}
}
s.server.Unlock()
loginNotif.WriteUint16(0x0010) // End it.
if len(loginNotif.Data()) > 2 {
s.QueueSend(loginNotif.Data())
}
}
doStageTransfer(s, pkt.AckHandle, pkt.StageID) doStageTransfer(s, pkt.AckHandle, pkt.StageID)
} }
@@ -185,9 +142,8 @@ func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysBackStage) pkt := p.(*mhfpacket.MsgSysBackStage)
// Transfer back to the saved stage ID before the previous move or enter. // Transfer back to the saved stage ID before the previous move or enter.
s.Lock() s.stageMoveStack.Unlock()
backStage, err := s.stageMoveStack.Pop() backStage, err := s.stageMoveStack.Pop()
s.Unlock()
if err != nil { if err != nil {
panic(err) panic(err)
@@ -199,10 +155,10 @@ func handleMsgSysBackStage(s *Session, p mhfpacket.MHFPacket) {
func handleMsgSysMoveStage(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysMoveStage(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysMoveStage) pkt := p.(*mhfpacket.MsgSysMoveStage)
// Push our current stage ID to the movement stack before entering another one. // Set a new move stack from the given stage ID if unlocked
s.Lock() if !s.stageMoveStack.Locked {
s.stageMoveStack.Push(s.stageID) s.stageMoveStack.Set(pkt.StageID)
s.Unlock() }
doStageTransfer(s, pkt.AckHandle, pkt.StageID) doStageTransfer(s, pkt.AckHandle, pkt.StageID)
} }
@@ -219,11 +175,9 @@ func handleMsgSysUnlockStage(s *Session, p mhfpacket.MHFPacket) {
s.reservationStage.RLock() s.reservationStage.RLock()
defer s.reservationStage.RUnlock() defer s.reservationStage.RUnlock()
destructMessage := &mhfpacket.MsgSysStageDestruct{}
for charID := range s.reservationStage.reservedClientSlots { for charID := range s.reservationStage.reservedClientSlots {
session := s.server.FindSessionByCharID(charID) session := s.server.FindSessionByCharID(charID)
session.QueueSendMHF(destructMessage) session.QueueSendMHF(&mhfpacket.MsgSysStageDestruct{})
} }
s.server.Lock() s.server.Lock()

View File

@@ -12,6 +12,49 @@ func handleMsgSysInsertUser(s *Session, p mhfpacket.MHFPacket) {}
func handleMsgSysDeleteUser(s *Session, p mhfpacket.MHFPacket) {} func handleMsgSysDeleteUser(s *Session, p mhfpacket.MHFPacket) {}
func broadcastNewUser(s *Session) {
s.logger.Debug(fmt.Sprintf("Broadcasting new user: %s (%d)", s.Name, s.charID))
clientNotif := byteframe.NewByteFrame()
var temp mhfpacket.MHFPacket
for _, session := range s.server.sessions {
if session == s || !session.binariesDone {
continue
}
temp = &mhfpacket.MsgSysInsertUser{CharID: session.charID}
clientNotif.WriteUint16(uint16(temp.Opcode()))
temp.Build(clientNotif, s.clientContext)
for i := 0; i < 3; i++ {
temp = &mhfpacket.MsgSysNotifyUserBinary{
CharID: session.charID,
BinaryType: uint8(i + 1),
}
clientNotif.WriteUint16(uint16(temp.Opcode()))
temp.Build(clientNotif, s.clientContext)
}
}
s.QueueSend(clientNotif.Data())
serverNotif := byteframe.NewByteFrame()
temp = &mhfpacket.MsgSysInsertUser{CharID: s.charID}
serverNotif.WriteUint16(uint16(temp.Opcode()))
temp.Build(serverNotif, s.clientContext)
for i := 0; i < 3; i++ {
temp = &mhfpacket.MsgSysNotifyUserBinary{
CharID: s.charID,
BinaryType: uint8(i + 1),
}
serverNotif.WriteUint16(uint16(temp.Opcode()))
temp.Build(serverNotif, s.clientContext)
}
for _, session := range s.server.sessions {
if session == s || !session.binariesDone {
continue
}
session.QueueSend(serverNotif.Data())
}
}
func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) { func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) {
pkt := p.(*mhfpacket.MsgSysSetUserBinary) pkt := p.(*mhfpacket.MsgSysSetUserBinary)
s.server.userBinaryPartsLock.Lock() s.server.userBinaryPartsLock.Lock()
@@ -27,9 +70,7 @@ func handleMsgSysSetUserBinary(s *Session, p mhfpacket.MHFPacket) {
} }
} }
s.binariesDone = true s.binariesDone = true
s.server.BroadcastMHF(&mhfpacket.MsgSysInsertUser{ broadcastNewUser(s)
CharID: s.charID,
}, s)
return return
} }

View File

@@ -282,7 +282,7 @@ func (s *Server) manageSessions() {
func (s *Server) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) { func (s *Server) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) {
// Broadcast the data. // Broadcast the data.
for _, session := range s.sessions { for _, session := range s.sessions {
if session == ignoredSession { if session == ignoredSession || !session.binariesDone {
continue continue
} }
@@ -300,14 +300,14 @@ func (s *Server) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session)
func (s *Server) WorldcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) { func (s *Server) WorldcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) {
for _, c := range s.Channels { for _, c := range s.Channels {
for _, s := range c.sessions { for _, session := range c.sessions {
if s == ignoredSession { if session == ignoredSession || !session.binariesDone {
continue continue
} }
bf := byteframe.NewByteFrame() bf := byteframe.NewByteFrame()
bf.WriteUint16(uint16(pkt.Opcode())) bf.WriteUint16(uint16(pkt.Opcode()))
pkt.Build(bf, s.clientContext) pkt.Build(bf, session.clientContext)
s.QueueSendNonBlocking(bf.Data()) session.QueueSendNonBlocking(bf.Data())
} }
} }
} }
@@ -389,7 +389,7 @@ func (s *Server) FindSessionByCharID(charID uint32) *Session {
return nil return nil
} }
func (s *Server) FindStageObjectByChar(charID uint32) *StageObject { func (s *Server) FindObjectByChar(charID uint32) *Object {
s.stagesLock.RLock() s.stagesLock.RLock()
defer s.stagesLock.RUnlock() defer s.stagesLock.RUnlock()
for _, stage := range s.stages { for _, stage := range s.stages {

View File

@@ -251,5 +251,9 @@ func (s *Session) logMessage(opcode uint16, data []byte, sender string, recipien
} }
fmt.Printf("[%s] -> [%s]\n", sender, recipient) fmt.Printf("[%s] -> [%s]\n", sender, recipient)
fmt.Printf("Opcode: %s\n", opcodePID) fmt.Printf("Opcode: %s\n", opcodePID)
fmt.Printf("Data [%d bytes]:\n%s\n", len(data), hex.Dump(data)) if len(data) <= s.server.erupeConfig.DevModeOptions.MaxHexdumpLength {
fmt.Printf("Data [%d bytes]:\n%s\n", len(data), hex.Dump(data))
} else {
fmt.Printf("Data [%d bytes]:\n(Too long!)\n\n", len(data))
}
} }

View File

@@ -9,18 +9,13 @@ import (
"erupe-ce/network/mhfpacket" "erupe-ce/network/mhfpacket"
) )
// StageObject holds infomation about a specific stage object. // Object holds infomation about a specific object.
type StageObject struct { type Object struct {
sync.RWMutex sync.RWMutex
id uint32 id uint32
ownerCharID uint32 ownerCharID uint32
x, y, z float32 x, y, z float32
} binary []byte
type ObjectMap struct {
id uint8
charid uint32
status bool
} }
// 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.
@@ -36,13 +31,10 @@ type Stage struct {
// Stage ID string // Stage ID string
id string id string
// Total count of objects ever created for this stage. Used for ObjID generation. // Objects
gameObjectCount uint32 objects map[uint32]*Object
objectIndex uint32
// Save all object in stage
objects map[uint32]*StageObject
objectList map[uint8]*ObjectMap
// 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[*Session]uint32
@@ -66,14 +58,12 @@ func NewStage(ID string) *Stage {
id: ID, id: ID,
clients: make(map[*Session]uint32), clients: make(map[*Session]uint32),
reservedClientSlots: make(map[uint32]bool), reservedClientSlots: make(map[uint32]bool),
objects: make(map[uint32]*StageObject), objects: make(map[uint32]*Object),
objectIndex: 0,
rawBinaryData: make(map[stageBinaryKey][]byte), rawBinaryData: make(map[stageBinaryKey][]byte),
maxPlayers: 4, maxPlayers: 4,
gameObjectCount: 1,
objectList: make(map[uint8]*ObjectMap),
createdAt: time.Now().Format("01-02-2006 15:04:05"), createdAt: time.Now().Format("01-02-2006 15:04:05"),
} }
s.InitObjectList()
return s return s
} }
@@ -81,7 +71,7 @@ func NewStage(ID string) *Stage {
func (s *Stage) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) { func (s *Stage) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) {
// Broadcast the data. // Broadcast the data.
for session := range s.clients { for session := range s.clients {
if session == ignoredSession { if session == ignoredSession || !session.binariesDone {
continue continue
} }
@@ -97,17 +87,6 @@ func (s *Stage) BroadcastMHF(pkt mhfpacket.MHFPacket, ignoredSession *Session) {
} }
} }
func (s *Stage) InitObjectList() {
for seq := uint8(0x7f); seq > uint8(0); seq-- {
newObj := &ObjectMap{
id: seq,
charid: uint32(0),
status: false,
}
s.objectList[seq] = newObj
}
}
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
@@ -120,8 +99,8 @@ func (s *Stage) isQuest() bool {
return len(s.reservedClientSlots) > 0 return len(s.reservedClientSlots) > 0
} }
func (stage *Stage) GetName() string { func (s *Stage) GetName() string {
switch stage.id { switch s.id {
case MezeportaStageId: case MezeportaStageId:
return "Mezeporta" return "Mezeporta"
case GuildHallLv1StageId: case GuildHallLv1StageId:
@@ -149,20 +128,7 @@ func (stage *Stage) GetName() string {
} }
} }
func (s *Stage) GetNewObjectID(CharID uint32) uint32 { func (s *Stage) NextObjectID() uint32 {
ObjId := uint8(0) s.objectIndex = s.objectIndex + 1
for seq := uint8(0x7f); seq > uint8(0); seq-- { return s.objectIndex
if s.objectList[seq].status == false {
ObjId = seq
break
}
}
s.objectList[ObjId].status = true
s.objectList[ObjId].charid = CharID
bf := byteframe.NewByteFrame()
bf.WriteUint8(uint8(0))
bf.WriteUint8(ObjId)
bf.WriteUint16(uint16(0))
obj := uint32(bf.Data()[3]) | uint32(bf.Data()[2])<<8 | uint32(bf.Data()[1])<<16 | uint32(bf.Data()[0])<<32
return obj
} }