Added multiplayer support (#336)

This commit is contained in:
Tim Sarbin 2020-06-18 14:11:04 -04:00 committed by GitHub
parent 16dc775be1
commit 2da08884c4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
33 changed files with 838 additions and 197 deletions

View File

@ -26,8 +26,9 @@ type DS1 struct {
func LoadDS1(fileData []byte) (*DS1, error) { func LoadDS1(fileData []byte) (*DS1, error) {
ds1 := &DS1{ ds1 := &DS1{
NumberOfFloors: 1, Act: 1,
NumberOfWalls: 1, NumberOfFloors: 0,
NumberOfWalls: 0,
NumberOfShadowLayers: 1, NumberOfShadowLayers: 1,
NumberOfSubstitutionLayers: 0, NumberOfSubstitutionLayers: 0,
} }
@ -61,7 +62,7 @@ func LoadDS1(fileData []byte) (*DS1, error) {
} }
if ds1.Version >= 9 && ds1.Version <= 13 { if ds1.Version >= 9 && ds1.Version <= 13 {
// Skipping two dwords because they are "meaningless"? // Skipping two dwords because they are "meaningless"?
br.SkipBytes(16) br.SkipBytes(8)
} }
if ds1.Version >= 4 { if ds1.Version >= 4 {
ds1.NumberOfWalls = br.GetInt32() ds1.NumberOfWalls = br.GetInt32()
@ -179,8 +180,7 @@ func LoadDS1(fileData []byte) (*DS1, error) {
newObject.X = int(br.GetInt32()) newObject.X = int(br.GetInt32())
newObject.Y = int(br.GetInt32()) newObject.Y = int(br.GetInt32())
newObject.Flags = int(br.GetInt32()) newObject.Flags = int(br.GetInt32())
//TODO: There's a crash here, we aren't loading this data right.... newObject.Lookup = d2datadict.LookupObject(int(ds1.Act), newObject.Type, newObject.Id)
newObject.Lookup = d2datadict.LookupObject(int(ds1.Act), int(newObject.Type), int(newObject.Id))
if newObject.Lookup != nil && newObject.Lookup.ObjectsTxtId != -1 { if newObject.Lookup != nil && newObject.Lookup.ObjectsTxtId != -1 {
newObject.ObjectInfo = d2datadict.Objects[newObject.Lookup.ObjectsTxtId] newObject.ObjectInfo = d2datadict.Objects[newObject.Lookup.ObjectsTxtId]
} }

View File

@ -11,6 +11,7 @@ const (
TrademarkScreen = "/data/global/ui/FrontEnd/trademarkscreenEXP.dc6" TrademarkScreen = "/data/global/ui/FrontEnd/trademarkscreenEXP.dc6"
GameSelectScreen = "/data/global/ui/FrontEnd/gameselectscreenEXP.dc6" GameSelectScreen = "/data/global/ui/FrontEnd/gameselectscreenEXP.dc6"
TcpIpBackground = "/data/global/ui/FrontEnd/TCPIPscreen.dc6"
Diablo2LogoFireLeft = "/data/global/ui/FrontEnd/D2logoFireLeft.DC6" Diablo2LogoFireLeft = "/data/global/ui/FrontEnd/D2logoFireLeft.DC6"
Diablo2LogoFireRight = "/data/global/ui/FrontEnd/D2logoFireRight.DC6" Diablo2LogoFireRight = "/data/global/ui/FrontEnd/D2logoFireRight.DC6"
Diablo2LogoBlackLeft = "/data/global/ui/FrontEnd/D2logoBlackLeft.DC6" Diablo2LogoBlackLeft = "/data/global/ui/FrontEnd/D2logoBlackLeft.DC6"

View File

@ -92,16 +92,16 @@ func LoadDictionary(dictionaryData []byte) {
} }
// Use the following code to write out the values // Use the following code to write out the values
/* /*=
f, err := os.OpenFile(`C:\Users\lunat\Desktop\D2\langdict.txt`, f, err := os.OpenFile(`C:\Users\lunat\Desktop\D2\langdict.txt`,
os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
if err != nil { if err != nil {
log.Println(err) log.Println(err)
} }
defer f.Close() defer f.Close()
if _, err := f.WriteString("\n[" + key + "] " + value); err != nil { if _, err := f.WriteString("\n[" + key + "] " + value); err != nil {
log.Println(err) log.Println(err)
} }
*/ */
} }
} }

View File

@ -18,12 +18,14 @@ type MapEntity interface {
GetPosition() (float64, float64) GetPosition() (float64, float64)
} }
// Represents the map data for a specific location
type MapEngine struct { type MapEngine struct {
seed int64 seed int64
regions []*MapRegion regions []*MapRegion
entities MapEntitiesSearcher entities MapEntitiesSearcher
} }
// Creates a new instance of the map engine
func CreateMapEngine() *MapEngine { func CreateMapEngine() *MapEngine {
engine := &MapEngine{ engine := &MapEngine{
seed: 0, seed: 0,
@ -48,6 +50,7 @@ func (m *MapEngine) GetStartPosition() (float64, float64) {
return startX, startY return startX, startY
} }
// Returns the center of the map
func (m *MapEngine) GetCenterPosition() (float64, float64) { func (m *MapEngine) GetCenterPosition() (float64, float64) {
var centerX, centerY float64 var centerX, centerY float64
if len(m.regions) > 0 { if len(m.regions) > 0 {
@ -83,11 +86,14 @@ func (m *MapEngine) GenerateAct1Overworld(cacheTiles bool) {
} }
} }
// Appends a region to the map
func (m *MapEngine) AppendRegion(region *MapRegion) { func (m *MapEngine) AppendRegion(region *MapRegion) {
// TODO: Stitch together region.walkableArea // TODO: Stitch together region.walkableArea
log.Printf("Warning: Walkable areas are not currently implemented")
m.regions = append(m.regions, region) m.regions = append(m.regions, region)
} }
// Returns the region located at the specified tile location
func (m *MapEngine) GetRegionAtTile(x, y int) *MapRegion { func (m *MapEngine) GetRegionAtTile(x, y int) *MapRegion {
for _, region := range m.regions { for _, region := range m.regions {
if region.tileRect.IsInRect(x, y) { if region.tileRect.IsInRect(x, y) {

View File

@ -1,11 +1,14 @@
package d2map package d2map
import ( import (
"image/color"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
) )
type Player struct { type Player struct {
@ -14,9 +17,11 @@ type Player struct {
Id string Id string
mode d2enum.AnimationMode mode d2enum.AnimationMode
direction int direction int
Name string
nameLabel d2ui.Label
} }
func CreatePlayer(id string, x, y int, direction int, heroType d2enum.Hero, equipment d2inventory.CharacterEquipment) *Player { func CreatePlayer(id, name string, x, y int, direction int, heroType d2enum.Hero, equipment d2inventory.CharacterEquipment) *Player {
object := &d2datadict.ObjectLookupRecord{ object := &d2datadict.ObjectLookupRecord{
Mode: d2enum.AnimationModePlayerNeutral.String(), Mode: d2enum.AnimationModePlayerNeutral.String(),
Base: "/data/global/chars", Base: "/data/global/chars",
@ -44,18 +49,32 @@ func CreatePlayer(id string, x, y int, direction int, heroType d2enum.Hero, equi
Equipment: equipment, Equipment: equipment,
mode: d2enum.AnimationModePlayerTownNeutral, mode: d2enum.AnimationModePlayerTownNeutral,
direction: direction, direction: direction,
Name: name,
nameLabel: d2ui.CreateLabel(d2resource.FontFormal11, d2resource.PaletteStatic),
}
result.nameLabel.Alignment = d2ui.LabelAlignCenter
result.nameLabel.SetText(name)
result.nameLabel.Color = color.White
err = result.SetMode(result.mode.String(), equipment.RightHand.GetWeaponClass(), direction)
if err != nil {
panic(err)
} }
result.SetMode(result.mode.String(), equipment.RightHand.GetWeaponClass(), direction)
return result return result
} }
func (v *Player) Advance(tickTime float64) { func (v *Player) Advance(tickTime float64) {
v.Step(tickTime) v.Step(tickTime)
v.AnimatedComposite.Advance(tickTime) v.AnimatedComposite.Advance(tickTime)
} }
func (v *Player) Render(target d2render.Surface) { func (v *Player) Render(target d2render.Surface) {
v.AnimatedComposite.Render(target) v.AnimatedComposite.Render(target)
offX := v.AnimatedComposite.offsetX + int((v.AnimatedComposite.subcellX-v.AnimatedComposite.subcellY)*16)
offY := v.AnimatedComposite.offsetY + int(((v.AnimatedComposite.subcellX+v.AnimatedComposite.subcellY)*8)-5)
v.nameLabel.X = offX
v.nameLabel.Y = offY - 100
v.nameLabel.Render(target)
} }
func (v *Player) GetPosition() (float64, float64) { func (v *Player) GetPosition() (float64, float64) {

View File

@ -676,6 +676,10 @@ func (mr *MapRegion) generateShadowCache(tile *d2ds1.FloorShadowRecord, tileX, t
tileData = &tileOptions[tileIndex] tileData = &tileOptions[tileIndex]
} }
if tileData.Width == 0 || tileData.Height == 0 {
return
}
tile.RandomIndex = tileIndex tile.RandomIndex = tileIndex
tileMinY := int32(0) tileMinY := int32(0)
tileMaxY := int32(0) tileMaxY := int32(0)

View File

@ -23,12 +23,14 @@ type TextBox struct {
bgSprite *Sprite bgSprite *Sprite
textLabel Label textLabel Label
lineBar Label lineBar Label
filter string
} }
func CreateTextbox() TextBox { func CreateTextbox() TextBox {
animation, _ := d2asset.LoadAnimation(d2resource.TextBox2, d2resource.PaletteUnits) animation, _ := d2asset.LoadAnimation(d2resource.TextBox2, d2resource.PaletteUnits)
bgSprite, _ := LoadSprite(animation) bgSprite, _ := LoadSprite(animation)
result := TextBox{ result := TextBox{
filter: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ",
bgSprite: bgSprite, bgSprite: bgSprite,
textLabel: CreateLabel(d2resource.FontFormal11, d2resource.PaletteUnits), textLabel: CreateLabel(d2resource.FontFormal11, d2resource.PaletteUnits),
lineBar: CreateLabel(d2resource.FontFormal11, d2resource.PaletteUnits), lineBar: CreateLabel(d2resource.FontFormal11, d2resource.PaletteUnits),
@ -39,6 +41,10 @@ func CreateTextbox() TextBox {
return result return result
} }
func (v *TextBox) SetFilter(filter string) {
v.filter = filter
}
func repeatingKeyPressed(key ebiten.Key) bool { func repeatingKeyPressed(key ebiten.Key) bool {
const ( const (
delay = 30 delay = 30
@ -93,7 +99,7 @@ func (v *TextBox) GetText() string {
func (v *TextBox) SetText(newText string) { func (v *TextBox) SetText(newText string) {
result := "" result := ""
for _, c := range newText { for _, c := range newText {
if !strings.Contains("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", string(c)) { if !strings.Contains(v.filter, string(c)) {
continue continue
} }
result += string(c) result += string(c)

View File

@ -6,6 +6,8 @@ import (
"os" "os"
"strings" "strings"
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
@ -17,7 +19,6 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gamestate"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
@ -43,14 +44,20 @@ type CharacterSelect struct {
characterStatsLabel [8]d2ui.Label characterStatsLabel [8]d2ui.Label
characterExpLabel [8]d2ui.Label characterExpLabel [8]d2ui.Label
characterImage [8]*d2map.Player characterImage [8]*d2map.Player
gameStates []*d2gamestate.GameState gameStates []*d2player.PlayerState
selectedCharacter int selectedCharacter int
mouseButtonPressed bool mouseButtonPressed bool
showDeleteConfirmation bool showDeleteConfirmation bool
connectionType d2clientconnectiontype.ClientConnectionType
connectionHost string
} }
func CreateCharacterSelect() *CharacterSelect { func CreateCharacterSelect(connectionType d2clientconnectiontype.ClientConnectionType, connectionHost string) *CharacterSelect {
return &CharacterSelect{selectedCharacter: -1} return &CharacterSelect{
selectedCharacter: -1,
connectionType: connectionType,
connectionHost: connectionHost,
}
} }
func (v *CharacterSelect) OnLoad() error { func (v *CharacterSelect) OnLoad() error {
@ -158,7 +165,7 @@ func (v *CharacterSelect) updateCharacterBoxes() {
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)
// 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] = d2map.CreatePlayer("", 0, 0, 0, v.characterImage[i] = d2map.CreatePlayer("", "", 0, 0, 0,
v.gameStates[idx].HeroType, v.gameStates[idx].HeroType,
d2inventory.HeroObjects[v.gameStates[idx].HeroType], d2inventory.HeroObjects[v.gameStates[idx].HeroType],
) )
@ -166,12 +173,12 @@ func (v *CharacterSelect) updateCharacterBoxes() {
} }
func (v *CharacterSelect) onNewCharButtonClicked() { func (v *CharacterSelect) onNewCharButtonClicked() {
d2screen.SetNextScreen(CreateSelectHeroClass()) d2screen.SetNextScreen(CreateSelectHeroClass(v.connectionType, v.connectionHost))
} }
func (v *CharacterSelect) onExitButtonClicked() { func (v *CharacterSelect) onExitButtonClicked() {
mainMenu := CreateMainMenu() mainMenu := CreateMainMenu()
mainMenu.ShowTrademarkScreen = false mainMenu.SetScreenMode(ScreenModeMainMenu)
d2screen.SetNextScreen(mainMenu) d2screen.SetNextScreen(mainMenu)
} }
@ -278,7 +285,7 @@ func (v *CharacterSelect) toggleDeleteCharacterDialog(showDialog bool) {
} }
func (v *CharacterSelect) refreshGameStates() { func (v *CharacterSelect) refreshGameStates() {
v.gameStates = d2gamestate.GetAllGameStates() v.gameStates = d2player.GetAllPlayerStates()
v.updateCharacterBoxes() v.updateCharacterBoxes()
if len(v.gameStates) > 0 { if len(v.gameStates) > 0 {
v.selectedCharacter = 0 v.selectedCharacter = 0
@ -293,7 +300,13 @@ func (v *CharacterSelect) refreshGameStates() {
} }
func (v *CharacterSelect) onOkButtonClicked() { func (v *CharacterSelect) onOkButtonClicked() {
gameClient, _ := d2client.Create(d2clientconnectiontype.Local) gameClient, _ := d2client.Create(v.connectionType)
gameClient.Open(v.gameStates[v.selectedCharacter].FilePath) switch v.connectionType {
case d2clientconnectiontype.LANClient:
gameClient.Open(v.connectionHost, v.gameStates[v.selectedCharacter].FilePath)
default:
gameClient.Open("", v.gameStates[v.selectedCharacter].FilePath)
}
d2screen.SetNextScreen(CreateGame(gameClient)) d2screen.SetNextScreen(CreateGame(gameClient))
} }

View File

@ -130,7 +130,7 @@ func (v *Credits) Advance(tickTime float64) error {
func (v *Credits) onExitButtonClicked() { func (v *Credits) onExitButtonClicked() {
mainMenu := CreateMainMenu() mainMenu := CreateMainMenu()
mainMenu.ShowTrademarkScreen = false mainMenu.SetScreenMode(ScreenModeMainMenu)
d2screen.SetNextScreen(mainMenu) d2screen.SetNextScreen(mainMenu)
} }

View File

@ -3,6 +3,8 @@ package d2gamescreen
import ( import (
"image/color" "image/color"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map"
@ -33,39 +35,6 @@ func CreateGame(gameClient *d2client.GameClient) *Game {
} }
func (v *Game) OnLoad() error { func (v *Game) OnLoad() error {
//animation, _ := d2asset.LoadAnimation(d2resource.PentSpin, d2resource.PaletteSky)
//v.pentSpinLeft, _ = d2ui.LoadSprite(animation)
//v.pentSpinLeft.PlayBackward()
//v.pentSpinLeft.SetPlayLengthMs(475)
//v.pentSpinLeft.SetPosition(100, 300)
//
//animation, _ = d2asset.LoadAnimation(d2resource.PentSpin, d2resource.PaletteSky)
//v.pentSpinRight, _ = d2ui.LoadSprite(animation)
//v.pentSpinRight.PlayForward()
//v.pentSpinRight.SetPlayLengthMs(475)
//v.pentSpinRight.SetPosition(650, 300)
//
//v.testLabel = d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteUnits)
//v.testLabel.Alignment = d2ui.LabelAlignCenter
//v.testLabel.SetText("Soon :tm:")
//v.testLabel.SetPosition(400, 250)
/*
startX, startY := v.mapEngine.GetStartPosition()
v.hero = d2map.CreateHero(
int(startX*5)+3,
int(startY*5)+3,
0,
v.gameState.HeroType,
v.gameState.Equipment,
)
v.mapEngine.AddEntity(v.hero)
v.gameControls = d2player.NewGameControls(v.hero, v.mapEngine)
v.gameControls.Load()
d2input.BindHandler(v.gameControls)
*/
return nil return nil
} }
@ -93,7 +62,7 @@ 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.gameClient) v.gameControls = d2player.NewGameControls(player, v.gameClient.MapEngine, v.mapRenderer, v)
v.gameControls.Load() v.gameControls.Load()
d2input.BindHandler(v.gameControls) d2input.BindHandler(v.gameControls)
@ -108,3 +77,9 @@ func (v *Game) Advance(tickTime float64) error {
} }
return nil return nil
} }
func (v *Game) OnPlayerMove(x, y float64) {
heroPosX := v.localPlayer.AnimatedComposite.LocationX / 5.0
heroPosY := v.localPlayer.AnimatedComposite.LocationY / 5.0
v.gameClient.SendPacketToServer(d2netpacket.CreateMovePlayerPacket(v.gameClient.PlayerId, heroPosX, heroPosY, x, y))
}

View File

@ -8,46 +8,72 @@ import (
"os/exec" "os/exec"
"runtime" "runtime"
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
"github.com/OpenDiablo2/OpenDiablo2/d2common" "github.com/OpenDiablo2/OpenDiablo2/d2common"
"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"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gamestate"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
) )
type MainMenuScreenMode int
const (
ScreenModeUnknown MainMenuScreenMode = iota
ScreenModeTrademark
ScreenModeMainMenu
ScreenModeMultiplayer
ScreenModeTcpIp
ScreenModeServerIp
)
// MainMenu represents the main menu // MainMenu represents the main menu
type MainMenu struct { type MainMenu struct {
tcpIpBackground *d2ui.Sprite
trademarkBackground *d2ui.Sprite trademarkBackground *d2ui.Sprite
background *d2ui.Sprite background *d2ui.Sprite
diabloLogoLeft *d2ui.Sprite diabloLogoLeft *d2ui.Sprite
diabloLogoRight *d2ui.Sprite diabloLogoRight *d2ui.Sprite
diabloLogoLeftBack *d2ui.Sprite diabloLogoLeftBack *d2ui.Sprite
diabloLogoRightBack *d2ui.Sprite diabloLogoRightBack *d2ui.Sprite
serverIpBackground *d2ui.Sprite
singlePlayerButton d2ui.Button singlePlayerButton d2ui.Button
multiplayerButton d2ui.Button
githubButton d2ui.Button githubButton d2ui.Button
exitDiabloButton d2ui.Button exitDiabloButton d2ui.Button
creditsButton d2ui.Button creditsButton d2ui.Button
cinematicsButton d2ui.Button cinematicsButton d2ui.Button
mapTestButton d2ui.Button mapTestButton d2ui.Button
networkTcpIpButton d2ui.Button
networkCancelButton d2ui.Button
btnTcpIpCancel d2ui.Button
btnTcpIpHostGame d2ui.Button
btnTcpIpJoinGame d2ui.Button
btnServerIpCancel d2ui.Button
btnServerIpOk d2ui.Button
copyrightLabel d2ui.Label copyrightLabel d2ui.Label
copyrightLabel2 d2ui.Label copyrightLabel2 d2ui.Label
openDiabloLabel d2ui.Label openDiabloLabel d2ui.Label
versionLabel d2ui.Label versionLabel d2ui.Label
commitLabel d2ui.Label commitLabel d2ui.Label
tcpIpOptionsLabel d2ui.Label
ShowTrademarkScreen bool tcpJoinGameLabel d2ui.Label
tcpJoinGameEntry d2ui.TextBox
screenMode MainMenuScreenMode
leftButtonHeld bool leftButtonHeld bool
} }
// CreateMainMenu creates an instance of MainMenu // CreateMainMenu creates an instance of MainMenu
func CreateMainMenu() *MainMenu { func CreateMainMenu() *MainMenu {
return &MainMenu{ return &MainMenu{
ShowTrademarkScreen: true, screenMode: ScreenModeUnknown,
leftButtonHeld: true, leftButtonHeld: true,
} }
} }
@ -93,6 +119,10 @@ func (v *MainMenu) OnLoad() error {
v.trademarkBackground, _ = d2ui.LoadSprite(animation) v.trademarkBackground, _ = d2ui.LoadSprite(animation)
v.trademarkBackground.SetPosition(0, 0) v.trademarkBackground.SetPosition(0, 0)
animation, _ = d2asset.LoadAnimation(d2resource.TcpIpBackground, d2resource.PaletteSky)
v.tcpIpBackground, _ = d2ui.LoadSprite(animation)
v.tcpIpBackground.SetPosition(0, 0)
animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoFireLeft, d2resource.PaletteUnits) animation, _ = d2asset.LoadAnimation(d2resource.Diablo2LogoFireLeft, d2resource.PaletteUnits)
v.diabloLogoLeft, _ = d2ui.LoadSprite(animation) v.diabloLogoLeft, _ = d2ui.LoadSprite(animation)
v.diabloLogoLeft.SetBlend(true) v.diabloLogoLeft.SetBlend(true)
@ -115,39 +145,99 @@ func (v *MainMenu) OnLoad() error {
v.exitDiabloButton = d2ui.CreateButton(d2ui.ButtonTypeWide, d2common.TranslateString("#1625")) v.exitDiabloButton = d2ui.CreateButton(d2ui.ButtonTypeWide, d2common.TranslateString("#1625"))
v.exitDiabloButton.SetPosition(264, 535) v.exitDiabloButton.SetPosition(264, 535)
v.exitDiabloButton.SetVisible(!v.ShowTrademarkScreen)
v.exitDiabloButton.OnActivated(func() { v.onExitButtonClicked() }) v.exitDiabloButton.OnActivated(func() { v.onExitButtonClicked() })
d2ui.AddWidget(&v.exitDiabloButton) d2ui.AddWidget(&v.exitDiabloButton)
v.creditsButton = d2ui.CreateButton(d2ui.ButtonTypeShort, d2common.TranslateString("#1627")) v.creditsButton = d2ui.CreateButton(d2ui.ButtonTypeShort, d2common.TranslateString("#1627"))
v.creditsButton.SetPosition(264, 505) v.creditsButton.SetPosition(264, 505)
v.creditsButton.SetVisible(!v.ShowTrademarkScreen)
v.creditsButton.OnActivated(func() { v.onCreditsButtonClicked() }) v.creditsButton.OnActivated(func() { v.onCreditsButtonClicked() })
d2ui.AddWidget(&v.creditsButton) d2ui.AddWidget(&v.creditsButton)
v.cinematicsButton = d2ui.CreateButton(d2ui.ButtonTypeShort, d2common.TranslateString("#1639")) v.cinematicsButton = d2ui.CreateButton(d2ui.ButtonTypeShort, d2common.TranslateString("#1639"))
v.cinematicsButton.SetPosition(401, 505) v.cinematicsButton.SetPosition(401, 505)
v.cinematicsButton.SetVisible(!v.ShowTrademarkScreen)
d2ui.AddWidget(&v.cinematicsButton) d2ui.AddWidget(&v.cinematicsButton)
v.singlePlayerButton = d2ui.CreateButton(d2ui.ButtonTypeWide, d2common.TranslateString("#1620")) v.singlePlayerButton = d2ui.CreateButton(d2ui.ButtonTypeWide, d2common.TranslateString("#1620"))
v.singlePlayerButton.SetPosition(264, 290) v.singlePlayerButton.SetPosition(264, 290)
v.singlePlayerButton.SetVisible(!v.ShowTrademarkScreen)
v.singlePlayerButton.OnActivated(func() { v.onSinglePlayerClicked() }) v.singlePlayerButton.OnActivated(func() { v.onSinglePlayerClicked() })
d2ui.AddWidget(&v.singlePlayerButton) d2ui.AddWidget(&v.singlePlayerButton)
v.multiplayerButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "MULTIPLAYER")
v.multiplayerButton.SetPosition(264, 330)
v.multiplayerButton.OnActivated(func() { v.onMultiplayerClicked() })
d2ui.AddWidget(&v.multiplayerButton)
v.githubButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "PROJECT WEBSITE") v.githubButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "PROJECT WEBSITE")
v.githubButton.SetPosition(264, 330) v.githubButton.SetPosition(264, 400)
v.githubButton.SetVisible(!v.ShowTrademarkScreen)
v.githubButton.OnActivated(func() { v.onGithubButtonClicked() }) v.githubButton.OnActivated(func() { v.onGithubButtonClicked() })
d2ui.AddWidget(&v.githubButton) d2ui.AddWidget(&v.githubButton)
v.mapTestButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "MAP ENGINE TEST") v.mapTestButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "MAP ENGINE TEST")
v.mapTestButton.SetPosition(264, 450) v.mapTestButton.SetPosition(264, 440)
v.mapTestButton.SetVisible(!v.ShowTrademarkScreen)
v.mapTestButton.OnActivated(func() { v.onMapTestClicked() }) v.mapTestButton.OnActivated(func() { v.onMapTestClicked() })
d2ui.AddWidget(&v.mapTestButton) d2ui.AddWidget(&v.mapTestButton)
v.networkTcpIpButton = d2ui.CreateButton(d2ui.ButtonTypeWide, d2common.TranslateString("#1666"))
v.networkTcpIpButton.SetPosition(264, 280)
v.networkTcpIpButton.OnActivated(func() { v.onNetworkTcpIpClicked() })
d2ui.AddWidget(&v.networkTcpIpButton)
v.networkCancelButton = d2ui.CreateButton(d2ui.ButtonTypeWide, d2common.TranslateString("cancel"))
v.networkCancelButton.SetPosition(264, 540)
v.networkCancelButton.OnActivated(func() { v.onNetworkCancelClicked() })
d2ui.AddWidget(&v.networkCancelButton)
v.btnTcpIpCancel = d2ui.CreateButton(d2ui.ButtonTypeMedium, d2common.TranslateString("cancel"))
v.btnTcpIpCancel.SetPosition(33, 543)
v.btnTcpIpCancel.OnActivated(func() { v.onTcpIpCancelClicked() })
d2ui.AddWidget(&v.btnTcpIpCancel)
v.btnTcpIpHostGame = d2ui.CreateButton(d2ui.ButtonTypeWide, d2common.TranslateString("#1675"))
v.btnTcpIpHostGame.SetPosition(264, 280)
v.btnTcpIpHostGame.OnActivated(func() { v.onTcpIpHostGameClicked() })
d2ui.AddWidget(&v.btnTcpIpHostGame)
v.btnTcpIpJoinGame = d2ui.CreateButton(d2ui.ButtonTypeWide, d2common.TranslateString("#1676"))
v.btnTcpIpJoinGame.SetPosition(264, 320)
v.btnTcpIpJoinGame.OnActivated(func() { v.onTcpIpJoinGameClicked() })
d2ui.AddWidget(&v.btnTcpIpJoinGame)
v.tcpIpOptionsLabel = d2ui.CreateLabel(d2resource.Font42, d2resource.PaletteUnits)
v.tcpIpOptionsLabel.SetPosition(400, 23)
v.tcpIpOptionsLabel.Alignment = d2ui.LabelAlignCenter
v.tcpIpOptionsLabel.SetText(d2common.TranslateString("#1667"))
animation, _ = d2asset.LoadAnimation(d2resource.PopUpOkCancel, d2resource.PaletteFechar)
v.serverIpBackground, _ = d2ui.LoadSprite(animation)
v.serverIpBackground.SetPosition(270, 175)
v.tcpJoinGameLabel = d2ui.CreateLabel(d2resource.Font16, d2resource.PaletteUnits)
v.tcpJoinGameLabel.Alignment = d2ui.LabelAlignCenter
v.tcpJoinGameLabel.SetText(d2common.CombineStrings(d2common.
SplitIntoLinesWithMaxWidth(d2common.TranslateString("#327"), 23)))
v.tcpJoinGameLabel.Color = color.RGBA{R: 216, G: 196, B: 128, A: 255}
v.tcpJoinGameLabel.SetPosition(400, 190)
v.tcpJoinGameEntry = d2ui.CreateTextbox()
v.tcpJoinGameEntry.SetPosition(318, 245)
v.tcpJoinGameEntry.SetFilter("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890._:")
d2ui.AddWidget(&v.tcpJoinGameEntry)
v.btnServerIpCancel = d2ui.CreateButton(d2ui.ButtonTypeOkCancel, d2common.TranslateString("#1612"))
v.btnServerIpCancel.SetPosition(285, 305)
v.btnServerIpCancel.OnActivated(func() { v.onBtnTcpIpCancelClicked() })
d2ui.AddWidget(&v.btnServerIpCancel)
v.btnServerIpOk = d2ui.CreateButton(d2ui.ButtonTypeOkCancel, d2common.TranslateString("#971"))
v.btnServerIpOk.SetPosition(420, 305)
v.btnServerIpOk.OnActivated(func() { v.onBtnTcpIpOkClicked() })
d2ui.AddWidget(&v.btnServerIpOk)
if v.screenMode == ScreenModeUnknown {
v.SetScreenMode(ScreenModeTrademark)
} else {
v.SetScreenMode(ScreenModeMainMenu)
}
return nil return nil
} }
@ -176,11 +266,11 @@ func openbrowser(url string) {
func (v *MainMenu) onSinglePlayerClicked() { func (v *MainMenu) onSinglePlayerClicked() {
// Go here only if existing characters are available to select // Go here only if existing characters are available to select
if d2gamestate.HasGameStates() { if d2player.HasGameStates() {
d2screen.SetNextScreen(CreateCharacterSelect()) d2screen.SetNextScreen(CreateCharacterSelect(d2clientconnectiontype.Local, v.tcpJoinGameEntry.GetText()))
return return
} }
d2screen.SetNextScreen(CreateSelectHeroClass()) d2screen.SetNextScreen(CreateSelectHeroClass(d2clientconnectiontype.Local, v.tcpJoinGameEntry.GetText()))
} }
func (v *MainMenu) onGithubButtonClicked() { func (v *MainMenu) onGithubButtonClicked() {
@ -197,20 +287,40 @@ func (v *MainMenu) onCreditsButtonClicked() {
// Render renders the main menu // Render renders the main menu
func (v *MainMenu) Render(screen d2render.Surface) error { func (v *MainMenu) Render(screen d2render.Surface) error {
if v.ShowTrademarkScreen { switch v.screenMode {
case ScreenModeTrademark:
v.trademarkBackground.RenderSegmented(screen, 4, 3, 0) v.trademarkBackground.RenderSegmented(screen, 4, 3, 0)
} else { case ScreenModeServerIp:
fallthrough
case ScreenModeTcpIp:
v.tcpIpBackground.RenderSegmented(screen, 4, 3, 0)
default:
v.background.RenderSegmented(screen, 4, 3, 0) v.background.RenderSegmented(screen, 4, 3, 0)
} }
v.diabloLogoLeftBack.Render(screen)
v.diabloLogoRightBack.Render(screen)
v.diabloLogoLeft.Render(screen)
v.diabloLogoRight.Render(screen)
if v.ShowTrademarkScreen { switch v.screenMode {
case ScreenModeTrademark:
fallthrough
case ScreenModeMainMenu:
fallthrough
case ScreenModeMultiplayer:
v.diabloLogoLeftBack.Render(screen)
v.diabloLogoRightBack.Render(screen)
v.diabloLogoLeft.Render(screen)
v.diabloLogoRight.Render(screen)
}
switch v.screenMode {
case ScreenModeServerIp:
v.tcpIpOptionsLabel.Render(screen)
v.serverIpBackground.RenderSegmented(screen, 2, 1, 0)
v.tcpJoinGameLabel.Render(screen)
case ScreenModeTcpIp:
v.tcpIpOptionsLabel.Render(screen)
case ScreenModeTrademark:
v.copyrightLabel.Render(screen) v.copyrightLabel.Render(screen)
v.copyrightLabel2.Render(screen) v.copyrightLabel2.Render(screen)
} else { case ScreenModeMainMenu:
v.openDiabloLabel.Render(screen) v.openDiabloLabel.Render(screen)
v.versionLabel.Render(screen) v.versionLabel.Render(screen)
v.commitLabel.Render(screen) v.commitLabel.Render(screen)
@ -221,29 +331,89 @@ func (v *MainMenu) Render(screen d2render.Surface) error {
// Update runs the update logic on the main menu // Update runs the update logic on the main menu
func (v *MainMenu) Advance(tickTime float64) error { func (v *MainMenu) Advance(tickTime float64) error {
v.diabloLogoLeftBack.Advance(tickTime) switch v.screenMode {
v.diabloLogoRightBack.Advance(tickTime) case ScreenModeMainMenu:
v.diabloLogoLeft.Advance(tickTime) fallthrough
v.diabloLogoRight.Advance(tickTime) case ScreenModeTrademark:
fallthrough
case ScreenModeMultiplayer:
v.diabloLogoLeftBack.Advance(tickTime)
v.diabloLogoRightBack.Advance(tickTime)
v.diabloLogoLeft.Advance(tickTime)
v.diabloLogoRight.Advance(tickTime)
case ScreenModeServerIp:
v.tcpJoinGameEntry.Update()
}
if v.ShowTrademarkScreen { switch v.screenMode {
case ScreenModeTrademark:
if d2ui.CursorButtonPressed(d2ui.CursorButtonLeft) { if d2ui.CursorButtonPressed(d2ui.CursorButtonLeft) {
if v.leftButtonHeld { if v.leftButtonHeld {
return nil return nil
} }
d2ui.WaitForMouseRelease() d2ui.WaitForMouseRelease()
v.SetScreenMode(ScreenModeMainMenu)
v.leftButtonHeld = true v.leftButtonHeld = true
v.ShowTrademarkScreen = false
v.exitDiabloButton.SetVisible(true)
v.creditsButton.SetVisible(true)
v.cinematicsButton.SetVisible(true)
v.singlePlayerButton.SetVisible(true)
v.githubButton.SetVisible(true)
v.mapTestButton.SetVisible(true)
} else { } else {
v.leftButtonHeld = false v.leftButtonHeld = false
} }
break
} }
return nil return nil
} }
func (v *MainMenu) SetScreenMode(screenMode MainMenuScreenMode) {
v.screenMode = screenMode
isMainMenu := screenMode == ScreenModeMainMenu
isMultiplayer := screenMode == ScreenModeMultiplayer
isTcpIp := screenMode == ScreenModeTcpIp
isServerIp := screenMode == ScreenModeServerIp
v.exitDiabloButton.SetVisible(isMainMenu)
v.creditsButton.SetVisible(isMainMenu)
v.cinematicsButton.SetVisible(isMainMenu)
v.singlePlayerButton.SetVisible(isMainMenu)
v.githubButton.SetVisible(isMainMenu)
v.mapTestButton.SetVisible(isMainMenu)
v.multiplayerButton.SetVisible(isMainMenu)
v.networkTcpIpButton.SetVisible(isMultiplayer)
v.networkCancelButton.SetVisible(isMultiplayer)
v.btnTcpIpCancel.SetVisible(isTcpIp)
v.btnTcpIpHostGame.SetVisible(isTcpIp)
v.btnTcpIpJoinGame.SetVisible(isTcpIp)
v.tcpJoinGameEntry.SetVisible(isServerIp)
v.btnServerIpOk.SetVisible(isServerIp)
v.btnServerIpCancel.SetVisible(isServerIp)
}
func (v *MainMenu) onNetworkCancelClicked() {
v.SetScreenMode(ScreenModeMainMenu)
}
func (v *MainMenu) onMultiplayerClicked() {
v.SetScreenMode(ScreenModeMultiplayer)
}
func (v *MainMenu) onNetworkTcpIpClicked() {
v.SetScreenMode(ScreenModeTcpIp)
}
func (v *MainMenu) onTcpIpCancelClicked() {
v.SetScreenMode(ScreenModeMultiplayer)
}
func (v *MainMenu) onTcpIpHostGameClicked() {
d2screen.SetNextScreen(CreateCharacterSelect(d2clientconnectiontype.LANServer, ""))
}
func (v *MainMenu) onTcpIpJoinGameClicked() {
v.SetScreenMode(ScreenModeServerIp)
}
func (v *MainMenu) onBtnTcpIpCancelClicked() {
v.SetScreenMode(ScreenModeTcpIp)
}
func (v *MainMenu) onBtnTcpIpOkClicked() {
d2screen.SetNextScreen(CreateCharacterSelect(d2clientconnectiontype.LANClient, v.tcpJoinGameEntry.GetText()))
}

View File

@ -4,10 +4,11 @@ import (
"math" "math"
"os" "os"
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dt1" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dt1"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gamestate"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
@ -76,7 +77,7 @@ var regions = []RegionSpec{
} }
type MapEngineTest struct { type MapEngineTest struct {
gameState *d2gamestate.GameState gameState *d2player.PlayerState
mapEngine *d2map.MapEngine mapEngine *d2map.MapEngine
mapRenderer *d2map.MapRenderer mapRenderer *d2map.MapRenderer
@ -97,7 +98,7 @@ func CreateMapEngineTest(currentRegion int, levelPreset int) *MapEngineTest {
regionSpec: RegionSpec{}, regionSpec: RegionSpec{},
filesCount: 0, filesCount: 0,
} }
result.gameState = d2gamestate.CreateTestGameState() result.gameState = d2player.CreateTestGameState()
return result return result
} }

View File

@ -4,6 +4,8 @@ import (
"image" "image"
"image/color" "image/color"
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
@ -14,7 +16,6 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gamestate"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
@ -64,12 +65,16 @@ type SelectHeroClass struct {
expansionCharLabel d2ui.Label expansionCharLabel d2ui.Label
hardcoreCheckbox d2ui.Checkbox hardcoreCheckbox d2ui.Checkbox
hardcoreCharLabel d2ui.Label hardcoreCharLabel d2ui.Label
connectionType d2clientconnectiontype.ClientConnectionType
connectionHost string
} }
func CreateSelectHeroClass() *SelectHeroClass { func CreateSelectHeroClass(connectionType d2clientconnectiontype.ClientConnectionType, connectionHost string) *SelectHeroClass {
result := &SelectHeroClass{ result := &SelectHeroClass{
heroRenderInfo: make(map[d2enum.Hero]*HeroRenderInfo), heroRenderInfo: make(map[d2enum.Hero]*HeroRenderInfo),
selectedHero: d2enum.HeroNone, selectedHero: d2enum.HeroNone,
connectionType: connectionType,
connectionHost: connectionHost,
} }
return result return result
} }
@ -420,13 +425,13 @@ func (v *SelectHeroClass) OnUnload() error {
} }
func (v SelectHeroClass) onExitButtonClicked() { func (v SelectHeroClass) onExitButtonClicked() {
d2screen.SetNextScreen(CreateCharacterSelect()) d2screen.SetNextScreen(CreateCharacterSelect(v.connectionType, v.connectionHost))
} }
func (v SelectHeroClass) onOkButtonClicked() { func (v SelectHeroClass) onOkButtonClicked() {
gameState := d2gamestate.CreateGameState(v.heroNameTextbox.GetText(), v.selectedHero, v.hardcoreCheckbox.GetCheckState()) gameState := d2player.CreatePlayerState(v.heroNameTextbox.GetText(), v.selectedHero, v.hardcoreCheckbox.GetCheckState())
gameClient, _ := d2client.Create(d2clientconnectiontype.Local) gameClient, _ := d2client.Create(d2clientconnectiontype.Local)
gameClient.Open(gameState.FilePath) gameClient.Open(v.connectionHost, gameState.FilePath)
d2screen.SetNextScreen(CreateGame(gameClient)) d2screen.SetNextScreen(CreateGame(gameClient))
} }

View File

@ -10,8 +10,6 @@ import (
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2term" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2term"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
) )
type Panel interface { type Panel interface {
@ -25,12 +23,12 @@ type Panel interface {
var missileID = 59 var missileID = 59
type GameControls struct { type GameControls struct {
hero *d2map.Player hero *d2map.Player
mapEngine *d2map.MapEngine mapEngine *d2map.MapEngine
mapRenderer *d2map.MapRenderer mapRenderer *d2map.MapRenderer
inventory *Inventory inventory *Inventory
heroStats *HeroStats heroStats *HeroStats
gameClient *d2client.GameClient inputListener InputCallbackListener
// UI // UI
globeSprite *d2ui.Sprite globeSprite *d2ui.Sprite
@ -39,19 +37,18 @@ type GameControls struct {
skillIcon *d2ui.Sprite skillIcon *d2ui.Sprite
} }
func NewGameControls(hero *d2map.Player, mapEngine *d2map.MapEngine, mapRenderer *d2map.MapRenderer, func NewGameControls(hero *d2map.Player, mapEngine *d2map.MapEngine, mapRenderer *d2map.MapRenderer, inputListener InputCallbackListener) *GameControls {
gameClient *d2client.GameClient) *GameControls {
d2term.BindAction("setmissile", "set missile id to summon on right click", func(id int) { d2term.BindAction("setmissile", "set missile id to summon on right click", func(id int) {
missileID = id missileID = id
}) })
return &GameControls{ return &GameControls{
hero: hero, hero: hero,
mapEngine: mapEngine, mapEngine: mapEngine,
gameClient: gameClient, inputListener: inputListener,
mapRenderer: mapRenderer, mapRenderer: mapRenderer,
inventory: NewInventory(), inventory: NewInventory(),
heroStats: NewHeroStats(), heroStats: NewHeroStats(),
} }
} }
@ -72,19 +69,9 @@ func (g *GameControls) OnMouseButtonDown(event d2input.MouseEvent) bool {
px, py := g.mapRenderer.ScreenToWorld(event.X, event.Y) px, py := g.mapRenderer.ScreenToWorld(event.X, event.Y)
px = float64(int(px*10)) / 10.0 px = float64(int(px*10)) / 10.0
py = float64(int(py*10)) / 10.0 py = float64(int(py*10)) / 10.0
heroPosX := g.hero.AnimatedComposite.LocationX / 5.0
heroPosY := g.hero.AnimatedComposite.LocationY / 5.0
if event.Button == d2input.MouseButtonLeft { if event.Button == d2input.MouseButtonLeft {
g.gameClient.SendPacketToServer(d2netpacket.CreateMovePlayerPacket(g.gameClient.PlayerId, heroPosX, heroPosY, px, py)) g.inputListener.OnPlayerMove(px, py)
//path, _, found := g.mapEngine.PathFind(heroPosX, heroPosY, px, py)
//if found {
// g.hero.AnimatedComposite.SetPath(path, func() {
// g.hero.AnimatedComposite.SetAnimationMode(
// d2enum.AnimationModeObjectNeutral.String(),
// )
// })
//}
return true return true
} }

View File

@ -0,0 +1,5 @@
package d2player
type InputCallbackListener interface {
OnPlayerMove(x, y float64)
}

View File

@ -1,4 +1,4 @@
package d2gamestate package d2player
import ( import (
"encoding/json" "encoding/json"
@ -8,41 +8,38 @@ import (
"path" "path"
"strconv" "strconv"
"strings" "strings"
"time"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory"
) )
type GameState struct { type PlayerState struct {
Seed int64 `json:"seed"` // TODO: Seed needs to be regenerated every time the game starts
HeroName string `json:"heroName"` HeroName string `json:"heroName"`
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"`
FilePath string `json:"-"` FilePath string `json:"-"`
Equipment d2inventory.CharacterEquipment `json:"equipment"` Equipment d2inventory.CharacterEquipment `json:"equipment"`
X float64 `json:"x"`
Y float64 `json:"y"`
} }
const GameStateVersion = uint32(2) // Update this when you make breaking changes
func HasGameStates() bool { func HasGameStates() bool {
basePath, _ := getGameBaseSavePath() basePath, _ := getGameBaseSavePath()
files, _ := ioutil.ReadDir(basePath) files, _ := ioutil.ReadDir(basePath)
return len(files) > 0 return len(files) > 0
} }
func GetAllGameStates() []*GameState { func GetAllPlayerStates() []*PlayerState {
basePath, _ := getGameBaseSavePath() basePath, _ := getGameBaseSavePath()
files, _ := ioutil.ReadDir(basePath) files, _ := ioutil.ReadDir(basePath)
result := make([]*GameState, 0) result := make([]*PlayerState, 0)
for _, file := range files { for _, file := range files {
fileName := file.Name() fileName := file.Name()
if file.IsDir() || len(fileName) < 5 || strings.ToLower(fileName[len(fileName)-4:]) != ".od2" { if file.IsDir() || len(fileName) < 5 || strings.ToLower(fileName[len(fileName)-4:]) != ".od2" {
continue continue
} }
gameState := LoadGameState(path.Join(basePath, file.Name())) gameState := LoadPlayerState(path.Join(basePath, file.Name()))
if gameState == nil { if gameState == nil {
continue continue
} }
@ -52,20 +49,18 @@ func GetAllGameStates() []*GameState {
} }
// CreateTestGameState is used for the map engine previewer // CreateTestGameState is used for the map engine previewer
func CreateTestGameState() *GameState { func CreateTestGameState() *PlayerState {
result := &GameState{ result := &PlayerState{}
Seed: time.Now().UnixNano(),
}
return result return result
} }
func LoadGameState(path string) *GameState { func LoadPlayerState(path string) *PlayerState {
strData, err := ioutil.ReadFile(path) strData, err := ioutil.ReadFile(path)
if err != nil { if err != nil {
return nil return nil
} }
result := &GameState{ result := &PlayerState{
FilePath: path, FilePath: path,
} }
err = json.Unmarshal(strData, result) err = json.Unmarshal(strData, result)
@ -75,12 +70,11 @@ func LoadGameState(path string) *GameState {
return result return result
} }
func CreateGameState(heroName string, hero d2enum.Hero, hardcore bool) *GameState { func CreatePlayerState(heroName string, hero d2enum.Hero, hardcore bool) *PlayerState {
result := &GameState{ result := &PlayerState{
HeroName: heroName, HeroName: heroName,
HeroType: hero, HeroType: hero,
Act: 1, Act: 1,
Seed: time.Now().UnixNano(),
Equipment: d2inventory.HeroObjects[hero], Equipment: d2inventory.HeroObjects[hero],
FilePath: "", FilePath: "",
} }
@ -110,7 +104,7 @@ func getFirstFreeFileName() string {
} }
} }
func (v *GameState) Save() { func (v *PlayerState) Save() {
if v.FilePath == "" { if v.FilePath == "" {
v.FilePath = getFirstFreeFileName() v.FilePath = getFirstFreeFileName()
} }

View File

@ -6,7 +6,7 @@ import (
) )
type ClientConnection interface { type ClientConnection interface {
Open(connectionString string) error Open(connectionString string, saveFilePath string) error
Close() error Close() error
SendPacketToServer(packet d2netpacket.NetPacket) error SendPacketToServer(packet d2netpacket.NetPacket) error
SetClientListener(listener d2networking.ClientListener) SetClientListener(listener d2networking.ClientListener)

View File

@ -3,5 +3,7 @@ package d2clientconnectiontype
type ClientConnectionType int type ClientConnectionType int
const ( const (
Local ClientConnectionType = 1 Local ClientConnectionType = iota
LANServer
LANClient
) )

View File

@ -1,6 +1,7 @@
package d2localclient package d2localclient
import ( import (
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
"github.com/OpenDiablo2/OpenDiablo2/d2networking" "github.com/OpenDiablo2/OpenDiablo2/d2networking"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2server" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2server"
@ -8,8 +9,10 @@ import (
) )
type LocalClientConnection struct { type LocalClientConnection struct {
clientListener d2networking.ClientListener clientListener d2networking.ClientListener
uniqueId string uniqueId string
openNetworkServer bool
playerState *d2player.PlayerState
} }
func (l LocalClientConnection) GetUniqueId() string { func (l LocalClientConnection) GetUniqueId() string {
@ -24,16 +27,18 @@ func (l *LocalClientConnection) SendPacketToClient(packet d2netpacket.NetPacket)
return l.clientListener.OnPacketReceived(packet) return l.clientListener.OnPacketReceived(packet)
} }
func Create() *LocalClientConnection { func Create(openNetworkServer bool) *LocalClientConnection {
result := &LocalClientConnection{ result := &LocalClientConnection{
uniqueId: uuid.NewV4().String(), uniqueId: uuid.NewV4().String(),
openNetworkServer: openNetworkServer,
} }
return result return result
} }
func (l *LocalClientConnection) Open(gameStatePath string) error { func (l *LocalClientConnection) Open(connectionString string, saveFilePath string) error {
d2server.Create(gameStatePath) l.SetPlayerState(d2player.LoadPlayerState(saveFilePath))
d2server.Create(l.openNetworkServer)
go d2server.Run() go d2server.Run()
d2server.OnClientConnected(l) d2server.OnClientConnected(l)
return nil return nil
@ -53,3 +58,11 @@ func (l *LocalClientConnection) SendPacketToServer(packet d2netpacket.NetPacket)
func (l *LocalClientConnection) SetClientListener(listener d2networking.ClientListener) { func (l *LocalClientConnection) SetClientListener(listener d2networking.ClientListener) {
l.clientListener = listener l.clientListener = listener
} }
func (l *LocalClientConnection) GetPlayerState() *d2player.PlayerState {
return l.playerState
}
func (l *LocalClientConnection) SetPlayerState(playerState *d2player.PlayerState) {
l.playerState = playerState
}

View File

@ -0,0 +1,155 @@
package d2remoteclient
import (
"bytes"
"compress/gzip"
"encoding/json"
"fmt"
"io"
"log"
"net"
"strings"
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
"github.com/OpenDiablo2/OpenDiablo2/d2networking"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
uuid "github.com/satori/go.uuid"
)
type RemoteClientConnection struct {
clientListener d2networking.ClientListener
uniqueId string
udpConnection *net.UDPConn
active bool
}
func (l RemoteClientConnection) GetUniqueId() string {
return l.uniqueId
}
func (l RemoteClientConnection) GetConnectionType() string {
return "Remote Client"
}
func (l *RemoteClientConnection) SendPacketToClient(packet d2netpacket.NetPacket) error { // WHAT IS THIS
return l.clientListener.OnPacketReceived(packet)
}
func Create() *RemoteClientConnection {
result := &RemoteClientConnection{
uniqueId: uuid.NewV4().String(),
}
return result
}
func (l *RemoteClientConnection) Open(connectionString string, saveFilePath string) error {
if strings.Index(connectionString, ":") == -1 {
connectionString += ":6669"
}
// TODO: Connect to the server
udpAddress, err := net.ResolveUDPAddr("udp", connectionString)
// TODO: Show connection error screen if connection fails
if err != nil {
return err
}
l.udpConnection, err = net.DialUDP("udp", nil, udpAddress)
// TODO: Show connection error screen if connection fails
if err != nil {
return err
}
l.active = true
go l.serverListener()
log.Printf("Connected to server at %s", l.udpConnection.RemoteAddr().String())
gameState := d2player.LoadPlayerState(saveFilePath)
l.SendPacketToServer(d2netpacket.CreatePlayerConnectionRequestPacket(l.GetUniqueId(), gameState))
return nil
}
func (l *RemoteClientConnection) Close() error {
l.active = false
// TODO: Disconnect from the server - send a disconnect packet
return nil
}
func (l *RemoteClientConnection) SendPacketToServer(packet d2netpacket.NetPacket) error {
data, err := json.Marshal(packet.PacketData)
if err != nil {
return err
}
var buff bytes.Buffer
buff.WriteByte(byte(packet.PacketType))
writer, _ := gzip.NewWriterLevel(&buff, gzip.BestCompression)
writer.Write(data)
writer.Close()
if _, err = l.udpConnection.Write(buff.Bytes()); err != nil {
return err
}
return nil
}
func (l *RemoteClientConnection) SetClientListener(listener d2networking.ClientListener) {
l.clientListener = listener
}
func (l *RemoteClientConnection) serverListener() {
buffer := make([]byte, 4096)
for l.active {
n, _, err := l.udpConnection.ReadFromUDP(buffer)
if err != nil {
fmt.Printf("Socket error: %s\n", err)
continue
}
if n <= 0 {
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.GenerateMap:
var packet d2netpacket.GenerateMapPacket
json.Unmarshal([]byte(stringData), &packet)
l.SendPacketToClient(d2netpacket.NetPacket{
PacketType: packetType,
PacketData: packet,
})
case d2netpackettype.MovePlayer:
var packet d2netpacket.MovePlayerPacket
json.Unmarshal([]byte(stringData), &packet)
l.SendPacketToClient(d2netpacket.NetPacket{
PacketType: packetType,
PacketData: packet,
})
case d2netpackettype.UpdateServerInfo:
var packet d2netpacket.UpdateServerInfoPacket
json.Unmarshal([]byte(stringData), &packet)
l.SendPacketToClient(d2netpacket.NetPacket{
PacketType: packetType,
PacketData: packet,
})
case d2netpackettype.AddPlayer:
var packet d2netpacket.AddPlayerPacket
json.Unmarshal([]byte(stringData), &packet)
l.SendPacketToClient(d2netpacket.NetPacket{
PacketType: packetType,
PacketData: packet,
})
default:
fmt.Printf("Unknown packet type %d\n", packetType)
}
}
}

View File

@ -4,21 +4,21 @@ import (
"fmt" "fmt"
"log" "log"
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gamestate"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2localclient" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/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
GameState *d2gamestate.GameState connectionType d2clientconnectiontype.ClientConnectionType
GameState *d2player.PlayerState
MapEngine *d2map.MapEngine MapEngine *d2map.MapEngine
PlayerId string PlayerId string
Players map[string]*d2map.Player Players map[string]*d2map.Player
@ -26,23 +26,27 @@ type GameClient struct {
func Create(connectionType d2clientconnectiontype.ClientConnectionType) (*GameClient, error) { func Create(connectionType d2clientconnectiontype.ClientConnectionType) (*GameClient, error) {
result := &GameClient{ result := &GameClient{
MapEngine: d2map.CreateMapEngine(), MapEngine: d2map.CreateMapEngine(),
Players: make(map[string]*d2map.Player, 0), Players: make(map[string]*d2map.Player, 0),
connectionType: connectionType,
} }
switch connectionType { switch connectionType {
case d2clientconnectiontype.LANClient:
result.clientConnection = d2remoteclient.Create()
case d2clientconnectiontype.LANServer:
result.clientConnection = d2localclient.Create(true)
case d2clientconnectiontype.Local: case d2clientconnectiontype.Local:
result.clientConnection = d2localclient.Create() result.clientConnection = d2localclient.Create(false)
result.clientConnection.SetClientListener(result)
default: default:
return nil, fmt.Errorf("unknown client connection type specified: %d", connectionType) return nil, fmt.Errorf("unknown client connection type specified: %d", connectionType)
} }
result.clientConnection.SetClientListener(result)
return result, nil return result, nil
} }
func (g *GameClient) Open(connectionString string) error { func (g *GameClient) Open(connectionString string, saveFilePath string) error {
return g.clientConnection.Open(connectionString) return g.clientConnection.Open(connectionString, saveFilePath)
} }
func (g *GameClient) Close() error { func (g *GameClient) Close() error {
@ -67,7 +71,7 @@ func (g *GameClient) OnPacketReceived(packet d2netpacket.NetPacket) error {
break break
case d2netpackettype.AddPlayer: case d2netpackettype.AddPlayer:
player := packet.PacketData.(d2netpacket.AddPlayerPacket) player := packet.PacketData.(d2netpacket.AddPlayerPacket)
newPlayer := d2map.CreatePlayer(player.Id, player.X, player.Y, 0, player.HeroType, player.Equipment) newPlayer := d2map.CreatePlayer(player.Id, player.Name, player.X, player.Y, 0, player.HeroType, player.Equipment)
g.Players[newPlayer.Id] = newPlayer g.Players[newPlayer.Id] = newPlayer
g.MapEngine.AddEntity(newPlayer) g.MapEngine.AddEntity(newPlayer)
break break

View File

@ -4,9 +4,12 @@ type NetPacketType uint32
// Warning: Do NOT re-arrange the order of these packet values unless you want to // Warning: Do NOT re-arrange the order of these packet values unless you want to
// break compatibility between clients of slightly different versions. // break compatibility between clients of slightly different versions.
// Also note that the packet id is a byte, so if we use more than 256
// of these then we are doing something very wrong.
const ( const (
UpdateServerInfo NetPacketType = iota UpdateServerInfo NetPacketType = iota
GenerateMap GenerateMap // Sent by the server to generate a map
AddPlayer AddPlayer // Server sends to the client to add a player
MovePlayer MovePlayer // Sent to the client or server to indicate player movement
PlayerConnectionRequest // Client sends to server to request a connection
) )

View File

@ -8,17 +8,19 @@ import (
type AddPlayerPacket struct { type AddPlayerPacket struct {
Id string `json:"id"` Id string `json:"id"`
Name string `json:"name"`
X int `json:"x"` X int `json:"x"`
Y int `json:"y"` Y int `json:"y"`
HeroType d2enum.Hero `json:"hero"` HeroType d2enum.Hero `json:"hero"`
Equipment d2inventory.CharacterEquipment `json:"equipment"` Equipment d2inventory.CharacterEquipment `json:"equipment"`
} }
func CreateAddPlayerPacket(id string, x, y int, heroType d2enum.Hero, equipment d2inventory.CharacterEquipment) NetPacket { func CreateAddPlayerPacket(id, name string, x, y int, heroType d2enum.Hero, equipment d2inventory.CharacterEquipment) NetPacket {
return NetPacket{ return NetPacket{
PacketType: d2netpackettype.AddPlayer, PacketType: d2netpackettype.AddPlayer,
PacketData: AddPlayerPacket{ PacketData: AddPlayerPacket{
Id: id, Id: id,
Name: name,
X: x, X: x,
Y: y, Y: y,
HeroType: heroType, HeroType: heroType,

View File

@ -0,0 +1,21 @@
package d2netpacket
import (
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
)
type PlayerConnectionRequestPacket struct {
Id string `json:"id"`
PlayerState *d2player.PlayerState `json:"gameState"`
}
func CreatePlayerConnectionRequestPacket(id string, playerState *d2player.PlayerState) NetPacket {
return NetPacket{
PacketType: d2netpackettype.PlayerConnectionRequest,
PacketData: PlayerConnectionRequestPacket{
Id: id,
PlayerState: playerState,
},
}
}

View File

@ -1,9 +1,14 @@
package d2server package d2server
import "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket" import (
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
)
type ClientConnection interface { type ClientConnection interface {
GetUniqueId() string GetUniqueId() string
GetConnectionType() string GetConnectionType() string
SendPacketToClient(packet d2netpacket.NetPacket) error SendPacketToClient(packet d2netpacket.NetPacket) error
GetPlayerState() *d2player.PlayerState
SetPlayerState(playerState *d2player.PlayerState)
} }

View File

@ -0,0 +1,60 @@
package d2udpclientconnection
import (
"bytes"
"compress/gzip"
"encoding/json"
"net"
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2player"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
)
type UDPClientConnection struct {
id string
address *net.UDPAddr
udpConnection *net.UDPConn
playerState *d2player.PlayerState
}
func CreateUDPClientConnection(udpConnection *net.UDPConn, id string, address *net.UDPAddr) *UDPClientConnection {
result := &UDPClientConnection{
id: id,
address: address,
udpConnection: udpConnection,
}
return result
}
func (u UDPClientConnection) GetUniqueId() string {
return u.id
}
func (u UDPClientConnection) GetConnectionType() string {
return "Remote Client"
}
func (u *UDPClientConnection) SendPacketToClient(packet d2netpacket.NetPacket) error {
data, err := json.Marshal(packet.PacketData)
if err != nil {
return err
}
var buff bytes.Buffer
buff.WriteByte(byte(packet.PacketType))
writer, _ := gzip.NewWriterLevel(&buff, gzip.BestCompression)
writer.Write(data)
writer.Close()
u.udpConnection.WriteToUDP(buff.Bytes(), u.address)
return nil
}
func (u *UDPClientConnection) SetPlayerState(playerState *d2player.PlayerState) {
u.playerState = playerState
}
func (u *UDPClientConnection) GetPlayerState() *d2player.PlayerState {
return u.playerState
}

View File

@ -1,27 +1,37 @@
package d2server package d2server
import ( import (
"bytes"
"compress/gzip"
"encoding/json"
"fmt"
"io"
"log" "log"
"net"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype" "strings"
"time"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2gamestate"
"github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2map"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype"
"github.com/OpenDiablo2/OpenDiablo2/d2networking/d2server/d2udpclientconnection"
"github.com/OpenDiablo2/OpenDiablo2/d2script"
"github.com/robertkrimen/otto"
) )
type GameServer struct { type GameServer struct {
gameState *d2gamestate.GameState
clientConnections map[string]ClientConnection clientConnections map[string]ClientConnection
mapEngines []*d2map.MapEngine mapEngines []*d2map.MapEngine
scriptEngine *d2script.ScriptEngine
udpConnection *net.UDPConn
seed int64
running bool
} }
var singletonServer *GameServer var singletonServer *GameServer
func Create(gameStatePath string) { func Create(openNetworkServer bool) {
log.Print("Creating GameServer") log.Print("Creating GameServer")
if singletonServer != nil { if singletonServer != nil {
return return
@ -30,20 +40,95 @@ func Create(gameStatePath string) {
singletonServer = &GameServer{ singletonServer = &GameServer{
clientConnections: make(map[string]ClientConnection), clientConnections: make(map[string]ClientConnection),
mapEngines: make([]*d2map.MapEngine, 0), mapEngines: make([]*d2map.MapEngine, 0),
gameState: d2gamestate.LoadGameState(gameStatePath), //gameState: d2player.LoadPlayerState(gameStatePath),
scriptEngine: d2script.CreateScriptEngine(),
seed: time.Now().UnixNano(),
} }
mapEngine := d2map.CreateMapEngine() mapEngine := d2map.CreateMapEngine()
mapEngine.GenerateMap(d2enum.RegionAct1Town, 1, 0, false) mapEngine.GenerateMap(d2enum.RegionAct1Town, 1, 0, false)
singletonServer.mapEngines = append(singletonServer.mapEngines, mapEngine) singletonServer.mapEngines = append(singletonServer.mapEngines, mapEngine)
singletonServer.scriptEngine.AddFunction("getMapEngines", func(call otto.FunctionCall) otto.Value {
val, err := singletonServer.scriptEngine.ToValue(singletonServer.mapEngines)
if err != nil {
fmt.Print(err.Error())
}
return val
})
if openNetworkServer {
createNetworkServer()
}
}
func createNetworkServer() {
s, err := net.ResolveUDPAddr("udp4", "0.0.0.0:6669")
if err != nil {
panic(err)
}
singletonServer.udpConnection, err = net.ListenUDP("udp4", s)
if err != nil {
panic(err)
}
singletonServer.udpConnection.SetReadBuffer(4096)
}
func runNetworkServer() {
buffer := make([]byte, 4096)
for singletonServer.running {
_, addr, err := singletonServer.udpConnection.ReadFromUDP(buffer)
if err != nil {
fmt.Printf("Socket error: %s\n", err)
continue
}
buff := bytes.NewBuffer(buffer)
packetTypeId, err := buff.ReadByte()
packetType := d2netpackettype.NetPacketType(packetTypeId)
reader, err := gzip.NewReader(buff)
sb := new(strings.Builder)
io.Copy(sb, reader)
stringData := sb.String()
switch packetType {
case d2netpackettype.PlayerConnectionRequest:
packetData := d2netpacket.PlayerConnectionRequestPacket{}
json.Unmarshal([]byte(stringData), &packetData)
clientConnection := d2udpclientconnection.CreateUDPClientConnection(singletonServer.udpConnection, packetData.Id, addr)
clientConnection.SetPlayerState(packetData.PlayerState)
OnClientConnected(clientConnection)
case d2netpackettype.MovePlayer:
packetData := d2netpacket.MovePlayerPacket{}
json.Unmarshal([]byte(stringData), &packetData)
netPacket := d2netpacket.NetPacket{
PacketType: packetType,
PacketData: packetData,
}
for _, player := range singletonServer.clientConnections {
player.SendPacketToClient(netPacket)
}
}
}
} }
func Run() { func Run() {
log.Print("Starting GameServer") 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() { func Stop() {
log.Print("Stopping GameServer") log.Print("Stopping GameServer")
singletonServer.running = false
if singletonServer.udpConnection != nil {
singletonServer.udpConnection.Close()
}
} }
func Destroy() { func Destroy() {
@ -55,17 +140,30 @@ func Destroy() {
} }
func OnClientConnected(client ClientConnection) { 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()) log.Printf("Client connected with an id of %s", client.GetUniqueId())
singletonServer.clientConnections[client.GetUniqueId()] = client singletonServer.clientConnections[client.GetUniqueId()] = client
client.SendPacketToClient(d2netpacket.CreateUpdateServerInfoPacket(singletonServer.gameState.Seed, client.GetUniqueId())) client.SendPacketToClient(d2netpacket.CreateUpdateServerInfoPacket(singletonServer.seed, client.GetUniqueId()))
client.SendPacketToClient(d2netpacket.CreateGenerateMapPacket(d2enum.RegionAct1Town, 1, 0)) client.SendPacketToClient(d2netpacket.CreateGenerateMapPacket(d2enum.RegionAct1Town, 1, 0))
// TODO: This needs to use a real method of loading characters instead of cloning the 'save file character' playerState := client.GetPlayerState()
sx, sy := singletonServer.mapEngines[0].GetStartPosition() // TODO: Another temporary hack createPlayerPacket := d2netpacket.CreateAddPlayerPacket(client.GetUniqueId(), playerState.HeroName, int(sx*5)+3, int(sy*5)+3,
createPlayerPacket := d2netpacket.CreateAddPlayerPacket(client.GetUniqueId(), int(sx*5)+3, int(sy*5)+3, playerState.HeroType, playerState.Equipment)
singletonServer.gameState.HeroType, singletonServer.gameState.Equipment)
for _, connection := range singletonServer.clientConnections { for _, connection := range singletonServer.clientConnections {
connection.SendPacketToClient(createPlayerPacket) 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.Equipment))
} }
} }
@ -79,6 +177,12 @@ func OnPacketReceived(client ClientConnection, packet d2netpacket.NetPacket) err
switch packet.PacketType { switch packet.PacketType {
case d2netpackettype.MovePlayer: case d2netpackettype.MovePlayer:
// TODO: This needs to be verified on the server (here) before sending to other clients.... // 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 { for _, player := range singletonServer.clientConnections {
player.SendPacketToClient(packet) player.SendPacketToClient(packet)
} }

44
d2script/scriptengine.go Normal file
View File

@ -0,0 +1,44 @@
package d2script
import (
"fmt"
"io/ioutil"
"github.com/robertkrimen/otto"
_ "github.com/robertkrimen/otto/underscore" // This causes the runtime to support underscore.js
)
type ScriptEngine struct {
vm *otto.Otto
}
func CreateScriptEngine() *ScriptEngine {
result := &ScriptEngine{
vm: otto.New(),
}
result.vm.Set("debugPrint", func(call otto.FunctionCall) otto.Value {
fmt.Printf("Script: %s\n", call.Argument(0).String())
return otto.Value{}
})
return result
}
func (s *ScriptEngine) ToValue(source interface{}) (otto.Value, error) {
return s.vm.ToValue(source)
}
func (s *ScriptEngine) AddFunction(name string, value interface{}) {
s.vm.Set(name, value)
}
func (s *ScriptEngine) RunScript(fileName string) (*otto.Value, error) {
fileData, _ := ioutil.ReadFile(fileName)
val, err := s.vm.Run(string(fileData))
if err != nil {
fmt.Printf("Error running script: %s\n", err.Error())
return nil, err
}
return &val, nil
}

2
go.mod
View File

@ -11,10 +11,12 @@ require (
github.com/hajimehoshi/ebiten v1.11.0-alpha.2.0.20200102072751-e66f1fb71e2e github.com/hajimehoshi/ebiten v1.11.0-alpha.2.0.20200102072751-e66f1fb71e2e
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
github.com/pebbe/zmq4 v1.2.1 // indirect github.com/pebbe/zmq4 v1.2.1 // indirect
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff
github.com/satori/go.uuid v1.2.0 github.com/satori/go.uuid v1.2.0
github.com/stretchr/testify v1.4.0 github.com/stretchr/testify v1.4.0
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 // indirect golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 // indirect
golang.org/x/mobile v0.0.0-20191115022231-f0c40035f2ba // indirect golang.org/x/mobile v0.0.0-20191115022231-f0c40035f2ba // indirect
golang.org/x/sys v0.0.0-20191115151921-52ab43148777 // indirect golang.org/x/sys v0.0.0-20191115151921-52ab43148777 // indirect
gopkg.in/alecthomas/kingpin.v2 v2.2.6 gopkg.in/alecthomas/kingpin.v2 v2.2.6
gopkg.in/sourcemap.v1 v1.0.5 // indirect
) )

32
go.sum
View File

@ -14,6 +14,8 @@ github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72 h1:b+9H1GAsx5RsjvDFLoS5zkNBzIQMuVKUYQDmxU3N5XE= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72 h1:b+9H1GAsx5RsjvDFLoS5zkNBzIQMuVKUYQDmxU3N5XE=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I=
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
github.com/go-restruct/restruct v0.0.0-20191227155143-5734170a48a1 h1:LoN2wx/aN8JPGebG+2DaUyk4M+xRcqJXfuIbs8AWHdE= github.com/go-restruct/restruct v0.0.0-20191227155143-5734170a48a1 h1:LoN2wx/aN8JPGebG+2DaUyk4M+xRcqJXfuIbs8AWHdE=
github.com/go-restruct/restruct v0.0.0-20191227155143-5734170a48a1/go.mod h1:KqrpKpn4M8OLznErihXTGLlsXFGeLxHUrLRRI/1YjGk= github.com/go-restruct/restruct v0.0.0-20191227155143-5734170a48a1/go.mod h1:KqrpKpn4M8OLznErihXTGLlsXFGeLxHUrLRRI/1YjGk=
github.com/gofrs/flock v0.7.1 h1:DP+LD/t0njgoPBvT5MJLeliUIVQR03hiKR6vezdwHlc= github.com/gofrs/flock v0.7.1 h1:DP+LD/t0njgoPBvT5MJLeliUIVQR03hiKR6vezdwHlc=
@ -22,12 +24,18 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGw
github.com/hajimehoshi/bitmapfont v1.2.0/go.mod h1:h9QrPk6Ktb2neObTlAbma6Ini1xgMjbJ3w7ysmD7IOU= github.com/hajimehoshi/bitmapfont v1.2.0/go.mod h1:h9QrPk6Ktb2neObTlAbma6Ini1xgMjbJ3w7ysmD7IOU=
github.com/hajimehoshi/ebiten v1.11.0-alpha.2.0.20200102072751-e66f1fb71e2e h1:NRGAeXOSMrAo0f4GPaUoiF61eo0wWrfgONPSTZA6Zhg= github.com/hajimehoshi/ebiten v1.11.0-alpha.2.0.20200102072751-e66f1fb71e2e h1:NRGAeXOSMrAo0f4GPaUoiF61eo0wWrfgONPSTZA6Zhg=
github.com/hajimehoshi/ebiten v1.11.0-alpha.2.0.20200102072751-e66f1fb71e2e/go.mod h1:0SLvfr8iI2NxzpNB/olBM+dLN9Ur5a9szG13wOgQ0nQ= github.com/hajimehoshi/ebiten v1.11.0-alpha.2.0.20200102072751-e66f1fb71e2e/go.mod h1:0SLvfr8iI2NxzpNB/olBM+dLN9Ur5a9szG13wOgQ0nQ=
github.com/hajimehoshi/ebiten v1.12.0-alpha.2.0.20200614175121-0b94e2e03645 h1:4h6uuzsCz94SOxvEZl+w8ICu90QHvn4l/G9VB7poNyw=
github.com/hajimehoshi/ebiten v1.12.0-alpha.2.0.20200614175121-0b94e2e03645/go.mod h1:T+uHfxpwulzepUikvWZ4F0G9gt8RzZnrKjH6BgoWDgw=
github.com/hajimehoshi/go-mp3 v0.2.1/go.mod h1:Rr+2P46iH6PwTPVgSsEwBkon0CK5DxCAeX/Rp65DCTE= github.com/hajimehoshi/go-mp3 v0.2.1/go.mod h1:Rr+2P46iH6PwTPVgSsEwBkon0CK5DxCAeX/Rp65DCTE=
github.com/hajimehoshi/go-mp3 v0.3.0/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM=
github.com/hajimehoshi/oto v0.3.4/go.mod h1:PgjqsBJff0efqL2nlMJidJgVJywLn6M4y8PI4TfeWfA= github.com/hajimehoshi/oto v0.3.4/go.mod h1:PgjqsBJff0efqL2nlMJidJgVJywLn6M4y8PI4TfeWfA=
github.com/hajimehoshi/oto v0.5.4 h1:Dn+WcYeF310xqStKm0tnvoruYUV5Sce8+sfUaIvWGkE= github.com/hajimehoshi/oto v0.5.4 h1:Dn+WcYeF310xqStKm0tnvoruYUV5Sce8+sfUaIvWGkE=
github.com/hajimehoshi/oto v0.5.4/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI= github.com/hajimehoshi/oto v0.5.4/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
github.com/hajimehoshi/oto v0.6.1 h1:7cJz/zRQV4aJvMSSRqzN2TImoVVMpE0BCY4nrNJaDOM=
github.com/hajimehoshi/oto v0.6.1/go.mod h1:0QXGEkbuJRohbJaxr7ZQSxnju7hEhseiPx2hrh6raOI=
github.com/jakecoffman/cp v0.1.0/go.mod h1:a3xPx9N8RyFAACD644t2dj/nK4SuLg1v+jL61m2yVo4= github.com/jakecoffman/cp v0.1.0/go.mod h1:a3xPx9N8RyFAACD644t2dj/nK4SuLg1v+jL61m2yVo4=
github.com/jfreymuth/oggvorbis v1.0.0/go.mod h1:abe6F9QRjuU9l+2jek3gj46lu40N4qlYxh2grqkLEDM= github.com/jfreymuth/oggvorbis v1.0.0/go.mod h1:abe6F9QRjuU9l+2jek3gj46lu40N4qlYxh2grqkLEDM=
github.com/jfreymuth/oggvorbis v1.0.1/go.mod h1:NqS+K+UXKje0FUYUPosyQ+XTVvjmVjps1aEZH1sumIk=
github.com/jfreymuth/vorbis v1.0.0/go.mod h1:8zy3lUAm9K/rJJk223RKy6vjCZTWC61NA2QD06bfOE0= github.com/jfreymuth/vorbis v1.0.0/go.mod h1:8zy3lUAm9K/rJJk223RKy6vjCZTWC61NA2QD06bfOE0=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
@ -37,6 +45,7 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/mattn/go-sqlite3 v1.13.0 h1:LnJI81JidiW9r7pS/hXe6cFeO5EXNq7KbfvoJLRI69c= github.com/mattn/go-sqlite3 v1.13.0 h1:LnJI81JidiW9r7pS/hXe6cFeO5EXNq7KbfvoJLRI69c=
github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= github.com/mattn/go-sqlite3 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U=
github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= github.com/mattn/go-sqlite3 v2.0.3+incompatible/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pebbe/zmq4 v1.2.1 h1:jrXQW3mD8Si2mcSY/8VBs2nNkK/sKCOEM0rHAfxyc8c= github.com/pebbe/zmq4 v1.2.1 h1:jrXQW3mD8Si2mcSY/8VBs2nNkK/sKCOEM0rHAfxyc8c=
github.com/pebbe/zmq4 v1.2.1/go.mod h1:7N4y5R18zBiu3l0vajMUWQgZyjv464prE8RCyBcmnZM= github.com/pebbe/zmq4 v1.2.1/go.mod h1:7N4y5R18zBiu3l0vajMUWQgZyjv464prE8RCyBcmnZM=
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA= github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
@ -44,13 +53,17 @@ github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff h1:+6NUiITWwE5q1KO6SAfUX918c+Tab0+tGAM/mtdlUyA=
github.com/robertkrimen/otto v0.0.0-20191219234010-c382bd3c16ff/go.mod h1:xvqspoSXJTIpemEonrMDFq6XzwHYYgToXWj5eRX1OtY=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk= github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= golang.org/x/exp v0.0.0-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4=
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw= golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 h1:A1gGSx58LAGVHUUsOf7IiR0u8Xb6W51gRwfDBhkdcaw=
@ -60,17 +73,26 @@ golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9/go.mod h1:FeLwcggjj3mMvU+o
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8 h1:hVwzHzIUGRjiF7EcUjqNxk3NCfkPxbDKRdnNE1Rpg0U=
golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20191009234506-e7c1f5e7dbb8/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/image v0.0.0-20200119044424-58c23975cae1 h1:5h3ngYt7+vXCDZCup/HkCQgW5XwmSvR/nA2JmJ0RErg=
golang.org/x/image v0.0.0-20200119044424-58c23975cae1/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE= golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o= golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
golang.org/x/mobile v0.0.0-20191025110607-73ccc5ba0426/go.mod h1:p895TfNkDgPEmEQrNiOtIl3j98d/tGU95djDj7NfyjQ= golang.org/x/mobile v0.0.0-20191025110607-73ccc5ba0426/go.mod h1:p895TfNkDgPEmEQrNiOtIl3j98d/tGU95djDj7NfyjQ=
golang.org/x/mobile v0.0.0-20191115022231-f0c40035f2ba h1:NVszahdZPQTROdO0F5gnXdZhGl2lXFb9w7Ek1F2Pbmk= golang.org/x/mobile v0.0.0-20191115022231-f0c40035f2ba h1:NVszahdZPQTROdO0F5gnXdZhGl2lXFb9w7Ek1F2Pbmk=
golang.org/x/mobile v0.0.0-20191115022231-f0c40035f2ba/go.mod h1:p895TfNkDgPEmEQrNiOtIl3j98d/tGU95djDj7NfyjQ= golang.org/x/mobile v0.0.0-20191115022231-f0c40035f2ba/go.mod h1:p895TfNkDgPEmEQrNiOtIl3j98d/tGU95djDj7NfyjQ=
golang.org/x/mobile v0.0.0-20200329125638-4c31acba0007 h1:JxsyO7zPDWn1rBZW8FV5RFwCKqYeXnyaS/VQPLpXu6I=
golang.org/x/mobile v0.0.0-20200329125638-4c31acba0007/go.mod h1:skQtrUTUwhdJvXM/2KKJzY8pDgNr9I/FOMqDVRPBUS4=
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY= golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
golang.org/x/mod v0.1.1-0.20191209134235-331c550502dd/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@ -78,16 +100,26 @@ golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7w
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777 h1:wejkGHRTr38uaKRqECZlsCsJ1/TGxIyFbH32x5zUdu4= golang.org/x/sys v0.0.0-20191115151921-52ab43148777 h1:wejkGHRTr38uaKRqECZlsCsJ1/TGxIyFbH32x5zUdu4=
golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191115151921-52ab43148777/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775 h1:TC0v2RSO1u2kn1ZugjrFXkRZAEaqMN/RW+OTZkBzmLE=
golang.org/x/sys v0.0.0-20200327173247-9dae0f8f5775/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190909214602-067311248421/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20190909214602-067311248421/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191026034945-b2104f82a97d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20191026034945-b2104f82a97d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20200117012304-6edc0a871e69/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
golang.org/x/tools v0.0.0-20200330175517-31583a0dbbc8/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/sourcemap.v1 v1.0.5 h1:inv58fC9f9J3TK2Y2R1NPntXEn3/wjWHkonhIUODNTI=
gopkg.in/sourcemap.v1 v1.0.5/go.mod h1:2RlvNNSMglmRrcvhfuzp4hQHwOtjxlbjX7UPY/GXb78=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

View File

@ -13,6 +13,8 @@ import (
"strconv" "strconv"
"sync" "sync"
"github.com/OpenDiablo2/OpenDiablo2/d2script"
"github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen"
"github.com/OpenDiablo2/OpenDiablo2/d2game/d2gamescreen" "github.com/OpenDiablo2/OpenDiablo2/d2game/d2gamescreen"
@ -190,6 +192,8 @@ func initialize() error {
d2ui.Initialize() d2ui.Initialize()
d2script.CreateScriptEngine()
return nil return nil
} }

1
scripts/server/server.js Normal file
View File

@ -0,0 +1 @@
debugPrint('Server script initialized.')

3
scripts/test.js Normal file
View File

@ -0,0 +1,3 @@
// debugPrint(test())