diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..db4de7c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "tdata"] + path = tdata + url = https://github.com/OpenFusionProject/tabledata.git diff --git a/compose.yaml b/compose.yaml index 3f3d674..3ae2df3 100644 --- a/compose.yaml +++ b/compose.yaml @@ -35,6 +35,9 @@ services: - DB_PASS=gopenfusion - DB_NAME=gopenfusion - REDIS_ADDR=redis:6379 + - TDATA_PATH=/tdata + volumes: + - './tdata:/tdata' ports: - '23001:23001' links: diff --git a/config/config.go b/config/config.go index 92b251b..93fc5ce 100644 --- a/config/config.go +++ b/config/config.go @@ -14,6 +14,7 @@ import ( DB_USER DB_PASS ANNOUNCE_IP + TDATA_PATH */ const ( @@ -78,3 +79,7 @@ func GetDBPass() string { func GetAnnounceIP() string { return getEnv("ANNOUNCE_IP", "127.0.0.1") } + +func GetTDataPath() string { + return getEnv("TDATA_PATH", "./tdata") +} diff --git a/core/entity/npc.go b/core/entity/npc.go new file mode 100644 index 0000000..b948a87 --- /dev/null +++ b/core/entity/npc.go @@ -0,0 +1,84 @@ +package entity + +import "github.com/CPunch/gopenfusion/core/protocol" + +type NPC struct { + ID int + X int `json:"iX"` + Y int `json:"iY"` + Z int `json:"iZ"` + Angle int `json:"iAngle"` + NPCType int `json:"iNPCType"` + Chunk ChunkPosition +} + +var nextNPCID = 0 + +func NewNPC(X, Y, Z, Angle int, npcType int) *NPC { + nextNPCID++ + return &NPC{ + ID: nextNPCID, + X: X, + Y: Y, + Z: Z, + Angle: Angle, + NPCType: npcType, + Chunk: MakeChunkPosition(X, Y), + } +} + +// ==================== Entity interface ==================== + +func (npc *NPC) GetKind() EntityKind { + return ENTITY_KIND_NPC +} + +func (npc *NPC) GetChunk() ChunkPosition { + return npc.Chunk +} + +func (npc *NPC) GetPosition() (x int, y int, z int) { + return npc.X, npc.Y, npc.Z +} + +func (npc *NPC) GetAngle() int { + return npc.Angle +} + +func (npc *NPC) SetChunk(chunk ChunkPosition) { + npc.Chunk = chunk +} + +func (npc *NPC) SetPosition(x, y, z int) { + npc.X = x + npc.Y = y + npc.Z = z +} + +func (npc *NPC) SetAngle(angle int) { + npc.Angle = angle +} + +func (npc *NPC) DisappearFromViewOf(peer *protocol.CNPeer) { + peer.Send(protocol.P_FE2CL_NPC_EXIT, protocol.SP_FE2CL_NPC_EXIT{ + INPC_ID: int32(npc.ID), + }) +} + +func (npc *NPC) EnterIntoViewOf(peer *protocol.CNPeer) { + peer.Send(protocol.P_FE2CL_NPC_NEW, protocol.SP_FE2CL_NPC_NEW{ + NPCAppearanceData: npc.GetAppearanceData(), + }) +} + +func (npc *NPC) GetAppearanceData() protocol.SNPCAppearanceData { + return protocol.SNPCAppearanceData{ + INPC_ID: int32(npc.ID), + INPCType: int32(npc.NPCType), + IHP: 100, + IX: int32(npc.X), + IY: int32(npc.Y), + IZ: int32(npc.Z), + IAngle: int32(npc.Angle), + } +} diff --git a/shard/join.go b/shard/join.go index b507506..bbe715d 100644 --- a/shard/join.go +++ b/shard/join.go @@ -58,8 +58,13 @@ func (server *ShardServer) RequestEnter(peer *protocol.CNPeer, pkt protocol.Pack log.Printf("Player %d (AccountID %d) entered\n", resp.IID, loginData.AccountID) + if err := peer.Send(protocol.P_FE2CL_REP_PC_ENTER_SUCC, resp); err != nil { + return err + } + + // we send the chunk updates (PC_NEW, NPC_NEW, etc.) after the enter packet server.updatePlayerPosition(plr, int(plr.X), int(plr.Y), int(plr.Z), int(plr.Angle)) - return peer.Send(protocol.P_FE2CL_REP_PC_ENTER_SUCC, resp) + return nil } func (server *ShardServer) LoadingComplete(peer *protocol.CNPeer, pkt protocol.Packet) error { diff --git a/shard/movement.go b/shard/movement.go index 76a0079..3a9d9f6 100644 --- a/shard/movement.go +++ b/shard/movement.go @@ -5,13 +5,12 @@ import ( "github.com/CPunch/gopenfusion/core/protocol" ) -func (server *ShardServer) updatePlayerPosition(plr *entity.Player, X, Y, Z, Angle int) error { +func (server *ShardServer) updatePlayerPosition(plr *entity.Player, X, Y, Z, Angle int) { plr.X = X plr.Y = Y plr.Z = Z plr.Angle = Angle server.updateEntityChunk(plr, plr.GetChunk(), entity.MakeChunkPosition(X, Y)) - return nil } func (server *ShardServer) playerMove(peer *protocol.CNPeer, pkt protocol.Packet) error { @@ -25,9 +24,7 @@ func (server *ShardServer) playerMove(peer *protocol.CNPeer, pkt protocol.Packet } // update chunking - if err := server.updatePlayerPosition(plr, int(move.IX), int(move.IY), int(move.IZ), int(move.IAngle)); err != nil { - return err - } + server.updatePlayerPosition(plr, int(move.IX), int(move.IY), int(move.IZ), int(move.IAngle)) return server.sendOthersPacket(plr, protocol.P_FE2CL_PC_MOVE, protocol.SP_FE2CL_PC_MOVE{ ICliTime: move.ICliTime, @@ -56,9 +53,7 @@ func (server *ShardServer) playerStop(peer *protocol.CNPeer, pkt protocol.Packet } // update chunking - if err := server.updatePlayerPosition(plr, int(stop.IX), int(stop.IY), int(stop.IZ), plr.Angle); err != nil { - return err - } + server.updatePlayerPosition(plr, int(stop.IX), int(stop.IY), int(stop.IZ), plr.Angle) return server.sendOthersPacket(plr, protocol.P_FE2CL_PC_STOP, protocol.SP_FE2CL_PC_STOP{ ICliTime: stop.ICliTime, @@ -81,9 +76,7 @@ func (server *ShardServer) playerJump(peer *protocol.CNPeer, pkt protocol.Packet } // update chunking - if err := server.updatePlayerPosition(plr, int(jump.IX), int(jump.IY), int(jump.IZ), plr.Angle); err != nil { - return err - } + server.updatePlayerPosition(plr, int(jump.IX), int(jump.IY), int(jump.IZ), plr.Angle) return server.sendOthersPacket(plr, protocol.P_FE2CL_PC_JUMP, protocol.SP_FE2CL_PC_JUMP{ ICliTime: jump.ICliTime, diff --git a/shard/npcloader.go b/shard/npcloader.go new file mode 100644 index 0000000..a7d2f89 --- /dev/null +++ b/shard/npcloader.go @@ -0,0 +1,32 @@ +package shard + +import ( + "encoding/json" + "log" + "os" + + "github.com/CPunch/gopenfusion/config" + "github.com/CPunch/gopenfusion/core/entity" +) + +type NPCData struct { + NPCs map[string]entity.NPC `json:"NPCs"` +} + +func (server *ShardServer) LoadNPCs() { + log.Print("Loading NPCs...") + + data, err := os.ReadFile(config.GetTDataPath() + "/NPCs.json") + if err != nil { + panic(err) + } + + // yes, we have to do it this way so our NPCs IDs will be incremented and unique + var NPCs NPCData + json.Unmarshal(data, &NPCs) + for _, npc := range NPCs.NPCs { + server.addEntity(entity.NewNPC(npc.X, npc.Y, npc.Z, npc.Angle, npc.NPCType)) + } + + log.Printf("Loaded %d NPCs!", len(NPCs.NPCs)) +} diff --git a/shard/shardServer.go b/shard/shardServer.go index d73d69b..e8a049e 100644 --- a/shard/shardServer.go +++ b/shard/shardServer.go @@ -72,7 +72,6 @@ func (server *ShardServer) handleEvents() { 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() } @@ -82,8 +81,9 @@ func (server *ShardServer) handleEvents() { } func (server *ShardServer) Start() { - log.Printf("Shard service hosted on %s:%d\n", config.GetAnnounceIP(), server.port) + server.LoadNPCs() + log.Printf("Shard service hosted on %s:%d\n", config.GetAnnounceIP(), server.port) go server.handleEvents() for { conn, err := server.listener.Accept() diff --git a/tdata b/tdata new file mode 160000 index 0000000..cc65dbb --- /dev/null +++ b/tdata @@ -0,0 +1 @@ +Subproject commit cc65dbb402b5baa2b604ed66132edd88cc82a52a