mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2025-01-26 19:27:31 -05:00
D2networking resolve lint issues (#492)
* Added automap.go stub * Handle errors in original AutoMap.txt file * Completed AutoMapRecord struct and comments * AutoMap loader implemented * Update from base repo * Comments added to d2netpacket and d2netpackettype. Note, the Overview for d2netpacket is in net_packet.go. It could be placed in a doc.go file but net_packet.go seemed appropriate in this case. * Comments added to d2server * client_connection.go missed from previous commit * Comments added to d2client * Doc.go added to d2networking and other corrections
This commit is contained in:
parent
aae565d528
commit
5ea6ada452
@ -2,6 +2,8 @@ package d2networking
|
||||
|
||||
import "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
|
||||
|
||||
// ClientListener is an interface used to pass packet data from
|
||||
// ClientConnections to GameServer and GameClient.
|
||||
type ClientListener interface {
|
||||
OnPacketReceived(packet d2netpacket.NetPacket) error
|
||||
}
|
||||
|
@ -5,6 +5,8 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
|
||||
)
|
||||
|
||||
// ClientConnection is an interface for abstracting local and
|
||||
// remote server connections.
|
||||
type ClientConnection interface {
|
||||
Open(connectionString string, saveFilePath string) error
|
||||
Close() error
|
||||
|
@ -1,9 +1,12 @@
|
||||
package d2clientconnectiontype
|
||||
|
||||
// ClientConnectionType is an enum referring to types implementing
|
||||
// d2server.ClientConnection and d2client.ClientConnection.
|
||||
type ClientConnectionType int
|
||||
|
||||
//
|
||||
const (
|
||||
Local ClientConnectionType = iota
|
||||
LANServer
|
||||
LANClient
|
||||
Local ClientConnectionType = iota // Local client
|
||||
LANServer // Server
|
||||
LANClient // Remote client
|
||||
)
|
||||
|
@ -1,3 +1,4 @@
|
||||
// Package d2localclient facilitates communication between a local client and server.
|
||||
package d2localclient
|
||||
|
||||
import (
|
||||
@ -9,25 +10,33 @@ import (
|
||||
uuid "github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
// LocalClientConnection is the implementation of ClientConnection
|
||||
// for a local client.
|
||||
type LocalClientConnection struct {
|
||||
clientListener d2networking.ClientListener
|
||||
uniqueId string
|
||||
openNetworkServer bool
|
||||
playerState *d2player.PlayerState
|
||||
clientListener d2networking.ClientListener // The game client
|
||||
uniqueId string // Unique ID generated on construction
|
||||
openNetworkServer bool // True if this is a server
|
||||
playerState *d2player.PlayerState // Local player state
|
||||
}
|
||||
|
||||
// GetUniqueId returns LocalClientConnection.uniqueId.
|
||||
func (l LocalClientConnection) GetUniqueId() string {
|
||||
return l.uniqueId
|
||||
}
|
||||
|
||||
// GetConnectionType returns an enum representing the connection type.
|
||||
// See: d2clientconnectiontype
|
||||
func (l LocalClientConnection) GetConnectionType() d2clientconnectiontype.ClientConnectionType {
|
||||
return d2clientconnectiontype.Local
|
||||
}
|
||||
|
||||
// SendPacketToClient passes a packet to the game client for processing.
|
||||
func (l *LocalClientConnection) SendPacketToClient(packet d2netpacket.NetPacket) error {
|
||||
return l.clientListener.OnPacketReceived(packet)
|
||||
}
|
||||
|
||||
// Create constructs a new LocalClientConnection and returns
|
||||
// a pointer to it.
|
||||
func Create(openNetworkServer bool) *LocalClientConnection {
|
||||
result := &LocalClientConnection{
|
||||
uniqueId: uuid.NewV4().String(),
|
||||
@ -37,6 +46,7 @@ func Create(openNetworkServer bool) *LocalClientConnection {
|
||||
return result
|
||||
}
|
||||
|
||||
// Open creates a new GameServer, runs the server and connects this client to it.
|
||||
func (l *LocalClientConnection) Open(connectionString string, saveFilePath string) error {
|
||||
l.SetPlayerState(d2player.LoadPlayerState(saveFilePath))
|
||||
d2server.Create(l.openNetworkServer)
|
||||
@ -45,6 +55,7 @@ func (l *LocalClientConnection) Open(connectionString string, saveFilePath strin
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close disconnects from the server and destroys it.
|
||||
func (l *LocalClientConnection) Close() error {
|
||||
l.SendPacketToServer(d2netpacket.CreateServerClosedPacket())
|
||||
d2server.OnClientDisconnected(l)
|
||||
@ -52,19 +63,23 @@ func (l *LocalClientConnection) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendPacketToServer calls d2server.OnPacketReceived with the given packet.
|
||||
func (l *LocalClientConnection) SendPacketToServer(packet d2netpacket.NetPacket) error {
|
||||
// TODO: This is going to blow up if the server has ceased to be.
|
||||
return d2server.OnPacketReceived(l, packet)
|
||||
}
|
||||
|
||||
// SetClientListener sets LocalClientConnection.clientListener to the given value.
|
||||
func (l *LocalClientConnection) SetClientListener(listener d2networking.ClientListener) {
|
||||
l.clientListener = listener
|
||||
}
|
||||
|
||||
// GetPlayerState returns LocalClientConnection.playerState.
|
||||
func (l *LocalClientConnection) GetPlayerState() *d2player.PlayerState {
|
||||
return l.playerState
|
||||
}
|
||||
|
||||
// SetPlayerState sets LocalClientConnection.playerState to the given value.
|
||||
func (l *LocalClientConnection) SetPlayerState(playerState *d2player.PlayerState) {
|
||||
l.playerState = playerState
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
// Package d2remoteclient facilitates communication between a remote client and server.
|
||||
package d2remoteclient
|
||||
|
||||
import (
|
||||
@ -5,12 +6,13 @@ import (
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
|
||||
"io"
|
||||
"log"
|
||||
"net"
|
||||
"strings"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking"
|
||||
@ -19,25 +21,33 @@ import (
|
||||
uuid "github.com/satori/go.uuid"
|
||||
)
|
||||
|
||||
// RemoteClientConnection is the implementation of ClientConnection
|
||||
// for a remote client.
|
||||
type RemoteClientConnection struct {
|
||||
clientListener d2networking.ClientListener
|
||||
uniqueId string
|
||||
udpConnection *net.UDPConn
|
||||
active bool
|
||||
clientListener d2networking.ClientListener // The GameClient
|
||||
uniqueId string // Unique ID generated on construction
|
||||
udpConnection *net.UDPConn // UDP connection to the server
|
||||
active bool // The connection is currently open
|
||||
}
|
||||
|
||||
// GetUniqueId returns RemoteClientConnection.uniqueId.
|
||||
func (l RemoteClientConnection) GetUniqueId() string {
|
||||
return l.uniqueId
|
||||
}
|
||||
|
||||
// GetConnectionType returns an enum representing the connection type.
|
||||
// See: d2clientconnectiontype
|
||||
func (l RemoteClientConnection) GetConnectionType() d2clientconnectiontype.ClientConnectionType {
|
||||
return d2clientconnectiontype.LANClient
|
||||
}
|
||||
|
||||
func (l *RemoteClientConnection) SendPacketToClient(packet d2netpacket.NetPacket) error { // WHAT IS THIS
|
||||
// SendPacketToClient passes a packet to the game client for processing.
|
||||
func (l *RemoteClientConnection) SendPacketToClient(packet d2netpacket.NetPacket) error {
|
||||
return l.clientListener.OnPacketReceived(packet)
|
||||
}
|
||||
|
||||
// Create constructs a new RemoteClientConnection
|
||||
// and returns a pointer to it.
|
||||
func Create() *RemoteClientConnection {
|
||||
result := &RemoteClientConnection{
|
||||
uniqueId: uuid.NewV4().String(),
|
||||
@ -46,6 +56,8 @@ func Create() *RemoteClientConnection {
|
||||
return result
|
||||
}
|
||||
|
||||
// Open runs serverListener() in a goroutine to continuously read UDP packets.
|
||||
// It also sends a PlayerConnectionRequestPacket packet to the server (see d2netpacket).
|
||||
func (l *RemoteClientConnection) Open(connectionString string, saveFilePath string) error {
|
||||
if !strings.Contains(connectionString, ":") {
|
||||
connectionString += ":6669"
|
||||
@ -75,6 +87,8 @@ func (l *RemoteClientConnection) Open(connectionString string, saveFilePath stri
|
||||
return nil
|
||||
}
|
||||
|
||||
// Close informs the server that this client has disconnected and sets
|
||||
// RemoteClientConnection.active to false.
|
||||
func (l *RemoteClientConnection) Close() error {
|
||||
l.active = false
|
||||
l.SendPacketToServer(d2netpacket.CreatePlayerDisconnectRequestPacket(l.GetUniqueId()))
|
||||
@ -82,6 +96,8 @@ func (l *RemoteClientConnection) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendPacketToServer compresses the JSON encoding of a NetPacket and
|
||||
// sends it to the server.
|
||||
func (l *RemoteClientConnection) SendPacketToServer(packet d2netpacket.NetPacket) error {
|
||||
data, err := json.Marshal(packet.PacketData)
|
||||
if err != nil {
|
||||
@ -98,10 +114,13 @@ func (l *RemoteClientConnection) SendPacketToServer(packet d2netpacket.NetPacket
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetClientListener sets RemoteClientConnection.clientListener to the given value.
|
||||
func (l *RemoteClientConnection) SetClientListener(listener d2networking.ClientListener) {
|
||||
l.clientListener = listener
|
||||
}
|
||||
|
||||
// serverListener runs a while loop, reading from the GameServer's UDP
|
||||
// connection.
|
||||
func (l *RemoteClientConnection) serverListener() {
|
||||
buffer := make([]byte, 4096)
|
||||
for l.active {
|
||||
|
7
d2networking/d2client/doc.go
Normal file
7
d2networking/d2client/doc.go
Normal file
@ -0,0 +1,7 @@
|
||||
// Package d2client provides client side connection, map and entity
|
||||
// management.
|
||||
/*
|
||||
GameClient declares the ClientConnection interface to interact with
|
||||
local and remote servers. LocalClientConnection is the host and
|
||||
creates the game server (see d2server).*/
|
||||
package d2client
|
@ -22,17 +22,20 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
||||
)
|
||||
|
||||
// GameClient manages a connection to d2server.GameServer
|
||||
// and keeps a synchronised copy of the map and entities.
|
||||
type GameClient struct {
|
||||
clientConnection ClientConnection
|
||||
connectionType d2clientconnectiontype.ClientConnectionType
|
||||
GameState *d2player.PlayerState
|
||||
MapEngine *d2mapengine.MapEngine
|
||||
PlayerId string
|
||||
Players map[string]*d2mapentity.Player
|
||||
Seed int64
|
||||
RegenMap bool
|
||||
clientConnection ClientConnection // Abstract local/remote connection
|
||||
connectionType d2clientconnectiontype.ClientConnectionType // Type of connection (local or remote)
|
||||
GameState *d2player.PlayerState // local player state
|
||||
MapEngine *d2mapengine.MapEngine // Map and entities
|
||||
PlayerId string // ID of the local player
|
||||
Players map[string]*d2mapentity.Player // IDs of the other players
|
||||
Seed int64 // Map seed
|
||||
RegenMap bool // Regenerate tile cache on render (map has changed)
|
||||
}
|
||||
|
||||
// Create constructs a new GameClient and returns a pointer to it.
|
||||
func Create(connectionType d2clientconnectiontype.ClientConnectionType) (*GameClient, error) {
|
||||
result := &GameClient{
|
||||
MapEngine: d2mapengine.CreateMapEngine(), // TODO: Mapgen - Needs levels.txt stuff
|
||||
@ -54,18 +57,26 @@ func Create(connectionType d2clientconnectiontype.ClientConnectionType) (*GameCl
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// Open creates the server and connects to it if the client is local.
|
||||
// If the client is remote it sends a PlayerConnectionRequestPacket to the
|
||||
// server (see d2netpacket).
|
||||
func (g *GameClient) Open(connectionString string, saveFilePath string) error {
|
||||
return g.clientConnection.Open(connectionString, saveFilePath)
|
||||
}
|
||||
|
||||
// Close destroys the server if the client is local. For remote clients
|
||||
// it sends a DisconnectRequestPacket (see d2netpacket).
|
||||
func (g *GameClient) Close() error {
|
||||
return g.clientConnection.Close()
|
||||
}
|
||||
|
||||
// Destroy does the same thing as Close.
|
||||
func (g *GameClient) Destroy() error {
|
||||
return g.clientConnection.Close()
|
||||
}
|
||||
|
||||
// OnPacketReceived is called by the ClientConection and processes incoming
|
||||
// packets.
|
||||
func (g *GameClient) OnPacketReceived(packet d2netpacket.NetPacket) error {
|
||||
switch packet.PacketType {
|
||||
case d2netpackettype.GenerateMap:
|
||||
@ -145,6 +156,8 @@ func (g *GameClient) OnPacketReceived(packet d2netpacket.NetPacket) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// SendPacketToServer calls server.OnPacketReceived if the client is local.
|
||||
// If it is remote the NetPacket sent over a UDP connection to the server.
|
||||
func (g *GameClient) SendPacketToServer(packet d2netpacket.NetPacket) error {
|
||||
return g.clientConnection.SendPacketToServer(packet)
|
||||
}
|
||||
|
@ -1,20 +1,28 @@
|
||||
// Package d2netpackettype defines the enumerable NetPacketType.
|
||||
package d2netpackettype
|
||||
|
||||
// NetPacketType is an enum referring to all packet types in package
|
||||
// d2netpacket.
|
||||
type NetPacketType uint32
|
||||
|
||||
// Warning: Do NOT re-arrange the order of these packet values unless you want to
|
||||
// break compatibility between clients of slightly different versions.
|
||||
// Also note that the packet id is a byte, so if we use more than 256
|
||||
// of these then we are doing something very wrong.
|
||||
//(Except NetPacket which declares a NetPacketType to specify the packet body
|
||||
// type. See d2netpackettype.NetPacket.)
|
||||
//
|
||||
// Warning
|
||||
//
|
||||
// Do NOT re-arrange the order of these packet values unless you want to
|
||||
// break compatibility between clients of slightly different versions.
|
||||
// Also note that the packet id is a byte, so if we use more than 256 of
|
||||
// these then we are doing something very wrong.
|
||||
const (
|
||||
UpdateServerInfo NetPacketType = iota
|
||||
GenerateMap // Sent by the server to generate a map
|
||||
AddPlayer // Server sends to the client to add a player
|
||||
MovePlayer // Sent to the client or server to indicate player movement
|
||||
PlayerConnectionRequest // Client sends to server to request a connection
|
||||
PlayerDisconnectionNotification // Client notifies the server that it is disconnecting
|
||||
Ping // Ping message type
|
||||
Pong // Pong message type
|
||||
ServerClosed // Local host has closed the server
|
||||
CastSkill // Sent to the client or server to indicate entity casting skill
|
||||
UpdateServerInfo NetPacketType = iota // Sent by the server, client sets the given player ID and map seed
|
||||
GenerateMap // Sent by the server, client generates a map
|
||||
AddPlayer // Sent by the server, client adds a player
|
||||
MovePlayer // Sent by client or server, moves a player entity
|
||||
PlayerConnectionRequest // Sent by the remote client when connecting
|
||||
PlayerDisconnectionNotification // Sent by the remote client when disconnecting
|
||||
Ping // Requests a Pong packet
|
||||
Pong // Responds to a Ping packet
|
||||
ServerClosed // Sent by the local host when it has closed the server
|
||||
CastSkill // Sent by client or server, indicates entity casting skill
|
||||
)
|
||||
|
@ -1,7 +1,22 @@
|
||||
// Package d2netpacket defines types which are encoded to JSON and sent in network
|
||||
// packet payloads.
|
||||
/*
|
||||
Package d2netpacket/d2netpackettype defines a uint32 enumerable representing each
|
||||
packet type.
|
||||
|
||||
A struct is defined for each packet type. Each struct comes with a function which
|
||||
returns a NetPacket declaring the type enum (header) followed by the associated
|
||||
struct (body). The NetPacket is marshalled to JSON for transport. On receipt of
|
||||
the packet, the enum is read as a single byte then the remaining data (the struct)
|
||||
is unmarshalled to the type associated with the type enum.*/
|
||||
package d2netpacket
|
||||
|
||||
import "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
||||
|
||||
// NetPacket is used to wrap and send all packet types under d2netpacket.
|
||||
// When decoding a packet: First the PacketType byte is read, then the
|
||||
// PacketData is unmarshalled to a struct of the type associated with
|
||||
// PacketType.
|
||||
type NetPacket struct {
|
||||
PacketType d2netpackettype.NetPacketType `json:"packetType"`
|
||||
PacketData interface{} `json:"packetData"`
|
||||
|
@ -7,6 +7,9 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
||||
)
|
||||
|
||||
// AddPlayerPacket contains the data required to create a Player entity.
|
||||
// It is sent by the server to create the entity for a newly connected
|
||||
// player on a client.
|
||||
type AddPlayerPacket struct {
|
||||
Id string `json:"id"`
|
||||
Name string `json:"name"`
|
||||
@ -14,9 +17,11 @@ type AddPlayerPacket struct {
|
||||
Y int `json:"y"`
|
||||
HeroType d2enum.Hero `json:"hero"`
|
||||
Equipment d2inventory.CharacterEquipment `json:"equipment"`
|
||||
Stats d2hero.HeroStatsState `json:"heroStats"`
|
||||
Stats d2hero.HeroStatsState `json:"heroStats"`
|
||||
}
|
||||
|
||||
// CreateAddPlayerPacket returns a NetPacket which declares an
|
||||
// AddPlayerPacket with the data in given parameters.
|
||||
func CreateAddPlayerPacket(id, name string, x, y int, heroType d2enum.Hero, stats d2hero.HeroStatsState, equipment d2inventory.CharacterEquipment) NetPacket {
|
||||
return NetPacket{
|
||||
PacketType: d2netpackettype.AddPlayer,
|
||||
@ -27,7 +32,7 @@ func CreateAddPlayerPacket(id, name string, x, y int, heroType d2enum.Hero, stat
|
||||
Y: y,
|
||||
HeroType: heroType,
|
||||
Equipment: equipment,
|
||||
Stats: stats,
|
||||
Stats: stats,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
@ -5,10 +5,15 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
||||
)
|
||||
|
||||
// GenerateMapPacket contains an enumerable representing a region. It
|
||||
// is sent by the server to generate the map for the given region on
|
||||
// a client.
|
||||
type GenerateMapPacket struct {
|
||||
RegionType d2enum.RegionIdType `json:"regionType"`
|
||||
}
|
||||
|
||||
// CreateGenerateMapPacket returns a NetPacket which declares a
|
||||
// GenerateMapPacket with the given regionType.
|
||||
func CreateGenerateMapPacket(regionType d2enum.RegionIdType) NetPacket {
|
||||
return NetPacket{
|
||||
PacketType: d2netpackettype.GenerateMap,
|
||||
|
@ -2,8 +2,9 @@ package d2netpacket
|
||||
|
||||
import "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
||||
|
||||
// MovePlayerPacket contains a movement command for a specific player entity.
|
||||
// It is sent by the server to move a player entity on a client.
|
||||
// TODO: Need to handle being on different maps
|
||||
|
||||
type MovePlayerPacket struct {
|
||||
PlayerId string `json:"playerId"`
|
||||
StartX float64 `json:"startX"`
|
||||
@ -12,6 +13,8 @@ type MovePlayerPacket struct {
|
||||
DestY float64 `json:"destY"`
|
||||
}
|
||||
|
||||
// CreateMovePlayerPacket returns a NetPacket which declares a MovePlayerPacket
|
||||
// with the given ID and movement command.
|
||||
func CreateMovePlayerPacket(playerId string, startX, startY, destX, destY float64) NetPacket {
|
||||
return NetPacket{
|
||||
PacketType: d2netpackettype.MovePlayer,
|
||||
|
@ -1,14 +1,19 @@
|
||||
package d2netpacket
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
||||
"time"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
||||
)
|
||||
|
||||
// PingPacket contains the time at which it was sent. It is sent by the
|
||||
// server and instructs the client to respond with a Pong packet.
|
||||
type PingPacket struct {
|
||||
TS time.Time `json:"ts"`
|
||||
}
|
||||
|
||||
// CreatePingPacket returns a NetPacket which declares a GenerateMapPacket
|
||||
// with the the current time.
|
||||
func CreatePingPacket() NetPacket {
|
||||
return NetPacket{
|
||||
PacketType: d2netpackettype.Ping,
|
||||
|
@ -2,8 +2,10 @@ package d2netpacket
|
||||
|
||||
import "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
||||
|
||||
// CastPacket contains a cast command for an entity. It is sent by the server
|
||||
// and instructs the client to trigger the use of the given skill on the given
|
||||
// entity.
|
||||
// TODO: Need to handle being on different maps
|
||||
|
||||
type CastPacket struct {
|
||||
SourceEntityID string `json:"sourceEntityId"`
|
||||
SkillID int `json:"skillId"`
|
||||
@ -12,6 +14,8 @@ type CastPacket struct {
|
||||
TargetEntityID string `json:"targetEntityId"`
|
||||
}
|
||||
|
||||
// CreateCastPacket returns a NetPacket which declares a CastPacket with the
|
||||
// given skill command.
|
||||
func CreateCastPacket(entityID string, skillID int, targetX, targetY float64) NetPacket {
|
||||
return NetPacket{
|
||||
PacketType: d2netpackettype.CastSkill,
|
||||
|
@ -5,11 +5,15 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
||||
)
|
||||
|
||||
// PlayerConnectionRequestPacket contains a player ID and game state.
|
||||
// It is sent by a remote client to initiate a connection (join a game).
|
||||
type PlayerConnectionRequestPacket struct {
|
||||
Id string `json:"id"`
|
||||
PlayerState *d2player.PlayerState `json:"gameState"`
|
||||
}
|
||||
|
||||
// CreatePlayerConnectionRequestPacket returns a NetPacket which defines a
|
||||
// PlayerConnectionRequestPacket with the given ID and game state.
|
||||
func CreatePlayerConnectionRequestPacket(id string, playerState *d2player.PlayerState) NetPacket {
|
||||
return NetPacket{
|
||||
PacketType: d2netpackettype.PlayerConnectionRequest,
|
||||
|
@ -5,11 +5,15 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
||||
)
|
||||
|
||||
// PlayerDisconnectRequestPacket contains a player ID and game state.
|
||||
// It is sent by a remote client to close the connection (leave a game).
|
||||
type PlayerDisconnectRequestPacket struct {
|
||||
Id string `json:"id"`
|
||||
PlayerState *d2player.PlayerState `json:"gameState"`
|
||||
PlayerState *d2player.PlayerState `json:"gameState"` // TODO: remove this? It isn't used.
|
||||
}
|
||||
|
||||
// CreatePlayerDisconnectRequestPacket returns a NetPacket which defines a
|
||||
// PlayerDisconnectRequestPacket with the given ID.
|
||||
func CreatePlayerDisconnectRequestPacket(id string) NetPacket {
|
||||
return NetPacket{
|
||||
PacketType: d2netpackettype.PlayerDisconnectionNotification,
|
||||
|
@ -1,15 +1,20 @@
|
||||
package d2netpacket
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
||||
"time"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
||||
)
|
||||
|
||||
// PongPacket contains the time at which it was sent and the ID of the
|
||||
// client. It is sent by the client in response to a Pong packet.
|
||||
type PongPacket struct {
|
||||
ID string `json:"id"`
|
||||
TS time.Time `json:"ts"`
|
||||
}
|
||||
|
||||
// CreatePongPacket returns a NetPacket which declares a PongPacket with
|
||||
// the current time and given ID.
|
||||
func CreatePongPacket(id string) NetPacket {
|
||||
return NetPacket{
|
||||
PacketType: d2netpackettype.Pong,
|
||||
|
@ -1,14 +1,19 @@
|
||||
package d2netpacket
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
||||
"time"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
||||
)
|
||||
|
||||
// ServerClosedPacket contains the current time. It is sent by the server
|
||||
// to inform a client that the server has shut down.
|
||||
type ServerClosedPacket struct {
|
||||
TS time.Time `json:"ts"`
|
||||
}
|
||||
|
||||
// CreateServerClosedPacket returns a NetPacket which declares a
|
||||
// ServerClosedPacket with the current time.
|
||||
func CreateServerClosedPacket() NetPacket {
|
||||
return NetPacket{
|
||||
PacketType: d2netpackettype.ServerClosed,
|
||||
|
@ -2,11 +2,15 @@ package d2netpacket
|
||||
|
||||
import "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
||||
|
||||
// UpdateServerInfoPacket contains the ID for a player and the map seed.
|
||||
// It is sent by the server to synchronise these values on the client.
|
||||
type UpdateServerInfoPacket struct {
|
||||
Seed int64 `json:"seed"`
|
||||
PlayerId string `json:"playerId"`
|
||||
}
|
||||
|
||||
// CreateUpdateServerInfoPacket returns a NetPacket which declares an
|
||||
// UpdateServerInfoPacket with the given player ID and map seed.
|
||||
func CreateUpdateServerInfoPacket(seed int64, playerId string) NetPacket {
|
||||
return NetPacket{
|
||||
PacketType: d2netpackettype.UpdateServerInfo,
|
||||
|
@ -6,6 +6,8 @@ import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
|
||||
)
|
||||
|
||||
// ClientConnection is an interface for abstracting local and remote
|
||||
// clients.
|
||||
type ClientConnection interface {
|
||||
GetUniqueId() string
|
||||
GetConnectionType() d2clientconnectiontype.ClientConnectionType
|
||||
|
@ -1,29 +1,29 @@
|
||||
package d2server
|
||||
|
||||
import (
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
|
||||
"log"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
|
||||
)
|
||||
|
||||
// ConnectionManager is responsible for cleanup up connections accepted by the game server. As the server communicates over
|
||||
// UDP and is stateless we need to implement some loose state management via a ping/pong system. ConnectionManager also handles
|
||||
// communication for graceful shutdowns.
|
||||
//
|
||||
// retries: # of attempts before the dropping the client
|
||||
// interval: How long to wait before each ping/pong test
|
||||
// gameServer: The *GameServer is argument provided for the connection manager to watch over
|
||||
// status: map of inflight ping/pong requests
|
||||
// ConnectionManager is responsible for cleanup up connections accepted by the game server.
|
||||
// As the server communicates over UDP and is stateless we need to implement some loose state
|
||||
// management via a ping/pong system. ConnectionManager also handles communication for
|
||||
// graceful shutdowns.
|
||||
type ConnectionManager struct {
|
||||
sync.RWMutex
|
||||
retries int
|
||||
interval time.Duration
|
||||
gameServer *GameServer
|
||||
status map[string]int
|
||||
retries int // Number of attempts before the dropping the client
|
||||
interval time.Duration // How long to wait before each ping/pong test
|
||||
gameServer *GameServer // The GameServer with the connections being managed
|
||||
status map[string]int // Map of inflight ping/pong requests
|
||||
}
|
||||
|
||||
// CreateConnectionManager constructs a new ConnectionManager and calls
|
||||
// ConnectionManager.Run() in a goroutine before retuning a pointer to
|
||||
// the new ConnectionManager.
|
||||
func CreateConnectionManager(gameServer *GameServer) *ConnectionManager {
|
||||
manager := &ConnectionManager{
|
||||
retries: 3,
|
||||
|
@ -1,24 +1,31 @@
|
||||
// Package d2udpclientconnection provides an implementation of a UDP client connection with a game state.
|
||||
package d2udpclientconnection
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"compress/gzip"
|
||||
"encoding/json"
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
|
||||
"net"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
|
||||
|
||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
|
||||
)
|
||||
|
||||
// UDPClientConnection is the implementation of the
|
||||
// d2server.ClientConnection interface to represent remote client from the
|
||||
// server perspective.
|
||||
type UDPClientConnection struct {
|
||||
id string
|
||||
address *net.UDPAddr
|
||||
udpConnection *net.UDPConn
|
||||
playerState *d2player.PlayerState
|
||||
id string // ID of the associated RemoteClientConnection
|
||||
address *net.UDPAddr // IP address of the associated RemoteClientConnection
|
||||
udpConnection *net.UDPConn // Server's UDP Connection
|
||||
playerState *d2player.PlayerState // Client's game state
|
||||
}
|
||||
|
||||
// CreateUDPClientConnection constructs a new UDPClientConnection and
|
||||
// returns a pointer to it.
|
||||
func CreateUDPClientConnection(udpConnection *net.UDPConn, id string, address *net.UDPAddr) *UDPClientConnection {
|
||||
result := &UDPClientConnection{
|
||||
id: id,
|
||||
@ -29,14 +36,19 @@ func CreateUDPClientConnection(udpConnection *net.UDPConn, id string, address *n
|
||||
return result
|
||||
}
|
||||
|
||||
// GetUniqueId returns UDPClientConnection.id
|
||||
func (u UDPClientConnection) GetUniqueId() string {
|
||||
return u.id
|
||||
}
|
||||
|
||||
// GetConnectionType returns an enum representing the connection type.
|
||||
// See: d2clientconnectiontype.
|
||||
func (u UDPClientConnection) GetConnectionType() d2clientconnectiontype.ClientConnectionType {
|
||||
return d2clientconnectiontype.LANClient
|
||||
}
|
||||
|
||||
// SendPacketToClient compresses the JSON encoding of a NetPacket and
|
||||
// sends it to the client.
|
||||
func (u *UDPClientConnection) SendPacketToClient(packet d2netpacket.NetPacket) error {
|
||||
data, err := json.Marshal(packet.PacketData)
|
||||
if err != nil {
|
||||
@ -55,10 +67,12 @@ func (u *UDPClientConnection) SendPacketToClient(packet d2netpacket.NetPacket) e
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetPlayerState sets UDP.playerState to the given value.
|
||||
func (u *UDPClientConnection) SetPlayerState(playerState *d2player.PlayerState) {
|
||||
u.playerState = playerState
|
||||
}
|
||||
|
||||
// GetPlayerState returns UDPClientConnection.playerState.
|
||||
func (u *UDPClientConnection) GetPlayerState() *d2player.PlayerState {
|
||||
return u.playerState
|
||||
}
|
||||
|
5
d2networking/d2server/doc.go
Normal file
5
d2networking/d2server/doc.go
Normal file
@ -0,0 +1,5 @@
|
||||
// Package d2server provides connection management and client synchronisation.
|
||||
/*
|
||||
Data is encoded to JSON and compressed using gzip. Transport is over UDP.
|
||||
The server is authoritative for both local and remote clients.*/
|
||||
package d2server
|
@ -24,6 +24,9 @@ import (
|
||||
"github.com/robertkrimen/otto"
|
||||
)
|
||||
|
||||
// GameServer owns the authoritative copy of the map and entities
|
||||
// It accepts incoming connections from local (host) and remote
|
||||
// clients.
|
||||
type GameServer struct {
|
||||
sync.RWMutex
|
||||
clientConnections map[string]ClientConnection
|
||||
@ -37,6 +40,11 @@ type GameServer struct {
|
||||
|
||||
var singletonServer *GameServer
|
||||
|
||||
// Create constructs a new GameServer and assigns it as a singleton. It
|
||||
// also generates the initial map and entities for the server.
|
||||
//
|
||||
// If openNetworkServer is true, the GameServer starts listening for UDP
|
||||
// packets.
|
||||
func Create(openNetworkServer bool) {
|
||||
log.Print("Creating GameServer")
|
||||
if singletonServer != nil {
|
||||
@ -84,6 +92,8 @@ func createNetworkServer() {
|
||||
singletonServer.udpConnection.SetReadBuffer(4096)
|
||||
}
|
||||
|
||||
// runNetworkServer runs a while loop, reading from the GameServer's UDP
|
||||
// connection.
|
||||
func runNetworkServer() {
|
||||
buffer := make([]byte, 4096)
|
||||
for singletonServer.running {
|
||||
@ -131,6 +141,8 @@ func runNetworkServer() {
|
||||
}
|
||||
}
|
||||
|
||||
// Run sets GameServer.running to true and call runNetworkServer
|
||||
// in a goroutine.
|
||||
func Run() {
|
||||
log.Print("Starting GameServer")
|
||||
singletonServer.running = true
|
||||
@ -141,6 +153,8 @@ func Run() {
|
||||
log.Print("Network server has been started")
|
||||
}
|
||||
|
||||
// Stop sets GameServer.running to false and closes the
|
||||
// GameServer's UDP connection.
|
||||
func Stop() {
|
||||
log.Print("Stopping GameServer")
|
||||
singletonServer.running = false
|
||||
@ -149,6 +163,7 @@ func Stop() {
|
||||
}
|
||||
}
|
||||
|
||||
// Destroy calls Stop() if the server exists.
|
||||
func Destroy() {
|
||||
if singletonServer == nil {
|
||||
return
|
||||
@ -157,6 +172,14 @@ func Destroy() {
|
||||
Stop()
|
||||
}
|
||||
|
||||
// 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.
|
||||
func OnClientConnected(client ClientConnection) {
|
||||
// Temporary position hack --------------------------------------------
|
||||
sx, sy := singletonServer.mapEngines[0].GetStartPosition() // TODO: Another temporary hack
|
||||
@ -186,11 +209,14 @@ func OnClientConnected(client ClientConnection) {
|
||||
|
||||
}
|
||||
|
||||
// OnClientDisconnected removes the given client from the list
|
||||
// of client connections.
|
||||
func OnClientDisconnected(client ClientConnection) {
|
||||
log.Printf("Client disconnected with an id of %s", client.GetUniqueId())
|
||||
delete(singletonServer.clientConnections, client.GetUniqueId())
|
||||
}
|
||||
|
||||
// OnPacketReceived is called by the local client to 'send' a packet to the server.
|
||||
func OnPacketReceived(client ClientConnection, packet d2netpacket.NetPacket) error {
|
||||
switch packet.PacketType {
|
||||
case d2netpackettype.MovePlayer:
|
||||
|
4
d2networking/doc.go
Normal file
4
d2networking/doc.go
Normal file
@ -0,0 +1,4 @@
|
||||
// Package d2networking provides client and server implementations for OpenDiablo2.
|
||||
/*
|
||||
The server is authoritative and communicates with local and remote clients over UDP.*/
|
||||
package d2networking
|
Loading…
Reference in New Issue
Block a user