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) {
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]
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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 (
"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()
}

View File

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

View File

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

View File

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

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

View File

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

View File

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

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

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

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

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/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=

View File

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

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