diff --git a/d2app/app.go b/d2app/app.go index b9783342..612d07c0 100644 --- a/d2app/app.go +++ b/d2app/app.go @@ -48,6 +48,7 @@ type App struct { gitBranch string gitCommit string terminal d2interface.Terminal + scriptEngine *d2script.ScriptEngine audio d2interface.AudioProvider renderer d2interface.Renderer tAllocSamples *ring.Ring @@ -69,12 +70,14 @@ const ( // Create creates a new instance of the application func Create(gitBranch, gitCommit string, terminal d2interface.Terminal, + scriptEngine *d2script.ScriptEngine, audio d2interface.AudioProvider, renderer d2interface.Renderer) *App { result := &App{ gitBranch: gitBranch, gitCommit: gitCommit, terminal: terminal, + scriptEngine: scriptEngine, audio: audio, renderer: renderer, tAllocSamples: createZeroedRing(nSamplesTAlloc), @@ -105,7 +108,7 @@ func (p *App) Run() error { return err } - d2screen.SetNextScreen(d2gamescreen.CreateMainMenu(p.renderer, p.audio, p.terminal)) + d2screen.SetNextScreen(d2gamescreen.CreateMainMenu(p.renderer, p.audio, p.terminal, p.scriptEngine)) if p.gitBranch == "" { p.gitBranch = "Local Build" @@ -142,6 +145,7 @@ func (p *App) initialize() error { {"timescale", "set scalar for elapsed time", p.setTimeScale}, {"quit", "exits the game", p.quitGame}, {"screen-gui", "enters the gui playground screen", p.enterGuiPlayground}, + {"js", "eval JS scripts", p.evalJS}, } for idx := range terminalActions { @@ -174,8 +178,6 @@ func (p *App) initialize() error { d2ui.Initialize(p.audio) - d2script.CreateScriptEngine() - return nil } @@ -470,6 +472,16 @@ func (p *App) dumpHeap() { } } +func (p *App) evalJS(code string) { + val, err := p.scriptEngine.Eval(code) + if err != nil { + p.terminal.OutputErrorf("%s", err) + return + } + + p.terminal.OutputInfof("%s", val) +} + func (p *App) toggleFullScreen() { fullscreen := !p.renderer.IsFullScreen() p.renderer.SetFullScreen(fullscreen) diff --git a/d2core/d2term/terminal.go b/d2core/d2term/terminal.go index 5fd1fdf5..68c84c25 100644 --- a/d2core/d2term/terminal.go +++ b/d2core/d2term/terminal.go @@ -3,7 +3,6 @@ package d2term import ( "errors" "fmt" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "image/color" "log" "math" @@ -13,8 +12,8 @@ import ( "strings" "github.com/OpenDiablo2/OpenDiablo2/d2common" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" - ) // TermCategory applies styles to the lines in the Terminal diff --git a/d2game/d2gamescreen/character_select.go b/d2game/d2gamescreen/character_select.go index a421d485..6099d6af 100644 --- a/d2game/d2gamescreen/character_select.go +++ b/d2game/d2gamescreen/character_select.go @@ -2,17 +2,16 @@ package d2gamescreen import ( "fmt" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui" "image/color" "math" "os" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" - + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2inventory" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen" @@ -20,6 +19,7 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype" + "github.com/OpenDiablo2/OpenDiablo2/d2script" ) // CharacterSelect represents the character select screen @@ -48,6 +48,7 @@ type CharacterSelect struct { connectionHost string audioProvider d2interface.AudioProvider terminal d2interface.Terminal + scriptEngine *d2script.ScriptEngine renderer d2interface.Renderer } @@ -56,7 +57,7 @@ func CreateCharacterSelect( renderer d2interface.Renderer, audioProvider d2interface.AudioProvider, connectionType d2clientconnectiontype.ClientConnectionType, - connectionHost string, term d2interface.Terminal, + connectionHost string, term d2interface.Terminal, scriptEngine *d2script.ScriptEngine, ) *CharacterSelect { return &CharacterSelect{ selectedCharacter: -1, @@ -65,6 +66,7 @@ func CreateCharacterSelect( connectionHost: connectionHost, audioProvider: audioProvider, terminal: term, + scriptEngine: scriptEngine, } } @@ -211,11 +213,12 @@ func (v *CharacterSelect) updateCharacterBoxes() { } func (v *CharacterSelect) onNewCharButtonClicked() { - d2screen.SetNextScreen(CreateSelectHeroClass(v.renderer, v.audioProvider, v.connectionType, v.connectionHost, v.terminal)) + d2screen.SetNextScreen(CreateSelectHeroClass(v.renderer, v.audioProvider, v.connectionType, v.connectionHost, + v.terminal, v.scriptEngine)) } func (v *CharacterSelect) onExitButtonClicked() { - mainMenu := CreateMainMenu(v.renderer, v.audioProvider, v.terminal) + mainMenu := CreateMainMenu(v.renderer, v.audioProvider, v.terminal, v.scriptEngine) mainMenu.setScreenMode(screenModeMainMenu) d2screen.SetNextScreen(mainMenu) } @@ -361,7 +364,7 @@ func (v *CharacterSelect) refreshGameStates() { } func (v *CharacterSelect) onOkButtonClicked() { - gameClient, _ := d2client.Create(v.connectionType) + gameClient, _ := d2client.Create(v.connectionType, v.scriptEngine) host := "" if v.connectionType == d2clientconnectiontype.LANClient { @@ -373,5 +376,5 @@ func (v *CharacterSelect) onOkButtonClicked() { fmt.Printf("can not connect to the host: %s", host) } - d2screen.SetNextScreen(CreateGame(v.renderer, v.audioProvider, gameClient, v.terminal)) + d2screen.SetNextScreen(CreateGame(v.renderer, v.audioProvider, gameClient, v.terminal, v.scriptEngine)) } diff --git a/d2game/d2gamescreen/credits.go b/d2game/d2gamescreen/credits.go index 6f2df167..c6bd4b79 100644 --- a/d2game/d2gamescreen/credits.go +++ b/d2game/d2gamescreen/credits.go @@ -9,14 +9,13 @@ import ( "path" "strings" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" - "github.com/OpenDiablo2/OpenDiablo2/d2common" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" + "github.com/OpenDiablo2/OpenDiablo2/d2script" ) type labelItem struct { @@ -37,10 +36,11 @@ type Credits struct { renderer d2interface.Renderer audioProvider d2interface.AudioProvider terminal d2interface.Terminal + scriptEngine *d2script.ScriptEngine } // CreateCredits creates an instance of the credits screen -func CreateCredits(renderer d2interface.Renderer, audioProvider d2interface.AudioProvider) *Credits { +func CreateCredits(renderer d2interface.Renderer, audioProvider d2interface.AudioProvider, scriptEngine *d2script.ScriptEngine) *Credits { result := &Credits{ labels: make([]*labelItem, 0), cycleTime: 0, @@ -48,6 +48,7 @@ func CreateCredits(renderer d2interface.Renderer, audioProvider d2interface.Audi cyclesTillNextLine: 0, renderer: renderer, audioProvider: audioProvider, + scriptEngine: scriptEngine, } return result @@ -159,7 +160,7 @@ func (v *Credits) Advance(tickTime float64) error { } func (v *Credits) onExitButtonClicked() { - mainMenu := CreateMainMenu(v.renderer, v.audioProvider, v.terminal) + mainMenu := CreateMainMenu(v.renderer, v.audioProvider, v.terminal, v.scriptEngine) mainMenu.setScreenMode(screenModeMainMenu) d2screen.SetNextScreen(mainMenu) } diff --git a/d2game/d2gamescreen/escape_menu.go b/d2game/d2gamescreen/escape_menu.go index 67e237a6..ff0eb592 100644 --- a/d2game/d2gamescreen/escape_menu.go +++ b/d2game/d2gamescreen/escape_menu.go @@ -2,13 +2,13 @@ package d2gamescreen import ( "fmt" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen" + "github.com/OpenDiablo2/OpenDiablo2/d2script" ) // TODO: fix pentagram @@ -71,6 +71,7 @@ type EscapeMenu struct { renderer d2interface.Renderer audioProvider d2interface.AudioProvider terminal d2interface.Terminal + scriptEngine *d2script.ScriptEngine } type layout struct { @@ -124,11 +125,13 @@ type actionableElement interface { } // NewEscapeMenu creates a new escape menu -func NewEscapeMenu(renderer d2interface.Renderer, audioProvider d2interface.AudioProvider, term d2interface.Terminal) *EscapeMenu { +func NewEscapeMenu(renderer d2interface.Renderer, audioProvider d2interface.AudioProvider, term d2interface.Terminal, + scriptEngine *d2script.ScriptEngine) *EscapeMenu { m := &EscapeMenu{ audioProvider: audioProvider, terminal: term, renderer: renderer, + scriptEngine: scriptEngine, } m.layouts = []*layout{ @@ -368,7 +371,7 @@ func (m *EscapeMenu) showLayout(id layoutID) { } if id == saveLayoutID { - mainMenu := CreateMainMenu(m.renderer, m.audioProvider, m.terminal) + mainMenu := CreateMainMenu(m.renderer, m.audioProvider, m.terminal, m.scriptEngine) mainMenu.setScreenMode(screenModeMainMenu) d2screen.SetNextScreen(mainMenu) diff --git a/d2game/d2gamescreen/game.go b/d2game/d2gamescreen/game.go index 60ee62f8..297ff168 100644 --- a/d2game/d2gamescreen/game.go +++ b/d2game/d2gamescreen/game.go @@ -1,23 +1,21 @@ package d2gamescreen import ( + "fmt" "image/color" "github.com/OpenDiablo2/OpenDiablo2/d2common" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" - - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen" - - "fmt" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2maprenderer" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen" "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client" "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2netpacket" + "github.com/OpenDiablo2/OpenDiablo2/d2script" ) const hideZoneTextAfterSeconds = 2.0 @@ -39,7 +37,7 @@ type Game struct { // CreateGame creates the Gameplay screen and returns a pointer to it func CreateGame(renderer d2interface.Renderer, audioProvider d2interface.AudioProvider, gameClient *d2client.GameClient, - term d2interface.Terminal) *Game { + term d2interface.Terminal, scriptEngine *d2script.ScriptEngine) *Game { result := &Game{ gameClient: gameClient, gameControls: nil, @@ -47,7 +45,7 @@ func CreateGame(renderer d2interface.Renderer, audioProvider d2interface.AudioPr lastRegionType: d2enum.RegionNone, ticksSinceLevelCheck: 0, mapRenderer: d2maprenderer.CreateMapRenderer(renderer, gameClient.MapEngine, term), - escapeMenu: NewEscapeMenu(renderer, audioProvider, term), + escapeMenu: NewEscapeMenu(renderer, audioProvider, term, scriptEngine), audioProvider: audioProvider, renderer: renderer, terminal: term, diff --git a/d2game/d2gamescreen/main_menu.go b/d2game/d2gamescreen/main_menu.go index 4f3db900..ed0171c9 100644 --- a/d2game/d2gamescreen/main_menu.go +++ b/d2game/d2gamescreen/main_menu.go @@ -3,28 +3,24 @@ package d2gamescreen import ( "fmt" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui" "image/color" "log" "os" "os/exec" "runtime" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" - - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" - - "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" - - "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype" - "github.com/OpenDiablo2/OpenDiablo2/d2common" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" + "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" + "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype" + "github.com/OpenDiablo2/OpenDiablo2/d2script" ) type mainMenuScreenMode int @@ -75,16 +71,19 @@ type MainMenu struct { renderer d2interface.Renderer audioProvider d2interface.AudioProvider terminal d2interface.Terminal + scriptEngine *d2script.ScriptEngine } // CreateMainMenu creates an instance of MainMenu -func CreateMainMenu(renderer d2interface.Renderer, audioProvider d2interface.AudioProvider, term d2interface.Terminal) *MainMenu { +func CreateMainMenu(renderer d2interface.Renderer, audioProvider d2interface.AudioProvider, term d2interface.Terminal, + scriptEngine *d2script.ScriptEngine) *MainMenu { return &MainMenu{ screenMode: screenModeUnknown, leftButtonHeld: true, renderer: renderer, audioProvider: audioProvider, terminal: term, + scriptEngine: scriptEngine, } } @@ -154,7 +153,7 @@ func (v *MainMenu) createLabels(loading d2screen.LoadingState) { loading.Progress(0.3) v.copyrightLabel2 = d2ui.CreateLabel(d2resource.FontFormal12, d2resource.PaletteStatic) - v.copyrightLabel2.Alignment =d2gui.HorizontalAlignCenter + v.copyrightLabel2.Alignment = d2gui.HorizontalAlignCenter v.copyrightLabel2.SetText("All Rights Reserved.") v.copyrightLabel2.Color = color.RGBA{R: 188, G: 168, B: 140, A: 255} v.copyrightLabel2.SetPosition(400, 525) @@ -287,12 +286,12 @@ func (v *MainMenu) onSinglePlayerClicked() { // Go here only if existing characters are available to select if d2player.HasGameStates() { d2screen.SetNextScreen(CreateCharacterSelect(v.renderer, v.audioProvider, d2clientconnectiontype.Local, - v.tcpJoinGameEntry.GetText(), v.terminal)) + v.tcpJoinGameEntry.GetText(), v.terminal, v.scriptEngine)) return } d2screen.SetNextScreen(CreateSelectHeroClass(v.renderer, v.audioProvider, - d2clientconnectiontype.Local, v.tcpJoinGameEntry.GetText(), v.terminal)) + d2clientconnectiontype.Local, v.tcpJoinGameEntry.GetText(), v.terminal, v.scriptEngine)) } func (v *MainMenu) onGithubButtonClicked() { @@ -321,7 +320,7 @@ func (v *MainMenu) onExitButtonClicked() { } func (v *MainMenu) onCreditsButtonClicked() { - d2screen.SetNextScreen(CreateCredits(v.renderer, v.audioProvider)) + d2screen.SetNextScreen(CreateCredits(v.renderer, v.audioProvider, v.scriptEngine)) } // Render renders the main menu @@ -487,7 +486,7 @@ func (v *MainMenu) onTCPIPCancelClicked() { func (v *MainMenu) onTCPIPHostGameClicked() { d2screen.SetNextScreen(CreateCharacterSelect(v.renderer, v.audioProvider, - d2clientconnectiontype.LANServer, "", v.terminal)) + d2clientconnectiontype.LANServer, "", v.terminal, v.scriptEngine)) } func (v *MainMenu) onTCPIPJoinGameClicked() { @@ -500,5 +499,5 @@ func (v *MainMenu) onBtnTCPIPCancelClicked() { func (v *MainMenu) onBtnTCPIPOkClicked() { d2screen.SetNextScreen(CreateCharacterSelect(v.renderer, v.audioProvider, - d2clientconnectiontype.LANClient, v.tcpJoinGameEntry.GetText(), v.terminal)) + d2clientconnectiontype.LANClient, v.tcpJoinGameEntry.GetText(), v.terminal, v.scriptEngine)) } diff --git a/d2game/d2gamescreen/select_hero_class.go b/d2game/d2gamescreen/select_hero_class.go index f01fb911..74df77ff 100644 --- a/d2game/d2gamescreen/select_hero_class.go +++ b/d2game/d2gamescreen/select_hero_class.go @@ -2,25 +2,22 @@ package d2gamescreen import ( "fmt" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui" "image" "image/color" - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" - - "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" - - "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client" - "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype" - "github.com/OpenDiablo2/OpenDiablo2/d2common" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2interface" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2resource" - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2asset" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2gui" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2screen" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2ui" + "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" + "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client" + "github.com/OpenDiablo2/OpenDiablo2/d2networking/d2client/d2clientconnectiontype" + "github.com/OpenDiablo2/OpenDiablo2/d2script" ) type heroRenderConfig struct { @@ -187,6 +184,7 @@ type SelectHeroClass struct { audioProvider d2interface.AudioProvider terminal d2interface.Terminal renderer d2interface.Renderer + scriptEngine *d2script.ScriptEngine } // CreateSelectHeroClass creates an instance of a SelectHeroClass @@ -196,6 +194,7 @@ func CreateSelectHeroClass( connectionType d2clientconnectiontype.ClientConnectionType, connectionHost string, terminal d2interface.Terminal, + scriptEngine *d2script.ScriptEngine, ) *SelectHeroClass { result := &SelectHeroClass{ heroRenderInfo: make(map[d2enum.Hero]*HeroRenderInfo), @@ -205,6 +204,7 @@ func CreateSelectHeroClass( audioProvider: audioProvider, terminal: terminal, renderer: renderer, + scriptEngine: scriptEngine, } return result @@ -341,7 +341,7 @@ func (v *SelectHeroClass) OnUnload() error { func (v *SelectHeroClass) onExitButtonClicked() { d2screen.SetNextScreen(CreateCharacterSelect(v.renderer, v.audioProvider, v.connectionType, - v.connectionHost, v.terminal)) + v.connectionHost, v.terminal, v.scriptEngine)) } func (v *SelectHeroClass) onOkButtonClicked() { @@ -351,13 +351,13 @@ func (v *SelectHeroClass) onOkButtonClicked() { d2datadict.CharStats[v.selectedHero], v.hardcoreCheckbox.GetCheckState(), ) - gameClient, _ := d2client.Create(d2clientconnectiontype.Local) + gameClient, _ := d2client.Create(d2clientconnectiontype.Local, v.scriptEngine) if err := gameClient.Open(v.connectionHost, gameState.FilePath); err != nil { fmt.Printf("can not connect to the host: %s\n", v.connectionHost) } - d2screen.SetNextScreen(CreateGame(v.renderer, v.audioProvider, gameClient, v.terminal)) + d2screen.SetNextScreen(CreateGame(v.renderer, v.audioProvider, gameClient, v.terminal, v.scriptEngine)) } // Render renders the Select Hero Class screen diff --git a/d2networking/d2client/game_client.go b/d2networking/d2client/game_client.go index c59fb31a..0330d3db 100644 --- a/d2networking/d2client/game_client.go +++ b/d2networking/d2client/game_client.go @@ -7,19 +7,17 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2common" "github.com/OpenDiablo2/OpenDiablo2/d2common/d2data/d2datadict" - - "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapgen" - + "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapengine" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapentity" - - "github.com/OpenDiablo2/OpenDiablo2/d2common/d2enum" + "github.com/OpenDiablo2/OpenDiablo2/d2core/d2map/d2mapgen" "github.com/OpenDiablo2/OpenDiablo2/d2game/d2player" "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" + "github.com/OpenDiablo2/OpenDiablo2/d2script" ) // GameClient manages a connection to d2server.GameServer @@ -27,20 +25,22 @@ import ( type GameClient struct { clientConnection ServerConnection // Abstract local/remote connection connectionType d2clientconnectiontype.ClientConnectionType // Type of connection (local or remote) - GameState *d2player.PlayerState // local player state - MapEngine *d2mapengine.MapEngine // Map and entities - PlayerId string // ID of the local player - Players map[string]*d2mapentity.Player // IDs of the other players - Seed int64 // Map seed - RegenMap bool // Regenerate tile cache on render (map has changed) + scriptEngine *d2script.ScriptEngine + GameState *d2player.PlayerState // local player state + MapEngine *d2mapengine.MapEngine // Map and entities + PlayerId string // ID of the local player + Players map[string]*d2mapentity.Player // IDs of the other players + Seed int64 // Map seed + RegenMap bool // Regenerate tile cache on render (map has changed) } // Create constructs a new GameClient and returns a pointer to it. -func Create(connectionType d2clientconnectiontype.ClientConnectionType) (*GameClient, error) { +func Create(connectionType d2clientconnectiontype.ClientConnectionType, scriptEngine *d2script.ScriptEngine) (*GameClient, error) { result := &GameClient{ MapEngine: d2mapengine.CreateMapEngine(), // TODO: Mapgen - Needs levels.txt stuff Players: make(map[string]*d2mapentity.Player), connectionType: connectionType, + scriptEngine: scriptEngine, } switch connectionType { @@ -61,18 +61,26 @@ func Create(connectionType d2clientconnectiontype.ClientConnectionType) (*GameCl // If the client is remote it sends a PlayerConnectionRequestPacket to the // server (see d2netpacket). func (g *GameClient) Open(connectionString string, saveFilePath string) error { + switch g.connectionType { + case d2clientconnectiontype.LANServer, d2clientconnectiontype.Local: + g.scriptEngine.AllowEval() + } return g.clientConnection.Open(connectionString, saveFilePath) } // Close destroys the server if the client is local. For remote clients // it sends a DisconnectRequestPacket (see d2netpacket). func (g *GameClient) Close() error { + switch g.connectionType { + case d2clientconnectiontype.LANServer, d2clientconnectiontype.Local: + g.scriptEngine.DisallowEval() + } return g.clientConnection.Close() } // Destroy does the same thing as Close. func (g *GameClient) Destroy() error { - return g.clientConnection.Close() + return g.Close() } // OnPacketReceived is called by the ClientConection and processes incoming diff --git a/d2script/scriptengine.go b/d2script/engine.go similarity index 68% rename from d2script/scriptengine.go rename to d2script/engine.go index efa9425e..18d2a41a 100644 --- a/d2script/scriptengine.go +++ b/d2script/engine.go @@ -1,6 +1,7 @@ package d2script import ( + "errors" "fmt" "io/ioutil" "path/filepath" @@ -11,24 +12,34 @@ import ( // ScriptEngine allows running JavaScript scripts type ScriptEngine struct { - vm *otto.Otto + vm *otto.Otto + isEvalAllowed bool } // CreateScriptEngine creates the script engine and returns a pointer to it. func CreateScriptEngine() *ScriptEngine { - result := &ScriptEngine{ - vm: otto.New(), - } - - err := result.vm.Set("debugPrint", func(call otto.FunctionCall) otto.Value { + vm := otto.New() + err := vm.Set("debugPrint", func(call otto.FunctionCall) otto.Value { fmt.Printf("Script: %s\n", call.Argument(0).String()) return otto.Value{} }) if err != nil { fmt.Printf("could not bind the 'debugPrint' to the given function in script engine") } + return &ScriptEngine{ + vm: vm, + isEvalAllowed: false, + } +} - return result +// AllowEval allows the evaluation of JS code. +func (s *ScriptEngine) AllowEval() { + s.isEvalAllowed = true +} + +// AllowEval disallows the evaluation of JS code. +func (s *ScriptEngine) DisallowEval() { + s.isEvalAllowed = false } // ToValue converts the given interface{} value to a otto.Value @@ -60,3 +71,16 @@ func (s *ScriptEngine) RunScript(fileName string) (*otto.Value, error) { return &val, nil } + +// Eval JS code. +func (s *ScriptEngine) Eval(code string) (string, error) { + if !s.isEvalAllowed { + return "", errors.New("disabled") + } + + val, err := s.vm.Eval(code) + if err != nil { + return "", err + } + return val.String(), nil +} diff --git a/main.go b/main.go index 1b4f4393..1b3000a5 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,7 @@ import ( "github.com/OpenDiablo2/OpenDiablo2/d2core/d2input" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2render/ebiten" "github.com/OpenDiablo2/OpenDiablo2/d2core/d2term" + "github.com/OpenDiablo2/OpenDiablo2/d2script" ) // GitBranch is set by the CI build process to the name of the branch @@ -40,12 +41,13 @@ func main() { d2input.Create() // TODO d2input singleton must be init before d2term term, err := d2term.Initialize() - if err != nil { log.Fatal(err) } - app := d2app.Create(GitBranch, GitCommit, term, audio, renderer) + scriptEngine := d2script.CreateScriptEngine() + + app := d2app.Create(GitBranch, GitCommit, term, scriptEngine, audio, renderer) if err := app.Run(); err != nil { log.Fatal(err) }