diff --git a/d2common/d2fileformats/d2ds1/ds1.go b/d2common/d2fileformats/d2ds1/ds1.go index 86ec24f3..2e5f5362 100644 --- a/d2common/d2fileformats/d2ds1/ds1.go +++ b/d2common/d2fileformats/d2ds1/ds1.go @@ -26,8 +26,9 @@ type DS1 struct { func LoadDS1(fileData []byte) (*DS1, error) { ds1 := &DS1{ - NumberOfFloors: 1, - NumberOfWalls: 1, + Act: 1, + NumberOfFloors: 0, + NumberOfWalls: 0, NumberOfShadowLayers: 1, NumberOfSubstitutionLayers: 0, } @@ -61,7 +62,7 @@ func LoadDS1(fileData []byte) (*DS1, error) { } if ds1.Version >= 9 && ds1.Version <= 13 { // Skipping two dwords because they are "meaningless"? - br.SkipBytes(16) + br.SkipBytes(8) } if ds1.Version >= 4 { ds1.NumberOfWalls = br.GetInt32() @@ -179,8 +180,7 @@ func LoadDS1(fileData []byte) (*DS1, error) { newObject.X = int(br.GetInt32()) newObject.Y = 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), int(newObject.Type), int(newObject.Id)) + newObject.Lookup = d2datadict.LookupObject(int(ds1.Act), newObject.Type, newObject.Id) if newObject.Lookup != nil && newObject.Lookup.ObjectsTxtId != -1 { newObject.ObjectInfo = d2datadict.Objects[newObject.Lookup.ObjectsTxtId] } diff --git a/d2common/d2resource/resource_paths.go b/d2common/d2resource/resource_paths.go index 5092ee7c..e1462dad 100644 --- a/d2common/d2resource/resource_paths.go +++ b/d2common/d2resource/resource_paths.go @@ -11,6 +11,7 @@ const ( TrademarkScreen = "/data/global/ui/FrontEnd/trademarkscreenEXP.dc6" GameSelectScreen = "/data/global/ui/FrontEnd/gameselectscreenEXP.dc6" + TcpIpBackground = "/data/global/ui/FrontEnd/TCPIPscreen.dc6" Diablo2LogoFireLeft = "/data/global/ui/FrontEnd/D2logoFireLeft.DC6" Diablo2LogoFireRight = "/data/global/ui/FrontEnd/D2logoFireRight.DC6" Diablo2LogoBlackLeft = "/data/global/ui/FrontEnd/D2logoBlackLeft.DC6" diff --git a/d2common/text_dictionary.go b/d2common/text_dictionary.go index 761f608e..d26696c6 100644 --- a/d2common/text_dictionary.go +++ b/d2common/text_dictionary.go @@ -92,16 +92,16 @@ func LoadDictionary(dictionaryData []byte) { } // Use the following code to write out the values - /* - f, err := os.OpenFile(`C:\Users\lunat\Desktop\D2\langdict.txt`, - os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) - if err != nil { - log.Println(err) - } - defer f.Close() - if _, err := f.WriteString("\n[" + key + "] " + value); err != nil { - log.Println(err) - } + /*= + f, err := os.OpenFile(`C:\Users\lunat\Desktop\D2\langdict.txt`, + os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) + if err != nil { + log.Println(err) + } + defer f.Close() + if _, err := f.WriteString("\n[" + key + "] " + value); err != nil { + log.Println(err) + } */ } } diff --git a/d2core/d2map/engine.go b/d2core/d2map/engine.go index ae86f37b..cb10cbd8 100644 --- a/d2core/d2map/engine.go +++ b/d2core/d2map/engine.go @@ -18,12 +18,14 @@ type MapEntity interface { GetPosition() (float64, float64) } +// Represents the map data for a specific location type MapEngine struct { seed int64 regions []*MapRegion entities MapEntitiesSearcher } +// Creates a new instance of the map engine func CreateMapEngine() *MapEngine { engine := &MapEngine{ seed: 0, @@ -48,6 +50,7 @@ func (m *MapEngine) GetStartPosition() (float64, float64) { return startX, startY } +// Returns the center of the map func (m *MapEngine) GetCenterPosition() (float64, float64) { var centerX, centerY float64 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) { // TODO: Stitch together region.walkableArea + log.Printf("Warning: Walkable areas are not currently implemented") m.regions = append(m.regions, region) } +// Returns the region located at the specified tile location func (m *MapEngine) GetRegionAtTile(x, y int) *MapRegion { for _, region := range m.regions { if region.tileRect.IsInRect(x, y) { diff --git a/d2core/d2map/player.go b/d2core/d2map/player.go index f37b63e7..e27ff61a 100644 --- a/d2core/d2map/player.go +++ b/d2core/d2map/player.go @@ -1,11 +1,14 @@ package d2map import ( + "image/color" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" ) type Player struct { @@ -14,9 +17,11 @@ type Player struct { Id string mode d2enum.AnimationMode 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{ Mode: d2enum.AnimationModePlayerNeutral.String(), Base: "/data/global/chars", @@ -44,18 +49,32 @@ func CreatePlayer(id string, x, y int, direction int, heroType d2enum.Hero, equi Equipment: equipment, mode: d2enum.AnimationModePlayerTownNeutral, 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 } func (v *Player) Advance(tickTime float64) { v.Step(tickTime) v.AnimatedComposite.Advance(tickTime) + } func (v *Player) Render(target d2render.Surface) { 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) { diff --git a/d2core/d2map/region.go b/d2core/d2map/region.go index 11bc1e71..0f5d1138 100644 --- a/d2core/d2map/region.go +++ b/d2core/d2map/region.go @@ -676,6 +676,10 @@ func (mr *MapRegion) generateShadowCache(tile *d2ds1.FloorShadowRecord, tileX, t tileData = &tileOptions[tileIndex] } + if tileData.Width == 0 || tileData.Height == 0 { + return + } + tile.RandomIndex = tileIndex tileMinY := int32(0) tileMaxY := int32(0) diff --git a/d2core/d2ui/textbox.go b/d2core/d2ui/textbox.go index 17ec23a8..c77c8aaa 100644 --- a/d2core/d2ui/textbox.go +++ b/d2core/d2ui/textbox.go @@ -23,12 +23,14 @@ type TextBox struct { bgSprite *Sprite textLabel Label lineBar Label + filter string } func CreateTextbox() TextBox { animation, _ := d2asset.LoadAnimation(d2resource.TextBox2, d2resource.PaletteUnits) bgSprite, _ := LoadSprite(animation) result := TextBox{ + filter: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", bgSprite: bgSprite, textLabel: CreateLabel(d2resource.FontFormal11, d2resource.PaletteUnits), lineBar: CreateLabel(d2resource.FontFormal11, d2resource.PaletteUnits), @@ -39,6 +41,10 @@ func CreateTextbox() TextBox { return result } +func (v *TextBox) SetFilter(filter string) { + v.filter = filter +} + func repeatingKeyPressed(key ebiten.Key) bool { const ( delay = 30 @@ -93,7 +99,7 @@ func (v *TextBox) GetText() string { func (v *TextBox) SetText(newText string) { result := "" for _, c := range newText { - if !strings.Contains("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ", string(c)) { + if !strings.Contains(v.filter, string(c)) { continue } result += string(c) diff --git a/d2game/d2gamescreen/character_select.go b/d2game/d2gamescreen/character_select.go index 03a52e3a..e67e9e44 100644 --- a/d2game/d2gamescreen/character_select.go +++ b/d2game/d2gamescreen/character_select.go @@ -6,6 +6,8 @@ import ( "os" "strings" + "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" + "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype" @@ -17,7 +19,6 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gamestate" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" @@ -43,14 +44,20 @@ type CharacterSelect struct { characterStatsLabel [8]d2ui.Label characterExpLabel [8]d2ui.Label characterImage [8]*d2map.Player - gameStates []*d2gamestate.GameState + gameStates []*d2player.PlayerState selectedCharacter int mouseButtonPressed bool showDeleteConfirmation bool + connectionType d2clientconnectiontype.ClientConnectionType + connectionHost string } -func CreateCharacterSelect() *CharacterSelect { - return &CharacterSelect{selectedCharacter: -1} +func CreateCharacterSelect(connectionType d2clientconnectiontype.ClientConnectionType, connectionHost string) *CharacterSelect { + return &CharacterSelect{ + selectedCharacter: -1, + connectionType: connectionType, + connectionHost: connectionHost, + } } 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.characterExpLabel[i].SetText(expText) // 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, d2inventory.HeroObjects[v.gameStates[idx].HeroType], ) @@ -166,12 +173,12 @@ func (v *CharacterSelect) updateCharacterBoxes() { } func (v *CharacterSelect) onNewCharButtonClicked() { - d2screen.SetNextScreen(CreateSelectHeroClass()) + d2screen.SetNextScreen(CreateSelectHeroClass(v.connectionType, v.connectionHost)) } func (v *CharacterSelect) onExitButtonClicked() { mainMenu := CreateMainMenu() - mainMenu.ShowTrademarkScreen = false + mainMenu.SetScreenMode(ScreenModeMainMenu) d2screen.SetNextScreen(mainMenu) } @@ -278,7 +285,7 @@ func (v *CharacterSelect) toggleDeleteCharacterDialog(showDialog bool) { } func (v *CharacterSelect) refreshGameStates() { - v.gameStates = d2gamestate.GetAllGameStates() + v.gameStates = d2player.GetAllPlayerStates() v.updateCharacterBoxes() if len(v.gameStates) > 0 { v.selectedCharacter = 0 @@ -293,7 +300,13 @@ func (v *CharacterSelect) refreshGameStates() { } func (v *CharacterSelect) onOkButtonClicked() { - gameClient, _ := d2client.Create(d2clientconnectiontype.Local) - gameClient.Open(v.gameStates[v.selectedCharacter].FilePath) + gameClient, _ := d2client.Create(v.connectionType) + 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)) } diff --git a/d2game/d2gamescreen/credits.go b/d2game/d2gamescreen/credits.go index 05f67258..3184f407 100644 --- a/d2game/d2gamescreen/credits.go +++ b/d2game/d2gamescreen/credits.go @@ -130,7 +130,7 @@ func (v *Credits) Advance(tickTime float64) error { func (v *Credits) onExitButtonClicked() { mainMenu := CreateMainMenu() - mainMenu.ShowTrademarkScreen = false + mainMenu.SetScreenMode(ScreenModeMainMenu) d2screen.SetNextScreen(mainMenu) } diff --git a/d2game/d2gamescreen/game.go b/d2game/d2gamescreen/game.go index 513e600b..e3cdf0b3 100644 --- a/d2game/d2gamescreen/game.go +++ b/d2game/d2gamescreen/game.go @@ -3,6 +3,8 @@ package d2gamescreen import ( "image/color" + "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket" + "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map" @@ -33,39 +35,6 @@ func CreateGame(gameClient *d2client.GameClient) *Game { } 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 } @@ -93,7 +62,7 @@ func (v *Game) Advance(tickTime float64) error { continue } 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() d2input.BindHandler(v.gameControls) @@ -108,3 +77,9 @@ func (v *Game) Advance(tickTime float64) error { } 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)) +} diff --git a/d2game/d2gamescreen/main_menu.go b/d2game/d2gamescreen/main_menu.go index 2d3521e9..c6821399 100644 --- a/d2game/d2gamescreen/main_menu.go +++ b/d2game/d2gamescreen/main_menu.go @@ -8,46 +8,72 @@ import ( "os/exec" "runtime" + "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" + + "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype" + "github.com/OpenDiablo2/OpenDiablo2/d2common" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gamestate" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" ) +type MainMenuScreenMode int + +const ( + ScreenModeUnknown MainMenuScreenMode = iota + ScreenModeTrademark + ScreenModeMainMenu + ScreenModeMultiplayer + ScreenModeTcpIp + ScreenModeServerIp +) + // MainMenu represents the main menu type MainMenu struct { + tcpIpBackground *d2ui.Sprite trademarkBackground *d2ui.Sprite background *d2ui.Sprite diabloLogoLeft *d2ui.Sprite diabloLogoRight *d2ui.Sprite diabloLogoLeftBack *d2ui.Sprite diabloLogoRightBack *d2ui.Sprite + serverIpBackground *d2ui.Sprite singlePlayerButton d2ui.Button + multiplayerButton d2ui.Button githubButton d2ui.Button exitDiabloButton d2ui.Button creditsButton d2ui.Button cinematicsButton 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 copyrightLabel2 d2ui.Label openDiabloLabel d2ui.Label versionLabel d2ui.Label commitLabel d2ui.Label - - ShowTrademarkScreen bool + tcpIpOptionsLabel d2ui.Label + tcpJoinGameLabel d2ui.Label + tcpJoinGameEntry d2ui.TextBox + screenMode MainMenuScreenMode leftButtonHeld bool } // CreateMainMenu creates an instance of MainMenu func CreateMainMenu() *MainMenu { return &MainMenu{ - ShowTrademarkScreen: true, - leftButtonHeld: true, + screenMode: ScreenModeUnknown, + leftButtonHeld: true, } } @@ -93,6 +119,10 @@ func (v *MainMenu) OnLoad() error { v.trademarkBackground, _ = d2ui.LoadSprite(animation) 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) v.diabloLogoLeft, _ = d2ui.LoadSprite(animation) v.diabloLogoLeft.SetBlend(true) @@ -115,39 +145,99 @@ func (v *MainMenu) OnLoad() error { v.exitDiabloButton = d2ui.CreateButton(d2ui.ButtonTypeWide, d2common.TranslateString("#1625")) v.exitDiabloButton.SetPosition(264, 535) - v.exitDiabloButton.SetVisible(!v.ShowTrademarkScreen) v.exitDiabloButton.OnActivated(func() { v.onExitButtonClicked() }) d2ui.AddWidget(&v.exitDiabloButton) v.creditsButton = d2ui.CreateButton(d2ui.ButtonTypeShort, d2common.TranslateString("#1627")) v.creditsButton.SetPosition(264, 505) - v.creditsButton.SetVisible(!v.ShowTrademarkScreen) v.creditsButton.OnActivated(func() { v.onCreditsButtonClicked() }) d2ui.AddWidget(&v.creditsButton) v.cinematicsButton = d2ui.CreateButton(d2ui.ButtonTypeShort, d2common.TranslateString("#1639")) v.cinematicsButton.SetPosition(401, 505) - v.cinematicsButton.SetVisible(!v.ShowTrademarkScreen) d2ui.AddWidget(&v.cinematicsButton) v.singlePlayerButton = d2ui.CreateButton(d2ui.ButtonTypeWide, d2common.TranslateString("#1620")) v.singlePlayerButton.SetPosition(264, 290) - v.singlePlayerButton.SetVisible(!v.ShowTrademarkScreen) v.singlePlayerButton.OnActivated(func() { v.onSinglePlayerClicked() }) 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.SetPosition(264, 330) - v.githubButton.SetVisible(!v.ShowTrademarkScreen) + v.githubButton.SetPosition(264, 400) v.githubButton.OnActivated(func() { v.onGithubButtonClicked() }) d2ui.AddWidget(&v.githubButton) v.mapTestButton = d2ui.CreateButton(d2ui.ButtonTypeWide, "MAP ENGINE TEST") - v.mapTestButton.SetPosition(264, 450) - v.mapTestButton.SetVisible(!v.ShowTrademarkScreen) + v.mapTestButton.SetPosition(264, 440) v.mapTestButton.OnActivated(func() { v.onMapTestClicked() }) - 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 } @@ -176,11 +266,11 @@ func openbrowser(url string) { func (v *MainMenu) onSinglePlayerClicked() { // Go here only if existing characters are available to select - if d2gamestate.HasGameStates() { - d2screen.SetNextScreen(CreateCharacterSelect()) + if d2player.HasGameStates() { + d2screen.SetNextScreen(CreateCharacterSelect(d2clientconnectiontype.Local, v.tcpJoinGameEntry.GetText())) return } - d2screen.SetNextScreen(CreateSelectHeroClass()) + d2screen.SetNextScreen(CreateSelectHeroClass(d2clientconnectiontype.Local, v.tcpJoinGameEntry.GetText())) } func (v *MainMenu) onGithubButtonClicked() { @@ -197,20 +287,40 @@ func (v *MainMenu) onCreditsButtonClicked() { // Render renders the main menu func (v *MainMenu) Render(screen d2render.Surface) error { - if v.ShowTrademarkScreen { + switch v.screenMode { + case ScreenModeTrademark: 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.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.copyrightLabel2.Render(screen) - } else { + case ScreenModeMainMenu: v.openDiabloLabel.Render(screen) v.versionLabel.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 func (v *MainMenu) Advance(tickTime float64) error { - v.diabloLogoLeftBack.Advance(tickTime) - v.diabloLogoRightBack.Advance(tickTime) - v.diabloLogoLeft.Advance(tickTime) - v.diabloLogoRight.Advance(tickTime) + switch v.screenMode { + case ScreenModeMainMenu: + fallthrough + 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 v.leftButtonHeld { return nil } d2ui.WaitForMouseRelease() + v.SetScreenMode(ScreenModeMainMenu) 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 { v.leftButtonHeld = false } + break } 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())) +} diff --git a/d2game/d2gamescreen/map_engine_testing.go b/d2game/d2gamescreen/map_engine_testing.go index cf5338bb..5479aea3 100644 --- a/d2game/d2gamescreen/map_engine_testing.go +++ b/d2game/d2gamescreen/map_engine_testing.go @@ -4,10 +4,11 @@ import ( "math" "os" + "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2fileformats/d2dt1" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gamestate" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" @@ -76,7 +77,7 @@ var regions = []RegionSpec{ } type MapEngineTest struct { - gameState *d2gamestate.GameState + gameState *d2player.PlayerState mapEngine *d2map.MapEngine mapRenderer *d2map.MapRenderer @@ -97,7 +98,7 @@ func CreateMapEngineTest(currentRegion int, levelPreset int) *MapEngineTest { regionSpec: RegionSpec{}, filesCount: 0, } - result.gameState = d2gamestate.CreateTestGameState() + result.gameState = d2player.CreateTestGameState() return result } diff --git a/d2game/d2gamescreen/select_hero_class.go b/d2game/d2gamescreen/select_hero_class.go index f8511458..057b8561 100644 --- a/d2game/d2gamescreen/select_hero_class.go +++ b/d2game/d2gamescreen/select_hero_class.go @@ -4,6 +4,8 @@ import ( "image" "image/color" + "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" + "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype" @@ -14,7 +16,6 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2audio" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gamestate" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" @@ -64,12 +65,16 @@ type SelectHeroClass struct { expansionCharLabel d2ui.Label hardcoreCheckbox d2ui.Checkbox hardcoreCharLabel d2ui.Label + connectionType d2clientconnectiontype.ClientConnectionType + connectionHost string } -func CreateSelectHeroClass() *SelectHeroClass { +func CreateSelectHeroClass(connectionType d2clientconnectiontype.ClientConnectionType, connectionHost string) *SelectHeroClass { result := &SelectHeroClass{ heroRenderInfo: make(map[d2enum.Hero]*HeroRenderInfo), selectedHero: d2enum.HeroNone, + connectionType: connectionType, + connectionHost: connectionHost, } return result } @@ -420,13 +425,13 @@ func (v *SelectHeroClass) OnUnload() error { } func (v SelectHeroClass) onExitButtonClicked() { - d2screen.SetNextScreen(CreateCharacterSelect()) + d2screen.SetNextScreen(CreateCharacterSelect(v.connectionType, v.connectionHost)) } 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.Open(gameState.FilePath) + gameClient.Open(v.connectionHost, gameState.FilePath) d2screen.SetNextScreen(CreateGame(gameClient)) } diff --git a/d2game/d2player/game_controls.go b/d2game/d2player/game_controls.go index 7103352a..8bd1e4da 100644 --- a/d2game/d2player/game_controls.go +++ b/d2game/d2player/game_controls.go @@ -10,8 +10,6 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2term" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" - "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client" - "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket" ) type Panel interface { @@ -25,12 +23,12 @@ type Panel interface { var missileID = 59 type GameControls struct { - hero *d2map.Player - mapEngine *d2map.MapEngine - mapRenderer *d2map.MapRenderer - inventory *Inventory - heroStats *HeroStats - gameClient *d2client.GameClient + hero *d2map.Player + mapEngine *d2map.MapEngine + mapRenderer *d2map.MapRenderer + inventory *Inventory + heroStats *HeroStats + inputListener InputCallbackListener // UI globeSprite *d2ui.Sprite @@ -39,19 +37,18 @@ type GameControls struct { skillIcon *d2ui.Sprite } -func NewGameControls(hero *d2map.Player, mapEngine *d2map.MapEngine, mapRenderer *d2map.MapRenderer, - gameClient *d2client.GameClient) *GameControls { +func NewGameControls(hero *d2map.Player, mapEngine *d2map.MapEngine, mapRenderer *d2map.MapRenderer, inputListener InputCallbackListener) *GameControls { d2term.BindAction("setmissile", "set missile id to summon on right click", func(id int) { missileID = id }) return &GameControls{ - hero: hero, - mapEngine: mapEngine, - gameClient: gameClient, - mapRenderer: mapRenderer, - inventory: NewInventory(), - heroStats: NewHeroStats(), + hero: hero, + mapEngine: mapEngine, + inputListener: inputListener, + mapRenderer: mapRenderer, + inventory: NewInventory(), + heroStats: NewHeroStats(), } } @@ -72,19 +69,9 @@ func (g *GameControls) OnMouseButtonDown(event d2input.MouseEvent) bool { px, py := g.mapRenderer.ScreenToWorld(event.X, event.Y) px = float64(int(px*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 { - g.gameClient.SendPacketToServer(d2netpacket.CreateMovePlayerPacket(g.gameClient.PlayerId, heroPosX, heroPosY, 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(), - // ) - // }) - //} + g.inputListener.OnPlayerMove(px, py) return true } diff --git a/d2game/d2player/input_callback_listener.go b/d2game/d2player/input_callback_listener.go new file mode 100644 index 00000000..dba83509 --- /dev/null +++ b/d2game/d2player/input_callback_listener.go @@ -0,0 +1,5 @@ +package d2player + +type InputCallbackListener interface { + OnPlayerMove(x, y float64) +} diff --git a/d2core/d2gamestate/d2gamestate.go b/d2game/d2player/player_state.go similarity index 74% rename from d2core/d2gamestate/d2gamestate.go rename to d2game/d2player/player_state.go index 8a20e748..8f5eada6 100644 --- a/d2core/d2gamestate/d2gamestate.go +++ b/d2game/d2player/player_state.go @@ -1,4 +1,4 @@ -package d2gamestate +package d2player import ( "encoding/json" @@ -8,41 +8,38 @@ import ( "path" "strconv" "strings" - "time" - - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory" ) -type GameState struct { - Seed int64 `json:"seed"` // TODO: Seed needs to be regenerated every time the game starts +type PlayerState struct { HeroName string `json:"heroName"` HeroType d2enum.Hero `json:"heroType"` HeroLevel int `json:"heroLevel"` Act int `json:"act"` FilePath string `json:"-"` 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 { basePath, _ := getGameBaseSavePath() files, _ := ioutil.ReadDir(basePath) return len(files) > 0 } -func GetAllGameStates() []*GameState { +func GetAllPlayerStates() []*PlayerState { basePath, _ := getGameBaseSavePath() files, _ := ioutil.ReadDir(basePath) - result := make([]*GameState, 0) + result := make([]*PlayerState, 0) for _, file := range files { fileName := file.Name() if file.IsDir() || len(fileName) < 5 || strings.ToLower(fileName[len(fileName)-4:]) != ".od2" { continue } - gameState := LoadGameState(path.Join(basePath, file.Name())) + gameState := LoadPlayerState(path.Join(basePath, file.Name())) if gameState == nil { continue } @@ -52,20 +49,18 @@ func GetAllGameStates() []*GameState { } // CreateTestGameState is used for the map engine previewer -func CreateTestGameState() *GameState { - result := &GameState{ - Seed: time.Now().UnixNano(), - } +func CreateTestGameState() *PlayerState { + result := &PlayerState{} return result } -func LoadGameState(path string) *GameState { +func LoadPlayerState(path string) *PlayerState { strData, err := ioutil.ReadFile(path) if err != nil { return nil } - result := &GameState{ + result := &PlayerState{ FilePath: path, } err = json.Unmarshal(strData, result) @@ -75,12 +70,11 @@ func LoadGameState(path string) *GameState { return result } -func CreateGameState(heroName string, hero d2enum.Hero, hardcore bool) *GameState { - result := &GameState{ +func CreatePlayerState(heroName string, hero d2enum.Hero, hardcore bool) *PlayerState { + result := &PlayerState{ HeroName: heroName, HeroType: hero, Act: 1, - Seed: time.Now().UnixNano(), Equipment: d2inventory.HeroObjects[hero], FilePath: "", } @@ -110,7 +104,7 @@ func getFirstFreeFileName() string { } } -func (v *GameState) Save() { +func (v *PlayerState) Save() { if v.FilePath == "" { v.FilePath = getFirstFreeFileName() } diff --git a/d2networking/d2client/client_connection.go b/d2networking/d2client/client_connection.go index fb6458ba..1ee9b078 100644 --- a/d2networking/d2client/client_connection.go +++ b/d2networking/d2client/client_connection.go @@ -6,7 +6,7 @@ import ( ) type ClientConnection interface { - Open(connectionString string) error + Open(connectionString string, saveFilePath string) error Close() error SendPacketToServer(packet d2netpacket.NetPacket) error SetClientListener(listener d2networking.ClientListener) diff --git a/d2networking/d2client/d2clientconnectiontype/connectiontype.go b/d2networking/d2client/d2clientconnectiontype/connectiontype.go index b310eb71..f42f447f 100644 --- a/d2networking/d2client/d2clientconnectiontype/connectiontype.go +++ b/d2networking/d2client/d2clientconnectiontype/connectiontype.go @@ -3,5 +3,7 @@ package d2clientconnectiontype type ClientConnectionType int const ( - Local ClientConnectionType = 1 + Local ClientConnectionType = iota + LANServer + LANClient ) diff --git a/d2networking/d2client/d2localclient/local_client_connection.go b/d2networking/d2client/d2localclient/local_client_connection.go index c1f8e9c3..740ca6c5 100644 --- a/d2networking/d2client/d2localclient/local_client_connection.go +++ b/d2networking/d2client/d2localclient/local_client_connection.go @@ -1,6 +1,7 @@ package d2localclient import ( + "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" "github.com/OpenDiablo2/OpenDiablo2/d2networking" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2server" @@ -8,8 +9,10 @@ import ( ) type LocalClientConnection struct { - clientListener d2networking.ClientListener - uniqueId string + clientListener d2networking.ClientListener + uniqueId string + openNetworkServer bool + playerState *d2player.PlayerState } func (l LocalClientConnection) GetUniqueId() string { @@ -24,16 +27,18 @@ func (l *LocalClientConnection) SendPacketToClient(packet d2netpacket.NetPacket) return l.clientListener.OnPacketReceived(packet) } -func Create() *LocalClientConnection { +func Create(openNetworkServer bool) *LocalClientConnection { result := &LocalClientConnection{ - uniqueId: uuid.NewV4().String(), + uniqueId: uuid.NewV4().String(), + openNetworkServer: openNetworkServer, } return result } -func (l *LocalClientConnection) Open(gameStatePath string) error { - d2server.Create(gameStatePath) +func (l *LocalClientConnection) Open(connectionString string, saveFilePath string) error { + l.SetPlayerState(d2player.LoadPlayerState(saveFilePath)) + d2server.Create(l.openNetworkServer) go d2server.Run() d2server.OnClientConnected(l) return nil @@ -53,3 +58,11 @@ func (l *LocalClientConnection) SendPacketToServer(packet d2netpacket.NetPacket) func (l *LocalClientConnection) SetClientListener(listener d2networking.ClientListener) { l.clientListener = listener } + +func (l *LocalClientConnection) GetPlayerState() *d2player.PlayerState { + return l.playerState +} + +func (l *LocalClientConnection) SetPlayerState(playerState *d2player.PlayerState) { + l.playerState = playerState +} diff --git a/d2networking/d2client/d2remoteclient/remote_client_connection.go b/d2networking/d2client/d2remoteclient/remote_client_connection.go new file mode 100644 index 00000000..3e220b15 --- /dev/null +++ b/d2networking/d2client/d2remoteclient/remote_client_connection.go @@ -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) + } + + } +} diff --git a/d2networking/d2client/game_client.go b/d2networking/d2client/game_client.go index 723cd53e..1e5f570a 100644 --- a/d2networking/d2client/game_client.go +++ b/d2networking/d2client/game_client.go @@ -4,21 +4,21 @@ import ( "fmt" "log" + "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" - - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gamestate" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map" - "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/d2netpackettype" ) type GameClient struct { clientConnection ClientConnection - GameState *d2gamestate.GameState + connectionType d2clientconnectiontype.ClientConnectionType + GameState *d2player.PlayerState MapEngine *d2map.MapEngine PlayerId string Players map[string]*d2map.Player @@ -26,23 +26,27 @@ type GameClient struct { func Create(connectionType d2clientconnectiontype.ClientConnectionType) (*GameClient, error) { result := &GameClient{ - MapEngine: d2map.CreateMapEngine(), - Players: make(map[string]*d2map.Player, 0), + MapEngine: d2map.CreateMapEngine(), + Players: make(map[string]*d2map.Player, 0), + 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() - result.clientConnection.SetClientListener(result) + 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) error { - return g.clientConnection.Open(connectionString) +func (g *GameClient) Open(connectionString string, saveFilePath string) error { + return g.clientConnection.Open(connectionString, saveFilePath) } func (g *GameClient) Close() error { @@ -67,7 +71,7 @@ func (g *GameClient) OnPacketReceived(packet d2netpacket.NetPacket) error { break case d2netpackettype.AddPlayer: 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.MapEngine.AddEntity(newPlayer) break diff --git a/d2networking/d2netpacket/d2netpackettype/message_type.go b/d2networking/d2netpacket/d2netpackettype/message_type.go index 829080c6..ba43f5eb 100644 --- a/d2networking/d2netpacket/d2netpackettype/message_type.go +++ b/d2networking/d2netpacket/d2netpackettype/message_type.go @@ -4,9 +4,12 @@ type NetPacketType uint32 // Warning: Do NOT re-arrange the order of these packet values unless you want to // break compatibility between clients of slightly different versions. +// Also note that the packet id is a byte, so if we use more than 256 +// of these then we are doing something very wrong. const ( - UpdateServerInfo NetPacketType = iota - GenerateMap - AddPlayer - MovePlayer + UpdateServerInfo NetPacketType = iota + GenerateMap // Sent by the server to generate a map + AddPlayer // Server sends to the client to add a player + MovePlayer // Sent to the client or server to indicate player movement + PlayerConnectionRequest // Client sends to server to request a connection ) diff --git a/d2networking/d2netpacket/packet_add_player.go b/d2networking/d2netpacket/packet_add_player.go index 60ac3429..990b3a0a 100644 --- a/d2networking/d2netpacket/packet_add_player.go +++ b/d2networking/d2netpacket/packet_add_player.go @@ -8,17 +8,19 @@ import ( type AddPlayerPacket struct { Id string `json:"id"` + Name string `json:"name"` X int `json:"x"` Y int `json:"y"` HeroType d2enum.Hero `json:"hero"` 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{ PacketType: d2netpackettype.AddPlayer, PacketData: AddPlayerPacket{ Id: id, + Name: name, X: x, Y: y, HeroType: heroType, diff --git a/d2networking/d2netpacket/packet_player_connection_request.go b/d2networking/d2netpacket/packet_player_connection_request.go new file mode 100644 index 00000000..f1c7ffea --- /dev/null +++ b/d2networking/d2netpacket/packet_player_connection_request.go @@ -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, + }, + } +} diff --git a/d2networking/d2server/client_connection.go b/d2networking/d2server/client_connection.go index 7bb98400..76299546 100644 --- a/d2networking/d2server/client_connection.go +++ b/d2networking/d2server/client_connection.go @@ -1,9 +1,14 @@ 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 { GetUniqueId() string GetConnectionType() string SendPacketToClient(packet d2netpacket.NetPacket) error + GetPlayerState() *d2player.PlayerState + SetPlayerState(playerState *d2player.PlayerState) } diff --git a/d2networking/d2server/d2udpclientconnection/udp_client_connection.go b/d2networking/d2server/d2udpclientconnection/udp_client_connection.go new file mode 100644 index 00000000..aa474378 --- /dev/null +++ b/d2networking/d2server/d2udpclientconnection/udp_client_connection.go @@ -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 +} diff --git a/d2networking/d2server/game_server.go b/d2networking/d2server/game_server.go index 10f72f53..8d74a544 100644 --- a/d2networking/d2server/game_server.go +++ b/d2networking/d2server/game_server.go @@ -1,27 +1,37 @@ package d2server import ( + "bytes" + "compress/gzip" + "encoding/json" + "fmt" + "io" "log" - - "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket/d2netpackettype" - - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map" - - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gamestate" + "net" + "strings" + "time" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map" "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 { - gameState *d2gamestate.GameState clientConnections map[string]ClientConnection mapEngines []*d2map.MapEngine + scriptEngine *d2script.ScriptEngine + udpConnection *net.UDPConn + seed int64 + running bool } var singletonServer *GameServer -func Create(gameStatePath string) { +func Create(openNetworkServer bool) { log.Print("Creating GameServer") if singletonServer != nil { return @@ -30,20 +40,95 @@ func Create(gameStatePath string) { singletonServer = &GameServer{ clientConnections: make(map[string]ClientConnection), mapEngines: make([]*d2map.MapEngine, 0), - gameState: d2gamestate.LoadGameState(gameStatePath), + //gameState: d2player.LoadPlayerState(gameStatePath), + scriptEngine: d2script.CreateScriptEngine(), + seed: time.Now().UnixNano(), } mapEngine := d2map.CreateMapEngine() mapEngine.GenerateMap(d2enum.RegionAct1Town, 1, 0, false) 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() { 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() { @@ -55,17 +140,30 @@ func Destroy() { } 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.gameState.Seed, client.GetUniqueId())) + client.SendPacketToClient(d2netpacket.CreateUpdateServerInfoPacket(singletonServer.seed, client.GetUniqueId())) 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' - sx, sy := singletonServer.mapEngines[0].GetStartPosition() // TODO: Another temporary hack - createPlayerPacket := d2netpacket.CreateAddPlayerPacket(client.GetUniqueId(), int(sx*5)+3, int(sy*5)+3, - singletonServer.gameState.HeroType, singletonServer.gameState.Equipment) + playerState := client.GetPlayerState() + createPlayerPacket := d2netpacket.CreateAddPlayerPacket(client.GetUniqueId(), playerState.HeroName, int(sx*5)+3, int(sy*5)+3, + playerState.HeroType, 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.Equipment)) } } @@ -79,6 +177,12 @@ func OnPacketReceived(client ClientConnection, packet d2netpacket.NetPacket) err 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) } diff --git a/d2script/scriptengine.go b/d2script/scriptengine.go new file mode 100644 index 00000000..722ead6c --- /dev/null +++ b/d2script/scriptengine.go @@ -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 +} diff --git a/go.mod b/go.mod index 958598bf..4cae0a0d 100644 --- a/go.mod +++ b/go.mod @@ -11,10 +11,12 @@ require ( github.com/hajimehoshi/ebiten v1.11.0-alpha.2.0.20200102072751-e66f1fb71e2e github.com/mattn/go-sqlite3 v2.0.3+incompatible // 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/stretchr/testify v1.4.0 golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136 // indirect golang.org/x/mobile v0.0.0-20191115022231-f0c40035f2ba // indirect golang.org/x/sys v0.0.0-20191115151921-52ab43148777 // indirect gopkg.in/alecthomas/kingpin.v2 v2.2.6 + gopkg.in/sourcemap.v1 v1.0.5 // indirect ) diff --git a/go.sum b/go.sum index db4026dc..033e329a 100644 --- a/go.sum +++ b/go.sum @@ -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/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-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/go.mod h1:KqrpKpn4M8OLznErihXTGLlsXFGeLxHUrLRRI/1YjGk= 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/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.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.3.0/go.mod h1:qMJj/CSDxx6CGHiZeCgbiq2DSUkbK0UbtXShQcnfyMM= 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/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/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/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= 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 v2.0.3+incompatible h1:gXHsfypPkaMZrKbD5209QV9jbUTJKjyR5WD3HYQSd+U= 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/go.mod h1:7N4y5R18zBiu3l0vajMUWQgZyjv464prE8RCyBcmnZM= 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/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= 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/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= 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/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-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-20190731235908-ec7cb31e5a56/go.mod h1:JhuoJpWY28nO4Vef9tZUw9qufEGTyX1+7lmHxV5q5G4= 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-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-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-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-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/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.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-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-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-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-20190312061237-fead79001313/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-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-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/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-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-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-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/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= 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/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/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go index 88e6d348..c3cf0819 100644 --- a/main.go +++ b/main.go @@ -13,6 +13,8 @@ import ( "strconv" "sync" + "github.com/OpenDiablo2/OpenDiablo2/d2script" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen" "github.com/OpenDiablo2/OpenDiablo2/d2game/d2gamescreen" @@ -190,6 +192,8 @@ func initialize() error { d2ui.Initialize() + d2script.CreateScriptEngine() + return nil } diff --git a/scripts/server/server.js b/scripts/server/server.js new file mode 100644 index 00000000..7fcbd4ad --- /dev/null +++ b/scripts/server/server.js @@ -0,0 +1 @@ +debugPrint('Server script initialized.') diff --git a/scripts/test.js b/scripts/test.js new file mode 100644 index 00000000..6fee806d --- /dev/null +++ b/scripts/test.js @@ -0,0 +1,3 @@ + +// debugPrint(test()) +