From f0b9bc6ed6c20229ac64419d49d426071dc0020d Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 25 Jun 2023 01:51:21 -0500 Subject: [PATCH] god forgive me for this commit --- config/config.go | 1 + core/db/players.go | 14 +++---- core/entity/chunk.go | 79 +++++++++++++++++++++++++++++++++++++ core/entity/entity.go | 20 ++++++++++ core/{ => entity}/player.go | 36 ++++++++++++++++- core/protocol/cnpeer.go | 28 +++++-------- core/protocol/event.go | 15 +++++++ login/login.go | 1 + login/loginServer.go | 47 ++++++++++++++++------ shard/chunks.go | 23 +++++++++++ shard/join.go | 57 +++++++++++++------------- shard/movement.go | 25 ++++++++++++ shard/shardServer.go | 77 +++++++++++++++++++++--------------- 13 files changed, 327 insertions(+), 96 deletions(-) create mode 100644 core/entity/chunk.go create mode 100644 core/entity/entity.go rename core/{ => entity}/player.go (75%) create mode 100644 core/protocol/event.go create mode 100644 shard/chunks.go diff --git a/config/config.go b/config/config.go index fd72a12..92b251b 100644 --- a/config/config.go +++ b/config/config.go @@ -33,6 +33,7 @@ var ( SHARD_PORT = 23001 LOGIN_TIMEOUT = time.Second * 30 + VIEW_DISTANCE = 25600 ) func getEnv(key string, fallback string) string { diff --git a/core/db/players.go b/core/db/players.go index ab0885a..6d88a24 100644 --- a/core/db/players.go +++ b/core/db/players.go @@ -4,7 +4,7 @@ import ( "database/sql" "github.com/CPunch/gopenfusion/config" - "github.com/CPunch/gopenfusion/core" + "github.com/CPunch/gopenfusion/core/entity" "github.com/CPunch/gopenfusion/core/protocol" "github.com/blockloop/scan" ) @@ -121,8 +121,8 @@ const ( INNER JOIN Accounts as acc ON p.AccountID = acc.AccountID ` ) -func (db *DBHandler) readPlayer(rows *sql.Rows) (*core.Player, error) { - plr := core.Player{ActiveNanoSlotNum: -1} +func (db *DBHandler) readPlayer(rows *sql.Rows) (*entity.Player, error) { + plr := entity.Player{ActiveNanoSlotNum: -1} if err := rows.Scan( &plr.PlayerID, &plr.AccountID, &plr.Slot, &plr.PCStyle.SzFirstName, &plr.PCStyle.SzLastName, @@ -162,13 +162,13 @@ func (db *DBHandler) readPlayer(rows *sql.Rows) (*core.Player, error) { return &plr, nil } -func (db *DBHandler) GetPlayer(PlayerID int) (*core.Player, error) { +func (db *DBHandler) GetPlayer(PlayerID int) (*entity.Player, error) { rows, err := db.Query(QUERY_PLAYERS+"WHERE p.PlayerID = $1", PlayerID) if err != nil { return nil, err } - var plr *core.Player + var plr *entity.Player for rows.Next() { plr, err = db.readPlayer(rows) if err != nil { @@ -179,13 +179,13 @@ func (db *DBHandler) GetPlayer(PlayerID int) (*core.Player, error) { return plr, nil } -func (db *DBHandler) GetPlayers(AccountID int) ([]core.Player, error) { +func (db *DBHandler) GetPlayers(AccountID int) ([]entity.Player, error) { rows, err := db.Query(QUERY_PLAYERS+"WHERE p.AccountID = $1", AccountID) if err != nil { return nil, err } - var plrs []core.Player + var plrs []entity.Player for rows.Next() { plr, err := db.readPlayer(rows) if err != nil { diff --git a/core/entity/chunk.go b/core/entity/chunk.go new file mode 100644 index 0000000..c3e3db0 --- /dev/null +++ b/core/entity/chunk.go @@ -0,0 +1,79 @@ +package entity + +import ( + "log" + "sync" + + "github.com/CPunch/gopenfusion/config" +) + +type ChunkPosition struct { + X int + Y int +} + +func makeChunkPosition(x, y int) ChunkPosition { + return ChunkPosition{ + X: x / (config.VIEW_DISTANCE / 3), + Y: y / (config.VIEW_DISTANCE / 3), + } +} + +type Chunk struct { + Position ChunkPosition + entities map[Entity]struct{} + lock sync.Mutex +} + +func NewChunk(position ChunkPosition) *Chunk { + return &Chunk{ + Position: position, + entities: make(map[Entity]struct{}), + } +} + +func (c *Chunk) AddEntity(entity Entity) { + entity.SetChunk(c) + c.entities[entity] = struct{}{} +} + +func (c *Chunk) RemoveEntity(entity Entity) { + entity.SetChunk(nil) + delete(c.entities, entity) +} + +// send packet to all peers in this chunk and kill each peer if error +func (c *Chunk) SendPacket(typeID uint32, pkt ...interface{}) { + c.lock.Lock() + defer c.lock.Unlock() + + for entity := range c.entities { + if entity.GetKind() != ENTITY_KIND_PLAYER { + continue + } + + plr, ok := entity.(*Player) + if !ok { + log.Panic("Chunk.SendPacket: entity kind was player, but is not a *Player") + } + peer := plr.Peer + + if err := peer.Send(typeID, pkt...); err != nil { + peer.Kill() + } + } +} + +func (c *Chunk) GetAdjacentPositions() []ChunkPosition { + return []ChunkPosition{ + {c.Position.X - 1, c.Position.Y - 1}, + {c.Position.X - 1, c.Position.Y}, + {c.Position.X - 1, c.Position.Y + 1}, + {c.Position.X, c.Position.Y - 1}, + {c.Position.X, c.Position.Y}, + {c.Position.X, c.Position.Y + 1}, + {c.Position.X + 1, c.Position.Y - 1}, + {c.Position.X + 1, c.Position.Y}, + {c.Position.X + 1, c.Position.Y + 1}, + } +} diff --git a/core/entity/entity.go b/core/entity/entity.go new file mode 100644 index 0000000..baaeeb3 --- /dev/null +++ b/core/entity/entity.go @@ -0,0 +1,20 @@ +package entity + +type EntityKind int + +const ( + ENTITY_KIND_PLAYER EntityKind = iota + ENTITY_KIND_NPC +) + +type Entity interface { + GetKind() EntityKind + + GetChunk() *Chunk + GetPosition() (x int, y int, z int) + GetAngle() int + + SetChunk(chunk *Chunk) + SetPosition(x, y, z int) + SetAngle(angle int) +} diff --git a/core/player.go b/core/entity/player.go similarity index 75% rename from core/player.go rename to core/entity/player.go index 3c3b4e1..f5af145 100644 --- a/core/player.go +++ b/core/entity/player.go @@ -1,4 +1,4 @@ -package core +package entity import ( "github.com/CPunch/gopenfusion/config" @@ -6,6 +6,8 @@ import ( ) type Player struct { + Peer *protocol.CNPeer + CurrentChunk *Chunk PlayerID int AccountID int AccountLevel int @@ -35,6 +37,38 @@ type Player struct { CurrentMissionID int } +// ==================== Entity interface ==================== + +func (plr *Player) GetKind() EntityKind { + return ENTITY_KIND_PLAYER +} + +func (plr *Player) GetChunk() *Chunk { + return plr.CurrentChunk +} + +func (plr *Player) GetPosition() (x int, y int, z int) { + return plr.X, plr.Y, plr.Z +} + +func (plr *Player) GetAngle() int { + return plr.Angle +} + +func (plr *Player) SetPosition(x, y, z int) { + plr.X = x + plr.Y = y + plr.Z = z +} + +func (plr *Player) SetAngle(angle int) { + plr.Angle = angle +} + +func (plr *Player) SetChunk(chunk *Chunk) { + plr.CurrentChunk = chunk +} + func (plr *Player) ToPCLoadData2CL() protocol.SPCLoadData2CL { return protocol.SPCLoadData2CL{ IUserLevel: int16(plr.AccountLevel), diff --git a/core/protocol/cnpeer.go b/core/protocol/cnpeer.go index 8b11ee6..2c19d66 100644 --- a/core/protocol/cnpeer.go +++ b/core/protocol/cnpeer.go @@ -15,15 +15,10 @@ const ( USE_FE ) -type PeerHandler interface { - HandlePacket(peer *CNPeer, typeID uint32, pkt Packet) error - Disconnect(peer *CNPeer) -} - // CNPeer is a simple wrapper for net.Conn connections to send/recv packets over the Fusionfall packet protocol. type CNPeer struct { conn net.Conn - handler PeerHandler + eRecv chan *Event SzID string E_key []byte FE_key []byte @@ -33,10 +28,10 @@ type CNPeer struct { alive bool } -func NewCNPeer(handler PeerHandler, conn net.Conn) *CNPeer { +func NewCNPeer(eRecv chan *Event, conn net.Conn) *CNPeer { return &CNPeer{ conn: conn, - handler: handler, + eRecv: eRecv, SzID: "", E_key: []byte(DEFAULT_KEY), FE_key: nil, @@ -93,13 +88,14 @@ func (peer *CNPeer) SetActiveKey(whichKey int) { } func (peer *CNPeer) Kill() { + log.Printf("Killing peer %p", peer) if !peer.alive { return } peer.alive = false peer.conn.Close() - peer.handler.Disconnect(peer) + peer.eRecv <- &Event{Type: EVENT_CLIENT_DISCONNECT, Peer: peer} } // meant to be invoked as a goroutine @@ -123,7 +119,6 @@ func (peer *CNPeer) Handler() { // grab buffer && read packet body if err := func() error { buf := pool.Get() - defer pool.Put(buf) if _, err := buf.ReadFrom(io.LimitReader(peer.conn, int64(sz))); err != nil { return fmt.Errorf("failed to read packet body! %v", err) } @@ -132,18 +127,15 @@ func (peer *CNPeer) Handler() { DecryptData(buf.Bytes(), peer.E_key) pkt := NewPacket(buf) - // create packet && read typeID - var typeID uint32 - if err := pkt.Decode(&typeID); err != nil { + // create packet && read pktID + var pktID uint32 + if err := pkt.Decode(&pktID); err != nil { return fmt.Errorf("failed to read packet type! %v", err) } // dispatch packet - log.Printf("Got packet ID: %x, with a sizeof: %d\n", typeID, sz) - if err := peer.handler.HandlePacket(peer, typeID, pkt); err != nil { - return err - } - + log.Printf("Got packet ID: %x, with a sizeof: %d\n", pktID, sz) + peer.eRecv <- &Event{Type: EVENT_CLIENT_PACKET, Peer: peer, Pkt: buf, PktID: pktID} return nil }(); err != nil { log.Printf("[FATAL] %v", err) diff --git a/core/protocol/event.go b/core/protocol/event.go new file mode 100644 index 0000000..bfb0613 --- /dev/null +++ b/core/protocol/event.go @@ -0,0 +1,15 @@ +package protocol + +import "bytes" + +const ( + EVENT_CLIENT_DISCONNECT = iota + EVENT_CLIENT_PACKET +) + +type Event struct { + Type int + Peer *CNPeer + Pkt *bytes.Buffer + PktID uint32 +} diff --git a/login/login.go b/login/login.go index 8173723..dbf1016 100644 --- a/login/login.go +++ b/login/login.go @@ -217,6 +217,7 @@ func (server *LoginServer) CharacterCreate(peer *protocol.CNPeer, pkt protocol.P pkt.Decode(&charPkt) if !validateCharacterCreation(&charPkt) { + log.Printf("Invalid character creation packet: %+v", charPkt) return SendFail(peer) } diff --git a/login/loginServer.go b/login/loginServer.go index f2dd945..e7dbdb8 100644 --- a/login/loginServer.go +++ b/login/loginServer.go @@ -9,6 +9,7 @@ import ( "github.com/CPunch/gopenfusion/config" "github.com/CPunch/gopenfusion/core/db" "github.com/CPunch/gopenfusion/core/protocol" + "github.com/CPunch/gopenfusion/core/protocol/pool" "github.com/CPunch/gopenfusion/core/redis" ) @@ -21,9 +22,10 @@ type LoginServer struct { port int dbHndlr *db.DBHandler redisHndlr *redis.RedisHandler - packetHandlers map[uint32]PacketHandler + eRecv chan *protocol.Event peers map[*protocol.CNPeer]bool - peersLock sync.Mutex + packetHandlers map[uint32]PacketHandler + peerLock sync.Mutex } func NewLoginServer(dbHndlr *db.DBHandler, redisHndlr *redis.RedisHandler, port int) (*LoginServer, error) { @@ -38,6 +40,7 @@ func NewLoginServer(dbHndlr *db.DBHandler, redisHndlr *redis.RedisHandler, port dbHndlr: dbHndlr, redisHndlr: redisHndlr, peers: make(map[*protocol.CNPeer]bool), + eRecv: make(chan *protocol.Event), } server.packetHandlers = map[uint32]PacketHandler{ @@ -63,6 +66,7 @@ func NewLoginServer(dbHndlr *db.DBHandler, redisHndlr *redis.RedisHandler, port func (server *LoginServer) Start() { log.Printf("Login service hosted on %s:%d\n", config.GetAnnounceIP(), server.port) + go server.handleEvents() for { conn, err := server.listener.Accept() if err != nil { @@ -70,13 +74,32 @@ func (server *LoginServer) Start() { return } - client := protocol.NewCNPeer(server, conn) - server.Connect(client) + client := protocol.NewCNPeer(server.eRecv, conn) + server.connect(client) go client.Handler() } } -func (server *LoginServer) HandlePacket(peer *protocol.CNPeer, typeID uint32, pkt protocol.Packet) error { +func (server *LoginServer) handleEvents() { + for { + select { + case event := <-server.eRecv: + switch event.Type { + case protocol.EVENT_CLIENT_DISCONNECT: + server.disconnect(event.Peer) + case protocol.EVENT_CLIENT_PACKET: + defer pool.Put(event.Pkt) + + if err := server.handlePacket(event.Peer, event.PktID, protocol.NewPacket(event.Pkt)); err != nil { + log.Printf("Error handling packet: %v", err) + event.Peer.Kill() + } + } + } + } +} + +func (server *LoginServer) handlePacket(peer *protocol.CNPeer, typeID uint32, pkt protocol.Packet) error { if hndlr, ok := server.packetHandlers[typeID]; ok { if err := hndlr(peer, pkt); err != nil { return err @@ -88,16 +111,18 @@ func (server *LoginServer) HandlePacket(peer *protocol.CNPeer, typeID uint32, pk return nil } -func (server *LoginServer) Disconnect(peer *protocol.CNPeer) { - server.peersLock.Lock() +func (server *LoginServer) disconnect(peer *protocol.CNPeer) { + server.peerLock.Lock() + defer server.peerLock.Unlock() + log.Printf("Peer %p disconnected from LOGIN\n", peer) delete(server.peers, peer) - server.peersLock.Unlock() } -func (server *LoginServer) Connect(peer *protocol.CNPeer) { - server.peersLock.Lock() +func (server *LoginServer) connect(peer *protocol.CNPeer) { + server.peerLock.Lock() + defer server.peerLock.Unlock() + log.Printf("New peer %p connected to LOGIN\n", peer) server.peers[peer] = true - server.peersLock.Unlock() } diff --git a/shard/chunks.go b/shard/chunks.go new file mode 100644 index 0000000..0c5b721 --- /dev/null +++ b/shard/chunks.go @@ -0,0 +1,23 @@ +package shard + +import "github.com/CPunch/gopenfusion/core/entity" + +func (server *ShardServer) getChunk(pos entity.ChunkPosition) *entity.Chunk { + chunk, ok := server.chunks[pos] + if !ok { + chunk = entity.NewChunk(pos) + server.chunks[pos] = chunk + } + + return chunk +} + +func (server *ShardServer) getViewableChunks(plr *entity.Player) []*entity.Chunk { + chunks := make([]*entity.Chunk, 0, 9) + + for _, pos := range plr.GetChunk().GetAdjacentPositions() { + chunks = append(chunks, server.getChunk(pos)) + } + + return chunks +} diff --git a/shard/join.go b/shard/join.go index 88f4046..c4b255e 100644 --- a/shard/join.go +++ b/shard/join.go @@ -5,10 +5,28 @@ import ( "log" "time" - "github.com/CPunch/gopenfusion/core" + "github.com/CPunch/gopenfusion/core/entity" "github.com/CPunch/gopenfusion/core/protocol" + "github.com/CPunch/gopenfusion/core/redis" ) +func (server *ShardServer) attachPlayer(peer *protocol.CNPeer, meta redis.LoginMetadata) (*entity.Player, error) { + // resending a shard enter packet? + old, err := server.getPlayer(peer) + if old != nil { + return nil, fmt.Errorf("resent enter packet!") + } + + // attach player + plr, err := server.dbHndlr.GetPlayer(int(meta.PlayerID)) + if err != nil { + return nil, err + } + + server.setPlayer(peer, plr) + return plr, err +} + func (server *ShardServer) RequestEnter(peer *protocol.CNPeer, pkt protocol.Packet) error { var enter protocol.SP_CL2FE_REQ_PC_ENTER pkt.Decode(&enter) @@ -20,38 +38,23 @@ func (server *ShardServer) RequestEnter(peer *protocol.CNPeer, pkt protocol.Pack return err } - // attach player - var resp *protocol.SP_FE2CL_REP_PC_ENTER_SUCC - if err := server.UpdatePlayer(peer, func(old *core.Player) (*core.Player, error) { - if old != nil { // resending a shard enter packet? - return nil, fmt.Errorf("resent enter packet!") - } - - plr, err := server.dbHndlr.GetPlayer(int(loginData.PlayerID)) - if err != nil { - return nil, err - } - - resp = &protocol.SP_FE2CL_REP_PC_ENTER_SUCC{ - IID: int32(plr.PlayerID), - PCLoadData2CL: plr.ToPCLoadData2CL(), - UiSvrTime: uint64(time.Now().Unix()), - } - - return plr, nil - }); err != nil { - peer.Send(protocol.P_FE2CL_REP_PC_ENTER_FAIL, protocol.SP_FE2CL_REP_PC_ENTER_FAIL{}) + plr, err := server.attachPlayer(peer, loginData) + if err != nil { return err } - // setup key - peer.E_key = protocol.CreateNewKey(resp.UiSvrTime, uint64(resp.IID+1), uint64(resp.PCLoadData2CL.IFusionMatter+1)) - peer.FE_key = loginData.FEKey - peer.SetActiveKey(protocol.USE_FE) + resp := &protocol.SP_FE2CL_REP_PC_ENTER_SUCC{ + IID: int32(plr.PlayerID), + PCLoadData2CL: plr.ToPCLoadData2CL(), + UiSvrTime: uint64(time.Now().Unix()), + } // setup peer + peer.E_key = protocol.CreateNewKey(resp.UiSvrTime, uint64(resp.IID+1), uint64(resp.PCLoadData2CL.IFusionMatter+1)) + peer.FE_key = loginData.FEKey peer.PlayerID = loginData.PlayerID peer.AccountID = loginData.AccountID + peer.SetActiveKey(protocol.USE_FE) log.Printf("Player %d (AccountID %d) entered\n", resp.IID, loginData.AccountID) return peer.Send(protocol.P_FE2CL_REP_PC_ENTER_SUCC, resp) @@ -61,7 +64,7 @@ func (server *ShardServer) LoadingComplete(peer *protocol.CNPeer, pkt protocol.P var loadComplete protocol.SP_CL2FE_REQ_PC_LOADING_COMPLETE pkt.Decode(&loadComplete) - plr, err := server.LoadPlayer(peer) + plr, err := server.getPlayer(peer) if err != nil { return err } diff --git a/shard/movement.go b/shard/movement.go index ea03ee4..40fed2f 100644 --- a/shard/movement.go +++ b/shard/movement.go @@ -1 +1,26 @@ package shard + +import ( + "github.com/CPunch/gopenfusion/core/protocol" +) + +func (server *ShardServer) updatePlayerPosition(peer *protocol.CNPeer, X, Y, Z, Angle int) error { + plr, err := server.getPlayer(peer) + if err != nil { + return err + } + + plr.X = X + plr.Y = Y + plr.Z = Z + plr.Angle = Angle + + return nil +} + +func (server *ShardServer) playerMove(peer *protocol.CNPeer, pkt protocol.Packet) error { + var move protocol.SP_CL2FE_REQ_PC_MOVE + pkt.Decode(&move) + + return server.updatePlayerPosition(peer, int(move.IX), int(move.IY), int(move.IZ), int(move.IAngle)) +} diff --git a/shard/shardServer.go b/shard/shardServer.go index d1fa742..098e5cd 100644 --- a/shard/shardServer.go +++ b/shard/shardServer.go @@ -7,9 +7,10 @@ import ( "sync" "github.com/CPunch/gopenfusion/config" - "github.com/CPunch/gopenfusion/core" "github.com/CPunch/gopenfusion/core/db" + "github.com/CPunch/gopenfusion/core/entity" "github.com/CPunch/gopenfusion/core/protocol" + "github.com/CPunch/gopenfusion/core/protocol/pool" "github.com/CPunch/gopenfusion/core/redis" ) @@ -22,9 +23,11 @@ type ShardServer struct { port int dbHndlr *db.DBHandler redisHndlr *redis.RedisHandler + eRecv chan *protocol.Event packetHandlers map[uint32]PacketHandler - peersLock sync.Mutex - peers map[*protocol.CNPeer]*core.Player + peers map[*protocol.CNPeer]*entity.Player + chunks map[entity.ChunkPosition]*entity.Chunk + peerLock sync.Mutex } func NewShardServer(dbHndlr *db.DBHandler, redisHndlr *redis.RedisHandler, port int) (*ShardServer, error) { @@ -39,6 +42,9 @@ func NewShardServer(dbHndlr *db.DBHandler, redisHndlr *redis.RedisHandler, port dbHndlr: dbHndlr, redisHndlr: redisHndlr, packetHandlers: make(map[uint32]PacketHandler), + peers: make(map[*protocol.CNPeer]*entity.Player), + chunks: make(map[entity.ChunkPosition]*entity.Chunk), + eRecv: make(chan *protocol.Event), } server.packetHandlers = map[uint32]PacketHandler{ @@ -54,13 +60,28 @@ func NewShardServer(dbHndlr *db.DBHandler, redisHndlr *redis.RedisHandler, port return server, nil } -func (server *ShardServer) RegisterPacketHandler(typeID uint32, hndlr PacketHandler) { - server.packetHandlers[typeID] = hndlr +func (server *ShardServer) handleEvents() { + for { + select { + case event := <-server.eRecv: + switch event.Type { + case protocol.EVENT_CLIENT_DISCONNECT: + server.disconnect(event.Peer) + case protocol.EVENT_CLIENT_PACKET: + defer pool.Put(event.Pkt) + log.Printf("Received packet %x from %p\n", event.PktID, event.Peer) + if err := server.handlePacket(event.Peer, event.PktID, protocol.NewPacket(event.Pkt)); err != nil { + event.Peer.Kill() + } + } + } + } } func (server *ShardServer) Start() { log.Printf("Shard service hosted on %s:%d\n", config.GetAnnounceIP(), server.port) + go server.handleEvents() for { conn, err := server.listener.Accept() if err != nil { @@ -68,8 +89,8 @@ func (server *ShardServer) Start() { return } - client := protocol.NewCNPeer(server, conn) - server.Connect(client) + client := protocol.NewCNPeer(server.eRecv, conn) + server.connect(client) go client.Handler() } } @@ -78,7 +99,7 @@ func (server *ShardServer) GetPort() int { return server.port } -func (server *ShardServer) HandlePacket(peer *protocol.CNPeer, typeID uint32, pkt protocol.Packet) error { +func (server *ShardServer) handlePacket(peer *protocol.CNPeer, typeID uint32, pkt protocol.Packet) error { if hndlr, ok := server.packetHandlers[typeID]; ok { if err := hndlr(peer, pkt); err != nil { return err @@ -90,47 +111,39 @@ func (server *ShardServer) HandlePacket(peer *protocol.CNPeer, typeID uint32, pk return nil } -func (server *ShardServer) Disconnect(peer *protocol.CNPeer) { +func (server *ShardServer) disconnect(peer *protocol.CNPeer) { + server.peerLock.Lock() + defer server.peerLock.Unlock() + log.Printf("Peer %p disconnected from SHARD\n", peer) delete(server.peers, peer) } -func (server *ShardServer) Connect(peer *protocol.CNPeer) { +func (server *ShardServer) connect(peer *protocol.CNPeer) { + server.peerLock.Lock() + defer server.peerLock.Unlock() + log.Printf("New peer %p connected to SHARD\n", peer) server.peers[peer] = nil } -// Returns a copy of the player -func (server *ShardServer) LoadPlayer(peer *protocol.CNPeer) (core.Player, error) { +func (server *ShardServer) getPlayer(peer *protocol.CNPeer) (*entity.Player, error) { plr, ok := server.peers[peer] if !ok { - return core.Player{}, fmt.Errorf("Player not found") + return nil, fmt.Errorf("Player not found") } - return *plr, nil + return plr, nil } -// UpdatePlayer locks the peers map, and calls the provided callback. The returned new pointer will be stored, however if an error returns it will be passed back. -// Since it is UNSAFE to write to the returned pointer from LoadPlayer, this wrapper is for the cases that state in the player struct needs to be updated. -// The pointers new and old may be the same if you are just updating struct fields. This function should NOT be called recursively. -func (server *ShardServer) UpdatePlayer(peer *protocol.CNPeer, f func(old *core.Player) (new *core.Player, err error)) error { - server.peersLock.Lock() - defer server.peersLock.Unlock() - - // on fail, the player should not be stored - new, err := f(server.peers[peer]) - if err != nil { - return err - } - - server.peers[peer] = new - return nil +func (server *ShardServer) setPlayer(peer *protocol.CNPeer, plr *entity.Player) { + server.peers[peer] = plr } // If f returns false the iteration is stopped. -func (server *ShardServer) RangePeers(f func(peer *protocol.CNPeer) bool) { - for peer := range server.peers { - if f(peer) { +func (server *ShardServer) rangePeers(f func(peer *protocol.CNPeer, plr *entity.Player) bool) { + for peer, plr := range server.peers { + if f(peer, plr) { return } }