stockfish for godot
html works
bendn 2022-09-12
commit 79eb3aa
-rw-r--r--.clang-format1
-rw-r--r--.github/FUNDING.yml1
-rw-r--r--.gitignore7
-rw-r--r--.gitmodules4
-rw-r--r--LICENSE21
-rw-r--r--Main.gd10
-rw-r--r--Main.tscn6
-rw-r--r--README.md5
-rw-r--r--SConstruct117
-rw-r--r--addons/stockfish.gd/LICENSE21
-rw-r--r--addons/stockfish.gd/README.md1
-rw-r--r--addons/stockfish.gd/load.js1
-rw-r--r--addons/stockfish.gd/package.json25
-rw-r--r--addons/stockfish.gd/stockfish_loader.gd64
-rwxr-xr-xexport_html.sh21
-rw-r--r--export_presets.cfg34
m---------godot-cpp0
-rw-r--r--project.godot28
-rw-r--r--src/gdlibrary.cpp16
-rw-r--r--src/subprocess.cpp25
-rw-r--r--src/subprocess.h19
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
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..9a9763e
--- /dev/null
+++ b/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/Main.gd b/Main.gd
new file mode 100644
index 0000000..71cc0b9
--- /dev/null
+++ b/Main.gd
@@ -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
+
+[![version](https://img.shields.io/badge/3.x-blue?logo=godot-engine&logoColor=white&label=godot&style=for-the-badge)](https://godotengine.org "Made with godot")
+[![package](https://img.shields.io/npm/v/@bendn/stockfish.gd?label=version&style=for-the-badge)](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