Compare commits

...

3 Commits

Author SHA1 Message Date
c4325475ed minor renaming 2023-06-27 00:48:17 -05:00
fd41b32b70 added NPCs; added tdata submodule
- use environment variable TDATA_PATH to specify the location of the
  tdata directory
2023-06-27 00:36:02 -05:00
80dc876517 added shard.addEntity() and shard.removeEntity() 2023-06-27 00:11:00 -05:00
11 changed files with 155 additions and 18 deletions

3
.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "tdata"]
path = tdata
url = https://github.com/OpenFusionProject/tabledata.git

View File

@ -35,6 +35,9 @@ services:
- DB_PASS=gopenfusion - DB_PASS=gopenfusion
- DB_NAME=gopenfusion - DB_NAME=gopenfusion
- REDIS_ADDR=redis:6379 - REDIS_ADDR=redis:6379
- TDATA_PATH=/tdata
volumes:
- './tdata:/tdata'
ports: ports:
- '23001:23001' - '23001:23001'
links: links:

View File

@ -14,6 +14,7 @@ import (
DB_USER DB_USER
DB_PASS DB_PASS
ANNOUNCE_IP ANNOUNCE_IP
TDATA_PATH
*/ */
const ( const (
@ -78,3 +79,7 @@ func GetDBPass() string {
func GetAnnounceIP() string { func GetAnnounceIP() string {
return getEnv("ANNOUNCE_IP", "127.0.0.1") return getEnv("ANNOUNCE_IP", "127.0.0.1")
} }
func GetTDataPath() string {
return getEnv("TDATA_PATH", "./tdata")
}

84
core/entity/npc.go Normal file
View File

@ -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),
}
}

View File

@ -4,6 +4,18 @@ import (
"github.com/CPunch/gopenfusion/core/entity" "github.com/CPunch/gopenfusion/core/entity"
) )
func (server *ShardServer) addEntity(e entity.Entity) {
pos := e.GetChunk()
server.addEntityToChunks(server.getViewableChunks(pos), e)
server.getChunk(pos).AddEntity(e)
}
func (server *ShardServer) removeEntity(e entity.Entity) {
pos := e.GetChunk()
server.removeEntityFromChunks(server.getViewableChunks(pos), e)
server.getChunk(pos).RemoveEntity(e)
}
func (server *ShardServer) getChunk(pos entity.ChunkPosition) *entity.Chunk { func (server *ShardServer) getChunk(pos entity.ChunkPosition) *entity.Chunk {
chunk, ok := server.chunks[pos] chunk, ok := server.chunks[pos]
if !ok { if !ok {
@ -35,7 +47,7 @@ func (server *ShardServer) sendOthersPacket(plr *entity.Player, typeID uint32, p
func (server *ShardServer) removeEntityFromChunks(chunks []*entity.Chunk, this entity.Entity) { func (server *ShardServer) removeEntityFromChunks(chunks []*entity.Chunk, this entity.Entity) {
for _, chunk := range chunks { for _, chunk := range chunks {
for e, _ := range chunk.Entities { for e := range chunk.Entities {
if e == this { if e == this {
continue continue
} }
@ -57,7 +69,7 @@ func (server *ShardServer) removeEntityFromChunks(chunks []*entity.Chunk, this e
func (server *ShardServer) addEntityToChunks(chunks []*entity.Chunk, this entity.Entity) { func (server *ShardServer) addEntityToChunks(chunks []*entity.Chunk, this entity.Entity) {
for _, chunk := range chunks { for _, chunk := range chunks {
for e, _ := range chunk.Entities { for e := range chunk.Entities {
if e == this { if e == this {
continue continue
} }

View File

@ -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) 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)) 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 { func (server *ShardServer) LoadingComplete(peer *protocol.CNPeer, pkt protocol.Packet) error {

View File

@ -5,13 +5,12 @@ import (
"github.com/CPunch/gopenfusion/core/protocol" "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.X = X
plr.Y = Y plr.Y = Y
plr.Z = Z plr.Z = Z
plr.Angle = Angle plr.Angle = Angle
server.updateEntityChunk(plr, plr.GetChunk(), entity.MakeChunkPosition(X, Y)) server.updateEntityChunk(plr, plr.GetChunk(), entity.MakeChunkPosition(X, Y))
return nil
} }
func (server *ShardServer) playerMove(peer *protocol.CNPeer, pkt protocol.Packet) error { 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 // update chunking
if err := server.updatePlayerPosition(plr, int(move.IX), int(move.IY), int(move.IZ), int(move.IAngle)); err != nil { server.updatePlayerPosition(plr, int(move.IX), int(move.IY), int(move.IZ), int(move.IAngle))
return err
}
return server.sendOthersPacket(plr, protocol.P_FE2CL_PC_MOVE, protocol.SP_FE2CL_PC_MOVE{ return server.sendOthersPacket(plr, protocol.P_FE2CL_PC_MOVE, protocol.SP_FE2CL_PC_MOVE{
ICliTime: move.ICliTime, ICliTime: move.ICliTime,
@ -56,9 +53,7 @@ func (server *ShardServer) playerStop(peer *protocol.CNPeer, pkt protocol.Packet
} }
// update chunking // update chunking
if err := server.updatePlayerPosition(plr, int(stop.IX), int(stop.IY), int(stop.IZ), plr.Angle); err != nil { server.updatePlayerPosition(plr, int(stop.IX), int(stop.IY), int(stop.IZ), plr.Angle)
return err
}
return server.sendOthersPacket(plr, protocol.P_FE2CL_PC_STOP, protocol.SP_FE2CL_PC_STOP{ return server.sendOthersPacket(plr, protocol.P_FE2CL_PC_STOP, protocol.SP_FE2CL_PC_STOP{
ICliTime: stop.ICliTime, ICliTime: stop.ICliTime,
@ -81,9 +76,7 @@ func (server *ShardServer) playerJump(peer *protocol.CNPeer, pkt protocol.Packet
} }
// update chunking // update chunking
if err := server.updatePlayerPosition(plr, int(jump.IX), int(jump.IY), int(jump.IZ), plr.Angle); err != nil { server.updatePlayerPosition(plr, int(jump.IX), int(jump.IY), int(jump.IZ), plr.Angle)
return err
}
return server.sendOthersPacket(plr, protocol.P_FE2CL_PC_JUMP, protocol.SP_FE2CL_PC_JUMP{ return server.sendOthersPacket(plr, protocol.P_FE2CL_PC_JUMP, protocol.SP_FE2CL_PC_JUMP{
ICliTime: jump.ICliTime, ICliTime: jump.ICliTime,

32
shard/npcloader.go Normal file
View File

@ -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))
}

View File

@ -72,7 +72,6 @@ func (server *ShardServer) handleEvents() {
server.disconnect(event.Peer) server.disconnect(event.Peer)
case protocol.EVENT_CLIENT_PACKET: case protocol.EVENT_CLIENT_PACKET:
defer pool.Put(event.Pkt) 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 { if err := server.handlePacket(event.Peer, event.PktID, protocol.NewPacket(event.Pkt)); err != nil {
event.Peer.Kill() event.Peer.Kill()
} }
@ -82,8 +81,9 @@ func (server *ShardServer) handleEvents() {
} }
func (server *ShardServer) Start() { 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() go server.handleEvents()
for { for {
conn, err := server.listener.Accept() conn, err := server.listener.Accept()
@ -122,8 +122,7 @@ func (server *ShardServer) disconnect(peer *protocol.CNPeer) {
plr, ok := server.peers[peer] plr, ok := server.peers[peer]
if ok { if ok {
log.Printf("Player %d (AccountID %d) disconnected\n", plr.PlayerID, plr.AccountID) log.Printf("Player %d (AccountID %d) disconnected\n", plr.PlayerID, plr.AccountID)
server.removeEntityFromChunks(server.getViewableChunks(plr.Chunk), plr) server.removeEntity(plr)
server.getChunk(plr.Chunk).RemoveEntity(plr)
} }
log.Printf("Peer %p disconnected from SHARD\n", peer) log.Printf("Peer %p disconnected from SHARD\n", peer)

1
tdata Submodule

@ -0,0 +1 @@
Subproject commit cc65dbb402b5baa2b604ed66132edd88cc82a52a