stockfish for godot
html works
| -rw-r--r-- | .clang-format | 1 | ||||
| -rw-r--r-- | .github/FUNDING.yml | 1 | ||||
| -rw-r--r-- | .gitignore | 7 | ||||
| -rw-r--r-- | .gitmodules | 4 | ||||
| -rw-r--r-- | LICENSE | 21 | ||||
| -rw-r--r-- | Main.gd | 10 | ||||
| -rw-r--r-- | Main.tscn | 6 | ||||
| -rw-r--r-- | README.md | 5 | ||||
| -rw-r--r-- | SConstruct | 117 | ||||
| -rw-r--r-- | addons/stockfish.gd/LICENSE | 21 | ||||
| -rw-r--r-- | addons/stockfish.gd/README.md | 1 | ||||
| -rw-r--r-- | addons/stockfish.gd/load.js | 1 | ||||
| -rw-r--r-- | addons/stockfish.gd/package.json | 25 | ||||
| -rw-r--r-- | addons/stockfish.gd/stockfish_loader.gd | 64 | ||||
| -rwxr-xr-x | export_html.sh | 21 | ||||
| -rw-r--r-- | export_presets.cfg | 34 | ||||
| m--------- | godot-cpp | 0 | ||||
| -rw-r--r-- | project.godot | 28 | ||||
| -rw-r--r-- | src/gdlibrary.cpp | 16 | ||||
| -rw-r--r-- | src/subprocess.cpp | 25 | ||||
| -rw-r--r-- | src/subprocess.h | 19 |
21 files changed, 427 insertions, 0 deletions
diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..c3fcefc --- /dev/null +++ b/.clang-format @@ -0,0 +1 @@ +"BreakBeforeBraces": Attach diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..1bfbcab --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +ko_fi: bendn diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f21958d --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +.vscode/ +.import/ +*.dblite +*.os +.gdignore +exports/ +web/ diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..2257299 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "godot-cpp"] + path = godot-cpp + url = https://github.com/godotengine/godot-cpp + branch = 3.x @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 bendn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. @@ -0,0 +1,10 @@ +extends Node + +var fish: StockfishLoader.Stockfish + + +func _ready() -> void: + var loader := StockfishLoader.new() + fish = loader.load_stockfish() + yield(fish, "engine_ready") + fish.run_command("go depth 5") diff --git a/Main.tscn b/Main.tscn new file mode 100644 index 0000000..cc043c2 --- /dev/null +++ b/Main.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=2] + +[ext_resource path="res://Main.gd" type="Script" id=1] + +[node name="Main" type="Node2D"] +script = ExtResource( 1 ) diff --git a/README.md b/README.md new file mode 100644 index 0000000..5da4f24 --- /dev/null +++ b/README.md @@ -0,0 +1,5 @@ +# stockfish + +[](https://godotengine.org "Made with godot") +[](https://www.npmjs.com/package/@bendn/stockfish.gd) +<a href='https://ko-fi.com/bendn' title='Buy me a coffee' target='_blank'><img height='28' src='https://storage.ko-fi.com/cdn/brandasset/kofi_button_red.png' alt='Buy me a coffee'> </a> diff --git a/SConstruct b/SConstruct new file mode 100644 index 0000000..2d4c8b5 --- /dev/null +++ b/SConstruct @@ -0,0 +1,117 @@ +#!python +import os + +opts = Variables([], ARGUMENTS) + +# Gets the standard flags CC, CCX, etc. +env = DefaultEnvironment() + +# Define our options +opts.Add(EnumVariable('target', "Compilation target", + 'debug', ['d', 'debug', 'r', 'release'])) +opts.Add(EnumVariable('platform', "Compilation platform", + '', ['', 'windows', 'x11', 'linux', 'osx'])) +opts.Add(EnumVariable('p', "Compilation target, alias for 'platform'", + '', ['', 'windows', 'x11', 'linux', 'osx'])) +opts.Add(BoolVariable('use_llvm', "Use the LLVM / Clang compiler", 'no')) +opts.Add(PathVariable('target_path', + 'The path where the lib is installed.', 'addons/chess.gd/bin/')) +opts.Add(PathVariable('target_name', 'The library name.', + 'libgdexample', PathVariable.PathAccept)) + +# Local dependency paths, adapt them to your setup +godot_headers_path = "godot-cpp/godot-headers/" +cpp_bindings_path = "godot-cpp/" +cpp_library = "libgodot-cpp" + +# only support 64 at this time.. +bits = 64 + +# Updates the environment with the option variables. +opts.Update(env) + +# Process some arguments +if env['use_llvm']: + env['CC'] = 'clang' + env['CXX'] = 'clang++' + +if env['p'] != '': + env['platform'] = env['p'] + +if env['platform'] == '': + print("No valid target platform selected.") + quit() + +# For the reference: +# - CCFLAGS are compilation flags shared between C and C++ +# - CFLAGS are for C-specific compilation flags +# - CXXFLAGS are for C++-specific compilation flags +# - CPPFLAGS are for pre-processor flags +# - CPPDEFINES are for pre-processor defines +# - LINKFLAGS are for linking flags + +# Check our platform specifics +if env['platform'] == "osx": + env['target_path'] += 'osx/' + cpp_library += '.osx' + env.Append(CCFLAGS=['-arch', 'x86_64']) + env.Append(CXXFLAGS=['-std=c++17']) + env.Append(LINKFLAGS=['-arch', 'x86_64']) + if env['target'] in ('debug', 'd'): + env.Append(CCFLAGS=['-g', '-O2']) + else: + env.Append(CCFLAGS=['-g', '-O3']) + +elif env['platform'] in ('x11', 'linux'): + env['target_path'] += 'x11/' + cpp_library += '.linux' + env.Append(CCFLAGS=['-fPIC']) + env.Append(CXXFLAGS=['-std=c++17']) + if env['target'] in ('debug', 'd'): + env.Append(CCFLAGS=['-g3', '-Og']) + else: + env.Append(CCFLAGS=['-g', '-O3']) + +elif env['platform'] == "windows": + env['target_path'] += 'win64/' + cpp_library += '.windows' + # This makes sure to keep the session environment variables on windows, + # that way you can run scons in a vs 2017 prompt and it will find all the required tools + env.Append(ENV=os.environ) + + env.Append(CPPDEFINES=['WIN32', '_WIN32', + '_WINDOWS', '_CRT_SECURE_NO_WARNINGS']) + env.Append(CCFLAGS=['-W3', '-GR']) + env.Append(CXXFLAGS='/std:c++17') + if env['target'] in ('debug', 'd'): + env.Append(CPPDEFINES=['_DEBUG']) + env.Append(CCFLAGS=['-EHsc', '-MDd', '-ZI']) + env.Append(LINKFLAGS=['-DEBUG']) + else: + env.Append(CPPDEFINES=['NDEBUG']) + env.Append(CCFLAGS=['-O2', '-EHsc', '-MD']) + +if env['target'] in ('debug', 'd'): + cpp_library += '.debug' +else: + cpp_library += '.release' + +cpp_library += '.' + str(bits) + +# make sure our binding library is properly includes +env.Append(CPPPATH=['.', godot_headers_path, cpp_bindings_path + 'include/', + cpp_bindings_path + 'include/core/', cpp_bindings_path + 'include/gen/']) +env.Append(LIBPATH=[cpp_bindings_path + 'bin/']) +env.Append(LIBS=[cpp_library]) + +# tweak this if you want to use different folders, or more folders, to store your source code in. +env.Append(CPPPATH=['src/']) +sources = Glob('src/*.cpp') + +library = env.SharedLibrary( + target=env['target_path'] + env['target_name'], source=sources) + +Default(library) + +# Generates help for the -h scons option. +Help(opts.GenerateHelpText(env)) diff --git a/addons/stockfish.gd/LICENSE b/addons/stockfish.gd/LICENSE new file mode 100644 index 0000000..9a9763e --- /dev/null +++ b/addons/stockfish.gd/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2022 bendn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/addons/stockfish.gd/README.md b/addons/stockfish.gd/README.md new file mode 100644 index 0000000..ad4885c --- /dev/null +++ b/addons/stockfish.gd/README.md @@ -0,0 +1 @@ +# stockfish diff --git a/addons/stockfish.gd/load.js b/addons/stockfish.gd/load.js new file mode 100644 index 0000000..1b9f90f --- /dev/null +++ b/addons/stockfish.gd/load.js @@ -0,0 +1 @@ +const dl=(url,onFinishDownload)=>{fetch(url).then(response=>response.arrayBuffer()).then(data=>onFinishDownload(data))};window.stockfish=null;let stockfish_state='LOADING';let output='';window.stockfishCommand=function(command){window.stockfish.postMessage(command)};const loadStockfish=async params=>{return await Stockfish(params)};const onFinishDownload=data=>{if(!data){window.stockfish_state='FAILED';window.stockfish_failed_load();return}loadStockfish({wasmBinary:data}).then(_stockfish=>{window.stockfish=_stockfish;window.stockfish_state='READY';window.stockfish.addMessageListener(line=>window.stockfish_data_recieved(line));window.stockfish_ready()}).catch(e=>{window.stockfish_state='FAILED';window.stockfish_failed_load();throw e})};dl('./lib/stockfish.wasm',onFinishDownload); diff --git a/addons/stockfish.gd/package.json b/addons/stockfish.gd/package.json new file mode 100644 index 0000000..128c3e2 --- /dev/null +++ b/addons/stockfish.gd/package.json @@ -0,0 +1,25 @@ +{ + "name": "@bendn/stockfish.gd", + "version": "1.0.1", + "description": "godot stockfish", + "main": "stockfish_loader.gd", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/bend-n/stockfish.gd.git" + }, + "keywords": [ + "godot", + "godot-engine", + "stockfish", + "chess" + ], + "author": "bendn", + "license": "MIT", + "bugs": { + "url": "https://github.com/bend-n/stockfish.gd/issues" + }, + "homepage": "https://github.com/bend-n/stockfish.gd#readme" +}
\ No newline at end of file diff --git a/addons/stockfish.gd/stockfish_loader.gd b/addons/stockfish.gd/stockfish_loader.gd new file mode 100644 index 0000000..a6df12f --- /dev/null +++ b/addons/stockfish.gd/stockfish_loader.gd @@ -0,0 +1,64 @@ +extends Reference +class_name StockfishLoader + + +func load_stockfish() -> Stockfish: + if not is_supported(): + push_error("Platform not supported") + return null + if OS.has_feature("JavaScript"): + var f = File.new() + f.open("res://addons/stockfish.gd/load.js") + JavaScript.eval(f.get_as_text()) + f.close() + return JSStockfish.new() + return null + + +func is_supported() -> bool: + if OS.has_feature("JavaScript"): + var has_wasm_buffer_atomics := "function s() {if(typeof WebAssembly!=='object')return false;const source=Uint8Array.from([0,97,115,109,1,0,0,0,1,5,1,96,0,1,123,3,2,1,0,7,8,1,4,116,101,115,116,0,0,10,15,1,13,0,65,0,253,17,65,0,253,17,253,186,1,11]);if(typeof WebAssembly.validate!=='function'||!WebAssembly.validate(source))return false;if(typeof Atomics!=='object')return false;if(typeof SharedArrayBuffer!=='function')return false;return true}; s()" + return JavaScript.eval(has_wasm_buffer_atomics) + return false + + +class Stockfish: + extends Reference + + signal engine_ready + signal data_received + signal load_failed + + func run_command(cmd: String) -> void: + pass + + +class JSStockfish: + extends Stockfish + + var data_recieved_callback := JavaScript.create_callback(self, "data_recieved") + var load_failed_callback := JavaScript.create_callback(self, "load_failed") + var ready_callback := JavaScript.create_callback(self, "ready") + + func _init() -> void: + JavaScript.get_interface("window").stockfish_data_recieved = data_recieved_callback + JavaScript.get_interface("window").stockfish_failed_load = load_failed_callback + JavaScript.get_interface("window").stockfish_ready = ready_callback + + func run_command(cmd: String) -> void: + JavaScript.eval("window.stockfishCommand('%s')" % cmd) + + # js callback arguments are in arrays. i guess its so that you can call functions with less args then they want? + func data_recieved(data:Array) -> void: + emit_signal("data_received", data[0]) + print(data[0]) + + # if _data is omitted, it will not work + func load_failed(_data:Array) -> void: + emit_signal("load_failed") + printerr("load failed") + + # ditto + func ready(_data:Array) -> void: + emit_signal("engine_ready") + diff --git a/export_html.sh b/export_html.sh new file mode 100755 index 0000000..0477477 --- /dev/null +++ b/export_html.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +set -e + +function install_libs() { + mkdir lib/ + wget -nv "https://cdn.jsdelivr.net/npm/stockfish-nnue.wasm/stockfish.js" -O lib/stockfish.js & + wget -nv "https://cdn.jsdelivr.net/npm/stockfish-nnue.wasm/stockfish.worker.js" -O lib/stockfish.worker.js & + wget -nv "https://cdn.jsdelivr.net/npm/stockfish-nnue.wasm/stockfish.wasm" -O lib/stockfish.wasm & + wget -nv "https://raw.githubusercontent.com/hi-ogawa/stockfish-nnue-wasm-demo/master/public/serve.json" -O serve.json & + wait +} + +[[ -d exports ]] && rm -rf exports +mkdir exports +[[ -f web/load.js ]] && uglifyjs web/load.js | tr "\"" "'" >addons/stockfish.gd/load.js +godot --no-window --export "HTML5" exports/index.html +cd exports +install_libs +touch .gdignore +serve --no-compression diff --git a/export_presets.cfg b/export_presets.cfg new file mode 100644 index 0000000..9a78f81 --- /dev/null +++ b/export_presets.cfg @@ -0,0 +1,34 @@ +[preset.0] + +name="HTML5" +platform="HTML5" +runnable=true +custom_features="" +export_filter="all_resources" +include_filter="*.js" +exclude_filter="" +export_path="" +script_export_mode=1 +script_encryption_key="" + +[preset.0.options] + +custom_template/debug="" +custom_template/release="" +variant/export_type=0 +vram_texture_compression/for_desktop=true +vram_texture_compression/for_mobile=false +html/export_icon=true +html/custom_html_shell="" +html/head_include="<script src=\"lib/stockfish.js\"></script>" +html/canvas_resize_policy=2 +html/focus_canvas_on_start=true +html/experimental_virtual_keyboard=false +progressive_web_app/enabled=false +progressive_web_app/offline_page="" +progressive_web_app/display=1 +progressive_web_app/orientation=0 +progressive_web_app/icon_144x144="" +progressive_web_app/icon_180x180="" +progressive_web_app/icon_512x512="" +progressive_web_app/background_color=Color( 0, 0, 0, 1 ) diff --git a/godot-cpp b/godot-cpp new file mode 160000 +Subproject 97c181a2461e590affab4c32638ca01928999db diff --git a/project.godot b/project.godot new file mode 100644 index 0000000..9dd90b6 --- /dev/null +++ b/project.godot @@ -0,0 +1,28 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=4 + +_global_script_classes=[ { +"base": "Reference", +"class": "StockfishLoader", +"language": "GDScript", +"path": "res://addons/stockfish.gd/stockfish_loader.gd" +} ] +_global_script_class_icons={ +"StockfishLoader": "" +} + +[application] + +config/name="stockfish.gd" +run/main_scene="res://Main.tscn" + +[rendering] + +quality/driver/driver_name="GLES2" diff --git a/src/gdlibrary.cpp b/src/gdlibrary.cpp new file mode 100644 index 0000000..e2c4273 --- /dev/null +++ b/src/gdlibrary.cpp @@ -0,0 +1,16 @@ +#include "subprocess.h" + +extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) { + godot::Godot::gdnative_init(o); +} + +extern "C" void GDN_EXPORT +godot_gdnative_terminate(godot_gdnative_terminate_options *o) { + godot::Godot::gdnative_terminate(o); +} + +extern "C" void GDN_EXPORT godot_nativescript_init(void *handle) { + godot::Godot::nativescript_init(handle); + + godot::register_class<godot::Subprocess>(); +} diff --git a/src/subprocess.cpp b/src/subprocess.cpp new file mode 100644 index 0000000..fd6fec7 --- /dev/null +++ b/src/subprocess.cpp @@ -0,0 +1,25 @@ +#include "subprocess.h" +using namespace godot; + +#include <iostream> +#include <sstream> + +using namespace godot; + +void Subprocess::_register_methods() { + register_signal<Subprocess>((char *)"recieve", "text", GODOT_VARIANT_STRING); + register_method("send", &Subprocess::send); +} + +void Subprocess::_init() { + String line; + bool running = true; + while (running && getline(std::cin, line)) { + std::istringstream iss(line); + Godot::print(line); + } +} + +static void Subprocess::send(const String &text) { + std::cout << text << std::endl; +} diff --git a/src/subprocess.h b/src/subprocess.h new file mode 100644 index 0000000..885836c --- /dev/null +++ b/src/subprocess.h @@ -0,0 +1,19 @@ +#ifndef ENGINE_H +#define ENGINE_H + +#include <Godot.hpp> + +namespace godot { + +class Subprocess : public Reference { + GODOT_CLASS(Subprocess, Reference) + +private: +public: + static void _register_methods(); + void _init(); + void send(String text); +}; + +} // namespace godot +#endif
\ No newline at end of file |