mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-09-28 22:26:30 -04:00
parent
fe47e51351
commit
1ca534cc13
@ -31,13 +31,13 @@ type LevelPresetRecord struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// CreateLevelPresetRecord parses a row from lvlprest.txt into a LevelPresetRecord
|
// CreateLevelPresetRecord parses a row from lvlprest.txt into a LevelPresetRecord
|
||||||
func createLevelPresetRecord(props []string) *LevelPresetRecord {
|
func createLevelPresetRecord(props []string) LevelPresetRecord {
|
||||||
i := -1
|
i := -1
|
||||||
inc := func() int {
|
inc := func() int {
|
||||||
i++
|
i++
|
||||||
return i
|
return i
|
||||||
}
|
}
|
||||||
result := &LevelPresetRecord{
|
result := LevelPresetRecord{
|
||||||
Name: props[inc()],
|
Name: props[inc()],
|
||||||
DefinitionId: d2common.StringToInt(props[inc()]),
|
DefinitionId: d2common.StringToInt(props[inc()]),
|
||||||
LevelId: d2common.StringToInt(props[inc()]),
|
LevelId: d2common.StringToInt(props[inc()]),
|
||||||
@ -69,10 +69,10 @@ func createLevelPresetRecord(props []string) *LevelPresetRecord {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
var LevelPresets map[int]*LevelPresetRecord
|
var LevelPresets map[int]LevelPresetRecord
|
||||||
|
|
||||||
func LoadLevelPresets(file []byte) {
|
func LoadLevelPresets(file []byte) {
|
||||||
LevelPresets = make(map[int]*LevelPresetRecord)
|
LevelPresets = make(map[int]LevelPresetRecord)
|
||||||
data := strings.Split(string(file), "\r\n")[1:]
|
data := strings.Split(string(file), "\r\n")[1:]
|
||||||
for _, line := range data {
|
for _, line := range data {
|
||||||
if len(line) == 0 {
|
if len(line) == 0 {
|
||||||
@ -88,7 +88,7 @@ func LoadLevelPresets(file []byte) {
|
|||||||
log.Printf("Loaded %d level presets", len(LevelPresets))
|
log.Printf("Loaded %d level presets", len(LevelPresets))
|
||||||
}
|
}
|
||||||
|
|
||||||
func LevelPreset(id int) *LevelPresetRecord {
|
func LevelPreset(id int) LevelPresetRecord {
|
||||||
for i := 0; i < len(LevelPresets); i++ {
|
for i := 0; i < len(LevelPresets); i++ {
|
||||||
if LevelPresets[i].DefinitionId == id {
|
if LevelPresets[i].DefinitionId == id {
|
||||||
return LevelPresets[i]
|
return LevelPresets[i]
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
type LevelSubstitutionRecord struct {
|
type LevelSubstitutionRecord struct {
|
||||||
// Description, reference only.
|
// Description, reference only.
|
||||||
Name string // Name
|
Name string // Name
|
||||||
|
|
||||||
// This value is used in Levels.txt, in the column 'SubType'. You'll notice
|
// This value is used in Levels.txt, in the column 'SubType'. You'll notice
|
||||||
// that in LvlSub.txt some rows use the same value, we can say they forms
|
// that in LvlSub.txt some rows use the same value, we can say they forms
|
||||||
// groups. If you count each row of a group starting from 0, then you'll
|
// groups. If you count each row of a group starting from 0, then you'll
|
||||||
|
@ -2,68 +2,51 @@ package d2datadict
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"log"
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type LevelTypeRecord struct {
|
type LevelTypeRecord struct {
|
||||||
Name string // Name
|
Name string
|
||||||
Id int // Id
|
Id int
|
||||||
Files []string // File 1 -- File 32
|
Files [32]string
|
||||||
Beta bool // Beta
|
Beta bool
|
||||||
Act int // Act
|
Act int
|
||||||
Expansion bool // Expansion
|
Expansion bool
|
||||||
}
|
}
|
||||||
|
|
||||||
var LevelTypes map[d2enum.RegionIdType]*LevelTypeRecord
|
var LevelTypes []LevelTypeRecord
|
||||||
|
|
||||||
func LoadLevelTypes(file []byte) {
|
func LoadLevelTypes(file []byte) {
|
||||||
LevelTypes = make(map[d2enum.RegionIdType]*LevelTypeRecord)
|
data := strings.Split(string(file), "\r\n")[1:]
|
||||||
dict := d2common.LoadDataDictionary(string(file))
|
LevelTypes = make([]LevelTypeRecord, len(data))
|
||||||
for idx := range dict.Data {
|
for i, j := 0, 0; i < len(data); i, j = i+1, j+1 {
|
||||||
record := &LevelTypeRecord{
|
idx := -1
|
||||||
Name: dict.GetString("Name", idx),
|
inc := func() int {
|
||||||
Id: dict.GetNumber("Id", idx),
|
idx++
|
||||||
Files: []string{
|
return idx
|
||||||
dict.GetString("File 1", idx),
|
|
||||||
dict.GetString("File 2", idx),
|
|
||||||
dict.GetString("File 3", idx),
|
|
||||||
dict.GetString("File 4", idx),
|
|
||||||
dict.GetString("File 5", idx),
|
|
||||||
dict.GetString("File 6", idx),
|
|
||||||
dict.GetString("File 7", idx),
|
|
||||||
dict.GetString("File 8", idx),
|
|
||||||
dict.GetString("File 9", idx),
|
|
||||||
dict.GetString("File 10", idx),
|
|
||||||
dict.GetString("File 11", idx),
|
|
||||||
dict.GetString("File 12", idx),
|
|
||||||
dict.GetString("File 13", idx),
|
|
||||||
dict.GetString("File 14", idx),
|
|
||||||
dict.GetString("File 15", idx),
|
|
||||||
dict.GetString("File 16", idx),
|
|
||||||
dict.GetString("File 17", idx),
|
|
||||||
dict.GetString("File 18", idx),
|
|
||||||
dict.GetString("File 19", idx),
|
|
||||||
dict.GetString("File 20", idx),
|
|
||||||
dict.GetString("File 21", idx),
|
|
||||||
dict.GetString("File 22", idx),
|
|
||||||
dict.GetString("File 23", idx),
|
|
||||||
dict.GetString("File 24", idx),
|
|
||||||
dict.GetString("File 25", idx),
|
|
||||||
dict.GetString("File 26", idx),
|
|
||||||
dict.GetString("File 27", idx),
|
|
||||||
dict.GetString("File 28", idx),
|
|
||||||
dict.GetString("File 29", idx),
|
|
||||||
dict.GetString("File 30", idx),
|
|
||||||
dict.GetString("File 31", idx),
|
|
||||||
dict.GetString("File 32", idx),
|
|
||||||
},
|
|
||||||
Beta: dict.GetNumber("Beta", idx) > 0,
|
|
||||||
Act: dict.GetNumber("Act", idx),
|
|
||||||
Expansion: dict.GetNumber("Expansion", idx) > 0,
|
|
||||||
}
|
}
|
||||||
LevelTypes[d2enum.RegionIdType(record.Id)] = record
|
if len(data[i]) == 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
parts := strings.Split(data[i], "\t")
|
||||||
|
if parts[0] == "Expansion" {
|
||||||
|
j--
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
LevelTypes[j].Name = parts[inc()]
|
||||||
|
LevelTypes[j].Id = d2common.StringToInt(parts[inc()])
|
||||||
|
for fileIdx := range LevelTypes[i].Files {
|
||||||
|
LevelTypes[j].Files[fileIdx] = parts[inc()]
|
||||||
|
if LevelTypes[j].Files[fileIdx] == "0" {
|
||||||
|
LevelTypes[j].Files[fileIdx] = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
LevelTypes[j].Beta = parts[inc()] != "1"
|
||||||
|
LevelTypes[j].Act = d2common.StringToInt(parts[inc()])
|
||||||
|
LevelTypes[j].Expansion = parts[inc()] != "1"
|
||||||
}
|
}
|
||||||
log.Printf("Loaded %d LevelType records", len(LevelTypes))
|
log.Printf("Loaded %d LevelType records", len(LevelTypes))
|
||||||
}
|
}
|
||||||
|
@ -38,7 +38,7 @@ type LevelDetailsRecord struct {
|
|||||||
AutomapIndex int // Layer
|
AutomapIndex int // Layer
|
||||||
|
|
||||||
// sizeX - SizeY in each difficuly. If this is a preset area this sets the
|
// sizeX - SizeY in each difficuly. If this is a preset area this sets the
|
||||||
// X size for the area. Otherwise use the same value here that are used in
|
// X size for the area. Othervise use the same value here that are used in
|
||||||
// lvlprest.txt to set the size for the .ds1 file.
|
// lvlprest.txt to set the size for the .ds1 file.
|
||||||
SizeXNormal int // SizeX
|
SizeXNormal int // SizeX
|
||||||
SizeYNormal int // SizeY
|
SizeYNormal int // SizeY
|
||||||
@ -141,14 +141,28 @@ type LevelDetailsRecord struct {
|
|||||||
// linked with, but the actuall number of Vis ( 0 - 7 ) is determined by
|
// linked with, but the actuall number of Vis ( 0 - 7 ) is determined by
|
||||||
// your actual map (the .ds1 fle).
|
// your actual map (the .ds1 fle).
|
||||||
// Example: Normally Cave levels are only using vis 0-3 and wilderness areas 4-7 .
|
// Example: Normally Cave levels are only using vis 0-3 and wilderness areas 4-7 .
|
||||||
WarpLevelId []int // Vis0 -- Vis7
|
LevelLinkId0 int // Vis0
|
||||||
|
LevelLinkId1 int // Vis1
|
||||||
|
LevelLinkId2 int // Vis2
|
||||||
|
LevelLinkId3 int // Vis3
|
||||||
|
LevelLinkId4 int // Vis4
|
||||||
|
LevelLinkId5 int // Vis5
|
||||||
|
LevelLinkId6 int // Vis6
|
||||||
|
LevelLinkId7 int // Vis7
|
||||||
|
|
||||||
// This controls the visual graphics then you move the mouse pointer over
|
// This controls the visual graphics then you move the mouse pointer over
|
||||||
// an entrance. To show the graphics you use an ID from lvlwarp.txt and the
|
// an entrance. To show the graphics you use an ID from lvlwarp.txt and the
|
||||||
// behavior on the graphics is controlled by lvlwarp.txt. Your Warps must
|
// behavior on the graphics is controlled by lvlwarp.txt. Your Warps must
|
||||||
// match your Vis.
|
// match your Vis.
|
||||||
// Example: If your level uses Vis 3,5,7 then you must also use Warp 3,5,7 .
|
// Example: If your level uses Vis 3,5,7 then you must also use Warp 3,5,7 .
|
||||||
WarpGraphicsId []int // Warp0 -- Warp7
|
WarpGraphicsId0 int // Warp0
|
||||||
|
WarpGraphicsId1 int // Warp1
|
||||||
|
WarpGraphicsId2 int // Warp2
|
||||||
|
WarpGraphicsId3 int // Warp3
|
||||||
|
WarpGraphicsId4 int // Warp4
|
||||||
|
WarpGraphicsId5 int // Warp5
|
||||||
|
WarpGraphicsId6 int // Warp6
|
||||||
|
WarpGraphicsId7 int // Warp7
|
||||||
|
|
||||||
// These settings handle the light intensity as well as its RGB components
|
// These settings handle the light intensity as well as its RGB components
|
||||||
LightIntensity int // Intensity
|
LightIntensity int // Intensity
|
||||||
@ -363,7 +377,7 @@ type LevelDetailsRecord struct {
|
|||||||
|
|
||||||
var LevelDetails map[int]*LevelDetailsRecord
|
var LevelDetails map[int]*LevelDetailsRecord
|
||||||
|
|
||||||
func GetLevelDetailsByLevelId(id int) *LevelDetailsRecord {
|
func GetLevelDetails(id int) *LevelDetailsRecord {
|
||||||
for i := 0; i < len(LevelDetails); i++ {
|
for i := 0; i < len(LevelDetails); i++ {
|
||||||
if LevelDetails[i].Id == id {
|
if LevelDetails[i].Id == id {
|
||||||
return LevelDetails[i]
|
return LevelDetails[i]
|
||||||
@ -373,85 +387,11 @@ func GetLevelDetailsByLevelId(id int) *LevelDetailsRecord {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetLevelDetailsByActId(act int) []*LevelDetailsRecord {
|
|
||||||
result := make([]*LevelDetailsRecord, 0)
|
|
||||||
for _, record := range LevelDetails {
|
|
||||||
if act == record.Act {
|
|
||||||
result = append(result, record)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
var actIds []int
|
|
||||||
|
|
||||||
func GetNumberOfActs() int {
|
|
||||||
return len(actIds)
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetActIds() []int {
|
|
||||||
return actIds
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetLevelWarpsByLevelId(id int) []*LevelWarpRecord {
|
|
||||||
result := make([]*LevelWarpRecord, 0)
|
|
||||||
level := LevelDetails[id]
|
|
||||||
for _, warpId := range level.WarpLevelId {
|
|
||||||
if warpId < 0 {
|
|
||||||
continue // there are -1 values for empty entries in the table
|
|
||||||
}
|
|
||||||
result = append(result, LevelWarps[warpId])
|
|
||||||
}
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetLevelPresetByLevelId(id int) *LevelPresetRecord {
|
|
||||||
for recordId, record := range LevelPresets {
|
|
||||||
if id == recordId {
|
|
||||||
return record
|
|
||||||
}
|
|
||||||
}
|
|
||||||
panic("couldn't find a preset.")
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetFirstLevelIdByActId(actId int) int {
|
|
||||||
recordsForAct := GetLevelDetailsByActId(actId)
|
|
||||||
lowest := -1
|
|
||||||
if len(recordsForAct) > 0 {
|
|
||||||
for _, record := range recordsForAct {
|
|
||||||
// need to account for level ID 0 which is an empty map in act 1
|
|
||||||
if record.Id == 0 {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if lowest < 0 {
|
|
||||||
lowest = record.Id
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if record.Id < lowest {
|
|
||||||
lowest = record.Id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return lowest
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func AppendIfMissing(slice []int, i int) []int {
|
|
||||||
for _, ele := range slice {
|
|
||||||
if ele == i {
|
|
||||||
return slice
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return append(slice, i)
|
|
||||||
}
|
|
||||||
|
|
||||||
func LoadLevelDetails(file []byte) {
|
func LoadLevelDetails(file []byte) {
|
||||||
dict := d2common.LoadDataDictionary(string(file))
|
dict := d2common.LoadDataDictionary(string(file))
|
||||||
numRecords := len(dict.Data)
|
numRecords := len(dict.Data)
|
||||||
LevelDetails = make(map[int]*LevelDetailsRecord, numRecords)
|
LevelDetails = make(map[int]*LevelDetailsRecord, numRecords)
|
||||||
|
|
||||||
actIds = make([]int, 0)
|
|
||||||
|
|
||||||
for idx := range dict.Data {
|
for idx := range dict.Data {
|
||||||
record := &LevelDetailsRecord{
|
record := &LevelDetailsRecord{
|
||||||
Name: dict.GetString("Name ", idx),
|
Name: dict.GetString("Name ", idx),
|
||||||
@ -485,26 +425,22 @@ func LoadLevelDetails(file []byte) {
|
|||||||
SubTheme: dict.GetNumber("SubTheme", idx),
|
SubTheme: dict.GetNumber("SubTheme", idx),
|
||||||
SubWaypoint: dict.GetNumber("SubWaypoint", idx),
|
SubWaypoint: dict.GetNumber("SubWaypoint", idx),
|
||||||
SubShrine: dict.GetNumber("SubShrine", idx),
|
SubShrine: dict.GetNumber("SubShrine", idx),
|
||||||
WarpLevelId: []int{
|
LevelLinkId0: dict.GetNumber("Vis0", idx),
|
||||||
dict.GetNumber("Vis0", idx),
|
LevelLinkId1: dict.GetNumber("Vis1", idx),
|
||||||
dict.GetNumber("Vis1", idx),
|
LevelLinkId2: dict.GetNumber("Vis2", idx),
|
||||||
dict.GetNumber("Vis2", idx),
|
LevelLinkId3: dict.GetNumber("Vis3", idx),
|
||||||
dict.GetNumber("Vis3", idx),
|
LevelLinkId4: dict.GetNumber("Vis4", idx),
|
||||||
dict.GetNumber("Vis4", idx),
|
LevelLinkId5: dict.GetNumber("Vis5", idx),
|
||||||
dict.GetNumber("Vis5", idx),
|
LevelLinkId6: dict.GetNumber("Vis6", idx),
|
||||||
dict.GetNumber("Vis6", idx),
|
LevelLinkId7: dict.GetNumber("Vis7", idx),
|
||||||
dict.GetNumber("Vis7", idx),
|
WarpGraphicsId0: dict.GetNumber("Warp0", idx),
|
||||||
},
|
WarpGraphicsId1: dict.GetNumber("Warp1", idx),
|
||||||
WarpGraphicsId: []int{
|
WarpGraphicsId2: dict.GetNumber("Warp2", idx),
|
||||||
dict.GetNumber("Vis0", idx),
|
WarpGraphicsId3: dict.GetNumber("Warp3", idx),
|
||||||
dict.GetNumber("Vis1", idx),
|
WarpGraphicsId4: dict.GetNumber("Warp4", idx),
|
||||||
dict.GetNumber("Vis2", idx),
|
WarpGraphicsId5: dict.GetNumber("Warp5", idx),
|
||||||
dict.GetNumber("Vis3", idx),
|
WarpGraphicsId6: dict.GetNumber("Warp6", idx),
|
||||||
dict.GetNumber("Vis4", idx),
|
WarpGraphicsId7: dict.GetNumber("Warp7", idx),
|
||||||
dict.GetNumber("Vis5", idx),
|
|
||||||
dict.GetNumber("Vis6", idx),
|
|
||||||
dict.GetNumber("Vis7", idx),
|
|
||||||
},
|
|
||||||
LightIntensity: dict.GetNumber("Intensity", idx),
|
LightIntensity: dict.GetNumber("Intensity", idx),
|
||||||
Red: dict.GetNumber("Red", idx),
|
Red: dict.GetNumber("Red", idx),
|
||||||
Green: dict.GetNumber("Green", idx),
|
Green: dict.GetNumber("Green", idx),
|
||||||
@ -604,7 +540,6 @@ func LoadLevelDetails(file []byte) {
|
|||||||
ObjectGroupSpawnChance6: dict.GetNumber("ObjPrb6", idx),
|
ObjectGroupSpawnChance6: dict.GetNumber("ObjPrb6", idx),
|
||||||
ObjectGroupSpawnChance7: dict.GetNumber("ObjPrb7", idx),
|
ObjectGroupSpawnChance7: dict.GetNumber("ObjPrb7", idx),
|
||||||
}
|
}
|
||||||
actIds = AppendIfMissing(actIds, record.Act)
|
|
||||||
LevelDetails[idx] = record
|
LevelDetails[idx] = record
|
||||||
}
|
}
|
||||||
log.Printf("Loaded %d LevelDetails records", len(LevelDetails))
|
log.Printf("Loaded %d LevelDetails records", len(LevelDetails))
|
||||||
|
@ -11,8 +11,7 @@ package d2enum
|
|||||||
type LevelGenerationType int
|
type LevelGenerationType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
LevelTypeNone LevelGenerationType = iota
|
LevelTypeRandomMaze LevelGenerationType = iota
|
||||||
LevelTypeRandomMaze
|
|
||||||
LevelTypePreset
|
LevelTypePreset
|
||||||
LevelTypeWilderness
|
LevelTypeWilderness
|
||||||
)
|
)
|
||||||
|
@ -18,7 +18,6 @@ func getDefaultConfig() *Configuration {
|
|||||||
VsyncEnabled: true,
|
VsyncEnabled: true,
|
||||||
SfxVolume: 1.0,
|
SfxVolume: 1.0,
|
||||||
BgmVolume: 0.3,
|
BgmVolume: 0.3,
|
||||||
MaxConnections: 8,
|
|
||||||
MpqPath: "C:/Program Files (x86)/Diablo II",
|
MpqPath: "C:/Program Files (x86)/Diablo II",
|
||||||
MpqLoadOrder: []string{
|
MpqLoadOrder: []string{
|
||||||
"Patch_D2.mpq",
|
"Patch_D2.mpq",
|
||||||
|
@ -16,7 +16,6 @@ type Configuration struct {
|
|||||||
FullScreen bool
|
FullScreen bool
|
||||||
RunInBackground bool
|
RunInBackground bool
|
||||||
VsyncEnabled bool
|
VsyncEnabled bool
|
||||||
MaxConnections int
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var singleton = getDefaultConfig()
|
var singleton = getDefaultConfig()
|
||||||
|
@ -1,60 +0,0 @@
|
|||||||
package d2mapengine
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
|
||||||
// "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MapAct struct {
|
|
||||||
realm *MapRealm
|
|
||||||
id int
|
|
||||||
levels map[int]*MapLevel
|
|
||||||
}
|
|
||||||
|
|
||||||
func (act *MapAct) isActive() bool {
|
|
||||||
for _, level := range act.levels {
|
|
||||||
if level.isActive() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func (act *MapAct) Advance(elapsed float64) {
|
|
||||||
if !act.isActive() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, level := range act.levels {
|
|
||||||
level.Advance(elapsed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (act *MapAct) Init(realm *MapRealm, actIndex int) {
|
|
||||||
act.realm = realm
|
|
||||||
act.levels = make(map[int]*MapLevel)
|
|
||||||
act.id = actIndex
|
|
||||||
|
|
||||||
actLevelRecords := d2datadict.GetLevelDetailsByActId(actIndex)
|
|
||||||
|
|
||||||
log.Printf("Initializing Act %d", actIndex)
|
|
||||||
for _, record := range actLevelRecords {
|
|
||||||
level := &MapLevel{}
|
|
||||||
levelId := record.Id
|
|
||||||
level.Init(act, levelId)
|
|
||||||
act.levels[levelId] = level
|
|
||||||
}
|
|
||||||
|
|
||||||
act.GenerateTown() // ensures that starting point is known for first player
|
|
||||||
}
|
|
||||||
|
|
||||||
func (act *MapAct) GenerateTown() {
|
|
||||||
townId := d2datadict.GetFirstLevelIdByActId(act.id)
|
|
||||||
act.levels[townId].GenerateMap()
|
|
||||||
}
|
|
||||||
|
|
||||||
func (act *MapAct) GenerateMap(levelId int) {
|
|
||||||
log.Printf("Generating map in Act %d", act.id)
|
|
||||||
act.levels[levelId].GenerateMap()
|
|
||||||
}
|
|
@ -24,7 +24,7 @@ type MapEngine struct {
|
|||||||
entities []d2mapentity.MapEntity // Entities on the map
|
entities []d2mapentity.MapEntity // Entities on the map
|
||||||
tiles []d2ds1.TileRecord // The map tiles
|
tiles []d2ds1.TileRecord // The map tiles
|
||||||
size d2common.Size // The size of the map, in tiles
|
size d2common.Size // The size of the map, in tiles
|
||||||
levelType *d2datadict.LevelTypeRecord // The level type of this map
|
levelType d2datadict.LevelTypeRecord // The level type of this map
|
||||||
dt1TileData []d2dt1.Tile // The DT1 tile data
|
dt1TileData []d2dt1.Tile // The DT1 tile data
|
||||||
walkMesh []d2common.PathTile // The walk mesh
|
walkMesh []d2common.PathTile // The walk mesh
|
||||||
startSubTileX int // The starting X position
|
startSubTileX int // The starting X position
|
||||||
@ -114,7 +114,7 @@ func (m *MapEngine) FindTile(style, sequence, tileType int32) d2dt1.Tile {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Returns the level type of this map
|
// Returns the level type of this map
|
||||||
func (m *MapEngine) LevelType() *d2datadict.LevelTypeRecord {
|
func (m *MapEngine) LevelType() d2datadict.LevelTypeRecord {
|
||||||
return m.levelType
|
return m.levelType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +0,0 @@
|
|||||||
package d2mapengine
|
|
||||||
|
|
||||||
type MapGenerator interface {
|
|
||||||
init(seed int64, level *MapLevel, engine *MapEngine)
|
|
||||||
generate()
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
package d2mapengine
|
|
||||||
|
|
||||||
type MapGeneratorMaze struct {
|
|
||||||
seed int64
|
|
||||||
level *MapLevel
|
|
||||||
engine *MapEngine
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MapGeneratorMaze) init(s int64, l *MapLevel, e *MapEngine) {
|
|
||||||
m.seed = s
|
|
||||||
m.level = l
|
|
||||||
m.engine = e
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MapGeneratorMaze) generate() {}
|
|
@ -1,66 +0,0 @@
|
|||||||
package d2mapengine
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
"math/rand"
|
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapstamp"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MapGeneratorPreset struct {
|
|
||||||
seed int64
|
|
||||||
level *MapLevel
|
|
||||||
engine *MapEngine
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MapGeneratorPreset) init(s int64, l *MapLevel, e *MapEngine) {
|
|
||||||
m.seed = s
|
|
||||||
m.level = l
|
|
||||||
m.engine = e
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MapGeneratorPreset) generate() {
|
|
||||||
rand.Seed(m.seed)
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////// FIXME
|
|
||||||
// TODO: we need to set the difficulty level of the realm in order to pull
|
|
||||||
// the right data from level details. testing this for now with normal diff
|
|
||||||
// NOTE: we would be setting difficulty level in the realm when a host
|
|
||||||
// is connected (the first player)
|
|
||||||
diffTestKey := "Normal"
|
|
||||||
m.level.act.realm.difficulty = d2datadict.DifficultyLevels[diffTestKey] // hack
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
difficulty := m.level.act.realm.difficulty
|
|
||||||
details := m.level.details
|
|
||||||
|
|
||||||
tileW, tileH := 0, 0
|
|
||||||
switch difficulty.Name {
|
|
||||||
case "Normal":
|
|
||||||
tileW = details.SizeXNormal
|
|
||||||
tileH = details.SizeYNormal
|
|
||||||
case "Nightmare":
|
|
||||||
tileW = details.SizeXNightmare
|
|
||||||
tileH = details.SizeYNightmare
|
|
||||||
case "Hell":
|
|
||||||
tileW = details.SizeXHell
|
|
||||||
tileH = details.SizeYHell
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: we shouldn't need to cast this to a RegionIdType
|
|
||||||
// In the long run, we aren't going to be using hardcoded enumerations
|
|
||||||
// we had initially made a list of them for testing, but not necessary now
|
|
||||||
levelTypeId := d2enum.RegionIdType(m.level.details.LevelType)
|
|
||||||
levelPresetId := m.level.preset.DefinitionId
|
|
||||||
|
|
||||||
m.engine.ResetMap(levelTypeId, tileW+1, tileH+1)
|
|
||||||
m.engine.levelType = m.level.types
|
|
||||||
|
|
||||||
stamp := d2mapstamp.LoadStamp(levelTypeId, levelPresetId, -1)
|
|
||||||
stampRegionPath := stamp.RegionPath()
|
|
||||||
log.Printf("Region Path: %s", stampRegionPath)
|
|
||||||
|
|
||||||
m.engine.PlaceStamp(stamp, 0, 0)
|
|
||||||
}
|
|
@ -1,15 +0,0 @@
|
|||||||
package d2mapengine
|
|
||||||
|
|
||||||
type MapGeneratorWilderness struct {
|
|
||||||
seed int64
|
|
||||||
level *MapLevel
|
|
||||||
engine *MapEngine
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MapGeneratorWilderness) init(s int64, l *MapLevel, e *MapEngine) {
|
|
||||||
m.seed = s
|
|
||||||
m.level = l
|
|
||||||
m.engine = e
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *MapGeneratorWilderness) generate() {}
|
|
@ -1,79 +0,0 @@
|
|||||||
package d2mapengine
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
|
||||||
)
|
|
||||||
|
|
||||||
type MapLevel struct {
|
|
||||||
act *MapAct
|
|
||||||
details *d2datadict.LevelDetailsRecord
|
|
||||||
preset *d2datadict.LevelPresetRecord
|
|
||||||
warps []*d2datadict.LevelWarpRecord
|
|
||||||
substitutions *d2datadict.LevelSubstitutionRecord
|
|
||||||
types *d2datadict.LevelTypeRecord
|
|
||||||
generator MapGenerator
|
|
||||||
mapEngine *MapEngine
|
|
||||||
isInit bool
|
|
||||||
isGenerated bool
|
|
||||||
}
|
|
||||||
|
|
||||||
func (level *MapLevel) isActive() bool {
|
|
||||||
// TODO: a level is active only if there is a player in the level
|
|
||||||
// or in an adjacent level
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func (level *MapLevel) Advance(elapsed float64) {
|
|
||||||
if !level.isActive() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
level.mapEngine.Advance(elapsed)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (level *MapLevel) Init(act *MapAct, levelId int) {
|
|
||||||
if level.isInit {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if levelId < 1 {
|
|
||||||
levelId = 1 // there is a Nonetype map at index 0 in levels.txt
|
|
||||||
}
|
|
||||||
level.act = act
|
|
||||||
level.details = d2datadict.GetLevelDetailsByLevelId(levelId)
|
|
||||||
level.preset = d2datadict.GetLevelPresetByLevelId(levelId)
|
|
||||||
level.warps = d2datadict.GetLevelWarpsByLevelId(levelId)
|
|
||||||
level.substitutions = d2datadict.LevelSubstitutions[level.details.SubType]
|
|
||||||
level.types = d2datadict.LevelTypes[d2enum.RegionIdType(level.details.LevelType)]
|
|
||||||
level.isInit = true
|
|
||||||
level.mapEngine = &MapEngine{}
|
|
||||||
level.mapEngine.seed = level.act.realm.seed
|
|
||||||
|
|
||||||
switch level.details.LevelGenerationType {
|
|
||||||
case d2enum.LevelTypeNone:
|
|
||||||
level.generator = nil
|
|
||||||
case d2enum.LevelTypeRandomMaze:
|
|
||||||
level.generator = &MapGeneratorMaze{}
|
|
||||||
case d2enum.LevelTypeWilderness:
|
|
||||||
level.generator = &MapGeneratorWilderness{}
|
|
||||||
case d2enum.LevelTypePreset:
|
|
||||||
level.generator = &MapGeneratorPreset{}
|
|
||||||
}
|
|
||||||
|
|
||||||
seed := act.realm.seed
|
|
||||||
if level.generator != nil {
|
|
||||||
log.Printf("Initializing Level: %s", level.details.Name)
|
|
||||||
level.generator.init(seed, level, level.mapEngine)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (level *MapLevel) GenerateMap() {
|
|
||||||
if level.isGenerated {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Printf("Generating Level: %s", level.details.Name)
|
|
||||||
level.generator.generate()
|
|
||||||
level.mapEngine.RegenerateWalkPaths()
|
|
||||||
level.isGenerated = true
|
|
||||||
}
|
|
@ -1,114 +0,0 @@
|
|||||||
package d2mapengine
|
|
||||||
|
|
||||||
import (
|
|
||||||
"log"
|
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
|
||||||
)
|
|
||||||
|
|
||||||
/*
|
|
||||||
A MapRealm represents the state of the maps/levels/quests for a server
|
|
||||||
|
|
||||||
A MapRealm has MapActs
|
|
||||||
A MapAct has MapLevels
|
|
||||||
A MapLevel has:
|
|
||||||
a MapEngine
|
|
||||||
a MapGenerator for the level
|
|
||||||
data records from the txt files for the level
|
|
||||||
|
|
||||||
The MapRealm is created by the game server
|
|
||||||
|
|
||||||
The first player to connect to the realm becomes the host
|
|
||||||
The host determines the difficulty and which quests are completed
|
|
||||||
|
|
||||||
The Realm, Acts, and Levels do not advance unless they are `active`
|
|
||||||
Nothing happens in a realm unless it is active
|
|
||||||
Levels do not generate maps until the level becomes `active`
|
|
||||||
|
|
||||||
A Level is active if a player is within it OR in an adjacent level
|
|
||||||
An Act is active if one of its levels is active
|
|
||||||
The Realm is active if and only if one of its Acts is active
|
|
||||||
*/
|
|
||||||
type MapRealm struct {
|
|
||||||
seed int64
|
|
||||||
difficulty *d2datadict.DifficultyLevelRecord
|
|
||||||
acts map[int]*MapAct
|
|
||||||
players map[string]string
|
|
||||||
host string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if the realm is in an active state
|
|
||||||
func (realm *MapRealm) isActive() bool {
|
|
||||||
return realm.hasActiveActs()
|
|
||||||
}
|
|
||||||
|
|
||||||
// Checks if there is an active act
|
|
||||||
func (realm *MapRealm) hasActiveActs() bool {
|
|
||||||
for _, act := range realm.acts {
|
|
||||||
if act.isActive() {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Advances the realm, which advances the acts, which advances the levels...
|
|
||||||
func (realm *MapRealm) Advance(elapsed float64) {
|
|
||||||
if !realm.isActive() {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, act := range realm.acts {
|
|
||||||
act.Advance(elapsed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sets the host of the realm, which determines quest availability for players
|
|
||||||
func (realm *MapRealm) SetHost(id string) {
|
|
||||||
if player, found := realm.players[id]; found {
|
|
||||||
realm.host = player
|
|
||||||
log.Printf("Host is now %s", id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds a player to the realm
|
|
||||||
func (realm *MapRealm) AddPlayer(id string, actId int) {
|
|
||||||
realm.players[id] = id
|
|
||||||
if realm.host == "" {
|
|
||||||
realm.SetHost(id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removes a player from the realm
|
|
||||||
func (realm *MapRealm) RemovePlayer(id string) {
|
|
||||||
delete(realm.players, id)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize the realm
|
|
||||||
func (realm *MapRealm) Init(seed int64) {
|
|
||||||
// realm.playerStates = make(map[string]*d2mapentitiy.Player)
|
|
||||||
|
|
||||||
log.Printf("Initializing Realm...")
|
|
||||||
realm.seed = seed
|
|
||||||
actIds := d2datadict.GetActIds()
|
|
||||||
realm.acts = make(map[int]*MapAct)
|
|
||||||
realm.players = make(map[string]string)
|
|
||||||
|
|
||||||
for _, actId := range actIds {
|
|
||||||
act := &MapAct{}
|
|
||||||
realm.acts[actId] = act
|
|
||||||
|
|
||||||
act.Init(realm, actId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (realm *MapRealm) GenerateMap(actId, levelId int) {
|
|
||||||
realm.acts[actId].GenerateMap(levelId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (realm *MapRealm) GetMapEngine(actId, levelId int) *MapEngine {
|
|
||||||
return realm.acts[actId].levels[levelId].mapEngine
|
|
||||||
}
|
|
||||||
|
|
||||||
func (realm *MapRealm) GetFirstActLevelId(actId int) int {
|
|
||||||
return d2datadict.GetFirstLevelIdByActId(actId)
|
|
||||||
}
|
|
@ -34,7 +34,7 @@ type Player struct {
|
|||||||
var baseWalkSpeed = 6.0
|
var baseWalkSpeed = 6.0
|
||||||
var baseRunSpeed = 9.0
|
var baseRunSpeed = 9.0
|
||||||
|
|
||||||
func CreatePlayer(id, name string, ActId, LevelId, x, y, direction int, heroType d2enum.Hero, stats d2hero.HeroStatsState, equipment d2inventory.CharacterEquipment) *Player {
|
func CreatePlayer(id, name string, x, y int, direction int, heroType d2enum.Hero, stats d2hero.HeroStatsState, equipment d2inventory.CharacterEquipment) *Player {
|
||||||
object := &d2datadict.ObjectLookupRecord{
|
object := &d2datadict.ObjectLookupRecord{
|
||||||
Mode: d2enum.AnimationModePlayerTownNeutral.String(),
|
Mode: d2enum.AnimationModePlayerTownNeutral.String(),
|
||||||
Base: "/data/global/chars",
|
Base: "/data/global/chars",
|
||||||
|
@ -26,7 +26,7 @@ func loadPreset(mapEngine *d2mapengine.MapEngine, id, index int) *d2mapstamp.Sta
|
|||||||
func GenerateAct1Overworld(mapEngine *d2mapengine.MapEngine) {
|
func GenerateAct1Overworld(mapEngine *d2mapengine.MapEngine) {
|
||||||
|
|
||||||
rand.Seed(mapEngine.Seed())
|
rand.Seed(mapEngine.Seed())
|
||||||
wilderness1Details := d2datadict.GetLevelDetailsByLevelId(2)
|
wilderness1Details := d2datadict.GetLevelDetails(2)
|
||||||
mapEngine.ResetMap(d2enum.RegionAct1Town, 150, 150)
|
mapEngine.ResetMap(d2enum.RegionAct1Town, 150, 150)
|
||||||
mapWidth := mapEngine.Size().Width
|
mapWidth := mapEngine.Size().Width
|
||||||
mapHeight := mapEngine.Size().Height
|
mapHeight := mapEngine.Size().Height
|
||||||
@ -69,7 +69,7 @@ func GenerateAct1Overworld(mapEngine *d2mapengine.MapEngine) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func generateWilderness1TownEast(mapEngine *d2mapengine.MapEngine, startX, startY int) {
|
func generateWilderness1TownEast(mapEngine *d2mapengine.MapEngine, startX, startY int) {
|
||||||
levelDetails := d2datadict.GetLevelDetailsByLevelId(2)
|
levelDetails := d2datadict.GetLevelDetails(2)
|
||||||
|
|
||||||
fenceNorthStamp := []*d2mapstamp.Stamp{
|
fenceNorthStamp := []*d2mapstamp.Stamp{
|
||||||
loadPreset(mapEngine, d2wilderness.TreeBorderNorth, 0),
|
loadPreset(mapEngine, d2wilderness.TreeBorderNorth, 0),
|
||||||
@ -131,7 +131,7 @@ func generateWilderness1TownEast(mapEngine *d2mapengine.MapEngine, startX, start
|
|||||||
}
|
}
|
||||||
|
|
||||||
func generateWilderness1TownSouth(mapEngine *d2mapengine.MapEngine, startX, startY int) {
|
func generateWilderness1TownSouth(mapEngine *d2mapengine.MapEngine, startX, startY int) {
|
||||||
levelDetails := d2datadict.GetLevelDetailsByLevelId(2)
|
levelDetails := d2datadict.GetLevelDetails(2)
|
||||||
|
|
||||||
fenceNorthStamp := []*d2mapstamp.Stamp{
|
fenceNorthStamp := []*d2mapstamp.Stamp{
|
||||||
loadPreset(mapEngine, d2wilderness.TreeBorderNorth, 0),
|
loadPreset(mapEngine, d2wilderness.TreeBorderNorth, 0),
|
||||||
@ -184,7 +184,7 @@ func generateWilderness1TownSouth(mapEngine *d2mapengine.MapEngine, startX, star
|
|||||||
}
|
}
|
||||||
|
|
||||||
func generateWilderness1TownWest(mapEngine *d2mapengine.MapEngine, startX, startY int) {
|
func generateWilderness1TownWest(mapEngine *d2mapengine.MapEngine, startX, startY int) {
|
||||||
levelDetails := d2datadict.GetLevelDetailsByLevelId(2)
|
levelDetails := d2datadict.GetLevelDetails(2)
|
||||||
|
|
||||||
fenceEastEdge := loadPreset(mapEngine, d2wilderness.TreeBoxSouthWest, 0)
|
fenceEastEdge := loadPreset(mapEngine, d2wilderness.TreeBoxSouthWest, 0)
|
||||||
fenceNorthWestStamp := loadPreset(mapEngine, d2wilderness.TreeBorderNorthWest, 0)
|
fenceNorthWestStamp := loadPreset(mapEngine, d2wilderness.TreeBorderNorthWest, 0)
|
||||||
@ -250,7 +250,7 @@ func generateWilderness1TownWest(mapEngine *d2mapengine.MapEngine, startX, start
|
|||||||
}
|
}
|
||||||
|
|
||||||
func generateWilderness1Contents(mapEngine *d2mapengine.MapEngine, rect d2common.Rectangle) {
|
func generateWilderness1Contents(mapEngine *d2mapengine.MapEngine, rect d2common.Rectangle) {
|
||||||
levelDetails := d2datadict.GetLevelDetailsByLevelId(2)
|
levelDetails := d2datadict.GetLevelDetails(2)
|
||||||
|
|
||||||
denOfEvil := loadPreset(mapEngine, d2wilderness.DenOfEvilEntrance, 0)
|
denOfEvil := loadPreset(mapEngine, d2wilderness.DenOfEvilEntrance, 0)
|
||||||
denOfEvilLoc := d2common.Point{
|
denOfEvilLoc := d2common.Point{
|
||||||
|
@ -19,8 +19,8 @@ import (
|
|||||||
// Represents a pre-fabricated map stamp that can be placed on a map
|
// Represents a pre-fabricated map stamp that can be placed on a map
|
||||||
type Stamp struct {
|
type Stamp struct {
|
||||||
regionPath string // The file path of the region
|
regionPath string // The file path of the region
|
||||||
levelType *d2datadict.LevelTypeRecord // The level type id for this stamp
|
levelType d2datadict.LevelTypeRecord // The level type id for this stamp
|
||||||
levelPreset *d2datadict.LevelPresetRecord // The level preset id for this stamp
|
levelPreset d2datadict.LevelPresetRecord // The level preset id for this stamp
|
||||||
tiles []d2dt1.Tile // The tiles contained on this stamp
|
tiles []d2dt1.Tile // The tiles contained on this stamp
|
||||||
ds1 *d2ds1.DS1 // The backing DS1 file for this stamp
|
ds1 *d2ds1.DS1 // The backing DS1 file for this stamp
|
||||||
}
|
}
|
||||||
@ -83,12 +83,12 @@ func (mr *Stamp) Size() d2common.Size {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Gets the level preset id
|
// Gets the level preset id
|
||||||
func (mr *Stamp) LevelPreset() *d2datadict.LevelPresetRecord {
|
func (mr *Stamp) LevelPreset() d2datadict.LevelPresetRecord {
|
||||||
return mr.levelPreset
|
return mr.levelPreset
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the level type id
|
// Returns the level type id
|
||||||
func (mr *Stamp) LevelType() *d2datadict.LevelTypeRecord {
|
func (mr *Stamp) LevelType() d2datadict.LevelTypeRecord {
|
||||||
return mr.levelType
|
return mr.levelType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -9,7 +9,6 @@ import (
|
|||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio"
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio"
|
||||||
@ -163,27 +162,11 @@ func (v *CharacterSelect) updateCharacterBoxes() {
|
|||||||
v.characterNameLabel[i].SetText(v.gameStates[idx].HeroName)
|
v.characterNameLabel[i].SetText(v.gameStates[idx].HeroName)
|
||||||
v.characterStatsLabel[i].SetText("Level 1 " + v.gameStates[idx].HeroType.String())
|
v.characterStatsLabel[i].SetText("Level 1 " + v.gameStates[idx].HeroType.String())
|
||||||
v.characterExpLabel[i].SetText(expText)
|
v.characterExpLabel[i].SetText(expText)
|
||||||
|
|
||||||
playerId := ""
|
|
||||||
playerName := ""
|
|
||||||
actId := d2datadict.GetActIds()[0]
|
|
||||||
levelId := d2datadict.GetFirstLevelIdByActId(actId)
|
|
||||||
x, y := 0, 0
|
|
||||||
dir := 0
|
|
||||||
heroType := v.gameStates[idx].HeroType
|
|
||||||
heroStats := *v.gameStates[idx].Stats
|
|
||||||
heroEquipment := d2inventory.HeroObjects[v.gameStates[idx].HeroType]
|
|
||||||
// TODO: Generate or load the object from the actual player data...
|
// TODO: Generate or load the object from the actual player data...
|
||||||
v.characterImage[i] = d2mapentity.CreatePlayer(
|
v.characterImage[i] = d2mapentity.CreatePlayer("", "", 0, 0, 0,
|
||||||
playerId,
|
v.gameStates[idx].HeroType,
|
||||||
playerName,
|
*v.gameStates[idx].Stats,
|
||||||
actId,
|
d2inventory.HeroObjects[v.gameStates[idx].HeroType],
|
||||||
levelId,
|
|
||||||
x, y,
|
|
||||||
dir,
|
|
||||||
heroType,
|
|
||||||
heroStats,
|
|
||||||
heroEquipment,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ import (
|
|||||||
|
|
||||||
type Game struct {
|
type Game struct {
|
||||||
gameClient *d2client.GameClient
|
gameClient *d2client.GameClient
|
||||||
MapRenderer *d2maprenderer.MapRenderer
|
mapRenderer *d2maprenderer.MapRenderer
|
||||||
gameControls *d2player.GameControls // TODO: Hack
|
gameControls *d2player.GameControls // TODO: Hack
|
||||||
localPlayer *d2mapentity.Player
|
localPlayer *d2mapentity.Player
|
||||||
lastRegionType d2enum.RegionIdType
|
lastRegionType d2enum.RegionIdType
|
||||||
@ -35,7 +35,7 @@ func CreateGame(gameClient *d2client.GameClient) *Game {
|
|||||||
localPlayer: nil,
|
localPlayer: nil,
|
||||||
lastRegionType: d2enum.RegionNone,
|
lastRegionType: d2enum.RegionNone,
|
||||||
ticksSinceLevelCheck: 0,
|
ticksSinceLevelCheck: 0,
|
||||||
MapRenderer: d2maprenderer.CreateMapRenderer(gameClient.MapEngine),
|
mapRenderer: d2maprenderer.CreateMapRenderer(gameClient.MapEngine),
|
||||||
escapeMenu: NewEscapeMenu(),
|
escapeMenu: NewEscapeMenu(),
|
||||||
}
|
}
|
||||||
result.escapeMenu.OnLoad()
|
result.escapeMenu.OnLoad()
|
||||||
@ -56,13 +56,11 @@ func (v *Game) OnUnload() error {
|
|||||||
func (v *Game) Render(screen d2render.Surface) error {
|
func (v *Game) Render(screen d2render.Surface) error {
|
||||||
if v.gameClient.RegenMap {
|
if v.gameClient.RegenMap {
|
||||||
v.gameClient.RegenMap = false
|
v.gameClient.RegenMap = false
|
||||||
v.MapRenderer.RegenerateTileCache()
|
v.mapRenderer.RegenerateTileCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
if v.MapRenderer != nil {
|
|
||||||
screen.Clear(color.Black)
|
screen.Clear(color.Black)
|
||||||
v.MapRenderer.Render(screen)
|
v.mapRenderer.Render(screen)
|
||||||
}
|
|
||||||
|
|
||||||
if v.gameControls != nil {
|
if v.gameControls != nil {
|
||||||
v.gameControls.Render(screen)
|
v.gameControls.Render(screen)
|
||||||
@ -71,7 +69,7 @@ func (v *Game) Render(screen d2render.Surface) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var zoneTextDuration = 2.0 // seconds
|
var hideZoneTextAfterSeconds = 2.0
|
||||||
|
|
||||||
func (v *Game) Advance(tickTime float64) error {
|
func (v *Game) Advance(tickTime float64) error {
|
||||||
if (v.escapeMenu != nil && !v.escapeMenu.IsOpen()) || len(v.gameClient.Players) != 1 {
|
if (v.escapeMenu != nil && !v.escapeMenu.IsOpen()) || len(v.gameClient.Players) != 1 {
|
||||||
@ -98,19 +96,11 @@ func (v *Game) Advance(tickTime float64) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// skip showing zone change text the first time we enter the world
|
// skip showing zone change text the first time we enter the world
|
||||||
notNone := v.lastRegionType != d2enum.RegionNone
|
if v.lastRegionType != d2enum.RegionNone && v.lastRegionType != tile.RegionType {
|
||||||
differentTileType := v.lastRegionType != tile.RegionType
|
//TODO: Should not be using RegionType as an index - this will return incorrect LevelDetails record for most of the zones.
|
||||||
if notNone && differentTileType {
|
v.gameControls.SetZoneChangeText(fmt.Sprintf("Entering The %s", d2datadict.LevelDetails[int(tile.RegionType)].LevelDisplayName))
|
||||||
//TODO: Should not be using RegionType as an index - this
|
|
||||||
// will return incorrect LevelDetails record for most of the
|
|
||||||
// zones.
|
|
||||||
levelId := int(tile.RegionType)
|
|
||||||
levelDetails := d2datadict.LevelDetails[levelId]
|
|
||||||
str := "Entering The %s"
|
|
||||||
name := levelDetails.LevelDisplayName
|
|
||||||
v.gameControls.SetZoneChangeText(fmt.Sprintf(str, name))
|
|
||||||
v.gameControls.ShowZoneChangeText()
|
v.gameControls.ShowZoneChangeText()
|
||||||
v.gameControls.HideZoneChangeTextAfter(zoneTextDuration)
|
v.gameControls.HideZoneChangeTextAfter(hideZoneTextAfterSeconds)
|
||||||
}
|
}
|
||||||
v.lastRegionType = tile.RegionType
|
v.lastRegionType = tile.RegionType
|
||||||
}
|
}
|
||||||
@ -124,9 +114,7 @@ func (v *Game) Advance(tickTime float64) error {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
v.localPlayer = player
|
v.localPlayer = player
|
||||||
engine := v.gameClient.MapEngine
|
v.gameControls = d2player.NewGameControls(player, v.gameClient.MapEngine, v.mapRenderer, v)
|
||||||
renderer := v.MapRenderer
|
|
||||||
v.gameControls = d2player.NewGameControls(player, engine, renderer, v)
|
|
||||||
v.gameControls.Load()
|
v.gameControls.Load()
|
||||||
d2input.BindHandler(v.gameControls)
|
d2input.BindHandler(v.gameControls)
|
||||||
|
|
||||||
@ -136,16 +124,14 @@ func (v *Game) Advance(tickTime float64) error {
|
|||||||
|
|
||||||
// Update the camera to focus on the player
|
// Update the camera to focus on the player
|
||||||
if v.localPlayer != nil && !v.gameControls.FreeCam {
|
if v.localPlayer != nil && !v.gameControls.FreeCam {
|
||||||
wx, wy := v.localPlayer.LocationX/5, v.localPlayer.LocationY/5
|
rx, ry := v.mapRenderer.WorldToOrtho(v.localPlayer.LocationX/5, v.localPlayer.LocationY/5)
|
||||||
rx, ry := v.MapRenderer.WorldToOrtho(wx, wy)
|
v.mapRenderer.MoveCameraTo(rx, ry)
|
||||||
v.MapRenderer.MoveCameraTo(rx, ry)
|
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Game) OnPlayerMove(x2, y2 float64) {
|
func (v *Game) OnPlayerMove(x, y float64) {
|
||||||
id := v.gameClient.PlayerId
|
heroPosX := v.localPlayer.LocationX / 5.0
|
||||||
x1, y1 := v.localPlayer.LocationX/5.0, v.localPlayer.LocationY/5.0
|
heroPosY := v.localPlayer.LocationY / 5.0
|
||||||
movePacket := d2netpacket.CreateMovePlayerPacket(id, x1, y1, x2, y2)
|
v.gameClient.SendPacketToServer(d2netpacket.CreateMovePlayerPacket(v.gameClient.PlayerId, heroPosX, heroPosY, x, y))
|
||||||
v.gameClient.SendPacketToServer(movePacket)
|
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ type PlayerState struct {
|
|||||||
HeroType d2enum.Hero `json:"heroType"`
|
HeroType d2enum.Hero `json:"heroType"`
|
||||||
HeroLevel int `json:"heroLevel"`
|
HeroLevel int `json:"heroLevel"`
|
||||||
Act int `json:"act"`
|
Act int `json:"act"`
|
||||||
Level int `json:"actLevel"`
|
|
||||||
FilePath string `json:"-"`
|
FilePath string `json:"-"`
|
||||||
Equipment d2inventory.CharacterEquipment `json:"equipment"`
|
Equipment d2inventory.CharacterEquipment `json:"equipment"`
|
||||||
Stats *d2hero.HeroStatsState `json:"stats"`
|
Stats *d2hero.HeroStatsState `json:"stats"`
|
||||||
@ -84,8 +83,7 @@ func CreatePlayerState(heroName string, hero d2enum.Hero, classStats d2datadict.
|
|||||||
result := &PlayerState{
|
result := &PlayerState{
|
||||||
HeroName: heroName,
|
HeroName: heroName,
|
||||||
HeroType: hero,
|
HeroType: hero,
|
||||||
Act: 0,
|
Act: 1,
|
||||||
Level: 1,
|
|
||||||
Stats: d2hero.CreateHeroStatsState(hero, classStats, 1, 0),
|
Stats: d2hero.CreateHeroStatsState(hero, classStats, 1, 0),
|
||||||
Equipment: d2inventory.HeroObjects[hero],
|
Equipment: d2inventory.HeroObjects[hero],
|
||||||
FilePath: "",
|
FilePath: "",
|
||||||
|
@ -1,38 +0,0 @@
|
|||||||
package d2client
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine"
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity"
|
|
||||||
d2cct "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2localclient"
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2remoteclient"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Creates a connections to the server and returns a game client instance
|
|
||||||
func Create(connectionType d2cct.ClientConnectionType) (*GameClient, error) {
|
|
||||||
result := &GameClient{
|
|
||||||
// TODO: Mapgen - Needs levels.txt stuff
|
|
||||||
MapEngine: d2mapengine.CreateMapEngine(),
|
|
||||||
Players: make(map[string]*d2mapentity.Player),
|
|
||||||
connectionType: connectionType,
|
|
||||||
realm: &d2mapengine.MapRealm{},
|
|
||||||
}
|
|
||||||
|
|
||||||
switch connectionType {
|
|
||||||
case d2cct.LANClient:
|
|
||||||
result.clientConnection = d2remoteclient.Create()
|
|
||||||
case d2cct.LANServer:
|
|
||||||
openSocket := true
|
|
||||||
result.clientConnection = d2localclient.Create(openSocket)
|
|
||||||
case d2cct.Local:
|
|
||||||
dontOpenSocket := false
|
|
||||||
result.clientConnection = d2localclient.Create(dontOpenSocket)
|
|
||||||
default:
|
|
||||||
str := "unknown client connection type specified: %d"
|
|
||||||
return nil, fmt.Errorf(str, connectionType)
|
|
||||||
}
|
|
||||||
result.clientConnection.SetClientListener(result)
|
|
||||||
return result, nil
|
|
||||||
}
|
|
@ -1,139 +1,92 @@
|
|||||||
package d2client
|
package d2client
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
// "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapgen"
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapgen"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine"
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity"
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2maprenderer"
|
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
|
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
|
||||||
d2cct "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
|
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2localclient"
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2remoteclient"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
|
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GameClient struct {
|
type GameClient struct {
|
||||||
clientConnection ClientConnection
|
clientConnection ClientConnection
|
||||||
connectionType d2cct.ClientConnectionType
|
connectionType d2clientconnectiontype.ClientConnectionType
|
||||||
GameState *d2player.PlayerState
|
GameState *d2player.PlayerState
|
||||||
MapEngine *d2mapengine.MapEngine
|
MapEngine *d2mapengine.MapEngine
|
||||||
MapRenderer *d2maprenderer.MapRenderer
|
|
||||||
realm *d2mapengine.MapRealm
|
|
||||||
PlayerId string
|
PlayerId string
|
||||||
Players map[string]*d2mapentity.Player
|
Players map[string]*d2mapentity.Player
|
||||||
Seed int64
|
Seed int64
|
||||||
RegenMap bool
|
RegenMap bool
|
||||||
}
|
}
|
||||||
|
|
||||||
// Using the `clientConnection`, opens a connection and passes the savefile path
|
func Create(connectionType d2clientconnectiontype.ClientConnectionType) (*GameClient, error) {
|
||||||
|
result := &GameClient{
|
||||||
|
MapEngine: d2mapengine.CreateMapEngine(), // TODO: Mapgen - Needs levels.txt stuff
|
||||||
|
Players: make(map[string]*d2mapentity.Player),
|
||||||
|
connectionType: connectionType,
|
||||||
|
}
|
||||||
|
|
||||||
|
switch connectionType {
|
||||||
|
case d2clientconnectiontype.LANClient:
|
||||||
|
result.clientConnection = d2remoteclient.Create()
|
||||||
|
case d2clientconnectiontype.LANServer:
|
||||||
|
result.clientConnection = d2localclient.Create(true)
|
||||||
|
case d2clientconnectiontype.Local:
|
||||||
|
result.clientConnection = d2localclient.Create(false)
|
||||||
|
default:
|
||||||
|
return nil, fmt.Errorf("unknown client connection type specified: %d", connectionType)
|
||||||
|
}
|
||||||
|
result.clientConnection.SetClientListener(result)
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (g *GameClient) Open(connectionString string, saveFilePath string) error {
|
func (g *GameClient) Open(connectionString string, saveFilePath string) error {
|
||||||
return g.clientConnection.Open(connectionString, saveFilePath)
|
return g.clientConnection.Open(connectionString, saveFilePath)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Closes the `clientConnection`
|
|
||||||
func (g *GameClient) Close() error {
|
func (g *GameClient) Close() error {
|
||||||
return g.clientConnection.Close()
|
return g.clientConnection.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Closes the `clientConnection`
|
|
||||||
func (g *GameClient) Destroy() error {
|
func (g *GameClient) Destroy() error {
|
||||||
return g.clientConnection.Close()
|
return g.clientConnection.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Routes the incoming packets to the packet handlers
|
|
||||||
func (g *GameClient) OnPacketReceived(packet d2netpacket.NetPacket) error {
|
func (g *GameClient) OnPacketReceived(packet d2netpacket.NetPacket) error {
|
||||||
switch packet.PacketType {
|
switch packet.PacketType {
|
||||||
|
|
||||||
// UNSURE: should we be bubbling up errors from these handler calls?
|
|
||||||
case d2netpackettype.UpdateServerInfo:
|
|
||||||
g.handleUpdateServerInfo(packet)
|
|
||||||
|
|
||||||
case d2netpackettype.AddPlayer:
|
|
||||||
g.handleAddPlayer(packet)
|
|
||||||
|
|
||||||
case d2netpackettype.GenerateMap:
|
case d2netpackettype.GenerateMap:
|
||||||
g.handleGenerateMap(packet)
|
mapData := packet.PacketData.(d2netpacket.GenerateMapPacket)
|
||||||
|
switch mapData.RegionType {
|
||||||
case d2netpackettype.MovePlayer:
|
case d2enum.RegionAct1Town:
|
||||||
g.handleMovePlayer(packet)
|
d2mapgen.GenerateAct1Overworld(g.MapEngine)
|
||||||
|
|
||||||
case d2netpackettype.Ping:
|
|
||||||
g.handlePong(packet)
|
|
||||||
|
|
||||||
case d2netpackettype.ServerClosed:
|
|
||||||
g.handleServerClosed(packet)
|
|
||||||
|
|
||||||
default:
|
|
||||||
log.Fatalf("Invalid packet type: %d", packet.PacketType)
|
|
||||||
}
|
}
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Using the `clientConnection`, sends a packet to the server
|
|
||||||
func (g *GameClient) SendPacketToServer(packet d2netpacket.NetPacket) error {
|
|
||||||
return g.clientConnection.SendPacketToServer(packet)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GameClient) handleUpdateServerInfo(p d2netpacket.NetPacket) {
|
|
||||||
serverInfo := p.PacketData.(d2netpacket.UpdateServerInfoPacket)
|
|
||||||
seed := serverInfo.Seed
|
|
||||||
playerId := serverInfo.PlayerId
|
|
||||||
|
|
||||||
g.Seed = seed
|
|
||||||
g.realm.Init(seed)
|
|
||||||
g.PlayerId = playerId
|
|
||||||
|
|
||||||
log.Printf("Player id set to %s", playerId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GameClient) handleAddPlayer(p d2netpacket.NetPacket) {
|
|
||||||
player := p.PacketData.(d2netpacket.AddPlayerPacket)
|
|
||||||
levelId := g.realm.GetFirstActLevelId(player.Act)
|
|
||||||
g.MapEngine = g.realm.GetMapEngine(player.Act, levelId)
|
|
||||||
|
|
||||||
pId := player.Id
|
|
||||||
pName := player.Name
|
|
||||||
pAct := player.Act
|
|
||||||
pLvlId := levelId
|
|
||||||
pX := player.X
|
|
||||||
pY := player.Y
|
|
||||||
pDir := 0
|
|
||||||
pHero := player.HeroType
|
|
||||||
pStat := player.Stats
|
|
||||||
pEquip := player.Equipment
|
|
||||||
|
|
||||||
// UNSURE: maybe we should be passing a struct instead of all the vars?
|
|
||||||
newPlayer := d2mapentity.CreatePlayer(
|
|
||||||
pId, pName, pAct, pLvlId, pX, pY, pDir, pHero, pStat, pEquip,
|
|
||||||
)
|
|
||||||
|
|
||||||
g.Players[newPlayer.Id] = newPlayer
|
|
||||||
g.realm.AddPlayer(pId, pAct)
|
|
||||||
g.MapEngine.AddEntity(newPlayer)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GameClient) handleGenerateMap(p d2netpacket.NetPacket) {
|
|
||||||
mapData := p.PacketData.(d2netpacket.GenerateMapPacket)
|
|
||||||
g.realm.GenerateMap(mapData.ActId, mapData.LevelId)
|
|
||||||
engine := g.realm.GetMapEngine(mapData.ActId, mapData.LevelId)
|
|
||||||
g.MapRenderer = d2maprenderer.CreateMapRenderer(engine)
|
|
||||||
g.RegenMap = true
|
g.RegenMap = true
|
||||||
}
|
case d2netpackettype.UpdateServerInfo:
|
||||||
|
serverInfo := packet.PacketData.(d2netpacket.UpdateServerInfoPacket)
|
||||||
func (g *GameClient) handleMovePlayer(p d2netpacket.NetPacket) {
|
g.MapEngine.SetSeed(serverInfo.Seed)
|
||||||
movePlayer := p.PacketData.(d2netpacket.MovePlayerPacket)
|
g.PlayerId = serverInfo.PlayerId
|
||||||
|
g.Seed = serverInfo.Seed
|
||||||
|
log.Printf("Player id set to %s", serverInfo.PlayerId)
|
||||||
|
case d2netpackettype.AddPlayer:
|
||||||
|
player := packet.PacketData.(d2netpacket.AddPlayerPacket)
|
||||||
|
newPlayer := d2mapentity.CreatePlayer(player.Id, player.Name, player.X, player.Y, 0, player.HeroType, player.Stats, player.Equipment)
|
||||||
|
g.Players[newPlayer.Id] = newPlayer
|
||||||
|
g.MapEngine.AddEntity(newPlayer)
|
||||||
|
case d2netpackettype.MovePlayer:
|
||||||
|
movePlayer := packet.PacketData.(d2netpacket.MovePlayerPacket)
|
||||||
player := g.Players[movePlayer.PlayerId]
|
player := g.Players[movePlayer.PlayerId]
|
||||||
x1, y1 := movePlayer.StartX, movePlayer.StartY
|
path, _, _ := g.MapEngine.PathFind(movePlayer.StartX, movePlayer.StartY, movePlayer.DestX, movePlayer.DestY)
|
||||||
x2, y2 := movePlayer.DestX, movePlayer.DestY
|
|
||||||
|
|
||||||
path, _, _ := g.MapEngine.PathFind(x1, y1, x2, y2)
|
|
||||||
|
|
||||||
if len(path) > 0 {
|
if len(path) > 0 {
|
||||||
player.SetPath(path, func() {
|
player.SetPath(path, func() {
|
||||||
tile := g.MapEngine.TileAt(player.TileX, player.TileY)
|
tile := g.MapEngine.TileAt(player.TileX, player.TileY)
|
||||||
@ -150,15 +103,18 @@ func (g *GameClient) handleMovePlayer(p d2netpacket.NetPacket) {
|
|||||||
player.SetAnimationMode(player.GetAnimationMode().String())
|
player.SetAnimationMode(player.GetAnimationMode().String())
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
case d2netpackettype.Ping:
|
||||||
|
g.clientConnection.SendPacketToServer(d2netpacket.CreatePongPacket(g.PlayerId))
|
||||||
func (g *GameClient) handlePong(p d2netpacket.NetPacket) {
|
case d2netpackettype.ServerClosed:
|
||||||
pong := d2netpacket.CreatePongPacket(g.PlayerId)
|
|
||||||
g.clientConnection.SendPacketToServer(pong)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (g *GameClient) handleServerClosed(p d2netpacket.NetPacket) {
|
|
||||||
// TODO: Need to be tied into a character save and exit
|
// TODO: Need to be tied into a character save and exit
|
||||||
log.Print("Server has been closed")
|
log.Print("Server has been closed")
|
||||||
os.Exit(0)
|
os.Exit(0)
|
||||||
|
default:
|
||||||
|
log.Fatalf("Invalid packet type: %d", packet.PacketType)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GameClient) SendPacketToServer(packet d2netpacket.NetPacket) error {
|
||||||
|
return g.clientConnection.SendPacketToServer(packet)
|
||||||
}
|
}
|
||||||
|
@ -12,19 +12,17 @@ type AddPlayerPacket struct {
|
|||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
X int `json:"x"`
|
X int `json:"x"`
|
||||||
Y int `json:"y"`
|
Y int `json:"y"`
|
||||||
Act int `json:"act"`
|
|
||||||
HeroType d2enum.Hero `json:"hero"`
|
HeroType d2enum.Hero `json:"hero"`
|
||||||
Equipment d2inventory.CharacterEquipment `json:"equipment"`
|
Equipment d2inventory.CharacterEquipment `json:"equipment"`
|
||||||
Stats d2hero.HeroStatsState `json:"heroStats"`
|
Stats d2hero.HeroStatsState `json:"heroStats"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateAddPlayerPacket(id, name string, act, x, y int, heroType d2enum.Hero, stats d2hero.HeroStatsState, equipment d2inventory.CharacterEquipment) NetPacket {
|
func CreateAddPlayerPacket(id, name string, x, y int, heroType d2enum.Hero, stats d2hero.HeroStatsState, equipment d2inventory.CharacterEquipment) NetPacket {
|
||||||
return NetPacket{
|
return NetPacket{
|
||||||
PacketType: d2netpackettype.AddPlayer,
|
PacketType: d2netpackettype.AddPlayer,
|
||||||
PacketData: AddPlayerPacket{
|
PacketData: AddPlayerPacket{
|
||||||
Id: id,
|
Id: id,
|
||||||
Name: name,
|
Name: name,
|
||||||
Act: act,
|
|
||||||
X: x,
|
X: x,
|
||||||
Y: y,
|
Y: y,
|
||||||
HeroType: heroType,
|
HeroType: heroType,
|
||||||
|
@ -1,20 +1,19 @@
|
|||||||
package d2netpacket
|
package d2netpacket
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GenerateMapPacket struct {
|
type GenerateMapPacket struct {
|
||||||
ActId int `json:"actId"`
|
RegionType d2enum.RegionIdType `json:"regionType"`
|
||||||
LevelId int `json:"levelId"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateGenerateMapPacket(actId, levelId int) NetPacket {
|
func CreateGenerateMapPacket(regionType d2enum.RegionIdType) NetPacket {
|
||||||
return NetPacket{
|
return NetPacket{
|
||||||
PacketType: d2netpackettype.GenerateMap,
|
PacketType: d2netpackettype.GenerateMap,
|
||||||
PacketData: GenerateMapPacket{
|
PacketData: GenerateMapPacket{
|
||||||
ActId: actId,
|
RegionType: regionType,
|
||||||
LevelId: levelId,
|
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,16 +8,13 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConnectionManager is responsible for cleanup up connections accepted by the
|
// ConnectionManager is responsible for cleanup up connections accepted by the game server. As the server communicates over
|
||||||
// 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
|
||||||
// UDP and is stateless we need to implement some loose state management via a
|
|
||||||
// ping/pong system. ConnectionManager also handles
|
|
||||||
// communication for graceful shutdowns.
|
// communication for graceful shutdowns.
|
||||||
//
|
//
|
||||||
// retries: # of attempts before the dropping the client
|
// retries: # of attempts before the dropping the client
|
||||||
// interval: How long to wait before each ping/pong test
|
// interval: How long to wait before each ping/pong test
|
||||||
// gameServer: The *GameServer is argument provided for the connection manager
|
// gameServer: The *GameServer is argument provided for the connection manager to watch over
|
||||||
// to watch over
|
|
||||||
// status: map of inflight ping/pong requests
|
// status: map of inflight ping/pong requests
|
||||||
type ConnectionManager struct {
|
type ConnectionManager struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
@ -53,8 +50,7 @@ func (c *ConnectionManager) Run() {
|
|||||||
func (c *ConnectionManager) checkPeers() {
|
func (c *ConnectionManager) checkPeers() {
|
||||||
for id, connection := range c.gameServer.clientConnections {
|
for id, connection := range c.gameServer.clientConnections {
|
||||||
if connection.GetConnectionType() != d2clientconnectiontype.Local {
|
if connection.GetConnectionType() != d2clientconnectiontype.Local {
|
||||||
pingPacket := d2netpacket.CreatePingPacket()
|
if err := connection.SendPacketToClient(d2netpacket.CreatePingPacket()); err != nil {
|
||||||
if err := connection.SendPacketToClient(pingPacket); err != nil {
|
|
||||||
log.Printf("Cannot ping client id: %s", id)
|
log.Printf("Cannot ping client id: %s", id)
|
||||||
}
|
}
|
||||||
c.RWMutex.Lock()
|
c.RWMutex.Lock()
|
||||||
@ -70,8 +66,7 @@ func (c *ConnectionManager) checkPeers() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recv simply resets the counter, acknowledging we have received a pong from
|
// Recv simply resets the counter, acknowledging we have received a pong from the client.
|
||||||
// the client.
|
|
||||||
func (c *ConnectionManager) Recv(id string) {
|
func (c *ConnectionManager) Recv(id string) {
|
||||||
c.status[id] = 0
|
c.status[id] = 0
|
||||||
}
|
}
|
||||||
@ -86,10 +81,8 @@ func (c *ConnectionManager) Drop(id string) {
|
|||||||
|
|
||||||
// Shutdown will notify all of the clients that the server has been shutdown.
|
// Shutdown will notify all of the clients that the server has been shutdown.
|
||||||
func (c *ConnectionManager) Shutdown() {
|
func (c *ConnectionManager) Shutdown() {
|
||||||
// TODO: Currently this will never actually get called as the go routines
|
// TODO: Currently this will never actually get called as the go routines are never signaled about the application termination.
|
||||||
// are never signaled about the application termination.
|
// Things can be done more cleanly once we have graceful exits however we still need to account for other OS Signals
|
||||||
// Things can be done more cleanly once we have graceful exits however we
|
|
||||||
// still need to account for other OS Signals
|
|
||||||
log.Print("Notifying clients server is shutting down...")
|
log.Print("Notifying clients server is shutting down...")
|
||||||
for _, connection := range c.gameServer.clientConnections {
|
for _, connection := range c.gameServer.clientConnections {
|
||||||
connection.SendPacketToClient(d2netpacket.CreateServerClosedPacket())
|
connection.SendPacketToClient(d2netpacket.CreateServerClosedPacket())
|
||||||
|
@ -1,243 +0,0 @@
|
|||||||
package d2server
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"compress/gzip"
|
|
||||||
"fmt"
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common"
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2config"
|
|
||||||
"io"
|
|
||||||
"log"
|
|
||||||
"net"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
// "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapgen"
|
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine"
|
|
||||||
|
|
||||||
// "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
|
|
||||||
packet "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
|
|
||||||
packettype "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2script"
|
|
||||||
"github.com/robertkrimen/otto"
|
|
||||||
)
|
|
||||||
|
|
||||||
var singletonServer *GameServer
|
|
||||||
|
|
||||||
func advance() {
|
|
||||||
now := d2common.Now()
|
|
||||||
elapsed := now - singletonServer.lastAdvance
|
|
||||||
singletonServer.realm.Advance(elapsed)
|
|
||||||
singletonServer.lastAdvance = now
|
|
||||||
}
|
|
||||||
|
|
||||||
func Create(openNetworkServer bool) {
|
|
||||||
log.Print("Creating GameServer")
|
|
||||||
if singletonServer != nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
config := d2config.Get()
|
|
||||||
maxConnections := config.MaxConnections
|
|
||||||
seed := time.Now().UnixNano()
|
|
||||||
|
|
||||||
singletonServer = &GameServer{
|
|
||||||
clientConnections: make(map[string]ClientConnection),
|
|
||||||
realm: &d2mapengine.MapRealm{},
|
|
||||||
scriptEngine: d2script.CreateScriptEngine(),
|
|
||||||
seed: seed,
|
|
||||||
maxClients: maxConnections,
|
|
||||||
lastAdvance: d2common.Now(),
|
|
||||||
}
|
|
||||||
|
|
||||||
singletonServer.realm.Init(seed)
|
|
||||||
singletonServer.manager = CreateConnectionManager(singletonServer)
|
|
||||||
|
|
||||||
// mapEngine := d2mapengine.CreateMapEngine()
|
|
||||||
// mapEngine.SetSeed(singletonServer.seed)
|
|
||||||
// TODO: Mapgen - Needs levels.txt stuff
|
|
||||||
// mapEngine.ResetMap(d2enum.RegionAct1Town, 100, 100)
|
|
||||||
// d2mapgen.GenerateAct1Overworld(mapEngine)
|
|
||||||
// singletonServer.mapEngines = append(singletonServer.mapEngines, mapEngine)
|
|
||||||
|
|
||||||
addScriptEngineFunctions()
|
|
||||||
|
|
||||||
if openNetworkServer {
|
|
||||||
createNetworkServer()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func addScriptEngineFunctions() {
|
|
||||||
singletonServer.scriptEngine.AddFunction("getMapEngines", ottoTestFunc)
|
|
||||||
}
|
|
||||||
|
|
||||||
func ottoTestFunc(call otto.FunctionCall) otto.Value {
|
|
||||||
val, err := singletonServer.scriptEngine.ToValue(singletonServer.realm)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Print(err.Error())
|
|
||||||
}
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
func createNetworkServer() {
|
|
||||||
s, err := net.ResolveUDPAddr("udp4", "0.0.0.0:6669")
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
singletonServer.udpConnection, err = net.ListenUDP("udp4", s)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
singletonServer.udpConnection.SetReadBuffer(4096)
|
|
||||||
}
|
|
||||||
|
|
||||||
func runNetworkServer() {
|
|
||||||
buffer := make([]byte, 4096)
|
|
||||||
srv := singletonServer
|
|
||||||
for srv.running {
|
|
||||||
advance()
|
|
||||||
|
|
||||||
_, addr, err := srv.udpConnection.ReadFromUDP(buffer)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("Socket error: %s\n", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
buff := bytes.NewBuffer(buffer)
|
|
||||||
packetTypeId, err := buff.ReadByte()
|
|
||||||
packetType := packettype.NetPacketType(packetTypeId)
|
|
||||||
reader, err := gzip.NewReader(buff)
|
|
||||||
sb := new(strings.Builder)
|
|
||||||
io.Copy(sb, reader)
|
|
||||||
stringData := sb.String()
|
|
||||||
|
|
||||||
switch packetType {
|
|
||||||
case packettype.PlayerConnectionRequest:
|
|
||||||
srv.handlePlayerConnRequest(addr, stringData)
|
|
||||||
case packettype.MovePlayer:
|
|
||||||
srv.handleMovePlayer(addr, stringData)
|
|
||||||
case packettype.Pong:
|
|
||||||
srv.handlePong(addr, stringData)
|
|
||||||
case packettype.ServerClosed:
|
|
||||||
srv.manager.Shutdown()
|
|
||||||
case packettype.PlayerDisconnectionNotification:
|
|
||||||
srv.handlePlayerDisconnectNotification(stringData)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Run() {
|
|
||||||
log.Print("Starting GameServer")
|
|
||||||
singletonServer.running = true
|
|
||||||
singletonServer.scriptEngine.RunScript("scripts/server/server.js")
|
|
||||||
if singletonServer.udpConnection != nil {
|
|
||||||
go runNetworkServer()
|
|
||||||
}
|
|
||||||
log.Print("Network server has been started")
|
|
||||||
}
|
|
||||||
|
|
||||||
func Stop() {
|
|
||||||
log.Print("Stopping GameServer")
|
|
||||||
singletonServer.running = false
|
|
||||||
if singletonServer.udpConnection != nil {
|
|
||||||
singletonServer.udpConnection.Close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func Destroy() {
|
|
||||||
if singletonServer == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
log.Print("Destroying GameServer")
|
|
||||||
Stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
func OnClientConnected(client ClientConnection) {
|
|
||||||
srv := singletonServer
|
|
||||||
realm := srv.realm
|
|
||||||
seed := srv.seed
|
|
||||||
state := client.GetPlayerState()
|
|
||||||
|
|
||||||
actId := state.Act
|
|
||||||
levelId := d2datadict.GetFirstLevelIdByActId(actId)
|
|
||||||
engine := realm.GetMapEngine(actId, levelId)
|
|
||||||
|
|
||||||
// params for AddPlayer packet, of new player
|
|
||||||
id := client.GetUniqueId()
|
|
||||||
|
|
||||||
name := state.HeroName
|
|
||||||
hero := state.HeroType
|
|
||||||
stats := *state.Stats
|
|
||||||
equip := state.Equipment
|
|
||||||
x, y := engine.GetStartPosition()
|
|
||||||
state.X = x
|
|
||||||
state.Y = y
|
|
||||||
|
|
||||||
infoPacket := packet.CreateUpdateServerInfoPacket(seed, id)
|
|
||||||
mapgenPacket := packet.CreateGenerateMapPacket(actId, levelId)
|
|
||||||
|
|
||||||
// UNSURE: maybe we should pass a struct instead of all of these args
|
|
||||||
addNew := packet.CreateAddPlayerPacket(
|
|
||||||
id, name, actId, int(x*5), int(y*5), hero, stats, equip,
|
|
||||||
)
|
|
||||||
|
|
||||||
srv.clientConnections[id] = client
|
|
||||||
|
|
||||||
client.SendPacketToClient(infoPacket)
|
|
||||||
client.SendPacketToClient(mapgenPacket)
|
|
||||||
|
|
||||||
log.Printf("Client connected with an id of %s", id)
|
|
||||||
realm.AddPlayer(id, state.Act)
|
|
||||||
|
|
||||||
// for each connection, send the AddPlayer packet for the new player
|
|
||||||
for _, connection := range srv.clientConnections {
|
|
||||||
conId := connection.GetUniqueId()
|
|
||||||
connection.SendPacketToClient(addNew)
|
|
||||||
|
|
||||||
if conId == id {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// send an AddPlayer for existing connections to the new player
|
|
||||||
conState := connection.GetPlayerState()
|
|
||||||
conActId := conState.Act
|
|
||||||
conName := conState.HeroName
|
|
||||||
conHero := conState.HeroType
|
|
||||||
conEquip := conState.Equipment
|
|
||||||
conStats := *conState.Stats
|
|
||||||
conX, conY := 0, 0
|
|
||||||
|
|
||||||
addExisting := packet.CreateAddPlayerPacket(
|
|
||||||
conId, conName, conActId, conX, conY, conHero, conStats, conEquip,
|
|
||||||
)
|
|
||||||
|
|
||||||
client.SendPacketToClient(addExisting)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func OnClientDisconnected(client ClientConnection) {
|
|
||||||
log.Printf("Client disconnected with an id of %s", client.GetUniqueId())
|
|
||||||
clientId := client.GetUniqueId()
|
|
||||||
delete(singletonServer.clientConnections, clientId)
|
|
||||||
singletonServer.realm.RemovePlayer(clientId)
|
|
||||||
}
|
|
||||||
|
|
||||||
func OnPacketReceived(client ClientConnection, p packet.NetPacket) error {
|
|
||||||
switch p.PacketType {
|
|
||||||
case packettype.MovePlayer:
|
|
||||||
// TODO: This needs to be verified on the server (here) before sending to other clients....
|
|
||||||
// TODO: Hacky, this should be updated in realtime ----------------
|
|
||||||
// TODO: Verify player id
|
|
||||||
playerState := singletonServer.clientConnections[client.GetUniqueId()].GetPlayerState()
|
|
||||||
playerState.X = p.PacketData.(packet.MovePlayerPacket).DestX
|
|
||||||
playerState.Y = p.PacketData.(packet.MovePlayerPacket).DestY
|
|
||||||
// ----------------------------------------------------------------
|
|
||||||
for _, player := range singletonServer.clientConnections {
|
|
||||||
player.SendPacketToClient(p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
@ -1,67 +1,209 @@
|
|||||||
package d2server
|
package d2server
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"compress/gzip"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapgen"
|
||||||
|
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine"
|
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine"
|
||||||
packet "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
|
|
||||||
packettype "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
||||||
d2udp "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2server/d2udpclientconnection"
|
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
||||||
|
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2server/d2udpclientconnection"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2script"
|
"github.com/OpenDiablo2/OpenDiablo2/d2script"
|
||||||
|
"github.com/robertkrimen/otto"
|
||||||
)
|
)
|
||||||
|
|
||||||
type GameServer struct {
|
type GameServer struct {
|
||||||
sync.RWMutex
|
sync.RWMutex
|
||||||
version string
|
|
||||||
clientConnections map[string]ClientConnection
|
clientConnections map[string]ClientConnection
|
||||||
manager *ConnectionManager
|
manager *ConnectionManager
|
||||||
realm *d2mapengine.MapRealm
|
mapEngines []*d2mapengine.MapEngine
|
||||||
scriptEngine *d2script.ScriptEngine
|
scriptEngine *d2script.ScriptEngine
|
||||||
udpConnection *net.UDPConn
|
udpConnection *net.UDPConn
|
||||||
seed int64
|
seed int64
|
||||||
running bool
|
running bool
|
||||||
maxClients int
|
|
||||||
lastAdvance float64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *GameServer) handlePlayerConnRequest(addr *net.UDPAddr, data string) {
|
var singletonServer *GameServer
|
||||||
packetData := &packet.PlayerConnectionRequestPacket{}
|
|
||||||
json.Unmarshal([]byte(data), packetData)
|
|
||||||
|
|
||||||
srvCon := srv.udpConnection
|
func Create(openNetworkServer bool) {
|
||||||
packetId := packetData.Id
|
log.Print("Creating GameServer")
|
||||||
clientCon := d2udp.CreateUDPClientConnection(srvCon, packetId, addr)
|
if singletonServer != nil {
|
||||||
|
return
|
||||||
state := packetData.PlayerState
|
|
||||||
clientCon.SetPlayerState(state)
|
|
||||||
OnClientConnected(clientCon)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (srv *GameServer) handleMovePlayer(addr *net.UDPAddr, data string) {
|
singletonServer = &GameServer{
|
||||||
packetData := &packet.MovePlayerPacket{}
|
clientConnections: make(map[string]ClientConnection),
|
||||||
json.Unmarshal([]byte(data), packetData)
|
mapEngines: make([]*d2mapengine.MapEngine, 0),
|
||||||
|
scriptEngine: d2script.CreateScriptEngine(),
|
||||||
|
seed: time.Now().UnixNano(),
|
||||||
|
}
|
||||||
|
|
||||||
netPacket := packet.NetPacket{
|
singletonServer.manager = CreateConnectionManager(singletonServer)
|
||||||
PacketType: packettype.MovePlayer,
|
|
||||||
|
mapEngine := d2mapengine.CreateMapEngine()
|
||||||
|
mapEngine.SetSeed(singletonServer.seed)
|
||||||
|
mapEngine.ResetMap(d2enum.RegionAct1Town, 100, 100) // TODO: Mapgen - Needs levels.txt stuff
|
||||||
|
d2mapgen.GenerateAct1Overworld(mapEngine)
|
||||||
|
singletonServer.mapEngines = append(singletonServer.mapEngines, mapEngine)
|
||||||
|
|
||||||
|
singletonServer.scriptEngine.AddFunction("getMapEngines", func(call otto.FunctionCall) otto.Value {
|
||||||
|
val, err := singletonServer.scriptEngine.ToValue(singletonServer.mapEngines)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Print(err.Error())
|
||||||
|
}
|
||||||
|
return val
|
||||||
|
})
|
||||||
|
|
||||||
|
if openNetworkServer {
|
||||||
|
createNetworkServer()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createNetworkServer() {
|
||||||
|
s, err := net.ResolveUDPAddr("udp4", "0.0.0.0:6669")
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
singletonServer.udpConnection, err = net.ListenUDP("udp4", s)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
singletonServer.udpConnection.SetReadBuffer(4096)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runNetworkServer() {
|
||||||
|
buffer := make([]byte, 4096)
|
||||||
|
for singletonServer.running {
|
||||||
|
_, addr, err := singletonServer.udpConnection.ReadFromUDP(buffer)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Socket error: %s\n", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
buff := bytes.NewBuffer(buffer)
|
||||||
|
packetTypeId, err := buff.ReadByte()
|
||||||
|
packetType := d2netpackettype.NetPacketType(packetTypeId)
|
||||||
|
reader, err := gzip.NewReader(buff)
|
||||||
|
sb := new(strings.Builder)
|
||||||
|
io.Copy(sb, reader)
|
||||||
|
stringData := sb.String()
|
||||||
|
switch packetType {
|
||||||
|
case d2netpackettype.PlayerConnectionRequest:
|
||||||
|
packetData := d2netpacket.PlayerConnectionRequestPacket{}
|
||||||
|
json.Unmarshal([]byte(stringData), &packetData)
|
||||||
|
clientConnection := d2udpclientconnection.CreateUDPClientConnection(singletonServer.udpConnection, packetData.Id, addr)
|
||||||
|
clientConnection.SetPlayerState(packetData.PlayerState)
|
||||||
|
OnClientConnected(clientConnection)
|
||||||
|
case d2netpackettype.MovePlayer:
|
||||||
|
packetData := d2netpacket.MovePlayerPacket{}
|
||||||
|
json.Unmarshal([]byte(stringData), &packetData)
|
||||||
|
netPacket := d2netpacket.NetPacket{
|
||||||
|
PacketType: packetType,
|
||||||
PacketData: packetData,
|
PacketData: packetData,
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, player := range srv.clientConnections {
|
for _, player := range singletonServer.clientConnections {
|
||||||
player.SendPacketToClient(netPacket)
|
player.SendPacketToClient(netPacket)
|
||||||
}
|
}
|
||||||
}
|
case d2netpackettype.Pong:
|
||||||
|
packetData := d2netpacket.PlayerConnectionRequestPacket{}
|
||||||
func (srv *GameServer) handlePong(addr *net.UDPAddr, data string) {
|
json.Unmarshal([]byte(stringData), &packetData)
|
||||||
packetData := packet.PlayerConnectionRequestPacket{}
|
singletonServer.manager.Recv(packetData.Id)
|
||||||
json.Unmarshal([]byte(data), &packetData)
|
case d2netpackettype.ServerClosed:
|
||||||
srv.manager.Recv(packetData.Id)
|
singletonServer.manager.Shutdown()
|
||||||
}
|
case d2netpackettype.PlayerDisconnectionNotification:
|
||||||
|
var packet d2netpacket.PlayerDisconnectRequestPacket
|
||||||
func (srv *GameServer) handlePlayerDisconnectNotification(data string) {
|
json.Unmarshal([]byte(stringData), &packet)
|
||||||
var packet packet.PlayerDisconnectRequestPacket
|
|
||||||
json.Unmarshal([]byte(data), &packet)
|
|
||||||
log.Printf("Received disconnect: %s", packet.Id)
|
log.Printf("Received disconnect: %s", packet.Id)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run() {
|
||||||
|
log.Print("Starting GameServer")
|
||||||
|
singletonServer.running = true
|
||||||
|
singletonServer.scriptEngine.RunScript("scripts/server/server.js")
|
||||||
|
if singletonServer.udpConnection != nil {
|
||||||
|
go runNetworkServer()
|
||||||
|
}
|
||||||
|
log.Print("Network server has been started")
|
||||||
|
}
|
||||||
|
|
||||||
|
func Stop() {
|
||||||
|
log.Print("Stopping GameServer")
|
||||||
|
singletonServer.running = false
|
||||||
|
if singletonServer.udpConnection != nil {
|
||||||
|
singletonServer.udpConnection.Close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Destroy() {
|
||||||
|
if singletonServer == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Print("Destroying GameServer")
|
||||||
|
Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func OnClientConnected(client ClientConnection) {
|
||||||
|
// Temporary position hack --------------------------------------------
|
||||||
|
sx, sy := singletonServer.mapEngines[0].GetStartPosition() // TODO: Another temporary hack
|
||||||
|
clientPlayerState := client.GetPlayerState()
|
||||||
|
clientPlayerState.X = sx
|
||||||
|
clientPlayerState.Y = sy
|
||||||
|
// --------------------------------------------------------------------
|
||||||
|
|
||||||
|
log.Printf("Client connected with an id of %s", client.GetUniqueId())
|
||||||
|
singletonServer.clientConnections[client.GetUniqueId()] = client
|
||||||
|
client.SendPacketToClient(d2netpacket.CreateUpdateServerInfoPacket(singletonServer.seed, client.GetUniqueId()))
|
||||||
|
client.SendPacketToClient(d2netpacket.CreateGenerateMapPacket(d2enum.RegionAct1Town))
|
||||||
|
|
||||||
|
playerState := client.GetPlayerState()
|
||||||
|
createPlayerPacket := d2netpacket.CreateAddPlayerPacket(client.GetUniqueId(), playerState.HeroName, int(sx*5)+3, int(sy*5)+3,
|
||||||
|
playerState.HeroType, *playerState.Stats, playerState.Equipment)
|
||||||
|
for _, connection := range singletonServer.clientConnections {
|
||||||
|
connection.SendPacketToClient(createPlayerPacket)
|
||||||
|
if connection.GetUniqueId() == client.GetUniqueId() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
conPlayerState := connection.GetPlayerState()
|
||||||
|
client.SendPacketToClient(d2netpacket.CreateAddPlayerPacket(connection.GetUniqueId(), conPlayerState.HeroName,
|
||||||
|
int(conPlayerState.X*5)+3, int(conPlayerState.Y*5)+3, conPlayerState.HeroType, *conPlayerState.Stats, conPlayerState.Equipment))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func OnClientDisconnected(client ClientConnection) {
|
||||||
|
log.Printf("Client disconnected with an id of %s", client.GetUniqueId())
|
||||||
|
delete(singletonServer.clientConnections, client.GetUniqueId())
|
||||||
|
}
|
||||||
|
|
||||||
|
func OnPacketReceived(client ClientConnection, packet d2netpacket.NetPacket) error {
|
||||||
|
switch packet.PacketType {
|
||||||
|
case d2netpackettype.MovePlayer:
|
||||||
|
// TODO: This needs to be verified on the server (here) before sending to other clients....
|
||||||
|
// TODO: Hacky, this should be updated in realtime ----------------
|
||||||
|
// TODO: Verify player id
|
||||||
|
playerState := singletonServer.clientConnections[client.GetUniqueId()].GetPlayerState()
|
||||||
|
playerState.X = packet.PacketData.(d2netpacket.MovePlayerPacket).DestX
|
||||||
|
playerState.Y = packet.PacketData.(d2netpacket.MovePlayerPacket).DestY
|
||||||
|
// ----------------------------------------------------------------
|
||||||
|
for _, player := range singletonServer.clientConnections {
|
||||||
|
player.SendPacketToClient(packet)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user