1
1
mirror of https://github.com/OpenDiablo2/OpenDiablo2 synced 2024-09-29 22:56:07 -04: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:
danhale-git 2020-06-29 22:01:26 +01:00 committed by GitHub
parent aae565d528
commit 5ea6ada452
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
25 changed files with 241 additions and 62 deletions

View File

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

View File

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

View File

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

View File

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

View File

@ -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 {

View 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

View File

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

View File

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

View File

@ -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"`

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

@ -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
View 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