2020-06-13 18:32:09 -04:00
|
|
|
package d2server
|
|
|
|
|
|
|
|
import (
|
2020-08-09 20:32:47 -04:00
|
|
|
"context"
|
2020-06-18 14:11:04 -04:00
|
|
|
"encoding/json"
|
2020-08-09 20:32:47 -04:00
|
|
|
"errors"
|
2020-06-26 17:12:19 -04:00
|
|
|
"fmt"
|
2020-06-13 18:32:09 -04:00
|
|
|
"log"
|
2020-06-18 14:11:04 -04:00
|
|
|
"net"
|
2020-06-22 20:31:42 -04:00
|
|
|
"sync"
|
2020-06-26 17:12:19 -04:00
|
|
|
"time"
|
|
|
|
|
2020-09-12 16:25:09 -04:00
|
|
|
"github.com/robertkrimen/otto"
|
|
|
|
|
2020-06-26 17:12:19 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
2020-09-12 16:51:30 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
2020-10-10 18:47:51 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2hero"
|
2020-07-17 22:11:16 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapgen"
|
2020-06-26 17:12:19 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
|
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
2020-09-12 16:25:09 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2server/d2tcpclientconnection"
|
2020-06-18 14:11:04 -04:00
|
|
|
"github.com/OpenDiablo2/OpenDiablo2/d2script"
|
2020-06-13 18:32:09 -04:00
|
|
|
)
|
|
|
|
|
2020-07-17 22:11:16 -04:00
|
|
|
const (
|
2020-09-12 16:25:09 -04:00
|
|
|
port = "6669"
|
2020-10-25 21:01:57 -04:00
|
|
|
chunkSize int = 4096 // nolint:deadcode,unused,varcheck // WIP
|
2020-08-09 20:32:47 -04:00
|
|
|
subtilesPerTile = 5
|
|
|
|
middleOfTileOffset = 3
|
2020-07-17 22:11:16 -04:00
|
|
|
)
|
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
var (
|
2020-09-12 16:25:09 -04:00
|
|
|
errPlayerAlreadyExists = errors.New("player already exists")
|
|
|
|
errServerFull = errors.New("server full") // Server currently at maximum TCP connections
|
2020-08-09 20:32:47 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
// GameServer manages a copy of the map and entities as well as manages packet routing and connections.
|
|
|
|
// It can accept connections from localhost as well remote clients. It can also be started in a standalone mode.
|
2020-06-13 18:32:09 -04:00
|
|
|
type GameServer struct {
|
2020-06-22 20:31:42 -04:00
|
|
|
sync.RWMutex
|
2020-08-09 20:32:47 -04:00
|
|
|
connections map[string]ClientConnection
|
|
|
|
listener net.Listener
|
|
|
|
networkServer bool
|
|
|
|
ctx context.Context
|
|
|
|
cancel context.CancelFunc
|
2020-09-12 16:51:30 -04:00
|
|
|
asset *d2asset.AssetManager
|
2020-06-26 17:12:19 -04:00
|
|
|
mapEngines []*d2mapengine.MapEngine
|
2020-06-18 14:11:04 -04:00
|
|
|
scriptEngine *d2script.ScriptEngine
|
|
|
|
seed int64
|
2020-08-09 20:32:47 -04:00
|
|
|
maxConnections int
|
|
|
|
packetManagerChan chan []byte
|
2020-10-31 14:30:08 -04:00
|
|
|
heroStateFactory *d2hero.HeroStateFactory
|
2020-06-13 18:32:09 -04:00
|
|
|
}
|
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
// NewGameServer builds a new GameServer that can be started
|
2020-06-29 17:01:26 -04:00
|
|
|
//
|
2020-08-09 20:32:47 -04:00
|
|
|
// ctx: required context item
|
|
|
|
// networkServer: true = 0.0.0.0 | false = 127.0.0.1
|
|
|
|
// maxConnections (default: 8): maximum number of TCP connections allowed open
|
2020-09-12 16:51:30 -04:00
|
|
|
func NewGameServer(asset *d2asset.AssetManager, networkServer bool,
|
|
|
|
maxConnections ...int) (*GameServer,
|
|
|
|
error) {
|
2020-08-09 20:32:47 -04:00
|
|
|
if len(maxConnections) == 0 {
|
|
|
|
maxConnections = []int{8}
|
2020-06-26 17:12:19 -04:00
|
|
|
}
|
|
|
|
|
2020-10-31 14:30:08 -04:00
|
|
|
heroStateFactory, err := d2hero.NewHeroStateFactory(asset)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
|
|
|
|
|
|
gameServer := &GameServer{
|
|
|
|
ctx: ctx,
|
|
|
|
cancel: cancel,
|
2020-09-12 16:51:30 -04:00
|
|
|
asset: asset,
|
2020-08-09 20:32:47 -04:00
|
|
|
connections: make(map[string]ClientConnection),
|
|
|
|
networkServer: networkServer,
|
|
|
|
maxConnections: maxConnections[0],
|
|
|
|
packetManagerChan: make(chan []byte),
|
2020-06-26 17:12:19 -04:00
|
|
|
mapEngines: make([]*d2mapengine.MapEngine, 0),
|
|
|
|
scriptEngine: d2script.CreateScriptEngine(),
|
|
|
|
seed: time.Now().UnixNano(),
|
2020-10-31 14:30:08 -04:00
|
|
|
heroStateFactory: heroStateFactory,
|
2020-06-26 17:12:19 -04:00
|
|
|
}
|
|
|
|
|
2020-09-12 16:51:30 -04:00
|
|
|
mapEngine := d2mapengine.CreateMapEngine(asset)
|
2020-08-09 20:32:47 -04:00
|
|
|
mapEngine.SetSeed(gameServer.seed)
|
2020-10-25 10:21:14 -04:00
|
|
|
mapEngine.ResetMap(d2enum.RegionAct1Town, 100, 100)
|
2020-09-20 17:52:01 -04:00
|
|
|
|
|
|
|
mapGen, err := d2mapgen.NewMapGenerator(asset, mapEngine)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
mapGen.GenerateAct1Overworld()
|
2020-06-18 14:11:04 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
gameServer.mapEngines = append(gameServer.mapEngines, mapEngine)
|
|
|
|
|
|
|
|
gameServer.scriptEngine.AddFunction("getMapEngines", func(call otto.FunctionCall) otto.Value {
|
2020-10-31 23:58:55 -04:00
|
|
|
val, err := gameServer.scriptEngine.ToValue(gameServer.mapEngines)
|
2020-06-26 17:12:19 -04:00
|
|
|
if err != nil {
|
|
|
|
fmt.Print(err.Error())
|
|
|
|
}
|
|
|
|
return val
|
|
|
|
})
|
2020-06-18 14:11:04 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
return gameServer, nil
|
2020-06-18 14:11:04 -04:00
|
|
|
}
|
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
// Start essentially starts all of the game server go routines as well as begins listening for connection. This will
|
|
|
|
// return an error if it is unable to bind to a socket.
|
|
|
|
func (g *GameServer) Start() error {
|
2020-09-12 16:25:09 -04:00
|
|
|
listenerAddress := "127.0.0.1:" + port
|
2020-08-09 20:32:47 -04:00
|
|
|
if g.networkServer {
|
2020-09-12 16:25:09 -04:00
|
|
|
listenerAddress = "0.0.0.0:" + port
|
2020-06-26 17:12:19 -04:00
|
|
|
}
|
2020-06-18 14:11:04 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
log.Printf("Starting Game Server @ %s\n", listenerAddress)
|
2020-09-12 16:25:09 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
l, err := net.Listen("tcp4", listenerAddress)
|
2020-06-26 17:12:19 -04:00
|
|
|
if err != nil {
|
2020-08-09 20:32:47 -04:00
|
|
|
return err
|
2020-06-18 14:11:04 -04:00
|
|
|
}
|
2020-07-17 22:11:16 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
g.listener = l
|
2020-07-17 22:11:16 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
go g.packetManager()
|
2020-06-26 17:12:19 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
go func() {
|
|
|
|
for {
|
|
|
|
c, err := g.listener.Accept()
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Unable to accept connection: %s\n", err)
|
2020-09-06 01:17:33 -04:00
|
|
|
return
|
2020-08-09 20:32:47 -04:00
|
|
|
}
|
2020-07-17 22:11:16 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
go g.handleConnection(c)
|
2020-06-26 17:12:19 -04:00
|
|
|
}
|
2020-08-09 20:32:47 -04:00
|
|
|
}()
|
2020-07-17 22:11:16 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
return nil
|
|
|
|
}
|
2020-07-02 16:29:28 -04:00
|
|
|
|
2020-09-12 16:25:09 -04:00
|
|
|
// Stop stops the game server
|
2020-08-09 20:32:47 -04:00
|
|
|
func (g *GameServer) Stop() {
|
|
|
|
g.Lock()
|
|
|
|
g.cancel()
|
2020-07-17 22:11:16 -04:00
|
|
|
|
2020-09-12 16:25:09 -04:00
|
|
|
if err := g.listener.Close(); err != nil {
|
|
|
|
log.Printf("failed to close the listener %s, err: %v\n", g.listener.Addr(), err)
|
|
|
|
}
|
2020-08-09 20:32:47 -04:00
|
|
|
}
|
2020-07-17 22:11:16 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
// packetManager is meant to be started as a Goroutine and is used to manage routing of packets to clients.
|
|
|
|
func (g *GameServer) packetManager() {
|
|
|
|
defer close(g.packetManagerChan)
|
|
|
|
|
|
|
|
for {
|
|
|
|
select {
|
|
|
|
// If the server is stopped we need to clean up the packet manager goroutine
|
|
|
|
case <-g.ctx.Done():
|
|
|
|
return
|
|
|
|
case p := <-g.packetManagerChan:
|
|
|
|
switch d2netpacket.InspectPacketType(p) {
|
|
|
|
case d2netpackettype.PlayerConnectionRequest:
|
|
|
|
player, err := d2netpacket.UnmarshalNetPacket(p)
|
|
|
|
if err != nil {
|
|
|
|
log.Printf("Unable to unmarshal PlayerConnectionRequestPacket: %s\n", err)
|
|
|
|
}
|
2020-09-12 16:25:09 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
g.sendPacketToClients(player)
|
|
|
|
case d2netpackettype.MovePlayer:
|
|
|
|
move, err := d2netpacket.UnmarshalNetPacket(p)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
continue
|
|
|
|
}
|
2020-09-12 16:25:09 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
g.sendPacketToClients(move)
|
2020-10-10 18:47:51 -04:00
|
|
|
case d2netpackettype.CastSkill:
|
|
|
|
castSkill, err := d2netpacket.UnmarshalNetPacket(p)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
g.sendPacketToClients(castSkill)
|
2020-08-09 20:32:47 -04:00
|
|
|
case d2netpackettype.SpawnItem:
|
|
|
|
item, err := d2netpacket.UnmarshalNetPacket(p)
|
|
|
|
if err != nil {
|
|
|
|
log.Println(err)
|
|
|
|
continue
|
|
|
|
}
|
2020-09-12 16:25:09 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
g.sendPacketToClients(item)
|
|
|
|
case d2netpackettype.ServerClosed:
|
|
|
|
g.Stop()
|
2020-06-30 20:01:51 -04:00
|
|
|
}
|
2020-06-26 17:12:19 -04:00
|
|
|
}
|
2020-06-18 14:11:04 -04:00
|
|
|
}
|
2020-06-13 18:32:09 -04:00
|
|
|
}
|
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
func (g *GameServer) sendPacketToClients(packet d2netpacket.NetPacket) {
|
|
|
|
for _, c := range g.connections {
|
2020-09-12 16:25:09 -04:00
|
|
|
if err := c.SendPacketToClient(packet); err != nil {
|
|
|
|
log.Printf("GameServer: error sending packet: %s to client %s: %s", packet.PacketType, c.GetUniqueID(), err)
|
|
|
|
}
|
2020-07-17 22:11:16 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
// handleConnection accepts an individual connection and starts pooling for new packets. It is recommended this is called
|
|
|
|
// via Go Routine. Context should be a property of the GameServer Struct.
|
|
|
|
func (g *GameServer) handleConnection(conn net.Conn) {
|
|
|
|
var connected int
|
2020-09-12 16:25:09 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
var packet d2netpacket.NetPacket
|
2020-07-17 22:11:16 -04:00
|
|
|
|
2020-09-12 16:25:09 -04:00
|
|
|
log.Printf("Accepting connection: %s\n", conn.RemoteAddr().String())
|
|
|
|
|
|
|
|
defer func() {
|
|
|
|
if err := conn.Close(); err != nil {
|
|
|
|
log.Printf("failed to close the connection: %s\n", conn.RemoteAddr())
|
|
|
|
}
|
|
|
|
}()
|
2020-07-17 22:11:16 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
decoder := json.NewDecoder(conn)
|
2020-07-17 22:11:16 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
for {
|
|
|
|
err := decoder.Decode(&packet)
|
2020-07-17 22:11:16 -04:00
|
|
|
if err != nil {
|
2020-08-09 20:32:47 -04:00
|
|
|
log.Println(err)
|
|
|
|
return // exit this connection as we could not read the first packet
|
2020-07-17 22:11:16 -04:00
|
|
|
}
|
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
// If this is the first packet we are seeing from this specific connection we first need to see if the client
|
|
|
|
// is sending a valid request. If this is a valid request, we will register it and flip the connected switch
|
|
|
|
// to.
|
|
|
|
if connected == 0 {
|
|
|
|
if packet.PacketType != d2netpackettype.PlayerConnectionRequest {
|
|
|
|
log.Printf("Closing connection with %s: did not receive new player connection request...\n", conn.RemoteAddr().String())
|
|
|
|
}
|
2020-07-30 15:04:05 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
if err := g.registerConnection(packet.PacketData, conn); err != nil {
|
|
|
|
switch err {
|
2020-09-12 16:25:09 -04:00
|
|
|
case errServerFull: // Server is currently full and not accepting new connections.
|
2020-11-01 08:26:15 -05:00
|
|
|
_, errServerFullPacket := conn.Write(d2netpacket.MarshalPacket(d2netpacket.CreateServerFullPacket()))
|
|
|
|
log.Println(errServerFullPacket)
|
2020-09-12 16:25:09 -04:00
|
|
|
case errPlayerAlreadyExists: // Player is already registered and did not disconnection correctly.
|
2020-08-09 20:32:47 -04:00
|
|
|
log.Println(err)
|
|
|
|
}
|
2020-11-01 08:26:15 -05:00
|
|
|
|
|
|
|
return
|
2020-08-09 20:32:47 -04:00
|
|
|
}
|
2020-07-30 15:04:05 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
connected = 1
|
|
|
|
}
|
2020-07-30 15:04:05 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
select {
|
|
|
|
case <-g.ctx.Done():
|
|
|
|
return
|
|
|
|
default:
|
|
|
|
g.packetManagerChan <- packet.PacketData
|
2020-07-30 15:04:05 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
// registerConnection accepts a PlayerConnectionRequestPacket and thread safely updates the connection pool
|
|
|
|
//
|
|
|
|
// Errors:
|
2020-09-12 16:25:09 -04:00
|
|
|
// - errServerFull
|
|
|
|
// - errPlayerAlreadyExists
|
2020-08-09 20:32:47 -04:00
|
|
|
func (g *GameServer) registerConnection(b []byte, conn net.Conn) error {
|
|
|
|
g.Lock()
|
|
|
|
|
|
|
|
// check to see if the server is full
|
|
|
|
if len(g.connections) >= g.maxConnections {
|
2020-09-12 16:25:09 -04:00
|
|
|
return errServerFull
|
2020-07-17 22:11:16 -04:00
|
|
|
}
|
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
// if it is not full, unmarshal the playerConnectionRequest
|
|
|
|
packet, err := d2netpacket.UnmarshalPlayerConnectionRequest(b)
|
2020-07-17 22:11:16 -04:00
|
|
|
if err != nil {
|
2020-08-09 20:32:47 -04:00
|
|
|
log.Printf("Failed to unmarshal PlayerConnectionRequest: %s\n", err)
|
2020-07-17 22:11:16 -04:00
|
|
|
}
|
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
// check to see if the player is already registered
|
|
|
|
if _, ok := g.connections[packet.ID]; ok {
|
2020-09-12 16:25:09 -04:00
|
|
|
return errPlayerAlreadyExists
|
2020-08-09 20:32:47 -04:00
|
|
|
}
|
2020-07-17 22:11:16 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
// Client a new TCP Client Connection and add it to the connections map
|
|
|
|
client := d2tcpclientconnection.CreateTCPClientConnection(conn, packet.ID)
|
|
|
|
client.SetPlayerState(packet.PlayerState)
|
|
|
|
log.Printf("Client connected with an id of %s", client.GetUniqueID())
|
|
|
|
g.connections[client.GetUniqueID()] = client
|
2020-07-17 22:11:16 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
// Temporary position hack --------------------------------------------
|
2020-10-25 18:36:12 -04:00
|
|
|
// https://github.com/OpenDiablo2/OpenDiablo2/issues/829
|
|
|
|
sx, sy := g.mapEngines[0].GetStartPosition()
|
2020-08-09 20:32:47 -04:00
|
|
|
clientPlayerState := client.GetPlayerState()
|
|
|
|
clientPlayerState.X = sx
|
|
|
|
clientPlayerState.Y = sy
|
|
|
|
// ---------
|
2020-07-17 22:11:16 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
// This really should be deferred however to much time will be spend holding a lock when we attempt to send a packet
|
|
|
|
g.Unlock()
|
2020-07-17 22:11:16 -04:00
|
|
|
|
2020-10-31 23:58:55 -04:00
|
|
|
g.handleClientConnection(client, sx, sy)
|
2020-07-17 22:11:16 -04:00
|
|
|
|
2020-08-09 20:32:47 -04:00
|
|
|
return nil
|
2020-06-26 17:12:19 -04:00
|
|
|
}
|
|
|
|
|
2020-06-29 17:01:26 -04:00
|
|
|
// OnClientConnected initializes the given ClientConnection. It sends the
|
|
|
|
// following packets to the newly connected client: UpdateServerInfoPacket,
|
|
|
|
// GenerateMapPacket, AddPlayerPacket.
|
|
|
|
//
|
|
|
|
// It also sends AddPlayerPackets for each other player entity to the new
|
|
|
|
// player and vice versa, so all player entities exist on all clients.
|
|
|
|
//
|
|
|
|
// For more information, see d2networking.d2netpacket.
|
2020-10-31 23:58:55 -04:00
|
|
|
func (g *GameServer) OnClientConnected(client ClientConnection) {
|
2020-06-26 17:12:19 -04:00
|
|
|
// Temporary position hack --------------------------------------------
|
2020-10-25 18:36:12 -04:00
|
|
|
// https://github.com/OpenDiablo2/OpenDiablo2/issues/829
|
2020-10-31 23:58:55 -04:00
|
|
|
sx, sy := g.mapEngines[0].GetStartPosition()
|
2020-06-26 17:12:19 -04:00
|
|
|
clientPlayerState := client.GetPlayerState()
|
|
|
|
clientPlayerState.X = sx
|
|
|
|
clientPlayerState.Y = sy
|
|
|
|
// --------------------------------------------------------------------
|
|
|
|
|
2020-07-17 22:11:16 -04:00
|
|
|
log.Printf("Client connected with an id of %s", client.GetUniqueID())
|
2020-10-31 23:58:55 -04:00
|
|
|
g.connections[client.GetUniqueID()] = client
|
2020-07-17 22:11:16 -04:00
|
|
|
|
2020-10-31 23:58:55 -04:00
|
|
|
g.handleClientConnection(client, sx, sy)
|
2020-09-12 16:25:09 -04:00
|
|
|
}
|
|
|
|
|
2020-10-31 23:58:55 -04:00
|
|
|
func (g *GameServer) handleClientConnection(client ClientConnection, x, y float64) {
|
|
|
|
err := client.SendPacketToClient(d2netpacket.CreateUpdateServerInfoPacket(g.seed, client.GetUniqueID()))
|
2020-06-30 20:01:51 -04:00
|
|
|
if err != nil {
|
2020-07-17 22:11:16 -04:00
|
|
|
log.Printf("GameServer: error sending UpdateServerInfoPacket to client %s: %s", client.GetUniqueID(), err)
|
2020-06-30 20:01:51 -04:00
|
|
|
}
|
2020-07-17 22:11:16 -04:00
|
|
|
|
2020-06-30 20:01:51 -04:00
|
|
|
err = client.SendPacketToClient(d2netpacket.CreateGenerateMapPacket(d2enum.RegionAct1Town))
|
|
|
|
if err != nil {
|
2020-07-17 22:11:16 -04:00
|
|
|
log.Printf("GameServer: error sending GenerateMapPacket to client %s: %s", client.GetUniqueID(), err)
|
2020-06-30 20:01:51 -04:00
|
|
|
}
|
2020-06-26 17:12:19 -04:00
|
|
|
|
|
|
|
playerState := client.GetPlayerState()
|
2020-07-17 22:11:16 -04:00
|
|
|
|
|
|
|
// these are in subtiles
|
2020-09-12 16:25:09 -04:00
|
|
|
playerX := int(x*subtilesPerTile) + middleOfTileOffset
|
|
|
|
playerY := int(y*subtilesPerTile) + middleOfTileOffset
|
|
|
|
|
2020-10-31 23:58:55 -04:00
|
|
|
d2hero.HydrateSkills(playerState.Skills, g.asset)
|
2020-10-10 18:47:51 -04:00
|
|
|
|
2020-09-12 16:25:09 -04:00
|
|
|
createPlayerPacket := d2netpacket.CreateAddPlayerPacket(
|
|
|
|
client.GetUniqueID(),
|
|
|
|
playerState.HeroName,
|
|
|
|
playerX,
|
|
|
|
playerY,
|
|
|
|
playerState.HeroType,
|
|
|
|
playerState.Stats,
|
2020-09-20 11:55:44 -04:00
|
|
|
playerState.Skills,
|
2020-09-12 16:25:09 -04:00
|
|
|
playerState.Equipment,
|
2020-10-31 14:30:08 -04:00
|
|
|
playerState.LeftSkill,
|
|
|
|
playerState.RightSkill,
|
2020-09-12 16:25:09 -04:00
|
|
|
)
|
|
|
|
|
2020-10-31 23:58:55 -04:00
|
|
|
for _, connection := range g.connections {
|
2020-06-30 20:01:51 -04:00
|
|
|
err := connection.SendPacketToClient(createPlayerPacket)
|
|
|
|
if err != nil {
|
2020-07-17 22:11:16 -04:00
|
|
|
log.Printf("GameServer: error sending %T to client %s: %s", createPlayerPacket, connection.GetUniqueID(), err)
|
2020-06-30 20:01:51 -04:00
|
|
|
}
|
2020-07-17 22:11:16 -04:00
|
|
|
|
|
|
|
if connection.GetUniqueID() == client.GetUniqueID() {
|
2020-06-26 17:12:19 -04:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
conPlayerState := connection.GetPlayerState()
|
2020-07-17 22:11:16 -04:00
|
|
|
playerX := int(conPlayerState.X*subtilesPerTile) + middleOfTileOffset
|
|
|
|
playerY := int(conPlayerState.Y*subtilesPerTile) + middleOfTileOffset
|
2020-09-12 16:25:09 -04:00
|
|
|
err = client.SendPacketToClient(
|
|
|
|
d2netpacket.CreateAddPlayerPacket(
|
|
|
|
connection.GetUniqueID(),
|
|
|
|
conPlayerState.HeroName,
|
|
|
|
playerX,
|
|
|
|
playerY,
|
|
|
|
conPlayerState.HeroType,
|
|
|
|
conPlayerState.Stats,
|
2020-09-20 11:55:44 -04:00
|
|
|
conPlayerState.Skills,
|
2020-09-12 16:25:09 -04:00
|
|
|
conPlayerState.Equipment,
|
2020-10-31 14:30:08 -04:00
|
|
|
conPlayerState.LeftSkill,
|
|
|
|
conPlayerState.RightSkill,
|
2020-09-12 16:25:09 -04:00
|
|
|
),
|
2020-07-17 22:11:16 -04:00
|
|
|
)
|
|
|
|
|
2020-06-30 20:01:51 -04:00
|
|
|
if err != nil {
|
2020-07-17 22:11:16 -04:00
|
|
|
log.Printf("GameServer: error sending CreateAddPlayerPacket to client %s: %s", connection.GetUniqueID(), err)
|
2020-06-30 20:01:51 -04:00
|
|
|
}
|
2020-06-26 17:12:19 -04:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-06-29 17:01:26 -04:00
|
|
|
// OnClientDisconnected removes the given client from the list
|
|
|
|
// of client connections.
|
2020-10-31 23:58:55 -04:00
|
|
|
func (g *GameServer) OnClientDisconnected(client ClientConnection) {
|
2020-07-17 22:11:16 -04:00
|
|
|
log.Printf("Client disconnected with an id of %s", client.GetUniqueID())
|
2020-10-31 23:58:55 -04:00
|
|
|
delete(g.connections, client.GetUniqueID())
|
2020-06-26 17:12:19 -04:00
|
|
|
}
|
|
|
|
|
2020-06-29 17:01:26 -04:00
|
|
|
// OnPacketReceived is called by the local client to 'send' a packet to the server.
|
2020-10-31 14:30:08 -04:00
|
|
|
// nolint:gocyclo // switch statement on packet type makes sense, no need to change
|
2020-10-31 23:58:55 -04:00
|
|
|
func (g *GameServer) OnPacketReceived(client ClientConnection, packet d2netpacket.NetPacket) error {
|
|
|
|
if g == nil {
|
|
|
|
return errors.New("game server is nil")
|
2020-10-25 18:36:12 -04:00
|
|
|
}
|
|
|
|
|
2020-06-26 17:12:19 -04:00
|
|
|
switch packet.PacketType {
|
|
|
|
case d2netpackettype.MovePlayer:
|
2020-09-23 13:30:54 -04:00
|
|
|
movePacket, err := d2netpacket.UnmarshalMovePlayer(packet.PacketData)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-10-31 23:58:55 -04:00
|
|
|
|
|
|
|
playerState := g.connections[client.GetUniqueID()].GetPlayerState()
|
2020-08-09 20:32:47 -04:00
|
|
|
playerState.X = movePacket.DestX
|
|
|
|
playerState.Y = movePacket.DestY
|
2020-10-31 23:58:55 -04:00
|
|
|
|
|
|
|
g.sendPacketToClients(packet)
|
|
|
|
case d2netpackettype.CastSkill, d2netpackettype.SpawnItem:
|
|
|
|
g.sendPacketToClients(packet)
|
2020-10-31 14:30:08 -04:00
|
|
|
case d2netpackettype.SavePlayer:
|
|
|
|
savePacket, err := d2netpacket.UnmarshalSavePlayer(packet.PacketData)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-10-31 23:58:55 -04:00
|
|
|
playerState := g.connections[client.GetUniqueID()].GetPlayerState()
|
2020-10-31 14:30:08 -04:00
|
|
|
playerState.LeftSkill = savePacket.LeftSkill
|
|
|
|
playerState.RightSkill = savePacket.RightSkill
|
|
|
|
|
2020-10-31 23:58:55 -04:00
|
|
|
err = g.heroStateFactory.Save(playerState)
|
2020-10-31 14:30:08 -04:00
|
|
|
if err != nil {
|
|
|
|
log.Printf("GameServer: error saving saving Player: %s", err)
|
|
|
|
}
|
2020-10-10 18:47:51 -04:00
|
|
|
default:
|
|
|
|
log.Printf("GameServer: received unknown packet %T", packet)
|
2020-06-26 17:12:19 -04:00
|
|
|
}
|
2020-07-17 22:11:16 -04:00
|
|
|
|
2020-06-26 17:12:19 -04:00
|
|
|
return nil
|
2020-06-13 18:32:09 -04:00
|
|
|
}
|