mirror of
https://github.com/OpenDiablo2/OpenDiablo2
synced 2024-06-02 22:11:10 +00:00
Refactor d2map (#468)
* WIP refactor of d2map stuff * more d2map refactor adding realm init to game client passing map engine from client and server into realm at init change `generate map packet` to have act and level index as data * client explodes, but getting there * realm now initializes, networking works, but map generators dont currently do anything * changed the way that level type records are loaded * fixed funcs for level data lookups * started implementing level generator, currently crashing * client no longer exploding * d2networking refactor put exports into d2client.go and d2server.go kept GameClient and GameServer methods into their respective files made methods for packet handlers instead of the giant switch statements * bugfix: getting first level id by act * minor refactor of gamescreen for readability * towns now generate on server start, create player takes act and level id as args, levels have their own map engine
This commit is contained in:
parent
b4cd37fe6a
commit
fe47e51351
|
@ -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,6 +9,7 @@ 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,51 +2,68 @@ 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 string // Name
|
||||||
Id int
|
Id int // Id
|
||||||
Files [32]string
|
Files []string // File 1 -- File 32
|
||||||
Beta bool
|
Beta bool // Beta
|
||||||
Act int
|
Act int // Act
|
||||||
Expansion bool
|
Expansion bool // Expansion
|
||||||
}
|
}
|
||||||
|
|
||||||
var LevelTypes []LevelTypeRecord
|
var LevelTypes map[d2enum.RegionIdType]*LevelTypeRecord
|
||||||
|
|
||||||
func LoadLevelTypes(file []byte) {
|
func LoadLevelTypes(file []byte) {
|
||||||
data := strings.Split(string(file), "\r\n")[1:]
|
LevelTypes = make(map[d2enum.RegionIdType]*LevelTypeRecord)
|
||||||
LevelTypes = make([]LevelTypeRecord, len(data))
|
dict := d2common.LoadDataDictionary(string(file))
|
||||||
for i, j := 0, 0; i < len(data); i, j = i+1, j+1 {
|
for idx := range dict.Data {
|
||||||
idx := -1
|
record := &LevelTypeRecord{
|
||||||
inc := func() int {
|
Name: dict.GetString("Name", idx),
|
||||||
idx++
|
Id: dict.GetNumber("Id", idx),
|
||||||
return idx
|
Files: []string{
|
||||||
|
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,
|
||||||
}
|
}
|
||||||
if len(data[i]) == 0 {
|
LevelTypes[d2enum.RegionIdType(record.Id)] = record
|
||||||
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. Othervise use the same value here that are used in
|
// X size for the area. Otherwise 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,28 +141,14 @@ 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 .
|
||||||
LevelLinkId0 int // Vis0
|
WarpLevelId []int // Vis0 -- Vis7
|
||||||
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 .
|
||||||
WarpGraphicsId0 int // Warp0
|
WarpGraphicsId []int // Warp0 -- Warp7
|
||||||
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
|
||||||
|
@ -377,7 +363,7 @@ type LevelDetailsRecord struct {
|
||||||
|
|
||||||
var LevelDetails map[int]*LevelDetailsRecord
|
var LevelDetails map[int]*LevelDetailsRecord
|
||||||
|
|
||||||
func GetLevelDetails(id int) *LevelDetailsRecord {
|
func GetLevelDetailsByLevelId(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]
|
||||||
|
@ -387,60 +373,138 @@ func GetLevelDetails(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),
|
||||||
Id: dict.GetNumber("Id", idx),
|
Id: dict.GetNumber("Id", idx),
|
||||||
Palette: dict.GetNumber("Pal", idx),
|
Palette: dict.GetNumber("Pal", idx),
|
||||||
Act: dict.GetNumber("Act", idx),
|
Act: dict.GetNumber("Act", idx),
|
||||||
QuestFlag: dict.GetNumber("QuestFlag", idx),
|
QuestFlag: dict.GetNumber("QuestFlag", idx),
|
||||||
QuestFlagExpansion: dict.GetNumber("QuestFlagEx", idx),
|
QuestFlagExpansion: dict.GetNumber("QuestFlagEx", idx),
|
||||||
AutomapIndex: dict.GetNumber("Layer", idx),
|
AutomapIndex: dict.GetNumber("Layer", idx),
|
||||||
SizeXNormal: dict.GetNumber("SizeX", idx),
|
SizeXNormal: dict.GetNumber("SizeX", idx),
|
||||||
SizeYNormal: dict.GetNumber("SizeY", idx),
|
SizeYNormal: dict.GetNumber("SizeY", idx),
|
||||||
SizeXNightmare: dict.GetNumber("SizeX(N)", idx),
|
SizeXNightmare: dict.GetNumber("SizeX(N)", idx),
|
||||||
SizeYNightmare: dict.GetNumber("SizeY(N)", idx),
|
SizeYNightmare: dict.GetNumber("SizeY(N)", idx),
|
||||||
SizeXHell: dict.GetNumber("SizeX(H)", idx),
|
SizeXHell: dict.GetNumber("SizeX(H)", idx),
|
||||||
SizeYHell: dict.GetNumber("SizeY(H)", idx),
|
SizeYHell: dict.GetNumber("SizeY(H)", idx),
|
||||||
WorldOffsetX: dict.GetNumber("OffsetX", idx),
|
WorldOffsetX: dict.GetNumber("OffsetX", idx),
|
||||||
WorldOffsetY: dict.GetNumber("OffsetY", idx),
|
WorldOffsetY: dict.GetNumber("OffsetY", idx),
|
||||||
DependantLevelID: dict.GetNumber("Depend", idx),
|
DependantLevelID: dict.GetNumber("Depend", idx),
|
||||||
TeleportFlag: d2enum.TeleportFlag(dict.GetNumber("Teleport", idx)),
|
TeleportFlag: d2enum.TeleportFlag(dict.GetNumber("Teleport", idx)),
|
||||||
EnableRain: dict.GetNumber("Rain", idx) > 0,
|
EnableRain: dict.GetNumber("Rain", idx) > 0,
|
||||||
EnableMud: dict.GetNumber("Mud", idx) > 0,
|
EnableMud: dict.GetNumber("Mud", idx) > 0,
|
||||||
EnablePerspective: dict.GetNumber("NoPer", idx) > 0,
|
EnablePerspective: dict.GetNumber("NoPer", idx) > 0,
|
||||||
EnableLineOfSightDraw: dict.GetNumber("LOSDraw", idx) > 0,
|
EnableLineOfSightDraw: dict.GetNumber("LOSDraw", idx) > 0,
|
||||||
EnableFloorFliter: dict.GetNumber("FloorFilter", idx) > 0,
|
EnableFloorFliter: dict.GetNumber("FloorFilter", idx) > 0,
|
||||||
EnableBlankScreen: dict.GetNumber("BlankScreen", idx) > 0,
|
EnableBlankScreen: dict.GetNumber("BlankScreen", idx) > 0,
|
||||||
EnableDrawEdges: dict.GetNumber("DrawEdges", idx) > 0,
|
EnableDrawEdges: dict.GetNumber("DrawEdges", idx) > 0,
|
||||||
IsInside: dict.GetNumber("IsInside", idx) > 0,
|
IsInside: dict.GetNumber("IsInside", idx) > 0,
|
||||||
LevelGenerationType: d2enum.LevelGenerationType(dict.GetNumber("DrlgType", idx)),
|
LevelGenerationType: d2enum.LevelGenerationType(dict.GetNumber("DrlgType", idx)),
|
||||||
LevelType: dict.GetNumber("LevelType", idx),
|
LevelType: dict.GetNumber("LevelType", idx),
|
||||||
SubType: dict.GetNumber("SubType", idx),
|
SubType: dict.GetNumber("SubType", idx),
|
||||||
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),
|
||||||
LevelLinkId0: dict.GetNumber("Vis0", idx),
|
WarpLevelId: []int{
|
||||||
LevelLinkId1: dict.GetNumber("Vis1", idx),
|
dict.GetNumber("Vis0", idx),
|
||||||
LevelLinkId2: dict.GetNumber("Vis2", idx),
|
dict.GetNumber("Vis1", idx),
|
||||||
LevelLinkId3: dict.GetNumber("Vis3", idx),
|
dict.GetNumber("Vis2", idx),
|
||||||
LevelLinkId4: dict.GetNumber("Vis4", idx),
|
dict.GetNumber("Vis3", idx),
|
||||||
LevelLinkId5: dict.GetNumber("Vis5", idx),
|
dict.GetNumber("Vis4", idx),
|
||||||
LevelLinkId6: dict.GetNumber("Vis6", idx),
|
dict.GetNumber("Vis5", idx),
|
||||||
LevelLinkId7: dict.GetNumber("Vis7", idx),
|
dict.GetNumber("Vis6", idx),
|
||||||
WarpGraphicsId0: dict.GetNumber("Warp0", idx),
|
dict.GetNumber("Vis7", idx),
|
||||||
WarpGraphicsId1: dict.GetNumber("Warp1", idx),
|
},
|
||||||
WarpGraphicsId2: dict.GetNumber("Warp2", idx),
|
WarpGraphicsId: []int{
|
||||||
WarpGraphicsId3: dict.GetNumber("Warp3", idx),
|
dict.GetNumber("Vis0", idx),
|
||||||
WarpGraphicsId4: dict.GetNumber("Warp4", idx),
|
dict.GetNumber("Vis1", idx),
|
||||||
WarpGraphicsId5: dict.GetNumber("Warp5", idx),
|
dict.GetNumber("Vis2", idx),
|
||||||
WarpGraphicsId6: dict.GetNumber("Warp6", idx),
|
dict.GetNumber("Vis3", idx),
|
||||||
WarpGraphicsId7: dict.GetNumber("Warp7", idx),
|
dict.GetNumber("Vis4", 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),
|
||||||
|
@ -540,6 +604,7 @@ 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,7 +11,8 @@ package d2enum
|
||||||
type LevelGenerationType int
|
type LevelGenerationType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
LevelTypeRandomMaze LevelGenerationType = iota
|
LevelTypeNone LevelGenerationType = iota
|
||||||
|
LevelTypeRandomMaze
|
||||||
LevelTypePreset
|
LevelTypePreset
|
||||||
LevelTypeWilderness
|
LevelTypeWilderness
|
||||||
)
|
)
|
||||||
|
|
|
@ -18,6 +18,7 @@ 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,6 +16,7 @@ type Configuration struct {
|
||||||
FullScreen bool
|
FullScreen bool
|
||||||
RunInBackground bool
|
RunInBackground bool
|
||||||
VsyncEnabled bool
|
VsyncEnabled bool
|
||||||
|
MaxConnections int
|
||||||
}
|
}
|
||||||
|
|
||||||
var singleton = getDefaultConfig()
|
var singleton = getDefaultConfig()
|
||||||
|
|
60
d2core/d2map/d2mapengine/act.go
Normal file
60
d2core/d2map/d2mapengine/act.go
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
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()
|
||||||
|
}
|
|
@ -20,16 +20,16 @@ import (
|
||||||
|
|
||||||
// Represents the map data for a specific location
|
// Represents the map data for a specific location
|
||||||
type MapEngine struct {
|
type MapEngine struct {
|
||||||
seed int64 // The map seed
|
seed int64 // The map seed
|
||||||
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
|
||||||
startSubTileY int // The starting Y position
|
startSubTileY int // The starting Y position
|
||||||
dt1Files []string // The list of DS1 strings
|
dt1Files []string // The list of DS1 strings
|
||||||
}
|
}
|
||||||
|
|
||||||
// Creates a new instance of the map engine
|
// Creates a new instance of the map engine
|
||||||
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
6
d2core/d2map/d2mapengine/generator.go
Normal file
6
d2core/d2map/d2mapengine/generator.go
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
package d2mapengine
|
||||||
|
|
||||||
|
type MapGenerator interface {
|
||||||
|
init(seed int64, level *MapLevel, engine *MapEngine)
|
||||||
|
generate()
|
||||||
|
}
|
15
d2core/d2map/d2mapengine/generator_maze.go
Normal file
15
d2core/d2map/d2mapengine/generator_maze.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
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() {}
|
66
d2core/d2map/d2mapengine/generator_preset.go
Normal file
66
d2core/d2map/d2mapengine/generator_preset.go
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
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)
|
||||||
|
}
|
15
d2core/d2map/d2mapengine/generator_wilderness.go
Normal file
15
d2core/d2map/d2mapengine/generator_wilderness.go
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
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() {}
|
79
d2core/d2map/d2mapengine/level.go
Normal file
79
d2core/d2map/d2mapengine/level.go
Normal file
|
@ -0,0 +1,79 @@
|
||||||
|
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
|
||||||
|
}
|
114
d2core/d2map/d2mapengine/realm.go
Normal file
114
d2core/d2map/d2mapengine/realm.go
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
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, x, y int, direction int, heroType d2enum.Hero, stats d2hero.HeroStatsState, equipment d2inventory.CharacterEquipment) *Player {
|
func CreatePlayer(id, name string, ActId, LevelId, x, y, 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.GetLevelDetails(2)
|
wilderness1Details := d2datadict.GetLevelDetailsByLevelId(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
|
||||||
|
@ -59,7 +59,7 @@ func GenerateAct1Overworld(mapEngine *d2mapengine.MapEngine) {
|
||||||
// West Exit
|
// West Exit
|
||||||
mapEngine.PlaceStamp(townStamp, mapWidth-townSize.Width, mapHeight-townSize.Height)
|
mapEngine.PlaceStamp(townStamp, mapWidth-townSize.Width, mapHeight-townSize.Height)
|
||||||
|
|
||||||
generateWilderness1TownWest(mapEngine, mapWidth-townSize.Width - wilderness1Details.SizeXNormal, mapHeight-wilderness1Details.SizeYNormal)
|
generateWilderness1TownWest(mapEngine, mapWidth-townSize.Width-wilderness1Details.SizeXNormal, mapHeight-wilderness1Details.SizeYNormal)
|
||||||
} else {
|
} else {
|
||||||
// North Exit
|
// North Exit
|
||||||
mapEngine.PlaceStamp(townStamp, mapWidth-townSize.Width, mapHeight-townSize.Height)
|
mapEngine.PlaceStamp(townStamp, mapWidth-townSize.Width, mapHeight-townSize.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.GetLevelDetails(2)
|
levelDetails := d2datadict.GetLevelDetailsByLevelId(2)
|
||||||
|
|
||||||
fenceNorthStamp := []*d2mapstamp.Stamp{
|
fenceNorthStamp := []*d2mapstamp.Stamp{
|
||||||
loadPreset(mapEngine, d2wilderness.TreeBorderNorth, 0),
|
loadPreset(mapEngine, d2wilderness.TreeBorderNorth, 0),
|
||||||
|
@ -102,36 +102,36 @@ func generateWilderness1TownEast(mapEngine *d2mapengine.MapEngine, startX, start
|
||||||
|
|
||||||
areaRect := d2common.Rectangle{
|
areaRect := d2common.Rectangle{
|
||||||
Left: startX,
|
Left: startX,
|
||||||
Top: startY+9,
|
Top: startY + 9,
|
||||||
Width: levelDetails.SizeXNormal,
|
Width: levelDetails.SizeXNormal,
|
||||||
Height: levelDetails.SizeYNormal-3,
|
Height: levelDetails.SizeYNormal - 3,
|
||||||
}
|
}
|
||||||
generateWilderness1Contents(mapEngine, areaRect)
|
generateWilderness1Contents(mapEngine, areaRect)
|
||||||
|
|
||||||
// Draw the north and south fence
|
// Draw the north and south fence
|
||||||
for i := 0; i < 9; i++ {
|
for i := 0; i < 9; i++ {
|
||||||
mapEngine.PlaceStamp(fenceNorthStamp[rand.Intn(3)], startX+(i*9), startY)
|
mapEngine.PlaceStamp(fenceNorthStamp[rand.Intn(3)], startX+(i*9), startY)
|
||||||
mapEngine.PlaceStamp(fenceSouthStamp[rand.Intn(3)], startX+(i*9), startY + (levelDetails.SizeYNormal +6))
|
mapEngine.PlaceStamp(fenceSouthStamp[rand.Intn(3)], startX+(i*9), startY+(levelDetails.SizeYNormal+6))
|
||||||
}
|
}
|
||||||
|
|
||||||
// West fence
|
// West fence
|
||||||
for i := 1; i < 6; i++ {
|
for i := 1; i < 6; i++ {
|
||||||
mapEngine.PlaceStamp(fenceWestStamp[rand.Intn(3)], startX, startY+ (levelDetails.SizeYNormal+6) - (i * 9))
|
mapEngine.PlaceStamp(fenceWestStamp[rand.Intn(3)], startX, startY+(levelDetails.SizeYNormal+6)-(i*9))
|
||||||
}
|
}
|
||||||
|
|
||||||
// East Fence
|
// East Fence
|
||||||
for i := 1; i < 10; i++ {
|
for i := 1; i < 10; i++ {
|
||||||
mapEngine.PlaceStamp(fenceEastStamp[rand.Intn(3)], startX + levelDetails.SizeXNormal, startY+(i*9))
|
mapEngine.PlaceStamp(fenceEastStamp[rand.Intn(3)], startX+levelDetails.SizeXNormal, startY+(i*9))
|
||||||
}
|
}
|
||||||
|
|
||||||
mapEngine.PlaceStamp(fenceSouthWestStamp, startX, startY+ levelDetails.SizeYNormal+6)
|
mapEngine.PlaceStamp(fenceSouthWestStamp, startX, startY+levelDetails.SizeYNormal+6)
|
||||||
mapEngine.PlaceStamp(fenceWestEdge, startX, startY+ (levelDetails.SizeYNormal-3) - 45)
|
mapEngine.PlaceStamp(fenceWestEdge, startX, startY+(levelDetails.SizeYNormal-3)-45)
|
||||||
mapEngine.PlaceStamp(fenceNorthEastStamp, startX+levelDetails.SizeXNormal, startY)
|
mapEngine.PlaceStamp(fenceNorthEastStamp, startX+levelDetails.SizeXNormal, startY)
|
||||||
mapEngine.PlaceStamp(fenceSouthEastStamp, startX+levelDetails.SizeXNormal, startY+levelDetails.SizeYNormal+6)
|
mapEngine.PlaceStamp(fenceSouthEastStamp, startX+levelDetails.SizeXNormal, startY+levelDetails.SizeYNormal+6)
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateWilderness1TownSouth(mapEngine *d2mapengine.MapEngine, startX, startY int) {
|
func generateWilderness1TownSouth(mapEngine *d2mapengine.MapEngine, startX, startY int) {
|
||||||
levelDetails := d2datadict.GetLevelDetails(2)
|
levelDetails := d2datadict.GetLevelDetailsByLevelId(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.GetLevelDetails(2)
|
levelDetails := d2datadict.GetLevelDetailsByLevelId(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)
|
||||||
|
@ -218,30 +218,30 @@ func generateWilderness1TownWest(mapEngine *d2mapengine.MapEngine, startX, start
|
||||||
// Draw the north and south fences
|
// Draw the north and south fences
|
||||||
for i := 0; i < 9; i++ {
|
for i := 0; i < 9; i++ {
|
||||||
if i > 0 && i < 8 {
|
if i > 0 && i < 8 {
|
||||||
mapEngine.PlaceStamp(fenceNorthStamp[rand.Intn(3)], startX + (i*9)-1, startY-15)
|
mapEngine.PlaceStamp(fenceNorthStamp[rand.Intn(3)], startX+(i*9)-1, startY-15)
|
||||||
}
|
}
|
||||||
mapEngine.PlaceStamp(fenceSouthStamp[rand.Intn(3)], startX+(i*9)-1, startY+levelDetails.SizeYNormal-12)
|
mapEngine.PlaceStamp(fenceSouthStamp[rand.Intn(3)], startX+(i*9)-1, startY+levelDetails.SizeYNormal-12)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the east fence
|
// Draw the east fence
|
||||||
for i := 0; i < 6; i++ {
|
for i := 0; i < 6; i++ {
|
||||||
mapEngine.PlaceStamp(fenceEastStamp[rand.Intn(3)], startX + levelDetails.SizeXNormal-9, startY + (i*9)-6)
|
mapEngine.PlaceStamp(fenceEastStamp[rand.Intn(3)], startX+levelDetails.SizeXNormal-9, startY+(i*9)-6)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the west fence
|
// Draw the west fence
|
||||||
for i := 0; i < 9; i++ {
|
for i := 0; i < 9; i++ {
|
||||||
mapEngine.PlaceStamp(fenceWestStamp[rand.Intn(3)], startX, startY + (i*9)-6)
|
mapEngine.PlaceStamp(fenceWestStamp[rand.Intn(3)], startX, startY+(i*9)-6)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw the west fence
|
// Draw the west fence
|
||||||
mapEngine.PlaceStamp(fenceEastEdge, startX + levelDetails.SizeXNormal-9, startY + 39)
|
mapEngine.PlaceStamp(fenceEastEdge, startX+levelDetails.SizeXNormal-9, startY+39)
|
||||||
mapEngine.PlaceStamp(fenceNorthWestStamp, startX, startY-15)
|
mapEngine.PlaceStamp(fenceNorthWestStamp, startX, startY-15)
|
||||||
mapEngine.PlaceStamp(fenceSouthWestStamp, startX, startY+levelDetails.SizeYNormal-12)
|
mapEngine.PlaceStamp(fenceSouthWestStamp, startX, startY+levelDetails.SizeYNormal-12)
|
||||||
mapEngine.PlaceStamp(fenceNorthEastStamp, startX+levelDetails.SizeXNormal-9, startY-15)
|
mapEngine.PlaceStamp(fenceNorthEastStamp, startX+levelDetails.SizeXNormal-9, startY-15)
|
||||||
|
|
||||||
areaRect := d2common.Rectangle{
|
areaRect := d2common.Rectangle{
|
||||||
Left: startX + 9,
|
Left: startX + 9,
|
||||||
Top: startY-10,
|
Top: startY - 10,
|
||||||
Width: levelDetails.SizeXNormal - 9,
|
Width: levelDetails.SizeXNormal - 9,
|
||||||
Height: levelDetails.SizeYNormal - 2,
|
Height: levelDetails.SizeYNormal - 2,
|
||||||
}
|
}
|
||||||
|
@ -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.GetLevelDetails(2)
|
levelDetails := d2datadict.GetLevelDetailsByLevelId(2)
|
||||||
|
|
||||||
denOfEvil := loadPreset(mapEngine, d2wilderness.DenOfEvilEntrance, 0)
|
denOfEvil := loadPreset(mapEngine, d2wilderness.DenOfEvilEntrance, 0)
|
||||||
denOfEvilLoc := d2common.Point{
|
denOfEvilLoc := d2common.Point{
|
||||||
|
@ -295,10 +295,10 @@ func generateWilderness1Contents(mapEngine *d2mapengine.MapEngine, rect d2common
|
||||||
for numPlaced < 25 {
|
for numPlaced < 25 {
|
||||||
stamp := stuff[rand.Intn(len(stuff))]
|
stamp := stuff[rand.Intn(len(stuff))]
|
||||||
|
|
||||||
stampRect := d2common.Rectangle {
|
stampRect := d2common.Rectangle{
|
||||||
Left: rect.Left+ rand.Intn(rect.Width) - stamp.Size().Width,
|
Left: rect.Left + rand.Intn(rect.Width) - stamp.Size().Width,
|
||||||
Top: rect.Top+rand.Intn(rect.Height) - stamp.Size().Height,
|
Top: rect.Top + rand.Intn(rect.Height) - stamp.Size().Height,
|
||||||
Width: stamp.Size().Width,
|
Width: stamp.Size().Width,
|
||||||
Height: stamp.Size().Height,
|
Height: stamp.Size().Height,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,11 +18,11 @@ 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
|
||||||
}
|
}
|
||||||
|
|
||||||
// Loads a stamp based on the supplied parameters
|
// Loads a stamp based on the supplied parameters
|
||||||
|
@ -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,6 +9,7 @@ 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"
|
||||||
|
@ -162,11 +163,27 @@ 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("", "", 0, 0, 0,
|
v.characterImage[i] = d2mapentity.CreatePlayer(
|
||||||
v.gameStates[idx].HeroType,
|
playerId,
|
||||||
*v.gameStates[idx].Stats,
|
playerName,
|
||||||
d2inventory.HeroObjects[v.gameStates[idx].HeroType],
|
actId,
|
||||||
|
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,11 +56,13 @@ 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()
|
||||||
}
|
}
|
||||||
|
|
||||||
screen.Clear(color.Black)
|
if v.MapRenderer != nil {
|
||||||
v.mapRenderer.Render(screen)
|
screen.Clear(color.Black)
|
||||||
|
v.MapRenderer.Render(screen)
|
||||||
|
}
|
||||||
|
|
||||||
if v.gameControls != nil {
|
if v.gameControls != nil {
|
||||||
v.gameControls.Render(screen)
|
v.gameControls.Render(screen)
|
||||||
|
@ -69,7 +71,7 @@ func (v *Game) Render(screen d2render.Surface) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
var hideZoneTextAfterSeconds = 2.0
|
var zoneTextDuration = 2.0 // seconds
|
||||||
|
|
||||||
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 {
|
||||||
|
@ -96,11 +98,19 @@ 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
|
||||||
if v.lastRegionType != d2enum.RegionNone && v.lastRegionType != tile.RegionType {
|
notNone := v.lastRegionType != d2enum.RegionNone
|
||||||
//TODO: Should not be using RegionType as an index - this will return incorrect LevelDetails record for most of the zones.
|
differentTileType := v.lastRegionType != tile.RegionType
|
||||||
v.gameControls.SetZoneChangeText(fmt.Sprintf("Entering The %s", d2datadict.LevelDetails[int(tile.RegionType)].LevelDisplayName))
|
if notNone && differentTileType {
|
||||||
|
//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(hideZoneTextAfterSeconds)
|
v.gameControls.HideZoneChangeTextAfter(zoneTextDuration)
|
||||||
}
|
}
|
||||||
v.lastRegionType = tile.RegionType
|
v.lastRegionType = tile.RegionType
|
||||||
}
|
}
|
||||||
|
@ -114,7 +124,9 @@ func (v *Game) Advance(tickTime float64) error {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
v.localPlayer = player
|
v.localPlayer = player
|
||||||
v.gameControls = d2player.NewGameControls(player, v.gameClient.MapEngine, v.mapRenderer, v)
|
engine := v.gameClient.MapEngine
|
||||||
|
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)
|
||||||
|
|
||||||
|
@ -124,14 +136,16 @@ 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 {
|
||||||
rx, ry := v.mapRenderer.WorldToOrtho(v.localPlayer.LocationX/5, v.localPlayer.LocationY/5)
|
wx, wy := v.localPlayer.LocationX/5, v.localPlayer.LocationY/5
|
||||||
v.mapRenderer.MoveCameraTo(rx, ry)
|
rx, ry := v.MapRenderer.WorldToOrtho(wx, wy)
|
||||||
|
v.MapRenderer.MoveCameraTo(rx, ry)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Game) OnPlayerMove(x, y float64) {
|
func (v *Game) OnPlayerMove(x2, y2 float64) {
|
||||||
heroPosX := v.localPlayer.LocationX / 5.0
|
id := v.gameClient.PlayerId
|
||||||
heroPosY := v.localPlayer.LocationY / 5.0
|
x1, y1 := v.localPlayer.LocationX/5.0, v.localPlayer.LocationY/5.0
|
||||||
v.gameClient.SendPacketToServer(d2netpacket.CreateMovePlayerPacket(v.gameClient.PlayerId, heroPosX, heroPosY, x, y))
|
movePacket := d2netpacket.CreateMovePlayerPacket(id, x1, y1, x2, y2)
|
||||||
|
v.gameClient.SendPacketToServer(movePacket)
|
||||||
}
|
}
|
||||||
|
|
|
@ -20,9 +20,10 @@ 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"`
|
||||||
X float64 `json:"x"`
|
X float64 `json:"x"`
|
||||||
Y float64 `json:"y"`
|
Y float64 `json:"y"`
|
||||||
}
|
}
|
||||||
|
@ -45,8 +46,8 @@ func GetAllPlayerStates() []*PlayerState {
|
||||||
gameState := LoadPlayerState(path.Join(basePath, file.Name()))
|
gameState := LoadPlayerState(path.Join(basePath, file.Name()))
|
||||||
if gameState == nil || gameState.HeroType == d2enum.HeroNone {
|
if gameState == nil || gameState.HeroType == d2enum.HeroNone {
|
||||||
continue
|
continue
|
||||||
// temporarily loading default class stats if the character was created before saving stats was introduced
|
// temporarily loading default class stats if the character was created before saving stats was introduced
|
||||||
// to be removed in the future
|
// to be removed in the future
|
||||||
} else if gameState.Stats == nil {
|
} else if gameState.Stats == nil {
|
||||||
gameState.Stats = d2hero.CreateHeroStatsState(gameState.HeroType, *d2datadict.CharStats[gameState.HeroType], 1, 0)
|
gameState.Stats = d2hero.CreateHeroStatsState(gameState.HeroType, *d2datadict.CharStats[gameState.HeroType], 1, 0)
|
||||||
gameState.Save()
|
gameState.Save()
|
||||||
|
@ -83,8 +84,9 @@ func CreatePlayerState(heroName string, hero d2enum.Hero, classStats d2datadict.
|
||||||
result := &PlayerState{
|
result := &PlayerState{
|
||||||
HeroName: heroName,
|
HeroName: heroName,
|
||||||
HeroType: hero,
|
HeroType: hero,
|
||||||
Act: 1,
|
Act: 0,
|
||||||
Stats: d2hero.CreateHeroStatsState(hero, classStats, 1, 0),
|
Level: 1,
|
||||||
|
Stats: d2hero.CreateHeroStatsState(hero, classStats, 1, 0),
|
||||||
Equipment: d2inventory.HeroObjects[hero],
|
Equipment: d2inventory.HeroObjects[hero],
|
||||||
FilePath: "",
|
FilePath: "",
|
||||||
}
|
}
|
||||||
|
|
38
d2networking/d2client/d2client.go
Normal file
38
d2networking/d2client/d2client.go
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
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,120 +1,164 @@
|
||||||
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"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
|
d2cct "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 d2clientconnectiontype.ClientConnectionType
|
connectionType d2cct.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
|
||||||
}
|
}
|
||||||
|
|
||||||
func Create(connectionType d2clientconnectiontype.ClientConnectionType) (*GameClient, error) {
|
// Using the `clientConnection`, opens a connection and passes the savefile path
|
||||||
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 {
|
||||||
case d2netpackettype.GenerateMap:
|
|
||||||
mapData := packet.PacketData.(d2netpacket.GenerateMapPacket)
|
|
||||||
switch mapData.RegionType {
|
|
||||||
case d2enum.RegionAct1Town:
|
|
||||||
d2mapgen.GenerateAct1Overworld(g.MapEngine)
|
|
||||||
}
|
|
||||||
g.RegenMap = true
|
|
||||||
case d2netpackettype.UpdateServerInfo:
|
|
||||||
serverInfo := packet.PacketData.(d2netpacket.UpdateServerInfoPacket)
|
|
||||||
g.MapEngine.SetSeed(serverInfo.Seed)
|
|
||||||
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]
|
|
||||||
path, _, _ := g.MapEngine.PathFind(movePlayer.StartX, movePlayer.StartY, movePlayer.DestX, movePlayer.DestY)
|
|
||||||
if len(path) > 0 {
|
|
||||||
player.SetPath(path, func() {
|
|
||||||
tile := g.MapEngine.TileAt(player.TileX, player.TileY)
|
|
||||||
if tile == nil {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
regionType := tile.RegionType
|
// UNSURE: should we be bubbling up errors from these handler calls?
|
||||||
if regionType == d2enum.RegionAct1Town {
|
case d2netpackettype.UpdateServerInfo:
|
||||||
player.SetIsInTown(true)
|
g.handleUpdateServerInfo(packet)
|
||||||
} else {
|
|
||||||
player.SetIsInTown(false)
|
case d2netpackettype.AddPlayer:
|
||||||
}
|
g.handleAddPlayer(packet)
|
||||||
player.SetAnimationMode(player.GetAnimationMode().String())
|
|
||||||
})
|
case d2netpackettype.GenerateMap:
|
||||||
}
|
g.handleGenerateMap(packet)
|
||||||
|
|
||||||
|
case d2netpackettype.MovePlayer:
|
||||||
|
g.handleMovePlayer(packet)
|
||||||
|
|
||||||
case d2netpackettype.Ping:
|
case d2netpackettype.Ping:
|
||||||
g.clientConnection.SendPacketToServer(d2netpacket.CreatePongPacket(g.PlayerId))
|
g.handlePong(packet)
|
||||||
|
|
||||||
case d2netpackettype.ServerClosed:
|
case d2netpackettype.ServerClosed:
|
||||||
// TODO: Need to be tied into a character save and exit
|
g.handleServerClosed(packet)
|
||||||
log.Print("Server has been closed")
|
|
||||||
os.Exit(0)
|
|
||||||
default:
|
default:
|
||||||
log.Fatalf("Invalid packet type: %d", packet.PacketType)
|
log.Fatalf("Invalid packet type: %d", packet.PacketType)
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Using the `clientConnection`, sends a packet to the server
|
||||||
func (g *GameClient) SendPacketToServer(packet d2netpacket.NetPacket) error {
|
func (g *GameClient) SendPacketToServer(packet d2netpacket.NetPacket) error {
|
||||||
return g.clientConnection.SendPacketToServer(packet)
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GameClient) handleMovePlayer(p d2netpacket.NetPacket) {
|
||||||
|
movePlayer := p.PacketData.(d2netpacket.MovePlayerPacket)
|
||||||
|
|
||||||
|
player := g.Players[movePlayer.PlayerId]
|
||||||
|
x1, y1 := movePlayer.StartX, movePlayer.StartY
|
||||||
|
x2, y2 := movePlayer.DestX, movePlayer.DestY
|
||||||
|
|
||||||
|
path, _, _ := g.MapEngine.PathFind(x1, y1, x2, y2)
|
||||||
|
|
||||||
|
if len(path) > 0 {
|
||||||
|
player.SetPath(path, func() {
|
||||||
|
tile := g.MapEngine.TileAt(player.TileX, player.TileY)
|
||||||
|
if tile == nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
regionType := tile.RegionType
|
||||||
|
if regionType == d2enum.RegionAct1Town {
|
||||||
|
player.SetIsInTown(true)
|
||||||
|
} else {
|
||||||
|
player.SetIsInTown(false)
|
||||||
|
}
|
||||||
|
player.SetAnimationMode(player.GetAnimationMode().String())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (g *GameClient) handlePong(p d2netpacket.NetPacket) {
|
||||||
|
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
|
||||||
|
log.Print("Server has been closed")
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
|
|
@ -12,22 +12,24 @@ 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, x, y int, heroType d2enum.Hero, stats d2hero.HeroStatsState, equipment d2inventory.CharacterEquipment) NetPacket {
|
func CreateAddPlayerPacket(id, name string, act, 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,
|
||||||
Equipment: equipment,
|
Equipment: equipment,
|
||||||
Stats: stats,
|
Stats: stats,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,20 @@
|
||||||
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 {
|
||||||
RegionType d2enum.RegionIdType `json:"regionType"`
|
ActId int `json:"actId"`
|
||||||
|
LevelId int `json:"levelId"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateGenerateMapPacket(regionType d2enum.RegionIdType) NetPacket {
|
func CreateGenerateMapPacket(actId, levelId int) NetPacket {
|
||||||
return NetPacket{
|
return NetPacket{
|
||||||
PacketType: d2netpackettype.GenerateMap,
|
PacketType: d2netpackettype.GenerateMap,
|
||||||
PacketData: GenerateMapPacket{
|
PacketData: GenerateMapPacket{
|
||||||
RegionType: regionType,
|
ActId: actId,
|
||||||
|
LevelId: levelId,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,13 +8,16 @@ import (
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ConnectionManager is responsible for cleanup up connections accepted by the game server. As the server communicates over
|
// ConnectionManager is responsible for cleanup up connections accepted by the
|
||||||
// UDP and is stateless we need to implement some loose state management via a ping/pong system. ConnectionManager also handles
|
// 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.
|
// 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 to watch over
|
// gameServer: The *GameServer is argument provided for the connection manager
|
||||||
|
// 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
|
||||||
|
@ -50,7 +53,8 @@ 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 {
|
||||||
if err := connection.SendPacketToClient(d2netpacket.CreatePingPacket()); err != nil {
|
pingPacket := d2netpacket.CreatePingPacket()
|
||||||
|
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()
|
||||||
|
@ -66,7 +70,8 @@ func (c *ConnectionManager) checkPeers() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Recv simply resets the counter, acknowledging we have received a pong from the client.
|
// Recv simply resets the counter, acknowledging we have received a pong from
|
||||||
|
// the client.
|
||||||
func (c *ConnectionManager) Recv(id string) {
|
func (c *ConnectionManager) Recv(id string) {
|
||||||
c.status[id] = 0
|
c.status[id] = 0
|
||||||
}
|
}
|
||||||
|
@ -81,8 +86,10 @@ 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 are never signaled about the application termination.
|
// TODO: Currently this will never actually get called as the go routines
|
||||||
// Things can be done more cleanly once we have graceful exits however we still need to account for other OS Signals
|
// 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
|
||||||
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())
|
||||||
|
|
243
d2networking/d2server/d2server.go
Normal file
243
d2networking/d2server/d2server.go
Normal file
|
@ -0,0 +1,243 @@
|
||||||
|
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,209 +1,67 @@
|
||||||
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"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
|
packettype "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
|
||||||
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
|
d2udp "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2server/d2udpclientconnection"
|
||||||
"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
|
||||||
mapEngines []*d2mapengine.MapEngine
|
realm *d2mapengine.MapRealm
|
||||||
scriptEngine *d2script.ScriptEngine
|
scriptEngine *d2script.ScriptEngine
|
||||||
udpConnection *net.UDPConn
|
udpConnection *net.UDPConn
|
||||||
seed int64
|
seed int64
|
||||||
running bool
|
running bool
|
||||||
|
maxClients int
|
||||||
|
lastAdvance float64
|
||||||
}
|
}
|
||||||
|
|
||||||
var singletonServer *GameServer
|
func (srv *GameServer) handlePlayerConnRequest(addr *net.UDPAddr, data string) {
|
||||||
|
packetData := &packet.PlayerConnectionRequestPacket{}
|
||||||
|
json.Unmarshal([]byte(data), packetData)
|
||||||
|
|
||||||
func Create(openNetworkServer bool) {
|
srvCon := srv.udpConnection
|
||||||
log.Print("Creating GameServer")
|
packetId := packetData.Id
|
||||||
if singletonServer != nil {
|
clientCon := d2udp.CreateUDPClientConnection(srvCon, packetId, addr)
|
||||||
return
|
|
||||||
|
state := packetData.PlayerState
|
||||||
|
clientCon.SetPlayerState(state)
|
||||||
|
OnClientConnected(clientCon)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (srv *GameServer) handleMovePlayer(addr *net.UDPAddr, data string) {
|
||||||
|
packetData := &packet.MovePlayerPacket{}
|
||||||
|
json.Unmarshal([]byte(data), packetData)
|
||||||
|
|
||||||
|
netPacket := packet.NetPacket{
|
||||||
|
PacketType: packettype.MovePlayer,
|
||||||
|
PacketData: packetData,
|
||||||
}
|
}
|
||||||
|
|
||||||
singletonServer = &GameServer{
|
for _, player := range srv.clientConnections {
|
||||||
clientConnections: make(map[string]ClientConnection),
|
player.SendPacketToClient(netPacket)
|
||||||
mapEngines: make([]*d2mapengine.MapEngine, 0),
|
|
||||||
scriptEngine: d2script.CreateScriptEngine(),
|
|
||||||
seed: time.Now().UnixNano(),
|
|
||||||
}
|
|
||||||
|
|
||||||
singletonServer.manager = CreateConnectionManager(singletonServer)
|
|
||||||
|
|
||||||
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() {
|
func (srv *GameServer) handlePong(addr *net.UDPAddr, data string) {
|
||||||
s, err := net.ResolveUDPAddr("udp4", "0.0.0.0:6669")
|
packetData := packet.PlayerConnectionRequestPacket{}
|
||||||
if err != nil {
|
json.Unmarshal([]byte(data), &packetData)
|
||||||
panic(err)
|
srv.manager.Recv(packetData.Id)
|
||||||
}
|
|
||||||
|
|
||||||
singletonServer.udpConnection, err = net.ListenUDP("udp4", s)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
singletonServer.udpConnection.SetReadBuffer(4096)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func runNetworkServer() {
|
func (srv *GameServer) handlePlayerDisconnectNotification(data string) {
|
||||||
buffer := make([]byte, 4096)
|
var packet packet.PlayerDisconnectRequestPacket
|
||||||
for singletonServer.running {
|
json.Unmarshal([]byte(data), &packet)
|
||||||
_, addr, err := singletonServer.udpConnection.ReadFromUDP(buffer)
|
log.Printf("Received disconnect: %s", packet.Id)
|
||||||
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,
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, player := range singletonServer.clientConnections {
|
|
||||||
player.SendPacketToClient(netPacket)
|
|
||||||
}
|
|
||||||
case d2netpackettype.Pong:
|
|
||||||
packetData := d2netpacket.PlayerConnectionRequestPacket{}
|
|
||||||
json.Unmarshal([]byte(stringData), &packetData)
|
|
||||||
singletonServer.manager.Recv(packetData.Id)
|
|
||||||
case d2netpackettype.ServerClosed:
|
|
||||||
singletonServer.manager.Shutdown()
|
|
||||||
case d2netpackettype.PlayerDisconnectionNotification:
|
|
||||||
var packet d2netpacket.PlayerDisconnectRequestPacket
|
|
||||||
json.Unmarshal([]byte(stringData), &packet)
|
|
||||||
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