From c3dc2093edd7c5e76d313528995b856f307675dd Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 30 Nov 2025 16:48:54 +0100 Subject: [PATCH 01/70] replace send/list commands with rust sidecar --- .cargo/config.toml | 11 +++ .envrc | 1 + .gitignore | 22 ++++- Cargo.toml | 18 ++++ Justfile | 19 +++++ README.md | 7 +- bb.edn | 2 - lua/defold/editor.lua | 23 +++-- lua/defold/init.lua | 31 +++++-- lua/defold/service/debugger.lua | 5 +- shell.nix | 15 ++++ src/defold/editor.clj | 97 --------------------- src/defold/main.clj | 9 +- src/editor.rs | 107 +++++++++++++++++++++++ src/error.rs | 27 ++++++ src/game_project.rs | 56 ++++++++++++ src/lib.rs | 145 ++++++++++++++++++++++++++++++++ 17 files changed, 459 insertions(+), 136 deletions(-) create mode 100644 .cargo/config.toml create mode 100644 .envrc create mode 100644 Cargo.toml create mode 100644 Justfile create mode 100644 shell.nix delete mode 100644 src/defold/editor.clj create mode 100644 src/editor.rs create mode 100644 src/error.rs create mode 100644 src/game_project.rs create mode 100644 src/lib.rs diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..d47f983 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,11 @@ +[target.x86_64-apple-darwin] +rustflags = [ + "-C", "link-arg=-undefined", + "-C", "link-arg=dynamic_lookup", +] + +[target.aarch64-apple-darwin] +rustflags = [ + "-C", "link-arg=-undefined", + "-C", "link-arg=dynamic_lookup", +] diff --git a/.envrc b/.envrc new file mode 100644 index 0000000..1d953f4 --- /dev/null +++ b/.envrc @@ -0,0 +1 @@ +use nix diff --git a/.gitignore b/.gitignore index d5f0966..0777e13 100644 --- a/.gitignore +++ b/.gitignore @@ -2,8 +2,8 @@ .lsp .nrepl* -# Created by https://www.toptal.com/developers/gitignore/api/windows,macos,linux,lua -# Edit at https://www.toptal.com/developers/gitignore?templates=windows,macos,linux,lua +# Created by https://www.toptal.com/developers/gitignore/api/windows,macos,linux,lua,rust +# Edit at https://www.toptal.com/developers/gitignore?templates=windows,macos,linux,lua,rust ### Linux ### *~ @@ -96,6 +96,22 @@ Temporary Items # iCloud generated files *.icloud +### Rust ### +# Generated by Cargo +# will have compiled files and executables +debug/ +target/ + +# Remove Cargo.lock from gitignore if creating an executable, leave it for libraries +# More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html +Cargo.lock + +# These are backup files generated by rustfmt +**/*.rs.bk + +# MSVC Windows builds of rustc generate these, which store debugging information +*.pdb + ### Windows ### # Windows thumbnail cache files Thumbs.db @@ -122,4 +138,4 @@ $RECYCLE.BIN/ # Windows shortcuts *.lnk -# End of https://www.toptal.com/developers/gitignore/api/windows,macos,linux,lua +# End of https://www.toptal.com/developers/gitignore/api/windows,macos,linux,lua,rust diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..f5f4e44 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "defold-nvim" +version = "0.1.0" +edition = "2024" + +[lib] +crate-type = ["cdylib"] + +[dependencies] +anyhow = "1.0.100" +netstat2 = "0.11.2" +nvim-oxi = { version = "0.6.0", features = ["neovim-0-11"] } +reqwest = { version = "0.12.24", features = ["blocking", "json"] } +rust-ini = "0.21.3" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.145" +sha3 = "0.10.8" +sysinfo = "0.37.2" diff --git a/Justfile b/Justfile new file mode 100644 index 0000000..1064b81 --- /dev/null +++ b/Justfile @@ -0,0 +1,19 @@ +watch: + watchexec -w src -r 'just build-and-link' + +build: + cargo build + +link: + #!/usr/bin/env bash + set -e + + if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]] || [[ "$OSTYPE" == "cygwin" ]]; then + cp -f "$(pwd)/target/debug/defold_nvim.dll" lua/defold/sidecar.dll + elif [[ "$(uname)" == "Darwin" ]]; then + cp -f "$(pwd)/target/debug/libdefold_nvim.dylib" lua/defold/sidecar.so + else + cp -f "$(pwd)/target/debug/libdefold_nvim.so" lua/defold/sidecar.so + fi + +build-and-link: build link diff --git a/README.md b/README.md index 4c8d460..51bf14d 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,8 @@ You're in luck, powershell is surprisingly capable so there is nothing else need "mfussenegger/nvim-dap", }, + version = "*", + opts = { -- config options, see below }, @@ -150,11 +152,6 @@ local config = { custom_arguments = nil, }, - babashka = { - -- Use a custom executable for babashka (default: nil) - custom_executable = nil, - }, - -- setup keymaps for Defold actions keymaps = { diff --git a/bb.edn b/bb.edn index 4162311..01659d4 100644 --- a/bb.edn +++ b/bb.edn @@ -4,9 +4,7 @@ setup {:task (apply defold/run-wrapped :setup *command-line-args*)} set-default-editor {:task (apply defold/run-wrapped :set-default-editor *command-line-args*)} install-dependencies {:task (apply defold/run-wrapped :install-dependencies *command-line-args*)} - list-commands {:task (apply defold/run-wrapped :list-commands *command-line-args*)} list-dependency-dirs {:task (apply defold/run-wrapped :list-dependency-dirs *command-line-args*)} - send-command {:task (apply defold/run-wrapped :send-command *command-line-args*)} launch-neovim {:task (apply defold/run-wrapped :launch-neovim *command-line-args*)} focus-neovim {:task (apply defold/run-wrapped :focus-neovim *command-line-args*)} focus-game {:task (apply defold/run-wrapped :focus-game *command-line-args*)} diff --git a/lua/defold/editor.lua b/lua/defold/editor.lua index 40b3675..53e502e 100644 --- a/lua/defold/editor.lua +++ b/lua/defold/editor.lua @@ -1,17 +1,13 @@ local M = {} ---List all available Defold commands +---@param port integer|nil ---@return table|nil -function M.list_commands() - local babashka = require "defold.service.babashka" +function M.list_commands(port) local log = require "defold.service.logger" + local sidecar = require "defold.sidecar" - local res = babashka.run_task_json "list-commands" - - if not res then - log.error "Could not fetch commands from Defold, maybe the editor isn't running?" - return nil - end + local res = sidecar.list_commands(port) if res.error then log.error(string.format("Could not fetch commands from Defold, because: %s", res.error)) @@ -22,19 +18,20 @@ function M.list_commands() end ---Sends a command to the Defold editor +---@param port integer|nil ---@param command string ---@param dont_report_error boolean|nil -function M.send_command(command, dont_report_error) - local babashka = require "defold.service.babashka" +function M.send_command(port, command, dont_report_error) local log = require "defold.service.logger" + local sidecar = require "defold.sidecar" - local res = babashka.run_task_json("send-command", { command }) + local res = sidecar.send_command(port, command) - if res.status == 202 then + if not res.error then return end - if dont_report_error or false then + if dont_report_error then return end diff --git a/lua/defold/init.lua b/lua/defold/init.lua index 5424e3a..e1cbf36 100644 --- a/lua/defold/init.lua +++ b/lua/defold/init.lua @@ -79,6 +79,9 @@ M.loaded = false ---@type DefoldNvimConfig M.config = default_config +---@type integer|nil +M.prev_editor_port = nil + ---Returns true if we are in a defold project ---@return boolean function M.is_defold_project() @@ -104,6 +107,8 @@ function M.setup(opts) M.config = vim.tbl_deep_extend("force", default_config, opts or {}) + -- TODO: check if sidecar is available, if not download it (which shouldnt be necessary with some pkg managers) + -- persist config for babashka local config_path = babashka.config_path() vim.fn.writefile({ @@ -196,6 +201,18 @@ function M.setup(opts) end, 0) end +---@return integer +function M.editor_port() + if M.prev_editor_port then + -- TODO: validate port + return M.prev_editor_port + end + + local sidecar = require "defold.sidecar" + M.prev_editor_port = sidecar.find_editor_port() + return M.prev_editor_port +end + function M.load_plugin() if M.loaded then return @@ -206,6 +223,7 @@ function M.load_plugin() -- register all filetypes vim.filetype.add(require("defold.config.filetype").full) + local sidecar = require "defold.sidecar" local babashka = require "defold.service.babashka" local debugger = require "defold.service.debugger" local editor = require "defold.editor" @@ -213,6 +231,7 @@ function M.load_plugin() local project = require "defold.project" log.debug "============= defold.nvim: Loaded plugin" + log.debug("Sidecar Version:" .. sidecar.version) log.debug("Babashka Path: " .. babashka.bb_path()) log.debug("Mobdap Path: " .. debugger.mobdap_path()) log.debug("Config: " .. vim.inspect(M.config)) @@ -222,7 +241,7 @@ function M.load_plugin() vim.api.nvim_create_autocmd("BufWritePost", { pattern = { "*.lua", "*.script", "*.gui_script" }, callback = function() - editor.send_command("hot-reload", true) + editor.send_command(M.editor_port(), "hot-reload", true) end, }) end @@ -256,26 +275,26 @@ function M.load_plugin() return end - editor.send_command(cmds[idx]) + editor.send_command(M.editor_port(), cmds[idx]) end) end, { nargs = 0, desc = "Select a command to run" }) -- add the ":DefoldSend cmd" command to send commands to the editor vim.api.nvim_create_user_command("DefoldSend", function(opt) - editor.send_command(opt.args) + editor.send_command(M.editor_port(), opt.args) end, { nargs = 1, desc = "Send a command to the Defold editor" }) -- add the ":DefoldFetch" command to fetch dependencies & annoatations vim.api.nvim_create_user_command("DefoldFetch", function(opt) -- when a user runs DefoldFetch I recon they also expect us to update the dependencies - editor.send_command("fetch-libraries", true) + editor.send_command(M.editor_port(), "fetch-libraries", true) project.install_dependencies(opt.bang) end, { bang = true, nargs = 0, desc = "Fetch & create Defold project dependency annotations" }) -- integrate the debugger into dap if M.config.debugger.enable then - debugger.register_nvim_dap() + debugger.register_nvim_dap(M.editor_port) end -- add snippets @@ -289,7 +308,7 @@ function M.load_plugin() log.debug(string.format("Setup action '%s' for keymap '%s'", action, vim.json.encode(keymap))) vim.keymap.set(keymap.mode, keymap.mapping, function() - editor.send_command(action) + editor.send_command(M.editor_port(), action) end) end diff --git a/lua/defold/service/debugger.lua b/lua/defold/service/debugger.lua index 3fac42a..b21a51c 100644 --- a/lua/defold/service/debugger.lua +++ b/lua/defold/service/debugger.lua @@ -42,7 +42,8 @@ function M.setup(custom_executable, custom_arguments) M.mobdap_path() end -function M.register_nvim_dap() +---@param editor_port_fn fun(): integer +function M.register_nvim_dap(editor_port_fn) local log = require "defold.service.logger" local ok, dap = pcall(require, "dap") @@ -91,7 +92,7 @@ function M.register_nvim_dap() dap.listeners.after.event_mobdap_waiting_for_connection.defold_nvim_start_game = function(_, _) log.debug "debugger: connected" - editor.send_command "build" + editor.send_command(editor_port_fn(), "build") end dap.listeners.after.event_stopped.defold_nvim_switch_focus_on_stop = function(_, _) diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..4f32adb --- /dev/null +++ b/shell.nix @@ -0,0 +1,15 @@ +{ + pkgs ? import { }, +}: + +pkgs.mkShell { + nativeBuildInputs = with pkgs; [ + pkg-config + ]; + + buildInputs = with pkgs; [ + openssl.dev + ]; + + RUST_BACKTRACE = "1"; +} diff --git a/src/defold/editor.clj b/src/defold/editor.clj deleted file mode 100644 index cbb50c2..0000000 --- a/src/defold/editor.clj +++ /dev/null @@ -1,97 +0,0 @@ -(ns defold.editor - (:require - [babashka.http-client :as http] - [babashka.process :refer [shell]] - [cheshire.core :as json] - [clojure.string :as string] - [defold.constants :refer [mobdap-port]] - [defold.utils :refer [command-exists?]] - [taoensso.timbre :as log])) - -(defn make-command-url [port cmd] - (str "http://127.0.0.1:" port "/command/" (string/lower-case cmd))) - -(defn- is-defold-port? [port] - (when (not= (Integer/parseInt port) mobdap-port) - (try (let [res (http/head (make-command-url port "") {:timeout 100}) - status (:status res)] - (= status 200)) - (catch Exception _ false)))) - -(defn- extract-port-generic [line] - (->> - (string/split line #" ") - (filter not-empty) - (map #(re-find #".*:(\d+)$" %)) - (filter some?) - (first) - (last))) - -(defn- find-port-generic [& cmd] - (try - (-> (apply shell {:out :string} cmd) - :out - (string/split-lines) - (->> (filter #(string/includes? % "java"))) - (->> (map extract-port-generic)) - (->> (filter is-defold-port?)) - (first)) - (catch Exception t - (do - (log/error "Error: " t) - (throw (ex-info (str "Could not find Defold port via '" (string/join " " cmd) "'.") {})))))) - -(defn- find-port-netstat [] - (some #(when (is-defold-port? %) %) - (-> (shell {:out :string :err :string} "netstat" "-anv") - :out - (string/split-lines) - (->> (filter #(string/includes? % "LISTEN"))) - (->> (map #(string/split % #" "))) - (->> (filter #(= (first %) "tcp"))) - (flatten) - (->> (map #(re-find #".*:(\d+)$" %))) - (->> (map second)) - (->> (filter some?)) - (->> (map Integer/parseInt)) - (->> (sort >))))) - -(defn find-port [] - (cond - (command-exists? "lsof") - (find-port-generic "lsof" "-nP" "-iTCP" "-sTCP:LISTEN") - - (command-exists? "ss") - (find-port-generic "ss" "-tplH4") - - (command-exists? "netstat") - (find-port-netstat) - - :else (throw (ex-info "Couldn't find either 'lsof', 'ss' or 'netstat', which is necessary to interact with Defold" {})))) - -(defn list-commands [] - (try - (let [port (find-port) - url (make-command-url port "")] - (-> - (http/get url) - :body - (json/parse-string))) - (catch Exception e - (let [msg (ex-message e)] - (log/error "Error: " msg e) - {"error" (ex-message e)})))) - -(defn send-command [cmd] - (try - (let [port (find-port) - url (make-command-url port cmd)] - {"status" (:status (http/post url))}) - (catch Exception e - (if (= 403 (get-in (Throwable->map e) [:data :status])) - (do (log/warn (format "Editor responded 403 Forbidden to %s" cmd)) - {"error" (ex-message e)}) - (let [msg (ex-message e)] - (log/error "Error: " msg e) - {"error" (ex-message e)}))))) - diff --git a/src/defold/main.clj b/src/defold/main.clj index ebcf9df..213305f 100644 --- a/src/defold/main.clj +++ b/src/defold/main.clj @@ -3,7 +3,6 @@ [babashka.fs :as fs] [cheshire.core :as json] [defold.debugger :as debugger] - [defold.editor :as editor] [defold.editor-config :as editor-config] [defold.focus :as focus] [defold.launcher :as launcher] @@ -37,7 +36,7 @@ (when-not (get-in conf ["plugin_config" "debugger" "custom_executable"]) (debugger/setup)) (when (and (= "neovide" (get-in conf ["plugin_config" "launcher" "type"])) - (not (get-in conf ["plugin_config" "launcher" "executable"]))) + (not (get-in conf ["plugin_config" "launcher" "executable"]))) (neovide/setup)) (print-json {:status 200})) (catch Throwable t @@ -54,15 +53,9 @@ ([_ _ game-project force-redownload] (print-json (project/install-dependencies game-project force-redownload)))) -(defmethod run :list-commands [_ _] - (print-json (editor/list-commands))) - (defmethod run :list-dependency-dirs [_ _ game-project] (print-json (project/list-dependency-dirs game-project))) -(defmethod run :send-command [_ _ cmd] - (print-json (editor/send-command cmd))) - (defmethod run :launch-neovim ([_ config-file root-dir filename] (let [conf (parse-config config-file)] diff --git a/src/editor.rs b/src/editor.rs new file mode 100644 index 0000000..a0d70e3 --- /dev/null +++ b/src/editor.rs @@ -0,0 +1,107 @@ +use std::collections::HashMap; + +use anyhow::{Context, Result, bail}; +use netstat2::{AddressFamilyFlags, ProtocolFlags, ProtocolSocketInfo, iterate_sockets_info}; +use sysinfo::System; + +fn command_url(port: u16, command: Option) -> String { + format!( + "http://127.0.0.1:{port}/command/{}", + command.unwrap_or_default() + ) +} + +pub fn find_port() -> Option { + let sys = System::new_all(); + + for (pid, proc) in sys.processes() { + if !proc + .name() + .to_ascii_lowercase() + .to_str() + .unwrap_or_default() + .contains("java") + { + continue; + } + + let ports = ports_for_pid(pid.as_u32()); + + if ports.is_err() { + continue; + } + + for port in ports.unwrap() { + if is_editor(port) { + return Some(port); + } + } + } + + None +} + +fn ports_for_pid(pid: u32) -> Result> { + let mut ports = Vec::new(); + + let af_flags = AddressFamilyFlags::IPV4 | AddressFamilyFlags::IPV6; + let proto_flags = ProtocolFlags::TCP | ProtocolFlags::UDP; + + for socket in iterate_sockets_info(af_flags, proto_flags)? { + let socket = socket?; + + if socket.associated_pids.contains(&pid) { + match &socket.protocol_socket_info { + ProtocolSocketInfo::Tcp(tcp) => { + ports.push(tcp.local_port); + } + ProtocolSocketInfo::Udp(udp) => { + ports.push(udp.local_port); + } + } + } + } + + Ok(ports) +} + +fn is_editor(port: u16) -> bool { + reqwest::blocking::Client::new() + .head(command_url(port, None)) + .send() + .is_ok_and(|r| r.status().is_success()) +} + +pub fn list_commands(port: Option) -> Result> { + let url = command_url( + port.or_else(|| find_port()) + .context("could not determine editor port")?, + None, + ); + + let res = reqwest::blocking::get(url)?; + + if !res.status().is_success() { + bail!("could not list commands, status: {:?}", res.status()); + } + + let content = res.text()?.to_string(); + + serde_json::from_str(&content).map_err(|err| anyhow::Error::from(err)) +} + +pub fn send_command(port: Option, cmd: String) -> Result<()> { + let url = command_url( + port.or_else(|| find_port()) + .context("could not determine editor port")?, + Some(cmd.clone()), + ); + + let res = reqwest::blocking::Client::new().post(url).send()?; + + if !res.status().is_success() { + bail!("could not send command {cmd}, status: {:?}", res.status()); + } + + Ok(()) +} diff --git a/src/error.rs b/src/error.rs new file mode 100644 index 0000000..dab9393 --- /dev/null +++ b/src/error.rs @@ -0,0 +1,27 @@ +use nvim_oxi::{conversion::ToObject, serde::Serializer}; +use serde::{Deserialize, Serialize}; + +#[derive(Serialize, Deserialize)] +pub struct LuaError { + pub error: String, +} + +impl From for LuaError { + fn from(value: String) -> Self { + LuaError { + error: format!("Err: {value}"), + } + } +} + +impl From for LuaError { + fn from(value: anyhow::Error) -> Self { + LuaError::from(format!("{value:?}")) + } +} + +impl ToObject for LuaError { + fn to_object(self) -> Result { + self.serialize(Serializer::new()).map_err(Into::into) + } +} diff --git a/src/game_project.rs b/src/game_project.rs new file mode 100644 index 0000000..8475134 --- /dev/null +++ b/src/game_project.rs @@ -0,0 +1,56 @@ +use std::{fs, path::PathBuf}; + +use anyhow::{Context, Result, bail}; +use ini::Ini; +use nvim_oxi::{conversion::ToObject, serde::Serializer}; +use serde::Serialize; + +#[derive(Debug, Serialize)] +pub struct GameProject { + pub title: String, + pub dependencies: Vec, +} + +impl GameProject { + pub fn load_from_path(path: PathBuf) -> Result { + if !path.exists() { + bail!("game.project file {path:?} could not be found"); + } + + GameProject::try_from(fs::read_to_string(path)?) + } +} + +impl TryFrom for GameProject { + type Error = anyhow::Error; + + fn try_from(value: String) -> std::result::Result { + let proj = Ini::load_from_str(value.as_str())?; + + let project_section = proj + .section(Some("project")) + .context("invalid game.project: no [project] section")?; + + let title = project_section + .get("title") + .context("invalid game.project: no title field in [project] section")? + .to_string(); + + let dependencies = project_section + .iter() + .filter(|(k, _)| k.starts_with("dependencies#")) + .map(|(_, v)| v.to_string()) + .collect(); + + Ok(GameProject { + title, + dependencies, + }) + } +} + +impl ToObject for GameProject { + fn to_object(self) -> std::result::Result { + self.serialize(Serializer::new()).map_err(Into::into) + } +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..f8ab95a --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,145 @@ +use anyhow::{Result, anyhow}; +use nvim_oxi::conversion::ToObject; +use nvim_oxi::{Dictionary, Function, Object, ObjectKind}; +use sha3::{Digest, Sha3_256}; + +use crate::game_project::GameProject; + +mod editor; +mod error; +mod game_project; + +use crate::error::*; + +#[nvim_oxi::plugin] +fn defold_sidecar() -> Dictionary { + Dictionary::from_iter([ + ("version", Object::from(env!("CARGO_PKG_VERSION"))), + ("sha3", Object::from(Function::from_fn(sha3))), + ( + "read_game_project", + Object::from(Function::from_fn(read_game_project)), + ), + ( + "find_editor_port", + Object::from(Function::from_fn(find_editor_port)), + ), + ( + "list_commands", + Object::from(Function::from_fn(list_commands)), + ), + ( + "send_command", + Object::from(Function::from_fn(send_command)), + ), + ]) +} + +fn sha3(input: Object) -> Object { + if input.kind() != ObjectKind::String { + return LuaError::from("sha3(input), expected input to be of type string".to_string()) + .to_object() + .unwrap(); + }; + + let input = unsafe { input.as_nvim_str_unchecked().to_string() }; + + let mut hasher = Sha3_256::new(); + hasher.update(input.as_bytes()); + let result = hasher.finalize(); + + format!("{:x}", result).into() +} + +fn read_game_project(path: Object) -> Object { + if path.kind() != ObjectKind::String { + return LuaError::from( + "read_game_project(path), expected path to be of type string".to_string(), + ) + .to_object() + .unwrap(); + }; + + let path = unsafe { path.as_nvim_str_unchecked().to_string() }; + + match GameProject::load_from_path(path.into()) { + Ok(game_project) => game_project.to_object().unwrap_or_else(|err| { + LuaError::from(anyhow::Error::from(err)) + .to_object() + .unwrap() + }), + Err(err) => LuaError::from(err).to_object().unwrap(), + } +} + +fn find_editor_port(_: ()) -> Object { + match editor::find_port() { + Some(port) => Object::from(port), + None => LuaError::from("Could not find editor port".to_string()) + .to_object() + .unwrap(), + } +} + +fn list_commands(port: Object) -> Object { + let port = match port.kind() { + ObjectKind::Integer => match u16::try_from(unsafe { port.as_integer_unchecked() }) { + Ok(port) => Some(port), + Err(err) => { + return LuaError::from(anyhow::Error::from(err)) + .to_object() + .unwrap(); + } + }, + _ => None, + }; + + let commands = editor::list_commands(port); + + let Ok(commands) = commands else { + return LuaError::from(anyhow::Error::from(commands.unwrap_err())) + .to_object() + .unwrap(); + }; + + Object::from_iter(commands) +} + +fn send_command((port, cmd): (Object, Object)) -> Object { + let Ok(port) = get_int_opt(port) else { + return LuaError::from("send_command(port, cmd): port was not int|nil".to_string()) + .to_object() + .unwrap(); + }; + + let Ok(cmd) = get_string(cmd) else { + return LuaError::from("send_command(port, cmd): cmd was not a string".to_string()) + .to_object() + .unwrap(); + }; + + if let Err(err) = editor::send_command(port, cmd) { + return LuaError::from(err).to_object().unwrap(); + } + + Object::from(Dictionary::new()) +} + +fn get_int_opt>(o: Object) -> Result> { + match o.kind() { + ObjectKind::Integer => match T::try_from(unsafe { o.as_integer_unchecked() }) { + Ok(v) => Ok(Some(v)), + Err(_) => Ok(None), + }, + ObjectKind::Nil => Ok(None), + _ => Err(anyhow!("object was not an integer")), + } +} + +fn get_string(o: Object) -> Result { + match o.kind() { + ObjectKind::String => String::try_from(unsafe { o.as_nvim_str_unchecked() }.to_string()) + .map_err(anyhow::Error::from), + _ => Err(anyhow!("object was not a string")), + } +} From 719d53a5df9ca0a14fb4b5d5b3450f20eea04f07 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 30 Nov 2025 17:20:39 +0100 Subject: [PATCH 02/70] add github actions pipeline --- .github/workflows/test.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..6e2a592 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,23 @@ +on: + pull_request: + push: + branches: + - master + +jobs: + test: + name: Test on ${{ matrix.os }} + runs-on: ${{ matrix.os }} + strategy: + matrix: + os: + - ubuntu-latest + - macos-latest + - windows-latest + + steps: + - uses: actions/checkout@v5 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + + - name: Run tests + run: cargo test --all --verbose From 83eda2c37c56042ab720842cea9555f4bf5d89ed Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Tue, 9 Dec 2025 23:05:02 +0100 Subject: [PATCH 03/70] refactor from nvim-oxi to mlua --- Cargo.toml | 8 +- lua/defold/editor.lua | 13 ++-- lua/defold/init.lua | 12 ++- lua/defold/service/os.lua | 4 + lua/defold/sidecar.lua | 64 ++++++++++++++++ src/error.rs | 27 ------- src/game_project.rs | 10 +-- src/lib.rs | 149 +++++++------------------------------- 8 files changed, 124 insertions(+), 163 deletions(-) create mode 100644 lua/defold/sidecar.lua delete mode 100644 src/error.rs diff --git a/Cargo.toml b/Cargo.toml index f5f4e44..d86c87b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -4,12 +4,18 @@ version = "0.1.0" edition = "2024" [lib] +path = "src/lib.rs" crate-type = ["cdylib"] [dependencies] anyhow = "1.0.100" +mlua = { version = "0.11.5", features = [ + "module", + "luajit", + "serde", + "anyhow", +] } netstat2 = "0.11.2" -nvim-oxi = { version = "0.6.0", features = ["neovim-0-11"] } reqwest = { version = "0.12.24", features = ["blocking", "json"] } rust-ini = "0.21.3" serde = { version = "1.0.228", features = ["derive"] } diff --git a/lua/defold/editor.lua b/lua/defold/editor.lua index 53e502e..33e04e0 100644 --- a/lua/defold/editor.lua +++ b/lua/defold/editor.lua @@ -7,13 +7,14 @@ function M.list_commands(port) local log = require "defold.service.logger" local sidecar = require "defold.sidecar" - local res = sidecar.list_commands(port) + local ok, res = pcall(sidecar.list_commands, port) - if res.error then - log.error(string.format("Could not fetch commands from Defold, because: %s", res.error)) + if not ok then + log.error(string.format("Could not fetch commands from Defold, because: %s", res)) return nil end + ---@cast res table return res end @@ -25,9 +26,9 @@ function M.send_command(port, command, dont_report_error) local log = require "defold.service.logger" local sidecar = require "defold.sidecar" - local res = sidecar.send_command(port, command) + local _, err = pcall(sidecar.send_command, port, command) - if not res.error then + if not err then return end @@ -35,7 +36,7 @@ function M.send_command(port, command, dont_report_error) return end - log.error(string.format("Could execute comannd '%s', because: %s", command, res.error or "Something went wrong!")) + log.error(string.format("Could not execute comannd '%s', because: %s", command, err or "Something went wrong!")) end return M diff --git a/lua/defold/init.lua b/lua/defold/init.lua index e1cbf36..5297f39 100644 --- a/lua/defold/init.lua +++ b/lua/defold/init.lua @@ -209,7 +209,17 @@ function M.editor_port() end local sidecar = require "defold.sidecar" - M.prev_editor_port = sidecar.find_editor_port() + + local ok, res = pcall(sidecar.find_editor_port) + + if ok then + ---@cast res integer + M.prev_editor_port = res + else + local log = require "defold.service.logger" + log.error(string.format("Could not find editor port, because: %s", res)) + end + return M.prev_editor_port end diff --git a/lua/defold/service/os.lua b/lua/defold/service/os.lua index c8499a5..095127d 100644 --- a/lua/defold/service/os.lua +++ b/lua/defold/service/os.lua @@ -34,6 +34,10 @@ function M.is_windows() return M.name() == "windows" end +function M.is_macos() + return M.name() == "macos" +end + function M.architecture() local machine = vim.loop.os_uname().machine diff --git a/lua/defold/sidecar.lua b/lua/defold/sidecar.lua new file mode 100644 index 0000000..cac38b6 --- /dev/null +++ b/lua/defold/sidecar.lua @@ -0,0 +1,64 @@ +local function lib_extension() + local os = require "defold.service.os" + + if os.is_windows() then + return ".dll" + elseif os.is_macos() then + return ".dylib" + else + return ".so" + end +end + +local function find_rust_lib_rootdir() + local os = require "defold.service.os" + local plugin_root = os.plugin_root() + + local file_name = string.format("defold_nvim%s", lib_extension()) + local file_name_alt = string.format("libdefold_nvim%s", lib_extension()) + + if + os.file_exists(vim.fs.joinpath(plugin_root, file_name)) + or os.file_exists(vim.fs.joinpath(plugin_root, file_name_alt)) + then + return plugin_root + elseif + os.file_exists(vim.fs.joinpath(plugin_root, "target", "debug", file_name)) + or os.file_exists(vim.fs.joinpath(plugin_root, "target", "debug", file_name_alt)) + then + return vim.fs.joinpath(plugin_root, "target", "debug") + elseif + os.file_exists(vim.fs.joinpath(plugin_root, "release", "debug", file_name)) + or os.file_exists(vim.fs.joinpath(plugin_root, "release", "debug", file_name_alt)) + then + return vim.fs.joinpath(plugin_root, "target", "release") + else + -- TODO: add auto download + error "Error: Could not find rust lib" + end +end + +local plugin_rootdir = find_rust_lib_rootdir() + +package.cpath = package.cpath + .. ";" + .. string.format("%s/lib?%s", plugin_rootdir, lib_extension()) + .. ";" + .. string.format("%s/?%s", plugin_rootdir, lib_extension()) + +---@class GameProject +---@field title string +---@field dependencies string[] + +---@class Sidecar +---@field version string +---@field sha3 function(input: string): string +---@field read_game_project function(path: string): GameProject +---@field find_editor_port function(): integer +---@field list_commands function(port: integer|nil): table +---@field send_command function(port: integer|nil, cmd: string) + +---@type Sidecar +local rust_plugin = require "defold_nvim" + +return rust_plugin diff --git a/src/error.rs b/src/error.rs deleted file mode 100644 index dab9393..0000000 --- a/src/error.rs +++ /dev/null @@ -1,27 +0,0 @@ -use nvim_oxi::{conversion::ToObject, serde::Serializer}; -use serde::{Deserialize, Serialize}; - -#[derive(Serialize, Deserialize)] -pub struct LuaError { - pub error: String, -} - -impl From for LuaError { - fn from(value: String) -> Self { - LuaError { - error: format!("Err: {value}"), - } - } -} - -impl From for LuaError { - fn from(value: anyhow::Error) -> Self { - LuaError::from(format!("{value:?}")) - } -} - -impl ToObject for LuaError { - fn to_object(self) -> Result { - self.serialize(Serializer::new()).map_err(Into::into) - } -} diff --git a/src/game_project.rs b/src/game_project.rs index 8475134..da5c0d9 100644 --- a/src/game_project.rs +++ b/src/game_project.rs @@ -2,7 +2,7 @@ use std::{fs, path::PathBuf}; use anyhow::{Context, Result, bail}; use ini::Ini; -use nvim_oxi::{conversion::ToObject, serde::Serializer}; +use mlua::UserData; use serde::Serialize; #[derive(Debug, Serialize)] @@ -21,6 +21,8 @@ impl GameProject { } } +impl UserData for GameProject {} + impl TryFrom for GameProject { type Error = anyhow::Error; @@ -48,9 +50,3 @@ impl TryFrom for GameProject { }) } } - -impl ToObject for GameProject { - fn to_object(self) -> std::result::Result { - self.serialize(Serializer::new()).map_err(Into::into) - } -} diff --git a/src/lib.rs b/src/lib.rs index f8ab95a..2d36595 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,145 +1,52 @@ -use anyhow::{Result, anyhow}; -use nvim_oxi::conversion::ToObject; -use nvim_oxi::{Dictionary, Function, Object, ObjectKind}; +use anyhow::Context; +use mlua::prelude::*; use sha3::{Digest, Sha3_256}; use crate::game_project::GameProject; mod editor; -mod error; mod game_project; -use crate::error::*; +#[mlua::lua_module] +fn defold_nvim(lua: &Lua) -> LuaResult { + let exports = lua.create_table()?; -#[nvim_oxi::plugin] -fn defold_sidecar() -> Dictionary { - Dictionary::from_iter([ - ("version", Object::from(env!("CARGO_PKG_VERSION"))), - ("sha3", Object::from(Function::from_fn(sha3))), - ( - "read_game_project", - Object::from(Function::from_fn(read_game_project)), - ), - ( - "find_editor_port", - Object::from(Function::from_fn(find_editor_port)), - ), - ( - "list_commands", - Object::from(Function::from_fn(list_commands)), - ), - ( - "send_command", - Object::from(Function::from_fn(send_command)), - ), - ]) -} - -fn sha3(input: Object) -> Object { - if input.kind() != ObjectKind::String { - return LuaError::from("sha3(input), expected input to be of type string".to_string()) - .to_object() - .unwrap(); - }; + exports.set("version", lua.create_string(env!("CARGO_PKG_VERSION"))?)?; + exports.set("sha3", lua.create_function(sha3)?)?; + exports.set("read_game_project", lua.create_function(read_game_project)?)?; + exports.set("find_editor_port", lua.create_function(find_editor_port)?)?; + exports.set("list_commands", lua.create_function(list_commands)?)?; + exports.set("send_command", lua.create_function(send_command)?)?; - let input = unsafe { input.as_nvim_str_unchecked().to_string() }; + Ok(exports) +} +fn sha3(_lua: &Lua, str: String) -> LuaResult { let mut hasher = Sha3_256::new(); - hasher.update(input.as_bytes()); + hasher.update(str.as_bytes()); let result = hasher.finalize(); - format!("{:x}", result).into() + Ok(format!("{:x}", result)) } -fn read_game_project(path: Object) -> Object { - if path.kind() != ObjectKind::String { - return LuaError::from( - "read_game_project(path), expected path to be of type string".to_string(), - ) - .to_object() - .unwrap(); - }; - - let path = unsafe { path.as_nvim_str_unchecked().to_string() }; - - match GameProject::load_from_path(path.into()) { - Ok(game_project) => game_project.to_object().unwrap_or_else(|err| { - LuaError::from(anyhow::Error::from(err)) - .to_object() - .unwrap() - }), - Err(err) => LuaError::from(err).to_object().unwrap(), - } +fn read_game_project(lua: &Lua, path: String) -> LuaResult { + let game_project = GameProject::load_from_path(path.into())?; + lua.create_ser_userdata(game_project) } -fn find_editor_port(_: ()) -> Object { - match editor::find_port() { - Some(port) => Object::from(port), - None => LuaError::from("Could not find editor port".to_string()) - .to_object() - .unwrap(), - } +fn find_editor_port(_lua: &Lua, _: ()) -> LuaResult { + let port = editor::find_port().context("could not find port")?; + Ok(port) } -fn list_commands(port: Object) -> Object { - let port = match port.kind() { - ObjectKind::Integer => match u16::try_from(unsafe { port.as_integer_unchecked() }) { - Ok(port) => Some(port), - Err(err) => { - return LuaError::from(anyhow::Error::from(err)) - .to_object() - .unwrap(); - } - }, - _ => None, - }; - - let commands = editor::list_commands(port); - - let Ok(commands) = commands else { - return LuaError::from(anyhow::Error::from(commands.unwrap_err())) - .to_object() - .unwrap(); - }; +fn list_commands(lua: &Lua, port: Option) -> LuaResult { + let commands = editor::list_commands(port)?; - Object::from_iter(commands) + lua.create_table_from(commands) } -fn send_command((port, cmd): (Object, Object)) -> Object { - let Ok(port) = get_int_opt(port) else { - return LuaError::from("send_command(port, cmd): port was not int|nil".to_string()) - .to_object() - .unwrap(); - }; - - let Ok(cmd) = get_string(cmd) else { - return LuaError::from("send_command(port, cmd): cmd was not a string".to_string()) - .to_object() - .unwrap(); - }; - - if let Err(err) = editor::send_command(port, cmd) { - return LuaError::from(err).to_object().unwrap(); - } - - Object::from(Dictionary::new()) -} - -fn get_int_opt>(o: Object) -> Result> { - match o.kind() { - ObjectKind::Integer => match T::try_from(unsafe { o.as_integer_unchecked() }) { - Ok(v) => Ok(Some(v)), - Err(_) => Ok(None), - }, - ObjectKind::Nil => Ok(None), - _ => Err(anyhow!("object was not an integer")), - } -} +fn send_command(_lua: &Lua, (port, cmd): (Option, String)) -> LuaResult<()> { + editor::send_command(port, cmd)?; -fn get_string(o: Object) -> Result { - match o.kind() { - ObjectKind::String => String::try_from(unsafe { o.as_nvim_str_unchecked() }.to_string()) - .map_err(anyhow::Error::from), - _ => Err(anyhow!("object was not a string")), - } + Ok(()) } From 127f6a202453c0c10c2784d211c491063d6327c7 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 14 Dec 2025 12:42:50 +0100 Subject: [PATCH 04/70] add support for setting default editor and add 'bridge' cli tool to run neovim --- .github/workflows/test.yml | 4 +- .gitignore | 2 + Cargo.toml | 27 +- Justfile | 15 +- README.md | 3 + bb.edn | 2 - crates/bridge/Cargo.toml | 17 + crates/bridge/src/launcher.rs | 447 ++++++++++++++++++++ crates/bridge/src/main.rs | 79 ++++ crates/bridge/src/plugin_config.rs | 41 ++ crates/bridge/src/utils.rs | 64 +++ crates/sidecar/Cargo.toml | 26 ++ crates/sidecar/assets/run_linux.sh | 2 + crates/sidecar/assets/run_macos.sh | 3 + crates/sidecar/assets/run_windows.bat | 2 + {src => crates/sidecar/src}/editor.rs | 0 crates/sidecar/src/editor_config.rs | 112 +++++ {src => crates/sidecar/src}/game_project.rs | 0 {src => crates/sidecar/src}/lib.rs | 15 +- lua/defold/init.lua | 43 +- lua/defold/sidecar.lua | 7 +- shell.nix | 1 + src/defold/editor_config.clj | 51 --- src/defold/launcher.clj | 167 -------- src/defold/main.clj | 14 - 25 files changed, 851 insertions(+), 293 deletions(-) create mode 100644 crates/bridge/Cargo.toml create mode 100644 crates/bridge/src/launcher.rs create mode 100644 crates/bridge/src/main.rs create mode 100644 crates/bridge/src/plugin_config.rs create mode 100644 crates/bridge/src/utils.rs create mode 100644 crates/sidecar/Cargo.toml create mode 100644 crates/sidecar/assets/run_linux.sh create mode 100644 crates/sidecar/assets/run_macos.sh create mode 100644 crates/sidecar/assets/run_windows.bat rename {src => crates/sidecar/src}/editor.rs (100%) create mode 100644 crates/sidecar/src/editor_config.rs rename {src => crates/sidecar/src}/game_project.rs (100%) rename {src => crates/sidecar/src}/lib.rs (77%) delete mode 100644 src/defold/editor_config.clj delete mode 100644 src/defold/launcher.clj diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6e2a592..a68699e 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -18,6 +18,8 @@ jobs: steps: - uses: actions/checkout@v5 - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + rustflags: "" - name: Run tests - run: cargo test --all --verbose + run: cargo test --workspace --verbose diff --git a/.gitignore b/.gitignore index 0777e13..0676706 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,5 @@ +/defold-nvim* +/libdefold-nvim* .clj-kondo .lsp .nrepl* diff --git a/Cargo.toml b/Cargo.toml index d86c87b..ff22efb 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,24 +1,3 @@ -[package] -name = "defold-nvim" -version = "0.1.0" -edition = "2024" - -[lib] -path = "src/lib.rs" -crate-type = ["cdylib"] - -[dependencies] -anyhow = "1.0.100" -mlua = { version = "0.11.5", features = [ - "module", - "luajit", - "serde", - "anyhow", -] } -netstat2 = "0.11.2" -reqwest = { version = "0.12.24", features = ["blocking", "json"] } -rust-ini = "0.21.3" -serde = { version = "1.0.228", features = ["derive"] } -serde_json = "1.0.145" -sha3 = "0.10.8" -sysinfo = "0.37.2" +[workspace] +members = ["crates/sidecar", "crates/bridge"] +resolver = "2" diff --git a/Justfile b/Justfile index 1064b81..96a98a5 100644 --- a/Justfile +++ b/Justfile @@ -1,19 +1,6 @@ watch: - watchexec -w src -r 'just build-and-link' + watchexec -w crates -r 'just build' build: cargo build -link: - #!/usr/bin/env bash - set -e - - if [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "win32" ]] || [[ "$OSTYPE" == "cygwin" ]]; then - cp -f "$(pwd)/target/debug/defold_nvim.dll" lua/defold/sidecar.dll - elif [[ "$(uname)" == "Darwin" ]]; then - cp -f "$(pwd)/target/debug/libdefold_nvim.dylib" lua/defold/sidecar.so - else - cp -f "$(pwd)/target/debug/libdefold_nvim.so" lua/defold/sidecar.so - fi - -build-and-link: build link diff --git a/README.md b/README.md index 51bf14d..3456166 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,9 @@ local config = { -- extra arguments passed to the `executable` (or neovide) extra_arguments = nil, + -- choose file based sockets (fsock, unix only) or network based sockets (netsock) or use nil for the default + socket_type = nil, + -- configure how the terminal is run (optional) terminal = { -- argument to define how to set the class name of the terminal, usually something like "--class=" diff --git a/bb.edn b/bb.edn index 01659d4..7a90109 100644 --- a/bb.edn +++ b/bb.edn @@ -2,10 +2,8 @@ :paths ["src"] :tasks {:requires ([defold.main :as defold]) setup {:task (apply defold/run-wrapped :setup *command-line-args*)} - set-default-editor {:task (apply defold/run-wrapped :set-default-editor *command-line-args*)} install-dependencies {:task (apply defold/run-wrapped :install-dependencies *command-line-args*)} list-dependency-dirs {:task (apply defold/run-wrapped :list-dependency-dirs *command-line-args*)} - launch-neovim {:task (apply defold/run-wrapped :launch-neovim *command-line-args*)} focus-neovim {:task (apply defold/run-wrapped :focus-neovim *command-line-args*)} focus-game {:task (apply defold/run-wrapped :focus-game *command-line-args*)} mobdap-path {:task (apply defold/run-wrapped :mobdap-path *command-line-args*)}}} diff --git a/crates/bridge/Cargo.toml b/crates/bridge/Cargo.toml new file mode 100644 index 0000000..9497e11 --- /dev/null +++ b/crates/bridge/Cargo.toml @@ -0,0 +1,17 @@ +[package] +name = "defold-nvim-bridge" +version = "0.1.0" +edition = "2024" + +[dependencies] +anyhow = "1.0.100" +clap = { version = "4.5.53", features = ["derive"] } +dirs = "6.0.0" +netstat2 = "0.11.2" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.145" +sha3 = "0.10.8" +tracing = "0.1.43" +tracing-appender = "0.2.4" +tracing-subscriber = "0.3.22" +which = "8.0.0" diff --git a/crates/bridge/src/launcher.rs b/crates/bridge/src/launcher.rs new file mode 100644 index 0000000..da4f512 --- /dev/null +++ b/crates/bridge/src/launcher.rs @@ -0,0 +1,447 @@ +use std::{ + fs::{self, File}, + io::BufReader, + path::PathBuf, + process::Command, +}; + +use anyhow::{Context, Result, bail}; +use serde::Deserialize; +use which::which; + +use crate::{ + plugin_config::{LauncherConfig, LauncherType, PluginConfig, SocketType}, + utils::{self, classname, is_port_in_use}, +}; + +const ERR_NEOVIDE_NOT_FOUND: &'static str = "Could not find Neovide, have you installed it?"; +const ERR_TERMINAL_NOT_FOUND: &'static str = "Could not find any suitable terminal"; + +const VAR_CLASSNAME: &'static str = "{CLASSNAME}"; +const VAR_ADDRESS: &'static str = "{ADDR}"; +const VAR_REMOTE_CMD: &'static str = "{REMOTE_CMD}"; + +#[derive(Debug, Deserialize)] +pub struct LaunchConfig { + // pub data_dir: PathBuf, + pub plugin_config: PluginConfig, +} + +impl LaunchConfig { + pub fn from_file(path: PathBuf) -> Result { + let file = File::open(path)?; + let reader = BufReader::new(file); + let res = serde_json::from_reader(reader)?; + Ok(res) + } +} + +#[derive(Debug)] +struct Launcher(PathBuf, Vec); + +impl Launcher { + fn run(self) -> Result<()> { + tracing::debug!("Run launcher {self:?}"); + + let exe = self.0; + let args = self.1; + + let out = Command::new(exe).args(args).output()?; + + if !out.stderr.is_empty() { + bail!(String::from_utf8(out.stderr)?); + } + + Ok(()) + } + + fn apply_var(self, var: &str, replace_with: String) -> Self { + Self( + self.0, + self.1 + .iter() + .map(|s| s.replace(var, &replace_with)) + .collect(), + ) + } +} + +const DEFAULT_TERMINALS: [(&'static str, &'static str, &'static str); 5] = [ + ("alacratty", "--class=", "-e"), + ("foot", "--app-id=", "-e"), + ("ghostty", "--class=", "-e"), + ("kitty", "--class=", "-e"), + ("st", "-c=", "-e"), +]; + +fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { + match cfg.launcher.as_ref().and_then(|cfg| cfg.launcher_type) { + Some(LauncherType::Neovide) => { + let executable = &cfg + .launcher + .as_ref() + .and_then(|exe| exe.executable.clone()) + .map(|s| s.into()) + .or_else(|| which("neovide").ok()) + .context(ERR_NEOVIDE_NOT_FOUND)?; + + if !executable.exists() { + bail!(ERR_NEOVIDE_NOT_FOUND); + } + + let mut args = Vec::new(); + + if let Some(extra_args) = cfg + .launcher + .as_ref() + .and_then(|cfg| Some(cfg.extra_arguments.clone().unwrap_or_default())) + { + for extra_arg in extra_args { + args.push(extra_arg); + } + } + + args.push("--neovim-bin".to_string()); + args.push(nvim.clone()); + + if cfg!(target_os = "linux") { + args.push("--wayland_app_id".to_string()); + args.push(VAR_CLASSNAME.to_string()); + + args.push("--x11-wm-class".to_string()); + args.push(VAR_CLASSNAME.to_string()); + } + + args.push("--".to_string()); + + args.push("--listen".to_string()); + args.push(VAR_ADDRESS.to_string()); + + args.push("--remote".to_string()); + args.push(VAR_REMOTE_CMD.to_string()); + + Ok(Launcher(executable.clone(), args)) + } + Some(LauncherType::Terminal) => { + let executable: Option = cfg + .launcher + .as_ref() + .and_then(|launcher| launcher.executable.clone()) + .map(|exe| exe.into()); + + let executable = if let Some(exe) = executable + && exe.exists() + { + let class_arg = cfg + .launcher + .as_ref() + .and_then(|l| l.terminal.clone()) + .and_then(|term| term.class_argument.clone()); + + let run_arg = cfg + .launcher + .as_ref() + .and_then(|l| l.terminal.clone()) + .and_then(|term| term.run_argument.clone()); + + let mut args = Vec::new(); + + if let Some(extra_args) = cfg + .launcher + .as_ref() + .and_then(|cfg| Some(cfg.extra_arguments.clone().unwrap_or_default())) + { + for extra_arg in extra_args { + args.push(extra_arg); + } + } + + if let Some(class_arg) = class_arg { + if class_arg.ends_with("=") { + args.push(class_arg + VAR_CLASSNAME); + } else { + args.push(class_arg); + args.push(VAR_CLASSNAME.to_string()); + } + } + + if let Some(run_arg) = run_arg { + if run_arg.ends_with("=") { + args.push(run_arg + nvim); + } else { + args.push(run_arg); + args.push(nvim.clone()); + } + } + + args.push("--listen".to_string()); + args.push(VAR_ADDRESS.to_string()); + + args.push("--remote".to_string()); + args.push(VAR_REMOTE_CMD.to_string()); + + Some(Launcher(exe, args)) + } else { + None + } + .or_else(|| { + let mut args = Vec::new(); + + if let Some(extra_args) = cfg + .launcher + .as_ref() + .and_then(|cfg| Some(cfg.extra_arguments.clone().unwrap_or_default())) + { + for extra_arg in extra_args { + args.push(extra_arg); + } + } + + // executable specifies only the name of which terminal we want to use + if let Some(exe_name) = cfg.launcher.as_ref().and_then(|cfg| cfg.executable.clone()) + { + if let Some((name, class_arg, run_arg)) = DEFAULT_TERMINALS + .iter() + .find(|(name, _, _)| *name == exe_name) + && let Ok(path) = which(name) + { + if class_arg.ends_with("=") { + args.push(class_arg.to_string() + VAR_CLASSNAME); + } else { + args.push(class_arg.to_string()); + args.push(VAR_CLASSNAME.to_string()); + } + + if run_arg.ends_with("=") { + args.push(run_arg.to_string() + nvim); + } else { + args.push(run_arg.to_string()); + args.push(nvim.to_string()); + } + + args.push("--listen".to_string()); + args.push(VAR_ADDRESS.to_string()); + + args.push("--remote".to_string()); + args.push(VAR_REMOTE_CMD.to_string()); + + return Some(Launcher(path, args)); + } + } + + // try finding one of our supported default terminals + for (name, class_arg, run_arg) in DEFAULT_TERMINALS.iter() { + if let Ok(path) = which(name) { + if class_arg.ends_with("=") { + args.push(class_arg.to_string() + VAR_CLASSNAME); + } else { + args.push(class_arg.to_string()); + args.push(VAR_CLASSNAME.to_string()); + } + + if run_arg.ends_with("=") { + args.push(run_arg.to_string() + nvim); + } else { + args.push(run_arg.to_string()); + args.push(nvim.to_string()); + } + + args.push("--listen".to_string()); + args.push(VAR_ADDRESS.to_string()); + + args.push("--remote".to_string()); + args.push(VAR_REMOTE_CMD.to_string()); + + return Some(Launcher(path, args)); + } + } + None + }) + .context(ERR_TERMINAL_NOT_FOUND)?; + + Ok(executable) + } + None => { + if let Ok(launcher) = create_launcher( + &PluginConfig { + launcher: cfg.launcher.as_ref().map(|launcher| LauncherConfig { + launcher_type: Some(LauncherType::Neovide), + ..launcher.clone() + }), + ..cfg.clone() + }, + &nvim, + ) { + return Ok(launcher); + } + + if let Ok(launcher) = create_launcher( + &PluginConfig { + launcher: cfg.launcher.as_ref().map(|launcher| LauncherConfig { + launcher_type: Some(LauncherType::Terminal), + ..launcher.clone() + }), + ..cfg.clone() + }, + &nvim, + ) { + return Ok(launcher); + } + + bail!("Could neither find Neovide nor any supported terminal") + } + } +} + +fn nvim_open_file_remote(nvim: &str, server: &str, remote_cmd: &str) -> Result<()> { + tracing::debug!("Open '{remote_cmd}' via socket: {server}"); + + let out = Command::new(nvim) + .arg("--server") + .arg(server) + .arg("--remote-send") + .arg(format!("\":edit {}\"", remote_cmd)) + .output()?; + + if !out.stderr.is_empty() { + bail!(String::from_utf8(out.stderr)?); + } + + Ok(()) +} + +fn run_fsock(launcher: Launcher, nvim: &str, root_dir: PathBuf, remote_cmd: &str) -> Result<()> { + let socket_file = utils::runtime_dir( + root_dir + .to_str() + .context("could not convert path to string")?, + )? + .join("neovim.sock"); + + tracing::debug!("Using fsock at {socket_file:?}"); + + let launcher = launcher.apply_var( + VAR_ADDRESS, + socket_file + .to_str() + .context("could not convert socket file to string")? + .to_string(), + ); + + if socket_file.exists() { + // if we couldnt communicate with the server despite existing apparently + // delete it and start a new instance + if let Err(err) = nvim_open_file_remote( + nvim, + socket_file + .to_str() + .context("could not convert path to string")?, + remote_cmd, + ) { + tracing::error!("Failed to communicate with neovim server: {err:?}"); + + fs::remove_file(socket_file)?; + launcher.run()?; + } + + return Ok(()); + } + + launcher.run()?; + Ok(()) +} + +fn run_netsock(launcher: Launcher, nvim: &str, root_dir: PathBuf, remote_cmd: &str) -> Result<()> { + let port_file = utils::runtime_dir( + root_dir + .to_str() + .context("could not convert path to string")?, + )? + .join("port"); + + let port: u16 = if port_file.exists() { + fs::read_to_string(&port_file)?.parse()? + } else { + utils::find_free_port() + }; + + let socket = format!("127.0.0.1:{port}"); + + tracing::debug!("Trying to use netsock with port {socket}"); + + if is_port_in_use(port) { + // if we couldnt communicate with the server despite existing apparently + // delete it and start a new instance + if let Err(err) = nvim_open_file_remote(nvim, &socket, remote_cmd) { + tracing::error!("Failed to communicate with neovim server: {err:?}"); + + let new_port = utils::find_free_port(); + let socket = format!("127.0.0.1:{new_port}"); + tracing::debug!("Trying to use netsock with port {socket}"); + fs::write(port_file, new_port.to_string())?; + launcher.apply_var(VAR_ADDRESS, socket).run()?; + } + + return Ok(()); + } + + fs::write(port_file, port.to_string())?; + launcher.apply_var(VAR_ADDRESS, socket).run()?; + Ok(()) +} + +pub fn run( + config: LaunchConfig, + root_dir: PathBuf, + file: PathBuf, + line: Option, +) -> Result<()> { + let nvim = which("nvim")? + .to_str() + .context("could not convert nvim path to string")? + .to_string(); + + let launcher = create_launcher(&config.plugin_config, &nvim)?; + + let launcher = if cfg!(target_os = "linux") { + launcher.apply_var( + VAR_CLASSNAME, + classname( + root_dir + .to_str() + .context("could not convert path to string")?, + )?, + ) + } else if cfg!(target_os = "macos") { + launcher + } else if cfg!(target_os = "windows") { + launcher + } else { + launcher + }; + + let file_str = file.to_str().context("could not convert path to string")?; + + let remote_cmd = match line { + Some(line) => format!("+{} {}", line, file_str), + None => file_str.to_string(), + }; + + let launcher = launcher.apply_var(VAR_REMOTE_CMD, remote_cmd.clone()); + + match config.plugin_config.launcher.and_then(|l| l.socket_type) { + Some(SocketType::Fsock) => run_fsock(launcher, &nvim, root_dir, &remote_cmd)?, + Some(SocketType::Netsock) => run_netsock(launcher, &nvim, root_dir, &remote_cmd)?, + None => { + if cfg!(target_os = "linux") || cfg!(target_os = "macos") { + run_fsock(launcher, &nvim, root_dir, &remote_cmd)? + } else { + run_netsock(launcher, &nvim, root_dir, &remote_cmd)? + } + } + } + + // TODO: switch focus + + Ok(()) +} diff --git a/crates/bridge/src/main.rs b/crates/bridge/src/main.rs new file mode 100644 index 0000000..1db8113 --- /dev/null +++ b/crates/bridge/src/main.rs @@ -0,0 +1,79 @@ +use std::{env, fs, io, path::absolute}; + +use anyhow::{Context, Result}; +use clap::{Parser, Subcommand, command}; +use tracing::Level; +use tracing_appender::rolling::daily; +use tracing_subscriber::fmt::writer::MakeWriterExt; + +mod launcher; +mod plugin_config; +mod utils; + +#[derive(Parser, Debug)] +// #[command(version)] +struct Args { + #[command(subcommand)] + cmd: Commands, +} + +#[derive(Subcommand, Debug)] +enum Commands { + LaunchNeovim { + #[clap(value_name = "LAUNCH_CONFIG", index = 1)] + launch_config: String, + + #[clap(value_name = "GAME_ROOT_DIR", index = 2)] + game_root_dir: String, + + #[clap(value_name = "FILE", index = 3)] + file: String, + + #[clap(value_name = "LINE", index = 4)] + line: Option, + }, +} + +fn main() -> Result<()> { + let logs = dirs::cache_dir() + .context("could not get cache dir")? + .join("defold.nvim") + .join("logs"); + + fs::create_dir_all(&logs)?; + + let (stdout, _stdout_guard) = tracing_appender::non_blocking(io::stdout()); + let (logfile, _logfile_guard) = tracing_appender::non_blocking(daily(logs, "bridge")); + + let writer = stdout.and(logfile); + + tracing_subscriber::fmt() + .with_file(true) + .with_line_number(true) + .with_max_level(Level::DEBUG) + .with_writer(writer) + .init(); + + tracing::info!("Starting defold.nvim bridge",); + tracing::debug!("CLI: {}", env::args().collect::>().join(" ")); + + let args = Args::parse(); + + tracing::debug!("Clap: {args:?}"); + + match args.cmd { + Commands::LaunchNeovim { + launch_config, + game_root_dir, + file, + line, + } => launcher::run( + launcher::LaunchConfig::from_file(absolute(launch_config)?)?, + absolute(game_root_dir)?, + absolute(file)?, + line, + )?, + } + + Ok(()) +} diff --git a/crates/bridge/src/plugin_config.rs b/crates/bridge/src/plugin_config.rs new file mode 100644 index 0000000..3d4b235 --- /dev/null +++ b/crates/bridge/src/plugin_config.rs @@ -0,0 +1,41 @@ +use serde::Deserialize; + +#[derive(Debug, Deserialize, Clone, Copy)] +pub enum LauncherType { + #[serde(rename = "neovide")] + Neovide, + + #[serde(rename = "terminal")] + Terminal, +} + +#[derive(Debug, Deserialize, Clone, Copy)] +pub enum SocketType { + #[serde(rename = "fsock")] + Fsock, + + #[serde(rename = "netsock")] + Netsock, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct LauncherConfig { + #[serde(rename = "type")] + pub launcher_type: Option, + + pub executable: Option, + pub extra_arguments: Option>, + pub terminal: Option, + pub socket_type: Option, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct TerminalConfig { + pub class_argument: Option, + pub run_argument: Option, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct PluginConfig { + pub launcher: Option, +} diff --git a/crates/bridge/src/utils.rs b/crates/bridge/src/utils.rs new file mode 100644 index 0000000..81e4047 --- /dev/null +++ b/crates/bridge/src/utils.rs @@ -0,0 +1,64 @@ +use std::{fs, net::TcpListener, path::PathBuf}; + +use anyhow::{Context, Result}; +use netstat2::{AddressFamilyFlags, ProtocolFlags, get_sockets_info}; +use sha3::{Digest, Sha3_256}; + +pub fn sha3(str: &str) -> String { + let mut hasher = Sha3_256::new(); + hasher.update(str.as_bytes()); + let result = hasher.finalize(); + + format!("{:x}", result) +} + +pub fn project_id(root_dir: &str) -> Result { + Ok(sha3(root_dir) + .get(0..8) + .context("could not create project id")? + .to_string()) +} + +pub fn classname(root_dir: &str) -> Result { + Ok(format!("com.defold.nvim.{}", project_id(root_dir)?)) +} + +pub fn runtime_dir(root_dir: &str) -> Result { + let dir = dirs::cache_dir() + .context("could not get cache dir")? + .join("defold.nvim") + .join("runtime") + .join(project_id(root_dir)?); + fs::create_dir_all(&dir)?; + Ok(dir) +} + +pub fn is_port_in_use(port: u16) -> bool { + let af_flags = AddressFamilyFlags::IPV4 | AddressFamilyFlags::IPV6; + let proto_flags = ProtocolFlags::TCP | ProtocolFlags::UDP; + if let Ok(sockets) = get_sockets_info(af_flags, proto_flags) { + for socket in sockets { + match socket.protocol_socket_info { + netstat2::ProtocolSocketInfo::Tcp(tcp) => { + if tcp.local_port == port { + return true; + } + } + netstat2::ProtocolSocketInfo::Udp(udp) => { + if udp.local_port == port { + return true; + } + } + } + } + } + false +} + +pub fn find_free_port() -> u16 { + TcpListener::bind("127.0.0.1:0") + .expect("Failed to bind") + .local_addr() + .expect("Failed to get local addr") + .port() +} diff --git a/crates/sidecar/Cargo.toml b/crates/sidecar/Cargo.toml new file mode 100644 index 0000000..644bd28 --- /dev/null +++ b/crates/sidecar/Cargo.toml @@ -0,0 +1,26 @@ +[package] +name = "defold-nvim-sidecar" +version = "0.1.0" +edition = "2024" + +[lib] +path = "src/lib.rs" +crate-type = ["cdylib"] + +[dependencies] +anyhow = "1.0.100" +dirs = "6.0.0" +edn-rs = "0.18.0" +mlua = { version = "0.11.5", features = [ + "module", + "luajit", + "serde", + "anyhow", +] } +netstat2 = "0.11.2" +reqwest = { version = "0.12.24", features = ["blocking", "json"] } +rust-ini = "0.21.3" +serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.145" +sha3 = "0.10.8" +sysinfo = "0.37.2" diff --git a/crates/sidecar/assets/run_linux.sh b/crates/sidecar/assets/run_linux.sh new file mode 100644 index 0000000..19bec0d --- /dev/null +++ b/crates/sidecar/assets/run_linux.sh @@ -0,0 +1,2 @@ +#!/usr/bin/env bash +{BRIDGE_PATH} launch-neovim "{LAUNCH_CONFIG}" "$(realpath .)" "$1" $2 diff --git a/crates/sidecar/assets/run_macos.sh b/crates/sidecar/assets/run_macos.sh new file mode 100644 index 0000000..29d77ff --- /dev/null +++ b/crates/sidecar/assets/run_macos.sh @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +export PATH="/usr/bin:/usr/local/bin:$PATH" +{BRIDGE_PATH} launch-neovim "{LAUNCH_CONFIG}" "$(realpath .)" "$1" $2 diff --git a/crates/sidecar/assets/run_windows.bat b/crates/sidecar/assets/run_windows.bat new file mode 100644 index 0000000..b0261b4 --- /dev/null +++ b/crates/sidecar/assets/run_windows.bat @@ -0,0 +1,2 @@ +@echo off +{BRIDGE_PATH} launch-neovim "{LAUNCH_CONFIG}" "%s" "%%CD%%" "%%~1" %%2 diff --git a/src/editor.rs b/crates/sidecar/src/editor.rs similarity index 100% rename from src/editor.rs rename to crates/sidecar/src/editor.rs diff --git a/crates/sidecar/src/editor_config.rs b/crates/sidecar/src/editor_config.rs new file mode 100644 index 0000000..a2c9cf7 --- /dev/null +++ b/crates/sidecar/src/editor_config.rs @@ -0,0 +1,112 @@ +use anyhow::{Context, Result, bail}; +use edn_rs::Edn; +use std::{fs, os::unix::fs::PermissionsExt, path::PathBuf, str::FromStr}; + +#[cfg(target_os = "linux")] +const RUN_SCRIPT: &'static str = include_str!("../assets/run_linux.sh"); + +#[cfg(target_os = "macos")] +const RUN_SCRIPT: &'static str = include_str!("../assets/run_macos.sh"); + +#[cfg(target_os = "windows")] +const RUN_SCRIPT: &'static str = include_str!("../assets/run_windows.bat"); + +#[cfg(target_os = "windows")] +const SCRIPT_EXT: &'static str = "bat"; + +#[cfg(not(target_os = "windows"))] +const SCRIPT_EXT: &'static str = "sh"; + +#[cfg(target_os = "windows")] +const EXE_SUFFIX: &'static str = ".exe"; + +#[cfg(not(target_os = "windows"))] +const EXE_SUFFIX: &'static str = ""; + +fn find_bridge_path(plugin_root: PathBuf) -> Result { + let exe = format!("defold-nvim-bridge{}", EXE_SUFFIX); + + if plugin_root.exists() { + let candidates = [ + plugin_root.join(&exe), + plugin_root.join("target").join("debug").join(&exe), + plugin_root.join("target").join("release").join(&exe), + ]; + + if let Some(bridge_path) = candidates.into_iter().find(|p| p.exists()) { + return Ok(bridge_path); + } + } + + // TODO: if not lets download it + panic!("not yet implemented!") +} + +fn create_runner_script(plugin_root: PathBuf, launch_config: PathBuf) -> Result { + let dir = dirs::data_dir() + .context("could not get data dir")? + .join("defold.nvim"); + fs::create_dir_all(&dir)?; + + let script_path = dir.join(format!("run.{SCRIPT_EXT}")); + + let bridge_path = find_bridge_path(plugin_root)?; + + fs::write( + &script_path, + RUN_SCRIPT + .replace( + "{BRIDGE_PATH}", + bridge_path + .to_str() + .context("could not convert bridge path")?, + ) + .replace( + "{LAUNCH_CONFIG}", + launch_config + .to_str() + .context("could not convert launch config")?, + ), + )?; + + #[cfg(not(target_os = "windows"))] + fs::set_permissions(&script_path, fs::Permissions::from_mode(0o700))?; + + Ok(script_path) +} + +pub fn set_default_editor(plugin_root: PathBuf, launch_config: PathBuf) -> Result<()> { + if !plugin_root.exists() { + bail!("plugin root '{plugin_root:?}' could not be found"); + } + + if !launch_config.exists() { + bail!("launch config path '{launch_config:?}' could not be found"); + } + + let config_dir = dirs::config_dir().context("could not find config dir")?; + let path = config_dir.join("Defold").join("prefs.editor_settings"); + + if !path.exists() { + bail!("prefs.editor_settings file {path:?} could not be found"); + } + + let data = fs::read_to_string(&path)?; + + let mut config = Edn::from_str(&data).map_err(|err| anyhow::anyhow!(err.to_string()))?; + + config[":code"][":custom-editor"] = Edn::Str( + create_runner_script(plugin_root, launch_config)? + .to_str() + .context("could not convert path to string")? + .to_string(), + ); + config[":code"][":open-file"] = Edn::Str("{file}".to_string()); + config[":code"][":open-file-at-line"] = Edn::Str("{file} {line}".to_string()); + + let config_str = &Edn::to_string(&config); + + fs::write(path, config_str)?; + + Ok(()) +} diff --git a/src/game_project.rs b/crates/sidecar/src/game_project.rs similarity index 100% rename from src/game_project.rs rename to crates/sidecar/src/game_project.rs diff --git a/src/lib.rs b/crates/sidecar/src/lib.rs similarity index 77% rename from src/lib.rs rename to crates/sidecar/src/lib.rs index 2d36595..e2f8e70 100644 --- a/src/lib.rs +++ b/crates/sidecar/src/lib.rs @@ -1,3 +1,5 @@ +use std::path::PathBuf; + use anyhow::Context; use mlua::prelude::*; use sha3::{Digest, Sha3_256}; @@ -5,10 +7,11 @@ use sha3::{Digest, Sha3_256}; use crate::game_project::GameProject; mod editor; +mod editor_config; mod game_project; #[mlua::lua_module] -fn defold_nvim(lua: &Lua) -> LuaResult { +fn defold_nvim_sidecar(lua: &Lua) -> LuaResult { let exports = lua.create_table()?; exports.set("version", lua.create_string(env!("CARGO_PKG_VERSION"))?)?; @@ -17,6 +20,10 @@ fn defold_nvim(lua: &Lua) -> LuaResult { exports.set("find_editor_port", lua.create_function(find_editor_port)?)?; exports.set("list_commands", lua.create_function(list_commands)?)?; exports.set("send_command", lua.create_function(send_command)?)?; + exports.set( + "set_default_editor", + lua.create_function(set_default_editor)?, + )?; Ok(exports) } @@ -50,3 +57,9 @@ fn send_command(_lua: &Lua, (port, cmd): (Option, String)) -> LuaResult<()> Ok(()) } + +fn set_default_editor(_lua: &Lua, (plugin_root, launch_config): (String, String)) -> LuaResult<()> { + editor_config::set_default_editor(PathBuf::from(plugin_root), PathBuf::from(launch_config))?; + + Ok(()) +} diff --git a/lua/defold/init.lua b/lua/defold/init.lua index 5297f39..302321a 100644 --- a/lua/defold/init.lua +++ b/lua/defold/init.lua @@ -8,6 +8,8 @@ local root_markers = { "game.project" } ---@class LauncherSettings Settings for the Neovim launcher run by Defold ---@field type "neovide"|"terminal" Neovim launcher run by Defold ---@field executable string|nil Executable to be used by the launcher, nil means we're trying to figure this out ourselves +---@field socket_type "fsock"|"netsock"|nil Run Neovims RPC protocol over file socket or network. Nil means it will be picked automatic (fsock on Unix, network on Windows) + ---@field extra_arguments table|nil Extra arguments passed to the `executable` (or neovide) ---@field terminal TerminalLauncherSettings|nil Settings for running via terminal @@ -41,7 +43,6 @@ local default_config = { set_default_editor = true, auto_fetch_dependencies = true, hot_reload_enabled = true, - launcher = "neovide", }, launcher = { @@ -100,6 +101,11 @@ function M.plugin_root() return os.plugin_root() end +---@return string +function M.launch_config() + return vim.fs.joinpath(vim.fn.stdpath "data", "defold.nvim", "config.json") +end + ---@param opts DefoldNvimConfig|nil function M.setup(opts) local babashka = require "defold.service.babashka" @@ -119,19 +125,25 @@ function M.setup(opts) }, }, config_path) + -- persist config for launcher + vim.fn.writefile({ + vim.fn.json_encode { + data_dir = vim.fn.stdpath "data", + plugin_config = M.config, + }, + }, M.launch_config()) + -- add setup defold command vim.api.nvim_create_user_command("SetupDefold", function() - local debugger = require "defold.service.debugger" - - local ok = babashka.setup(M.config.babashka.custom_executable) + local sidecar = require "defold.sidecar" + local ok, err = pcall(sidecar.set_default_editor, M.plugin_root(), M.launch_config()) if not ok then - return + log.error(string.format("Could not set default editor because: %s", err)) end - babashka.run_task "set-default-editor" - if M.config.debugger.enable then + local debugger = require "defold.service.debugger" debugger.setup(M.config.debugger.custom_executable, M.config.debugger.custom_arguments) end @@ -174,20 +186,19 @@ function M.setup(opts) }) vim.defer_fn(function() - local debugger = require "defold.service.debugger" - - -- prepare plugin components before loading - local ok = babashka.setup(M.config.babashka.custom_executable) - - if not ok then - return - end + babashka.setup(M.config.babashka.custom_executable) if M.config.defold.set_default_editor then - babashka.run_task_json "set-default-editor" + local sidecar = require "defold.sidecar" + local ok, err = pcall(sidecar.set_default_editor, M.plugin_root(), M.launch_config()) + + if not ok then + log.error(string.format("Could not set default editor because: %s", err)) + end end if M.config.debugger.enable then + local debugger = require "defold.service.debugger" debugger.setup(M.config.debugger.custom_executable, M.config.debugger.custom_arguments) end diff --git a/lua/defold/sidecar.lua b/lua/defold/sidecar.lua index cac38b6..ad85bb1 100644 --- a/lua/defold/sidecar.lua +++ b/lua/defold/sidecar.lua @@ -14,8 +14,8 @@ local function find_rust_lib_rootdir() local os = require "defold.service.os" local plugin_root = os.plugin_root() - local file_name = string.format("defold_nvim%s", lib_extension()) - local file_name_alt = string.format("libdefold_nvim%s", lib_extension()) + local file_name = string.format("defold_nvim_sidecar%s", lib_extension()) + local file_name_alt = string.format("libdefold_nvim_sidecar%s", lib_extension()) if os.file_exists(vim.fs.joinpath(plugin_root, file_name)) @@ -57,8 +57,9 @@ package.cpath = package.cpath ---@field find_editor_port function(): integer ---@field list_commands function(port: integer|nil): table ---@field send_command function(port: integer|nil, cmd: string) +---@field set_default_editor function(plugin_root: string, launch_config: string) ---@type Sidecar -local rust_plugin = require "defold_nvim" +local rust_plugin = require "defold_nvim_sidecar" return rust_plugin diff --git a/shell.nix b/shell.nix index 4f32adb..d586e14 100644 --- a/shell.nix +++ b/shell.nix @@ -12,4 +12,5 @@ pkgs.mkShell { ]; RUST_BACKTRACE = "1"; + RUST_LOG = "debug"; } diff --git a/src/defold/editor_config.clj b/src/defold/editor_config.clj deleted file mode 100644 index 7a0274e..0000000 --- a/src/defold/editor_config.clj +++ /dev/null @@ -1,51 +0,0 @@ -(ns defold.editor-config - (:require - [babashka.fs :as fs :refer [which]] - [clojure.edn :as edn] - [defold.utils :refer [cache-dir config-dir determine-os escape-spaces]] - [taoensso.timbre :as log])) - -(defn- editor-settings-filepath [] - (config-dir "Defold" "prefs.editor_settings")) - -(defn- read-editor-settings [path] - (edn/read-string (slurp path))) - -(defn- save-editor-settings [path config] - (spit path config)) - -(defn- bb-edn [] - (System/getProperty "babashka.config")) - -(defn- create-runner-script [config-path bb-path] - (let [os (determine-os) - [run-file content] - (case os - :linux ["run.sh" (format "#!/usr/bin/env bash\n%s --config \"%s\" run launch-neovim \"%s\" \"$(realpath .)\" \"$1\" $2" (escape-spaces bb-path) (bb-edn) config-path)] - :mac ["run.sh" (format "#!/usr/bin/env bash\nexport PATH=\"/usr/bin:/usr/local/bin:$PATH\"\n%s --config \"%s\" run launch-neovim \"%s\" \"$(realpath .)\" \"$1\" $2" (escape-spaces bb-path) (bb-edn) config-path)] - :windows ["run.bat" (format "@echo off\r\n\"%s\" --config \"%s\" run launch-neovim \"%s\" \"%%CD%%\" \"%%~1\" %%2" bb-path (bb-edn) config-path)] - :unknown (let [ex (ex-info "Can't create runner script for unknown operating system" {})] - (log/error (ex-message ex) ex) - (throw ex))) - runner-path (cache-dir "defold.nvim" run-file)] - (fs/create-dirs (fs/parent runner-path)) - (spit runner-path content) - (when (or (= os :linux) (= os :mac)) - (fs/set-posix-file-permissions runner-path "rwxr-xr-x")) - runner-path)) - -(defn- update-editor-settings [config config-path bb-path] - (-> config - (assoc-in [:code :custom-editor] (create-runner-script config-path bb-path)) - (assoc-in [:code :open-file] "{file}") - (assoc-in [:code :open-file-at-line] "{file} {line}"))) - -(defn set-default-editor [config-path bb-path] - (let [bb-path (or bb-path (which "bb")) - settings-file (editor-settings-filepath)] - (if (not (fs/exists? settings-file)) - {"error" (str "Could not find Defold 'prefs.editor_settings' at " settings-file)} - (let [settings (read-editor-settings settings-file)] - (save-editor-settings settings-file (pr-str (update-editor-settings settings config-path bb-path))) - {"status" 200})))) - diff --git a/src/defold/launcher.clj b/src/defold/launcher.clj deleted file mode 100644 index 35af83e..0000000 --- a/src/defold/launcher.clj +++ /dev/null @@ -1,167 +0,0 @@ -(ns defold.launcher - (:require - [babashka.fs :as fs] - [babashka.process :refer [shell]] - [clojure.string :as string] - [defold.constants :refer [base-class-name]] - [defold.focus :refer [switch-focus]] - [defold.neovide :as neovide] - [defold.utils :refer [cache-dir command-exists? escape-spaces linux? - merge-seq-setters project-id run-shell - seq-replace-var windows?]] - [taoensso.timbre :as log])) - -(defn- runtime-dir [project-root] - (let [p (cache-dir "defold.nvim" "runtime" (project-id project-root))] - (fs/create-dirs p) - p)) - -(defn- make-neovim-edit-command [file-name line] - (if line - (format "edit +%s %s" line (escape-spaces file-name)) - (format "edit %s" (escape-spaces file-name)))) - -(defn- launch [launcher classname addr filename line] - (let [remote-cmd (if line - [(format "+%s" line) filename] - [filename]) - args (-> (:args launcher) - (seq-replace-var :classname classname) - (seq-replace-var :addr addr) - (seq-replace-var :remote-cmd remote-cmd) - merge-seq-setters - flatten)] - (log/debug "Launch cmd" (:cmd launcher)) - (log/debug "Launch arguments" args) - (try - (apply run-shell (:cmd launcher) args) - (catch Throwable t - (log/error "Failed to launch:" t) - (System/exit 1))))) - -(defn- run-fsock [launcher neovim root-dir _ filename line edit-cmd] - (let [runtime (runtime-dir root-dir) - socket-file (str (fs/path runtime "neovim.sock")) - class-name (format base-class-name (project-id root-dir))] - (if (fs/exists? socket-file) - (try - (run-shell neovim "--server" socket-file "--remote-send" (format "\":%s\"" edit-cmd)) - (catch Exception e - (log/error "Failed to communicate with neovim server:" e) - - ; if we couldnt communicate with the server despite existing apparently - ; delete it and start a new instance - (fs/delete-if-exists socket-file) - (launch launcher class-name socket-file filename line))) - (launch launcher class-name socket-file filename line)))) - -(defn- win-port-in-use? [port] - (string/includes? (:out (shell {:out :string} "netstat" "-aon")) (format ":%s" port))) - -(defn- win-find-free-port [] - (loop [port (+ 1024 (rand-int (- 65535 1024)))] - (if (not (win-port-in-use? port)) - port - (recur (+ 1024 (rand-int (- 65535 1024))))))) - -(defn- run-netsock [launcher neovim root-dir class-name filename line edit-cmd] - (let [runtime (runtime-dir root-dir) - port-path (str (fs/path runtime "port")) - exists? (fs/exists? port-path) - port (if exists? (slurp port-path) (win-find-free-port)) - socket (format "127.0.0.1:%s" port)] - (log/info "run-netsock: Port:" port) - (if (win-port-in-use? port) - (try - (run-shell neovim "--server" socket "--remote-send" (format "\":%s\"" edit-cmd)) - (catch Exception e - (log/error "Failed to communicate with neovim server:" e) - (let [new-port (win-find-free-port)] - (spit port-path new-port) - (launch launcher class-name socket filename line)))) - (launch launcher class-name socket filename line)))) - -(defn- create-neovide-launcher [cfg neovim] - (let [args (vec - (flatten - (filter some? - ["--neovim-bin" neovim - (get-in cfg ["extra_arguments"]) - (when (linux?) - ["--wayland_app_id" :classname - "--x11-wm-class" :classname]) - "--" - "--listen" :addr - "--remote" :remote-cmd])))] - (cond - ; custom executable - (and (some? (cfg "executable")) (fs/exists? (cfg "executable"))) - {:cmd (cfg "executable") - :args args} - - ; command already installed - (command-exists? "neovide") - {:cmd (str (fs/which "neovide")) - :args args} - - :else - {:cmd (let [neovide (neovide/executable-path)] - (when-not neovide - (throw (ex-info "Could not find neovide, have you installed it?" {}))) - neovide) - :args args}))) - -(def ^:private default-terminals - [["ghostty" "--class=" "-e"] - ["foot" "--app-id=" "-e"] - ["kitty" "--class=" "-e"] - ["alacritty" "--class=" "-e"] - ["st" "-c" "-e"]]) - -(defn- create-terminal-launcher [cfg neovim] - (let [class-arg (get-in cfg ["terminal" "class_argument"]) - run-arg (or (get-in cfg ["terminal" "run_argument"]) "-e") - nvim-args (filter some? ["--listen" :addr "--remote" :remote-cmd])] - (cond - ; custom executable - (and (some? (cfg "executable")) (fs/exists? (cfg "executable"))) - {:cmd (cfg "executable") - :args (filter some? - (flatten - (concat - [(when class-arg [class-arg :classname]) - run-arg neovim] - (cfg "extra_arguments") - nvim-args)))} - - ; check default terminals - (some #(command-exists? (first %)) default-terminals) - (let [[term class-arg run-arg] (some #(when (command-exists? (first %)) %) default-terminals)] - {:cmd term - :args (flatten (concat [class-arg :classname run-arg neovim] nvim-args))}) - - :else - (throw (ex-info "Could not find a supported terminal launcher" {}))))) - -(defn- create-launcher [cfg neovim] - (case (cfg "type") - "neovide" (create-neovide-launcher cfg neovim) - "terminal" (create-terminal-launcher cfg neovim) - (throw (ex-info (format "Unknown launcher type: %s" (cfg "type")) {:launcher-config cfg})))) - -(defn run [launcher-config root-dir file-name line] - (let [neovim (str (fs/which "nvim")) - launcher (create-launcher launcher-config neovim) - line (when line (Integer/parseInt line)) - class-name (format base-class-name (project-id root-dir)) - edit-cmd (make-neovim-edit-command file-name line)] - (when (or (nil? (:cmd launcher)) (not (command-exists? (:cmd launcher)))) - (log/error "Could not create launcher" (:cmd launcher) launcher-config) - (System/exit 1)) - (when (not (command-exists? neovim)) - (log/error "Could not find neovim" neovim) - (System/exit 1)) - (cond - (windows?) (run-netsock launcher neovim root-dir class-name file-name line edit-cmd) - :else (run-fsock launcher neovim root-dir class-name file-name line edit-cmd)) - (switch-focus :class class-name))) diff --git a/src/defold/main.clj b/src/defold/main.clj index 213305f..01ba1da 100644 --- a/src/defold/main.clj +++ b/src/defold/main.clj @@ -3,9 +3,7 @@ [babashka.fs :as fs] [cheshire.core :as json] [defold.debugger :as debugger] - [defold.editor-config :as editor-config] [defold.focus :as focus] - [defold.launcher :as launcher] [defold.logging :as logging] [defold.neovide :as neovide] [defold.project :as project] @@ -43,10 +41,6 @@ (log/error "Error:" (ex-message t) t) (print-json {:status 500 :error (ex-message t)})))) -(defmethod run :set-default-editor [_ config-file] - (let [conf (parse-config config-file)] - (print-json (editor-config/set-default-editor config-file (conf "bb_path"))))) - (defmethod run :install-dependencies ([_ _ game-project] (print-json (project/install-dependencies game-project false))) @@ -56,14 +50,6 @@ (defmethod run :list-dependency-dirs [_ _ game-project] (print-json (project/list-dependency-dirs game-project))) -(defmethod run :launch-neovim - ([_ config-file root-dir filename] - (let [conf (parse-config config-file)] - (launcher/run (get-in conf ["plugin_config" "launcher"]) root-dir filename nil))) - ([_ config-file root-dir filename line] - (let [conf (parse-config config-file)] - (launcher/run (get-in conf ["plugin_config" "launcher"]) root-dir filename line)))) - (defmethod run :focus-neovim [_ _ root-dir] (print-json (focus/focus-neovim root-dir))) From 0c5fa1ebb6ed050b21609ee1fe8569e54f4f6a91 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 14 Dec 2025 13:02:59 +0100 Subject: [PATCH 05/70] fix unix permissions not being included on windows --- crates/sidecar/src/editor_config.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/sidecar/src/editor_config.rs b/crates/sidecar/src/editor_config.rs index a2c9cf7..2e9941a 100644 --- a/crates/sidecar/src/editor_config.rs +++ b/crates/sidecar/src/editor_config.rs @@ -1,6 +1,6 @@ use anyhow::{Context, Result, bail}; use edn_rs::Edn; -use std::{fs, os::unix::fs::PermissionsExt, path::PathBuf, str::FromStr}; +use std::{fs, path::PathBuf, str::FromStr}; #[cfg(target_os = "linux")] const RUN_SCRIPT: &'static str = include_str!("../assets/run_linux.sh"); @@ -70,7 +70,10 @@ fn create_runner_script(plugin_root: PathBuf, launch_config: PathBuf) -> Result< )?; #[cfg(not(target_os = "windows"))] - fs::set_permissions(&script_path, fs::Permissions::from_mode(0o700))?; + fs::set_permissions( + &script_path, + ::from_mode(0o700), + )?; Ok(script_path) } From 5cb0841abe87353f477137377d45092e72d1c88e Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 14 Dec 2025 14:40:08 +0100 Subject: [PATCH 06/70] add support for focus switching --- Cargo.toml | 2 +- bb.edn | 2 - crates/bridge/Cargo.toml | 2 +- crates/bridge/src/launcher.rs | 15 +- crates/bridge/src/main.rs | 14 ++ crates/bridge/src/utils.rs | 21 +- crates/core/Cargo.toml | 13 ++ crates/core/src/focus.rs | 192 +++++++++++++++++++ crates/{sidecar => core}/src/game_project.rs | 3 - crates/core/src/lib.rs | 3 + crates/core/src/utils.rs | 21 ++ crates/sidecar/Cargo.toml | 15 +- crates/sidecar/src/lib.rs | 64 +++++-- lua/defold/service/debugger.lua | 22 ++- lua/defold/sidecar.lua | 2 + shell.nix | 1 - src/defold/focus.clj | 116 ----------- src/defold/main.clj | 7 - 18 files changed, 333 insertions(+), 182 deletions(-) create mode 100644 crates/core/Cargo.toml create mode 100644 crates/core/src/focus.rs rename crates/{sidecar => core}/src/game_project.rs (95%) create mode 100644 crates/core/src/lib.rs create mode 100644 crates/core/src/utils.rs delete mode 100644 src/defold/focus.clj diff --git a/Cargo.toml b/Cargo.toml index ff22efb..b294f10 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,3 @@ [workspace] -members = ["crates/sidecar", "crates/bridge"] +members = ["crates/sidecar", "crates/bridge", "crates/core"] resolver = "2" diff --git a/bb.edn b/bb.edn index 7a90109..809da70 100644 --- a/bb.edn +++ b/bb.edn @@ -4,6 +4,4 @@ setup {:task (apply defold/run-wrapped :setup *command-line-args*)} install-dependencies {:task (apply defold/run-wrapped :install-dependencies *command-line-args*)} list-dependency-dirs {:task (apply defold/run-wrapped :list-dependency-dirs *command-line-args*)} - focus-neovim {:task (apply defold/run-wrapped :focus-neovim *command-line-args*)} - focus-game {:task (apply defold/run-wrapped :focus-game *command-line-args*)} mobdap-path {:task (apply defold/run-wrapped :mobdap-path *command-line-args*)}}} diff --git a/crates/bridge/Cargo.toml b/crates/bridge/Cargo.toml index 9497e11..547ac6a 100644 --- a/crates/bridge/Cargo.toml +++ b/crates/bridge/Cargo.toml @@ -6,11 +6,11 @@ edition = "2024" [dependencies] anyhow = "1.0.100" clap = { version = "4.5.53", features = ["derive"] } +defold-nvim-core = { path = "../core" } dirs = "6.0.0" netstat2 = "0.11.2" serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.145" -sha3 = "0.10.8" tracing = "0.1.43" tracing-appender = "0.2.4" tracing-subscriber = "0.3.22" diff --git a/crates/bridge/src/launcher.rs b/crates/bridge/src/launcher.rs index da4f512..ec3f6e2 100644 --- a/crates/bridge/src/launcher.rs +++ b/crates/bridge/src/launcher.rs @@ -6,12 +6,13 @@ use std::{ }; use anyhow::{Context, Result, bail}; +use defold_nvim_core::{focus::focus_neovim, utils::classname}; use serde::Deserialize; use which::which; use crate::{ plugin_config::{LauncherConfig, LauncherType, PluginConfig, SocketType}, - utils::{self, classname, is_port_in_use}, + utils::{self, is_port_in_use}, }; const ERR_NEOVIDE_NOT_FOUND: &'static str = "Could not find Neovide, have you installed it?"; @@ -430,18 +431,20 @@ pub fn run( let launcher = launcher.apply_var(VAR_REMOTE_CMD, remote_cmd.clone()); match config.plugin_config.launcher.and_then(|l| l.socket_type) { - Some(SocketType::Fsock) => run_fsock(launcher, &nvim, root_dir, &remote_cmd)?, - Some(SocketType::Netsock) => run_netsock(launcher, &nvim, root_dir, &remote_cmd)?, + Some(SocketType::Fsock) => run_fsock(launcher, &nvim, root_dir.clone(), &remote_cmd)?, + Some(SocketType::Netsock) => run_netsock(launcher, &nvim, root_dir.clone(), &remote_cmd)?, None => { if cfg!(target_os = "linux") || cfg!(target_os = "macos") { - run_fsock(launcher, &nvim, root_dir, &remote_cmd)? + run_fsock(launcher, &nvim, root_dir.clone(), &remote_cmd)? } else { - run_netsock(launcher, &nvim, root_dir, &remote_cmd)? + run_netsock(launcher, &nvim, root_dir.clone(), &remote_cmd)? } } } - // TODO: switch focus + if let Err(err) = focus_neovim(root_dir) { + tracing::error!("Could not switch focus to neovim {err:?}"); + } Ok(()) } diff --git a/crates/bridge/src/main.rs b/crates/bridge/src/main.rs index 1db8113..d44235c 100644 --- a/crates/bridge/src/main.rs +++ b/crates/bridge/src/main.rs @@ -2,6 +2,7 @@ use std::{env, fs, io, path::absolute}; use anyhow::{Context, Result}; use clap::{Parser, Subcommand, command}; +use defold_nvim_core::focus::{focus_game, focus_neovim}; use tracing::Level; use tracing_appender::rolling::daily; use tracing_subscriber::fmt::writer::MakeWriterExt; @@ -19,6 +20,7 @@ struct Args { #[derive(Subcommand, Debug)] enum Commands { + /// Open a file in Neovim or launch a new instance LaunchNeovim { #[clap(value_name = "LAUNCH_CONFIG", index = 1)] launch_config: String, @@ -32,6 +34,16 @@ enum Commands { #[clap(value_name = "LINE", index = 4)] line: Option, }, + /// Focus the currently open instance of Neovim + FocusNeovim { + #[clap(value_name = "GAME_ROOT_DIR", index = 1)] + game_root_dir: String, + }, + /// Focus the game + FocusGame { + #[clap(value_name = "GAME_ROOT_DIR", index = 1)] + game_root_dir: String, + }, } fn main() -> Result<()> { @@ -73,6 +85,8 @@ fn main() -> Result<()> { absolute(file)?, line, )?, + Commands::FocusNeovim { game_root_dir } => focus_neovim(absolute(game_root_dir)?)?, + Commands::FocusGame { game_root_dir } => focus_game(absolute(game_root_dir)?)?, } Ok(()) diff --git a/crates/bridge/src/utils.rs b/crates/bridge/src/utils.rs index 81e4047..ebc3148 100644 --- a/crates/bridge/src/utils.rs +++ b/crates/bridge/src/utils.rs @@ -1,27 +1,8 @@ use std::{fs, net::TcpListener, path::PathBuf}; use anyhow::{Context, Result}; +use defold_nvim_core::utils::project_id; use netstat2::{AddressFamilyFlags, ProtocolFlags, get_sockets_info}; -use sha3::{Digest, Sha3_256}; - -pub fn sha3(str: &str) -> String { - let mut hasher = Sha3_256::new(); - hasher.update(str.as_bytes()); - let result = hasher.finalize(); - - format!("{:x}", result) -} - -pub fn project_id(root_dir: &str) -> Result { - Ok(sha3(root_dir) - .get(0..8) - .context("could not create project id")? - .to_string()) -} - -pub fn classname(root_dir: &str) -> Result { - Ok(format!("com.defold.nvim.{}", project_id(root_dir)?)) -} pub fn runtime_dir(root_dir: &str) -> Result { let dir = dirs::cache_dir() diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml new file mode 100644 index 0000000..5e3e228 --- /dev/null +++ b/crates/core/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "defold-nvim-core" +version = "0.1.0" +edition = "2024" + +[dependencies] +anyhow = "1.0.100" +rust-ini = "0.21.3" +serde = { version = "1.0.228", features = ["derive"] } +sha3 = "0.10.8" +strum = { version = "0.27.2", features = ["derive"] } +tracing = "0.1.43" +which = "8.0.0" diff --git a/crates/core/src/focus.rs b/crates/core/src/focus.rs new file mode 100644 index 0000000..46a8456 --- /dev/null +++ b/crates/core/src/focus.rs @@ -0,0 +1,192 @@ +use std::path::PathBuf; + +use anyhow::{Context, Result, bail}; +use std::process::Command; +use which::which; + +use strum::IntoEnumIterator; + +use crate::{game_project::GameProject, utils::classname}; + +#[derive(Debug)] +enum SwitcherType { + Class(String), + Title(String), + AppName(String), +} + +impl SwitcherType { + fn value(&self) -> String { + match self { + SwitcherType::Class(c) => c.clone(), + SwitcherType::Title(t) => t.clone(), + SwitcherType::AppName(a) => a.clone(), + } + } +} + +#[derive(Debug, strum::EnumIter)] +enum Switcher { + #[cfg(target_os = "linux")] + HyprCtl, + + #[cfg(target_os = "linux")] + SwayMsg, + + #[cfg(target_os = "linux")] + WmCtrl, + + #[cfg(target_os = "linux")] + XDoTool, + + #[cfg(target_os = "macos")] + OsaScript, +} + +impl Switcher { + fn path(&self) -> Option { + #[cfg(target_os = "linux")] + match self { + Switcher::HyprCtl => which("hyprctl").ok(), + Switcher::SwayMsg => which("swaymsg").ok(), + Switcher::WmCtrl => which("wmctrl").ok(), + Switcher::XDoTool => which("xdotool").ok(), + } + #[cfg(target_os = "macos")] + match self { + Switcher::OsaScript => which("osascript").ok(), + } + #[cfg(target_os = "windows")] + None + } + + fn from_env() -> Option { + Self::iter().find(|sw| sw.path().is_some()) + } +} + +fn switch(switcher_type: SwitcherType) -> Result<()> { + tracing::info!("Switching to {switcher_type:?}"); + + let Some(switcher) = Switcher::from_env() else { + tracing::error!("No supported focus switcher found, do nothing..."); + return Ok(()); + }; + + #[cfg(target_os = "linux")] + return match switcher { + Switcher::HyprCtl => { + Command::new(switcher.path().unwrap()) + .arg("dispatch") + .arg("focuswindow") + .arg(match switcher_type { + SwitcherType::Class(class) => format!("class:{class}"), + SwitcherType::Title(title) => format!("title:{title}"), + _ => bail!("Unsupported switcher type {switcher_type:?} for {switcher:?}"), + }) + .spawn()? + .wait()?; + + Ok(()) + } + Switcher::SwayMsg => { + Command::new(switcher.path().unwrap()) + .arg(format!( + "[{}={}] focus", + match switcher_type { + SwitcherType::Class(_) => format!("class"), + SwitcherType::Title(_) => format!("title"), + _ => bail!("Unsupported switcher type {switcher_type:?} for {switcher:?}"), + }, + switcher_type.value(), + )) + .spawn()? + .wait()?; + + Ok(()) + } + Switcher::WmCtrl => { + let mut cmd = Command::new(switcher.path().unwrap()); + + if matches!(switcher_type, SwitcherType::Class(_)) { + cmd.arg("-x"); + } + + cmd.arg("-a").arg(switcher_type.value()).spawn()?.wait()?; + + Ok(()) + } + Switcher::XDoTool => { + Command::new(switcher.path().unwrap()) + .arg("search") + .arg(match switcher_type { + SwitcherType::Class(_) => format!("--class"), + SwitcherType::Title(_) => format!("--title"), + _ => bail!("Unsupported switcher type {switcher_type:?} for {switcher:?}"), + }) + .arg(switcher_type.value()) + .arg("windowactivate") + .spawn()? + .wait()?; + + Ok(()) + } + }; + + #[cfg(target_os = "macos")] + return match switcher { + Switcher::OsaScript => { + Command::new(switcher.path().unwrap()) + .arg("-e") + .arg(match switcher_type { + SwitcherType::AppName(app_name) => format!("'tell application \"System Events\" to tell process \"{app_name}\" to set frontmost to true'"), + _ => bail!("Unsupported switcher type {switcher_type:?} for {switcher:?}"), + }) + .spawn()? + .wait()?; + + Ok(()) + } + }; + + #[cfg(target_os = "windows")] + return Ok(()); +} + +pub fn focus_neovim(root_dir: PathBuf) -> Result<()> { + if !root_dir.join("game.project").exists() { + bail!("Could not find game.project file in {root_dir:?}: Not a valid Defold directory"); + } + + if cfg!(target_os = "linux") { + let class = classname( + root_dir + .to_str() + .context("could not convert path to string")?, + )?; + + return switch(SwitcherType::Class(class)); + } + + tracing::error!("Focus switching to Neovim is not support on current platform"); + + Ok(()) +} + +pub fn focus_game(root_dir: PathBuf) -> Result<()> { + if !root_dir.join("game.project").exists() { + bail!("Could not find game.project file in {root_dir:?}: Not a valid Defold directory"); + } + + let game_project = GameProject::load_from_path(root_dir.join("game.project"))?; + + if cfg!(target_os = "linux") { + return switch(SwitcherType::Title(game_project.title)); + } else if cfg!(target_os = "macos") { + return switch(SwitcherType::AppName(game_project.title)); + } + + tracing::error!("Focus switching to the Game is not support on current platform"); + + Ok(()) +} diff --git a/crates/sidecar/src/game_project.rs b/crates/core/src/game_project.rs similarity index 95% rename from crates/sidecar/src/game_project.rs rename to crates/core/src/game_project.rs index da5c0d9..176059a 100644 --- a/crates/sidecar/src/game_project.rs +++ b/crates/core/src/game_project.rs @@ -2,7 +2,6 @@ use std::{fs, path::PathBuf}; use anyhow::{Context, Result, bail}; use ini::Ini; -use mlua::UserData; use serde::Serialize; #[derive(Debug, Serialize)] @@ -21,8 +20,6 @@ impl GameProject { } } -impl UserData for GameProject {} - impl TryFrom for GameProject { type Error = anyhow::Error; diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs new file mode 100644 index 0000000..b1b5d31 --- /dev/null +++ b/crates/core/src/lib.rs @@ -0,0 +1,3 @@ +pub mod focus; +pub mod game_project; +pub mod utils; diff --git a/crates/core/src/utils.rs b/crates/core/src/utils.rs new file mode 100644 index 0000000..b476b64 --- /dev/null +++ b/crates/core/src/utils.rs @@ -0,0 +1,21 @@ +use anyhow::{Context, Result}; +use sha3::{Digest, Sha3_256}; + +pub fn sha3(str: &str) -> String { + let mut hasher = Sha3_256::new(); + hasher.update(str.as_bytes()); + let result = hasher.finalize(); + + format!("{:x}", result) +} + +pub fn project_id(root_dir: &str) -> Result { + Ok(sha3(root_dir) + .get(0..8) + .context("could not create project id")? + .to_string()) +} + +pub fn classname(root_dir: &str) -> Result { + Ok(format!("com.defold.nvim.{}", project_id(root_dir)?)) +} diff --git a/crates/sidecar/Cargo.toml b/crates/sidecar/Cargo.toml index 644bd28..239416c 100644 --- a/crates/sidecar/Cargo.toml +++ b/crates/sidecar/Cargo.toml @@ -9,18 +9,19 @@ crate-type = ["cdylib"] [dependencies] anyhow = "1.0.100" +defold-nvim-core = { path = "../core" } dirs = "6.0.0" edn-rs = "0.18.0" mlua = { version = "0.11.5", features = [ - "module", - "luajit", - "serde", - "anyhow", + "module", + "luajit", + "serde", + "anyhow", ] } netstat2 = "0.11.2" reqwest = { version = "0.12.24", features = ["blocking", "json"] } -rust-ini = "0.21.3" -serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.145" -sha3 = "0.10.8" sysinfo = "0.37.2" +tracing = "0.1.43" +tracing-appender = "0.2.4" +tracing-subscriber = "0.3.22" diff --git a/crates/sidecar/src/lib.rs b/crates/sidecar/src/lib.rs index e2f8e70..1290cb1 100644 --- a/crates/sidecar/src/lib.rs +++ b/crates/sidecar/src/lib.rs @@ -1,17 +1,46 @@ -use std::path::PathBuf; +use std::{ + fs, + path::{PathBuf, absolute}, + sync::OnceLock, +}; use anyhow::Context; +use defold_nvim_core::{ + focus, + game_project::GameProject, + utils::{self}, +}; +use mlua::Value; use mlua::prelude::*; -use sha3::{Digest, Sha3_256}; - -use crate::game_project::GameProject; +use tracing::Level; +use tracing_appender::rolling::daily; mod editor; mod editor_config; -mod game_project; + +static LOG_INIT: OnceLock<()> = OnceLock::new(); #[mlua::lua_module] fn defold_nvim_sidecar(lua: &Lua) -> LuaResult { + LOG_INIT.get_or_init(|| { + let logs = dirs::cache_dir() + .expect("could not get cache dir") + .join("defold.nvim") + .join("logs"); + + fs::create_dir_all(&logs).expect("could not create logs dir"); + + let rolling = daily(logs, "sidecar"); + + tracing_subscriber::fmt() + .with_ansi(false) + .with_file(true) + .with_line_number(true) + .with_max_level(Level::DEBUG) + .with_writer(rolling) + .init(); + }); + let exports = lua.create_table()?; exports.set("version", lua.create_string(env!("CARGO_PKG_VERSION"))?)?; @@ -24,21 +53,20 @@ fn defold_nvim_sidecar(lua: &Lua) -> LuaResult { "set_default_editor", lua.create_function(set_default_editor)?, )?; + exports.set("focus_neovim", lua.create_function(focus_neovim)?)?; + exports.set("focus_game", lua.create_function(focus_game)?)?; Ok(exports) } fn sha3(_lua: &Lua, str: String) -> LuaResult { - let mut hasher = Sha3_256::new(); - hasher.update(str.as_bytes()); - let result = hasher.finalize(); - - Ok(format!("{:x}", result)) + Ok(utils::sha3(&str)) } -fn read_game_project(lua: &Lua, path: String) -> LuaResult { +fn read_game_project(lua: &Lua, path: String) -> LuaResult { let game_project = GameProject::load_from_path(path.into())?; - lua.create_ser_userdata(game_project) + let val = lua.to_value(&game_project)?; + Ok(val) } fn find_editor_port(_lua: &Lua, _: ()) -> LuaResult { @@ -63,3 +91,15 @@ fn set_default_editor(_lua: &Lua, (plugin_root, launch_config): (String, String) Ok(()) } + +fn focus_neovim(_lua: &Lua, game_root: String) -> LuaResult<()> { + focus::focus_neovim(absolute(game_root)?)?; + + Ok(()) +} + +fn focus_game(_lua: &Lua, game_root: String) -> LuaResult<()> { + focus::focus_game(absolute(game_root)?)?; + + Ok(()) +} diff --git a/lua/defold/service/debugger.lua b/lua/defold/service/debugger.lua index b21a51c..98c8622 100644 --- a/lua/defold/service/debugger.lua +++ b/lua/defold/service/debugger.lua @@ -46,14 +46,12 @@ end function M.register_nvim_dap(editor_port_fn) local log = require "defold.service.logger" - local ok, dap = pcall(require, "dap") - - if not ok then + local dap_installed, dap = pcall(require, "dap") + if not dap_installed then log.error "Debugger enabled but could not find plugin: mfussenegger/nvim-dap" return end - local babashka = require "defold.service.babashka" local editor = require "defold.editor" local project = require "defold.project" @@ -98,15 +96,27 @@ function M.register_nvim_dap(editor_port_fn) dap.listeners.after.event_stopped.defold_nvim_switch_focus_on_stop = function(_, _) log.debug "debugger: event stopped" + local sidecar = require "defold.sidecar" + local rootdir = vim.fs.root(0, { "game.project", ".git" }) - babashka.run_task_json("focus-neovim", { rootdir }) + + local ok, err = pcall(sidecar.focus_neovim, rootdir) + if not ok then + log.error(string.format("Could not focus neovim: %s", err)) + end end dap.listeners.after.continue.defold_nvim_switch_focus_on_continue = function(_, _) log.debug "debugger: continued" + local sidecar = require "defold.sidecar" + local rootdir = vim.fs.root(0, { "game.project", ".git" }) - babashka.run_task_json("focus-game", { rootdir }) + + local ok, err = pcall(sidecar.focus_game, rootdir) + if not ok then + log.error(string.format("Could not focus neovim: %s", err)) + end end end diff --git a/lua/defold/sidecar.lua b/lua/defold/sidecar.lua index ad85bb1..fbfdd9b 100644 --- a/lua/defold/sidecar.lua +++ b/lua/defold/sidecar.lua @@ -58,6 +58,8 @@ package.cpath = package.cpath ---@field list_commands function(port: integer|nil): table ---@field send_command function(port: integer|nil, cmd: string) ---@field set_default_editor function(plugin_root: string, launch_config: string) +---@field focus_neovim function(game_root: string) +---@field focus_game function(game_root: string) ---@type Sidecar local rust_plugin = require "defold_nvim_sidecar" diff --git a/shell.nix b/shell.nix index d586e14..4f32adb 100644 --- a/shell.nix +++ b/shell.nix @@ -12,5 +12,4 @@ pkgs.mkShell { ]; RUST_BACKTRACE = "1"; - RUST_LOG = "debug"; } diff --git a/src/defold/focus.clj b/src/defold/focus.clj deleted file mode 100644 index 74fc1d0..0000000 --- a/src/defold/focus.clj +++ /dev/null @@ -1,116 +0,0 @@ -(ns defold.focus - (:require - [babashka.fs :as fs] - [defold.constants :refer [base-class-name]] - [defold.utils :refer [command-exists? determine-os load-ini project-id - run-shell]] - [taoensso.timbre :as log])) - -(defn switch-focus [type name] - (when (not-any? #(= % type) [:class :title :app-name]) - (throw (ex-info (format "Unknown switcher type: %s" type) {:type type}))) - - (log/info "Switching to:" type name) - (case (determine-os) - :linux - (cond - (command-exists? "hyprctl") - (run-shell - "hyprctl" - "dispatch" - "focuswindow" - (case type - :class (format "class:%s" name) - :title (format "title:%s" name) - (throw (ex-info (format "Unknown switcher type for hyprctl: %s" type) {:type type})))) - - (command-exists? "swaymsg") - (run-shell - "swaymsg" - (format - "'[%s=%s] focus'" - (case type - :class "class" - :title "title" - (throw (ex-info (format "Unknown switcher type for swaymsg: %s" type) {:type type}))) - name)) - - (command-exists? "wmctrl") - (run-shell - "wmctrl" - (when (= type :class) "-x") - "-a" - name) - - (command-exists? "xdotool") - (run-shell - "xdotool" - "search" - (case type - :class "--class" - :title "--title" - (throw (ex-info (format "Unknown switcher type for xdotool: %s" type) {:type type}))) - name - "windowactivate") - - :else - (log/error "No supported focus switcher for Linux found, do nothing...")) - - :mac - (cond - (command-exists? "osascript") - (case type - :app-name - (run-shell - "osascript" - "-e" - (format "'tell application \"System Events\" to tell process \"%s\" to set frontmost to true'" name)) - (throw (ex-info (format "Unknown switcher type for osascript: %s" type) {:type type}))) - - :else - (log/error "No supported focus switcher for MacOS found, do nothing...")) - - :windows - (cond - - :else - (log/error "No supported focus switcher for Windows found, do nothing...")))) - -(defn focus-neovim [root-dir] - (try - (assert (some? root-dir)) - (assert (fs/exists? (fs/path root-dir "game.project"))) - (case (determine-os) - :linux - (let [class-name (format base-class-name (project-id root-dir)) - res (with-out-str (switch-focus :class class-name))] - {"status" 200 "res" res}) - - {"status" 200 "res" (format "not supported on os: %s" (determine-os))}) - - (catch Throwable t - (log/error "focus-neovim: Error" (ex-message t) t) - {"error" (ex-message t)}))) - -(defn focus-game [root-dir] - (try - (assert (some? root-dir)) - (let [game-project (fs/path root-dir "game.project") - _ (assert (fs/exists? game-project)) - config (load-ini game-project) - title (get-in config ["project" "title"]) - _ (assert (some? title))] - (case (determine-os) - :linux - (let [res (with-out-str (switch-focus :title title))] - {"status" 200 "res" res}) - - :mac - (let [res (with-out-str (switch-focus :app-name title))] - {"status" 200 "res" res}) - - {"status" 200 "res" (format "not supported on os: %s" (determine-os))})) - - (catch Throwable t - (log/error "focus-game: Error" (ex-message t) t) - {"error" (ex-message t)}))) diff --git a/src/defold/main.clj b/src/defold/main.clj index 01ba1da..c521175 100644 --- a/src/defold/main.clj +++ b/src/defold/main.clj @@ -3,7 +3,6 @@ [babashka.fs :as fs] [cheshire.core :as json] [defold.debugger :as debugger] - [defold.focus :as focus] [defold.logging :as logging] [defold.neovide :as neovide] [defold.project :as project] @@ -50,12 +49,6 @@ (defmethod run :list-dependency-dirs [_ _ game-project] (print-json (project/list-dependency-dirs game-project))) -(defmethod run :focus-neovim [_ _ root-dir] - (print-json (focus/focus-neovim root-dir))) - -(defmethod run :focus-game [_ _ root-dir] - (print-json (focus/focus-game root-dir))) - (defmethod run :mobdap-path [_ _] (let [path (debugger/executable-path)] (if (fs/exists? path) From 127c832d7b65097911c91c3a44621e74c8d88887 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Wed, 17 Dec 2025 10:09:42 +0100 Subject: [PATCH 07/70] add support for downloading/managing neovide --- .editorconfig | 7 ++ crates/bridge/Cargo.toml | 4 + crates/bridge/src/launcher.rs | 8 ++ crates/bridge/src/main.rs | 7 ++ crates/bridge/src/neovide.rs | 142 ++++++++++++++++++++++++++++++++++ crates/core/Cargo.toml | 3 + crates/core/src/cache.rs | 45 +++++++++++ crates/core/src/github.rs | 70 +++++++++++++++++ crates/core/src/lib.rs | 2 + crates/sidecar/Cargo.toml | 8 +- crates/sidecar/src/lib.rs | 42 ++++++++-- lua/defold/init.lua | 12 +-- lua/defold/service/github.lua | 30 +++++++ lua/defold/sidecar.lua | 2 + 14 files changed, 361 insertions(+), 21 deletions(-) create mode 100644 .editorconfig create mode 100644 crates/bridge/src/neovide.rs create mode 100644 crates/core/src/cache.rs create mode 100644 crates/core/src/github.rs create mode 100644 lua/defold/service/github.lua diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..a882442 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,7 @@ +root = true + +[*] +end_of_line = lf +insert_final_newline = true +indent_style = space +indent_size = 4 diff --git a/crates/bridge/Cargo.toml b/crates/bridge/Cargo.toml index 547ac6a..f3daec9 100644 --- a/crates/bridge/Cargo.toml +++ b/crates/bridge/Cargo.toml @@ -8,10 +8,14 @@ anyhow = "1.0.100" clap = { version = "4.5.53", features = ["derive"] } defold-nvim-core = { path = "../core" } dirs = "6.0.0" +flate2 = "1.1.5" netstat2 = "0.11.2" serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.145" +tar = "0.4.44" tracing = "0.1.43" tracing-appender = "0.2.4" tracing-subscriber = "0.3.22" +version-compare = "0.2.1" which = "8.0.0" +zip = "6.0.0" diff --git a/crates/bridge/src/launcher.rs b/crates/bridge/src/launcher.rs index ec3f6e2..a52c901 100644 --- a/crates/bridge/src/launcher.rs +++ b/crates/bridge/src/launcher.rs @@ -11,6 +11,7 @@ use serde::Deserialize; use which::which; use crate::{ + neovide, plugin_config::{LauncherConfig, LauncherType, PluginConfig, SocketType}, utils::{self, is_port_in_use}, }; @@ -84,6 +85,13 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { .and_then(|exe| exe.executable.clone()) .map(|s| s.into()) .or_else(|| which("neovide").ok()) + .or_else(|| match neovide::update_or_install() { + Ok(path) => Some(path), + Err(err) => { + tracing::error!("Could not download neovide because: {err:?}"); + None + } + }) .context(ERR_NEOVIDE_NOT_FOUND)?; if !executable.exists() { diff --git a/crates/bridge/src/main.rs b/crates/bridge/src/main.rs index d44235c..5087062 100644 --- a/crates/bridge/src/main.rs +++ b/crates/bridge/src/main.rs @@ -8,6 +8,7 @@ use tracing_appender::rolling::daily; use tracing_subscriber::fmt::writer::MakeWriterExt; mod launcher; +mod neovide; mod plugin_config; mod utils; @@ -44,6 +45,8 @@ enum Commands { #[clap(value_name = "GAME_ROOT_DIR", index = 1)] game_root_dir: String, }, + /// Downloads Neovide + DownloadNeovide, } fn main() -> Result<()> { @@ -87,6 +90,10 @@ fn main() -> Result<()> { )?, Commands::FocusNeovim { game_root_dir } => focus_neovim(absolute(game_root_dir)?)?, Commands::FocusGame { game_root_dir } => focus_game(absolute(game_root_dir)?)?, + Commands::DownloadNeovide => { + let path = neovide::update_or_install()?; + tracing::info!("Installed neovide at {path:?}"); + } } Ok(()) diff --git a/crates/bridge/src/neovide.rs b/crates/bridge/src/neovide.rs new file mode 100644 index 0000000..e81f09a --- /dev/null +++ b/crates/bridge/src/neovide.rs @@ -0,0 +1,142 @@ +use std::{ + fs::{self, File, Permissions}, + path::PathBuf, +}; + +use anyhow::{Context, Result, bail}; +use defold_nvim_core::github; +use flate2::read::GzDecoder; +use tar::Archive; +use version_compare::Version; +use zip::ZipArchive; + +#[cfg(target_os = "linux")] +use std::os::unix::fs::PermissionsExt; + +const OWNER: &'static str = "neovide"; +const REPOSITORY: &'static str = "neovide"; + +#[cfg(target_os = "linux")] +const NAME: &'static str = "neovide-linux-x86_64.tar.gz"; + +#[cfg(target_os = "macos")] +const NAME: &'static str = "not supported"; + +#[cfg(target_os = "windows")] +const NAME: &'static str = "neovide.exe.zip"; + +fn path() -> Result { + let dir = dirs::state_dir() + .context("could not get state dir")? + .join("defold.nvim") + .join("bin"); + + fs::create_dir_all(&dir)?; + + let suffix = if cfg!(target_os = "windows") { + ".exe" + } else { + "" + }; + + Ok(dir.join(format!("neovide{suffix}"))) +} + +fn version_path() -> Result { + let dir = dirs::state_dir() + .context("could not get state dir")? + .join("defold.nvim") + .join("meta"); + + fs::create_dir_all(&dir)?; + + Ok(dir.join("neovide_version")) +} + +fn version() -> Result { + let file = version_path()?; + + if !file.exists() { + bail!("Version not found"); + } + + Ok(fs::read_to_string(file)?) +} + +pub fn is_update_available() -> Result { + // macos is unsupported + if cfg!(target_os = "macos") { + tracing::debug!("MacOS is not supported, no update available"); + return Ok(false); + } + + let Ok(v) = version() else { + return Ok(true); + }; + + tracing::debug!("Neovide Version {v} installed"); + + let Some(installed) = Version::from(&v) else { + return Ok(true); + }; + + let release = github::fetch_release(OWNER, REPOSITORY)?; + + tracing::debug!("Neovide Version {} is newest", release.tag_name); + + let Some(current) = Version::from(&release.tag_name) else { + return Ok(false); + }; + + Ok(current > installed) +} + +pub fn update_or_install() -> Result { + if !is_update_available()? { + return Ok(path()?); + } + + let (downloaded_file, release) = github::download_release(OWNER, REPOSITORY, NAME)?; + + tracing::debug!("New Neovide version found {}", release.tag_name); + + let parent_dir = downloaded_file + .parent() + .map(PathBuf::from) + .context("could not get parent dir")?; + + let file = File::open(downloaded_file)?; + + if cfg!(target_os = "linux") { + let tar = GzDecoder::new(file); + let mut archive = Archive::new(tar); + archive.unpack(&parent_dir)?; + + let neovide_path = parent_dir.join("neovide"); + + if !neovide_path.exists() { + bail!("Neovide doesnt exist after unpacking?"); + } + + fs::set_permissions(&neovide_path, Permissions::from_mode(0o700))?; + + fs::rename(neovide_path, path()?)?; + } else if cfg!(target_os = "windows") { + let mut archive = ZipArchive::new(file)?; + archive.extract(&parent_dir)?; + + let neovide_path = parent_dir.join("neovide.exe"); + + if !neovide_path.exists() { + bail!("Neovide doesnt exist after unpacking?"); + } + + fs::rename(neovide_path, path()?)?; + } else { + bail!("Unsupported OS"); + } + + fs::write(version_path()?, release.tag_name)?; + + path() +} diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 5e3e228..8fc09fd 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -5,8 +5,11 @@ edition = "2024" [dependencies] anyhow = "1.0.100" +dirs = "6.0.0" +reqwest = { version = "0.12.26", features = ["blocking", "json"] } rust-ini = "0.21.3" serde = { version = "1.0.228", features = ["derive"] } +serde_json = "1.0.145" sha3 = "0.10.8" strum = { version = "0.27.2", features = ["derive"] } tracing = "0.1.43" diff --git a/crates/core/src/cache.rs b/crates/core/src/cache.rs new file mode 100644 index 0000000..73abed0 --- /dev/null +++ b/crates/core/src/cache.rs @@ -0,0 +1,45 @@ +use crate::utils::sha3; +use anyhow::{Context, Result}; +use std::{fs, path::PathBuf, time::Duration}; + +fn cache_dir() -> Result { + let dir = dirs::cache_dir() + .context("could not get cache dir")? + .join("defold.nvim") + .join("cache"); + + fs::create_dir_all(&dir)?; + + Ok(dir) +} + +pub fn get(key: &str) -> Option { + let Ok(dir) = cache_dir() else { + tracing::error!("Could not get cache dir"); + return None; + }; + + let path = dir.join(sha3(key)); + + if path.exists() { + let modified = path.metadata().ok()?.modified().ok()?; + + if modified.elapsed().ok()? < Duration::from_secs(3600) { + return fs::read_to_string(&path).ok(); + } else { + fs::remove_file(&path).ok(); + } + } + + None +} + +pub fn set(key: &str, value: &str) { + let Ok(dir) = cache_dir() else { + tracing::error!("Could not get cache dir"); + return; + }; + + let path = dir.join(sha3(key)); + fs::write(path, value).ok(); +} diff --git a/crates/core/src/github.rs b/crates/core/src/github.rs new file mode 100644 index 0000000..0765bb6 --- /dev/null +++ b/crates/core/src/github.rs @@ -0,0 +1,70 @@ +use std::{ + env::temp_dir, + fs::{self, File}, + io, + path::PathBuf, +}; + +use anyhow::{Result, bail}; +use serde::{Deserialize, Serialize}; + +use crate::cache; + +const USER_AGENT: &'static str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36"; + +#[derive(Debug, Serialize, Deserialize)] +pub struct Asset { + pub name: String, + pub browser_download_url: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct Release { + pub tag_name: String, + pub assets: Vec, +} + +pub fn fetch_release(owner: &str, repo: &str) -> Result { + let url = format!("https://api.github.com/repos/{owner}/{repo}/releases/latest"); + + if let Some(str) = cache::get(&url) { + if let Ok(res) = serde_json::from_str(&str) { + tracing::debug!("Serving {url} from cache"); + return Ok(res); + } + } + + let client = reqwest::blocking::Client::new(); + let res = client.get(&url).header("User-Agent", USER_AGENT).send()?; + + let release: Release = res.json()?; + + cache::set(&url, &serde_json::to_string(&release)?); + + Ok(release) +} + +pub fn download_release(owner: &str, repo: &str, name: &str) -> Result<(PathBuf, Release)> { + let temp = temp_dir() + .join("defold.nvim") + .join("download") + .join(owner) + .join(repo); + fs::create_dir_all(&temp)?; + + let release = fetch_release(&owner, &repo)?; + + let Some(asset) = release.assets.iter().find(|asset| asset.name == name) else { + bail!("Could not find asset {name} at {owner}/{repo}"); + }; + + let download_file = temp.join(&asset.name); + + let mut res = reqwest::blocking::get(&asset.browser_download_url)?; + res.error_for_status_ref()?; + + let mut file = File::create(&download_file)?; + io::copy(&mut res, &mut file)?; + + Ok((download_file, release)) +} diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index b1b5d31..4090822 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -1,3 +1,5 @@ +mod cache; pub mod focus; pub mod game_project; +pub mod github; pub mod utils; diff --git a/crates/sidecar/Cargo.toml b/crates/sidecar/Cargo.toml index 239416c..2dfe6b1 100644 --- a/crates/sidecar/Cargo.toml +++ b/crates/sidecar/Cargo.toml @@ -13,10 +13,10 @@ defold-nvim-core = { path = "../core" } dirs = "6.0.0" edn-rs = "0.18.0" mlua = { version = "0.11.5", features = [ - "module", - "luajit", - "serde", - "anyhow", + "module", + "luajit", + "serde", + "anyhow", ] } netstat2 = "0.11.2" reqwest = { version = "0.12.24", features = ["blocking", "json"] } diff --git a/crates/sidecar/src/lib.rs b/crates/sidecar/src/lib.rs index 1290cb1..77d95c6 100644 --- a/crates/sidecar/src/lib.rs +++ b/crates/sidecar/src/lib.rs @@ -1,17 +1,18 @@ -use std::{ - fs, - path::{PathBuf, absolute}, - sync::OnceLock, -}; - use anyhow::Context; use defold_nvim_core::{ focus, game_project::GameProject, + github, utils::{self}, }; use mlua::Value; use mlua::prelude::*; +use std::{ + fs::{self, File}, + io, + path::{PathBuf, absolute}, + sync::OnceLock, +}; use tracing::Level; use tracing_appender::rolling::daily; @@ -55,6 +56,11 @@ fn defold_nvim_sidecar(lua: &Lua) -> LuaResult { )?; exports.set("focus_neovim", lua.create_function(focus_neovim)?)?; exports.set("focus_game", lua.create_function(focus_game)?)?; + exports.set("download", lua.create_function(download)?)?; + exports.set( + "fetch_github_release", + lua.create_function(fetch_github_release)?, + )?; Ok(exports) } @@ -103,3 +109,27 @@ fn focus_game(_lua: &Lua, game_root: String) -> LuaResult<()> { Ok(()) } + +fn download(_lua: &Lua, (url, location): (String, String)) -> LuaResult<()> { + let res = reqwest::blocking::get(url); + if let Err(err) = res { + return Err(anyhow::Error::from(err).into()); + } + + let mut res = res.unwrap(); + + if let Err(err) = res.error_for_status_ref() { + return Err(anyhow::Error::from(err).into()); + } + + let mut file = File::create(location)?; + io::copy(&mut res, &mut file)?; + + Ok(()) +} + +fn fetch_github_release(lua: &Lua, (owner, repo): (String, String)) -> LuaResult { + let res = github::fetch_release(&owner, &repo)?; + let res = lua.to_value(&res)?; + Ok(res) +} diff --git a/lua/defold/init.lua b/lua/defold/init.lua index 302321a..13239b5 100644 --- a/lua/defold/init.lua +++ b/lua/defold/init.lua @@ -9,7 +9,6 @@ local root_markers = { "game.project" } ---@field type "neovide"|"terminal" Neovim launcher run by Defold ---@field executable string|nil Executable to be used by the launcher, nil means we're trying to figure this out ourselves ---@field socket_type "fsock"|"netsock"|nil Run Neovims RPC protocol over file socket or network. Nil means it will be picked automatic (fsock on Unix, network on Windows) - ---@field extra_arguments table|nil Extra arguments passed to the `executable` (or neovide) ---@field terminal TerminalLauncherSettings|nil Settings for running via terminal @@ -115,20 +114,11 @@ function M.setup(opts) -- TODO: check if sidecar is available, if not download it (which shouldnt be necessary with some pkg managers) - -- persist config for babashka - local config_path = babashka.config_path() - vim.fn.writefile({ - vim.fn.json_encode { - data_dir = vim.fn.stdpath "data", - bb_path = babashka.bb_path(), - plugin_config = M.config, - }, - }, config_path) - -- persist config for launcher vim.fn.writefile({ vim.fn.json_encode { data_dir = vim.fn.stdpath "data", + bb_path = babashka.bb_path(), plugin_config = M.config, }, }, M.launch_config()) diff --git a/lua/defold/service/github.lua b/lua/defold/service/github.lua new file mode 100644 index 0000000..0600f1a --- /dev/null +++ b/lua/defold/service/github.lua @@ -0,0 +1,30 @@ +local M = {} + +---@param owner string +---@param repository string +---@param current_version string|nil +---@return boolean +function M.is_update_available(owner, repository, current_version) + local log = require "defold.service.logger" + local sidecar = require "defold.sidecar" + + local ok, res = pcall(sidecar.fetch_github_release, owner, repository) + if not ok then + log.error(string.format("Could not check if github repo update is available because: %s", res)) + return false + end + + -- no release exists? + if not res.tag_name then + return false + end + + -- we dont have a version specified yet so everything is new + if current_version == nil then + return true + end + + return vim.version.lt(current_version, res.tag_name) +end + +return M diff --git a/lua/defold/sidecar.lua b/lua/defold/sidecar.lua index fbfdd9b..47158af 100644 --- a/lua/defold/sidecar.lua +++ b/lua/defold/sidecar.lua @@ -60,6 +60,8 @@ package.cpath = package.cpath ---@field set_default_editor function(plugin_root: string, launch_config: string) ---@field focus_neovim function(game_root: string) ---@field focus_game function(game_root: string) +---@field download function(url: string, location: string) +---@field fetch_github_release function(owner: string, repo: string) ---@type Sidecar local rust_plugin = require "defold_nvim_sidecar" From 9bd5d76566e5e8a767a82f4509632ac566421d47 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Wed, 17 Dec 2025 12:06:55 +0100 Subject: [PATCH 08/70] add clippy support and fix linter results --- .clippy.toml | 8 ++ .editorconfig | 3 + .github/workflows/test.yml | 3 + crates/bridge/src/launcher.rs | 127 ++++++++++++++-------------- crates/bridge/src/main.rs | 2 +- crates/bridge/src/neovide.rs | 8 +- crates/core/src/focus.rs | 8 +- crates/core/src/github.rs | 9 +- crates/sidecar/src/editor.rs | 6 +- crates/sidecar/src/editor_config.rs | 6 +- 10 files changed, 95 insertions(+), 85 deletions(-) create mode 100644 .clippy.toml diff --git a/.clippy.toml b/.clippy.toml new file mode 100644 index 0000000..099d14c --- /dev/null +++ b/.clippy.toml @@ -0,0 +1,8 @@ +no_deps = true + +[lints.clippy] +correctness = "deny" +complexity = "deny" +perf = "warn" +pedantic = "warn" +too_many_lines = "allow" diff --git a/.editorconfig b/.editorconfig index a882442..429cbce 100644 --- a/.editorconfig +++ b/.editorconfig @@ -5,3 +5,6 @@ end_of_line = lf insert_final_newline = true indent_style = space indent_size = 4 + +[*.{yml,yaml}] +indent_size = 2 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a68699e..39fb0e6 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -23,3 +23,6 @@ jobs: - name: Run tests run: cargo test --workspace --verbose + + - name: Clippy + run: cargo clippy diff --git a/crates/bridge/src/launcher.rs b/crates/bridge/src/launcher.rs index a52c901..cd6b5ee 100644 --- a/crates/bridge/src/launcher.rs +++ b/crates/bridge/src/launcher.rs @@ -1,7 +1,7 @@ use std::{ fs::{self, File}, io::BufReader, - path::PathBuf, + path::{Path, PathBuf}, process::Command, }; @@ -16,12 +16,12 @@ use crate::{ utils::{self, is_port_in_use}, }; -const ERR_NEOVIDE_NOT_FOUND: &'static str = "Could not find Neovide, have you installed it?"; -const ERR_TERMINAL_NOT_FOUND: &'static str = "Could not find any suitable terminal"; +const ERR_NEOVIDE_NOT_FOUND: &str = "Could not find Neovide, have you installed it?"; +const ERR_TERMINAL_NOT_FOUND: &str = "Could not find any suitable terminal"; -const VAR_CLASSNAME: &'static str = "{CLASSNAME}"; -const VAR_ADDRESS: &'static str = "{ADDR}"; -const VAR_REMOTE_CMD: &'static str = "{REMOTE_CMD}"; +const VAR_CLASSNAME: &str = "{CLASSNAME}"; +const VAR_ADDRESS: &str = "{ADDR}"; +const VAR_REMOTE_CMD: &str = "{REMOTE_CMD}"; #[derive(Debug, Deserialize)] pub struct LaunchConfig { @@ -57,18 +57,18 @@ impl Launcher { Ok(()) } - fn apply_var(self, var: &str, replace_with: String) -> Self { + fn apply_var(self, var: &str, replace_with: &str) -> Self { Self( self.0, self.1 .iter() - .map(|s| s.replace(var, &replace_with)) + .map(|s| s.replace(var, replace_with)) .collect(), ) } } -const DEFAULT_TERMINALS: [(&'static str, &'static str, &'static str); 5] = [ +const DEFAULT_TERMINALS: [(&str, &str, &str); 5] = [ ("alacratty", "--class=", "-e"), ("foot", "--app-id=", "-e"), ("ghostty", "--class=", "-e"), @@ -83,7 +83,7 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { .launcher .as_ref() .and_then(|exe| exe.executable.clone()) - .map(|s| s.into()) + .map(Into::into) .or_else(|| which("neovide").ok()) .or_else(|| match neovide::update_or_install() { Ok(path) => Some(path), @@ -103,7 +103,7 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { if let Some(extra_args) = cfg .launcher .as_ref() - .and_then(|cfg| Some(cfg.extra_arguments.clone().unwrap_or_default())) + .map(|cfg| cfg.extra_arguments.clone().unwrap_or_default()) { for extra_arg in extra_args { args.push(extra_arg); @@ -136,7 +136,7 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { .launcher .as_ref() .and_then(|launcher| launcher.executable.clone()) - .map(|exe| exe.into()); + .map(Into::into); let executable = if let Some(exe) = executable && exe.exists() @@ -158,7 +158,7 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { if let Some(extra_args) = cfg .launcher .as_ref() - .and_then(|cfg| Some(cfg.extra_arguments.clone().unwrap_or_default())) + .map(|cfg| cfg.extra_arguments.clone().unwrap_or_default()) { for extra_arg in extra_args { args.push(extra_arg); @@ -166,7 +166,7 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { } if let Some(class_arg) = class_arg { - if class_arg.ends_with("=") { + if class_arg.ends_with('=') { args.push(class_arg + VAR_CLASSNAME); } else { args.push(class_arg); @@ -175,7 +175,7 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { } if let Some(run_arg) = run_arg { - if run_arg.ends_with("=") { + if run_arg.ends_with('=') { args.push(run_arg + nvim); } else { args.push(run_arg); @@ -199,7 +199,7 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { if let Some(extra_args) = cfg .launcher .as_ref() - .and_then(|cfg| Some(cfg.extra_arguments.clone().unwrap_or_default())) + .map(|cfg| cfg.extra_arguments.clone().unwrap_or_default()) { for extra_arg in extra_args { args.push(extra_arg); @@ -208,51 +208,49 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { // executable specifies only the name of which terminal we want to use if let Some(exe_name) = cfg.launcher.as_ref().and_then(|cfg| cfg.executable.clone()) - { - if let Some((name, class_arg, run_arg)) = DEFAULT_TERMINALS + && let Some((name, class_arg, run_arg)) = DEFAULT_TERMINALS .iter() .find(|(name, _, _)| *name == exe_name) - && let Ok(path) = which(name) - { - if class_arg.ends_with("=") { - args.push(class_arg.to_string() + VAR_CLASSNAME); - } else { - args.push(class_arg.to_string()); - args.push(VAR_CLASSNAME.to_string()); - } + && let Ok(path) = which(name) + { + if class_arg.ends_with('=') { + args.push((*class_arg).to_string() + VAR_CLASSNAME); + } else { + args.push((*class_arg).to_string()); + args.push(VAR_CLASSNAME.to_string()); + } - if run_arg.ends_with("=") { - args.push(run_arg.to_string() + nvim); - } else { - args.push(run_arg.to_string()); - args.push(nvim.to_string()); - } + if run_arg.ends_with('=') { + args.push((*run_arg).to_string() + nvim); + } else { + args.push((*run_arg).to_string()); + args.push((*nvim).clone()); + } - args.push("--listen".to_string()); - args.push(VAR_ADDRESS.to_string()); + args.push("--listen".to_string()); + args.push(VAR_ADDRESS.to_string()); - args.push("--remote".to_string()); - args.push(VAR_REMOTE_CMD.to_string()); + args.push("--remote".to_string()); + args.push(VAR_REMOTE_CMD.to_string()); - return Some(Launcher(path, args)); - } + return Some(Launcher(path, args)); } // try finding one of our supported default terminals - for (name, class_arg, run_arg) in DEFAULT_TERMINALS.iter() { + for (name, class_arg, run_arg) in &DEFAULT_TERMINALS { if let Ok(path) = which(name) { - if class_arg.ends_with("=") { - args.push(class_arg.to_string() + VAR_CLASSNAME); + if class_arg.ends_with('=') { + args.push((*class_arg).to_string() + VAR_CLASSNAME); } else { - args.push(class_arg.to_string()); + args.push((*class_arg).to_string()); args.push(VAR_CLASSNAME.to_string()); } - if run_arg.ends_with("=") { - args.push(run_arg.to_string() + nvim); + if run_arg.ends_with('=') { + args.push((*run_arg).to_string() + nvim); } else { - args.push(run_arg.to_string()); - args.push(nvim.to_string()); + args.push((*run_arg).to_string()); + args.push((*nvim).clone()); } args.push("--listen".to_string()); @@ -277,9 +275,8 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { launcher_type: Some(LauncherType::Neovide), ..launcher.clone() }), - ..cfg.clone() }, - &nvim, + nvim, ) { return Ok(launcher); } @@ -290,9 +287,8 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { launcher_type: Some(LauncherType::Terminal), ..launcher.clone() }), - ..cfg.clone() }, - &nvim, + nvim, ) { return Ok(launcher); } @@ -309,7 +305,7 @@ fn nvim_open_file_remote(nvim: &str, server: &str, remote_cmd: &str) -> Result<( .arg("--server") .arg(server) .arg("--remote-send") - .arg(format!("\":edit {}\"", remote_cmd)) + .arg(format!("\":edit {remote_cmd}\"")) .output()?; if !out.stderr.is_empty() { @@ -319,7 +315,7 @@ fn nvim_open_file_remote(nvim: &str, server: &str, remote_cmd: &str) -> Result<( Ok(()) } -fn run_fsock(launcher: Launcher, nvim: &str, root_dir: PathBuf, remote_cmd: &str) -> Result<()> { +fn run_fsock(launcher: Launcher, nvim: &str, root_dir: &Path, remote_cmd: &str) -> Result<()> { let socket_file = utils::runtime_dir( root_dir .to_str() @@ -333,8 +329,7 @@ fn run_fsock(launcher: Launcher, nvim: &str, root_dir: PathBuf, remote_cmd: &str VAR_ADDRESS, socket_file .to_str() - .context("could not convert socket file to string")? - .to_string(), + .context("could not convert socket file to string")?, ); if socket_file.exists() { @@ -360,7 +355,7 @@ fn run_fsock(launcher: Launcher, nvim: &str, root_dir: PathBuf, remote_cmd: &str Ok(()) } -fn run_netsock(launcher: Launcher, nvim: &str, root_dir: PathBuf, remote_cmd: &str) -> Result<()> { +fn run_netsock(launcher: Launcher, nvim: &str, root_dir: &Path, remote_cmd: &str) -> Result<()> { let port_file = utils::runtime_dir( root_dir .to_str() @@ -388,21 +383,21 @@ fn run_netsock(launcher: Launcher, nvim: &str, root_dir: PathBuf, remote_cmd: &s let socket = format!("127.0.0.1:{new_port}"); tracing::debug!("Trying to use netsock with port {socket}"); fs::write(port_file, new_port.to_string())?; - launcher.apply_var(VAR_ADDRESS, socket).run()?; + launcher.apply_var(VAR_ADDRESS, &socket).run()?; } return Ok(()); } fs::write(port_file, port.to_string())?; - launcher.apply_var(VAR_ADDRESS, socket).run()?; + launcher.apply_var(VAR_ADDRESS, &socket).run()?; Ok(()) } pub fn run( config: LaunchConfig, root_dir: PathBuf, - file: PathBuf, + file: &Path, line: Option, ) -> Result<()> { let nvim = which("nvim")? @@ -415,15 +410,17 @@ pub fn run( let launcher = if cfg!(target_os = "linux") { launcher.apply_var( VAR_CLASSNAME, - classname( + &classname( root_dir .to_str() .context("could not convert path to string")?, )?, ) } else if cfg!(target_os = "macos") { + // TODO: macos launcher } else if cfg!(target_os = "windows") { + // TODO: windows launcher } else { launcher @@ -432,20 +429,20 @@ pub fn run( let file_str = file.to_str().context("could not convert path to string")?; let remote_cmd = match line { - Some(line) => format!("+{} {}", line, file_str), + Some(line) => format!("+{line} {file_str}"), None => file_str.to_string(), }; - let launcher = launcher.apply_var(VAR_REMOTE_CMD, remote_cmd.clone()); + let launcher = launcher.apply_var(VAR_REMOTE_CMD, &remote_cmd.clone()); match config.plugin_config.launcher.and_then(|l| l.socket_type) { - Some(SocketType::Fsock) => run_fsock(launcher, &nvim, root_dir.clone(), &remote_cmd)?, - Some(SocketType::Netsock) => run_netsock(launcher, &nvim, root_dir.clone(), &remote_cmd)?, + Some(SocketType::Fsock) => run_fsock(launcher, &nvim, &root_dir, &remote_cmd)?, + Some(SocketType::Netsock) => run_netsock(launcher, &nvim, &root_dir, &remote_cmd)?, None => { if cfg!(target_os = "linux") || cfg!(target_os = "macos") { - run_fsock(launcher, &nvim, root_dir.clone(), &remote_cmd)? + run_fsock(launcher, &nvim, &root_dir, &remote_cmd)?; } else { - run_netsock(launcher, &nvim, root_dir.clone(), &remote_cmd)? + run_netsock(launcher, &nvim, &root_dir, &remote_cmd)?; } } } diff --git a/crates/bridge/src/main.rs b/crates/bridge/src/main.rs index 5087062..b9b4893 100644 --- a/crates/bridge/src/main.rs +++ b/crates/bridge/src/main.rs @@ -85,7 +85,7 @@ fn main() -> Result<()> { } => launcher::run( launcher::LaunchConfig::from_file(absolute(launch_config)?)?, absolute(game_root_dir)?, - absolute(file)?, + &absolute(file)?, line, )?, Commands::FocusNeovim { game_root_dir } => focus_neovim(absolute(game_root_dir)?)?, diff --git a/crates/bridge/src/neovide.rs b/crates/bridge/src/neovide.rs index e81f09a..db6ecb7 100644 --- a/crates/bridge/src/neovide.rs +++ b/crates/bridge/src/neovide.rs @@ -13,11 +13,11 @@ use zip::ZipArchive; #[cfg(target_os = "linux")] use std::os::unix::fs::PermissionsExt; -const OWNER: &'static str = "neovide"; -const REPOSITORY: &'static str = "neovide"; +const OWNER: &str = "neovide"; +const REPOSITORY: &str = "neovide"; #[cfg(target_os = "linux")] -const NAME: &'static str = "neovide-linux-x86_64.tar.gz"; +const NAME: &str = "neovide-linux-x86_64.tar.gz"; #[cfg(target_os = "macos")] const NAME: &'static str = "not supported"; @@ -93,7 +93,7 @@ pub fn is_update_available() -> Result { pub fn update_or_install() -> Result { if !is_update_available()? { - return Ok(path()?); + return path(); } let (downloaded_file, release) = github::download_release(OWNER, REPOSITORY, NAME)?; diff --git a/crates/core/src/focus.rs b/crates/core/src/focus.rs index 46a8456..660c331 100644 --- a/crates/core/src/focus.rs +++ b/crates/core/src/focus.rs @@ -94,8 +94,8 @@ fn switch(switcher_type: SwitcherType) -> Result<()> { .arg(format!( "[{}={}] focus", match switcher_type { - SwitcherType::Class(_) => format!("class"), - SwitcherType::Title(_) => format!("title"), + SwitcherType::Class(_) => "class".to_string(), + SwitcherType::Title(_) => "title".to_string(), _ => bail!("Unsupported switcher type {switcher_type:?} for {switcher:?}"), }, switcher_type.value(), @@ -120,8 +120,8 @@ fn switch(switcher_type: SwitcherType) -> Result<()> { Command::new(switcher.path().unwrap()) .arg("search") .arg(match switcher_type { - SwitcherType::Class(_) => format!("--class"), - SwitcherType::Title(_) => format!("--title"), + SwitcherType::Class(_) => "--class".to_string(), + SwitcherType::Title(_) => "--title".to_string(), _ => bail!("Unsupported switcher type {switcher_type:?} for {switcher:?}"), }) .arg(switcher_type.value()) diff --git a/crates/core/src/github.rs b/crates/core/src/github.rs index 0765bb6..8dece10 100644 --- a/crates/core/src/github.rs +++ b/crates/core/src/github.rs @@ -10,7 +10,7 @@ use serde::{Deserialize, Serialize}; use crate::cache; -const USER_AGENT: &'static str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36"; +const USER_AGENT: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36"; #[derive(Debug, Serialize, Deserialize)] pub struct Asset { @@ -27,12 +27,11 @@ pub struct Release { pub fn fetch_release(owner: &str, repo: &str) -> Result { let url = format!("https://api.github.com/repos/{owner}/{repo}/releases/latest"); - if let Some(str) = cache::get(&url) { - if let Ok(res) = serde_json::from_str(&str) { + if let Some(str) = cache::get(&url) + && let Ok(res) = serde_json::from_str(&str) { tracing::debug!("Serving {url} from cache"); return Ok(res); } - } let client = reqwest::blocking::Client::new(); let res = client.get(&url).header("User-Agent", USER_AGENT).send()?; @@ -52,7 +51,7 @@ pub fn download_release(owner: &str, repo: &str, name: &str) -> Result<(PathBuf, .join(repo); fs::create_dir_all(&temp)?; - let release = fetch_release(&owner, &repo)?; + let release = fetch_release(owner, repo)?; let Some(asset) = release.assets.iter().find(|asset| asset.name == name) else { bail!("Could not find asset {name} at {owner}/{repo}"); diff --git a/crates/sidecar/src/editor.rs b/crates/sidecar/src/editor.rs index a0d70e3..8dc1c65 100644 --- a/crates/sidecar/src/editor.rs +++ b/crates/sidecar/src/editor.rs @@ -74,7 +74,7 @@ fn is_editor(port: u16) -> bool { pub fn list_commands(port: Option) -> Result> { let url = command_url( - port.or_else(|| find_port()) + port.or_else(find_port) .context("could not determine editor port")?, None, ); @@ -87,12 +87,12 @@ pub fn list_commands(port: Option) -> Result> { let content = res.text()?.to_string(); - serde_json::from_str(&content).map_err(|err| anyhow::Error::from(err)) + serde_json::from_str(&content).map_err(anyhow::Error::from) } pub fn send_command(port: Option, cmd: String) -> Result<()> { let url = command_url( - port.or_else(|| find_port()) + port.or_else(find_port) .context("could not determine editor port")?, Some(cmd.clone()), ); diff --git a/crates/sidecar/src/editor_config.rs b/crates/sidecar/src/editor_config.rs index 2e9941a..7f12511 100644 --- a/crates/sidecar/src/editor_config.rs +++ b/crates/sidecar/src/editor_config.rs @@ -3,7 +3,7 @@ use edn_rs::Edn; use std::{fs, path::PathBuf, str::FromStr}; #[cfg(target_os = "linux")] -const RUN_SCRIPT: &'static str = include_str!("../assets/run_linux.sh"); +const RUN_SCRIPT: &str = include_str!("../assets/run_linux.sh"); #[cfg(target_os = "macos")] const RUN_SCRIPT: &'static str = include_str!("../assets/run_macos.sh"); @@ -15,13 +15,13 @@ const RUN_SCRIPT: &'static str = include_str!("../assets/run_windows.bat"); const SCRIPT_EXT: &'static str = "bat"; #[cfg(not(target_os = "windows"))] -const SCRIPT_EXT: &'static str = "sh"; +const SCRIPT_EXT: &str = "sh"; #[cfg(target_os = "windows")] const EXE_SUFFIX: &'static str = ".exe"; #[cfg(not(target_os = "windows"))] -const EXE_SUFFIX: &'static str = ""; +const EXE_SUFFIX: &str = ""; fn find_bridge_path(plugin_root: PathBuf) -> Result { let exe = format!("defold-nvim-bridge{}", EXE_SUFFIX); From f2e908ff2dac6e52293d3952dad48d9d658ec6a4 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Wed, 17 Dec 2025 12:13:10 +0100 Subject: [PATCH 09/70] fix building on macos --- crates/bridge/src/neovide.rs | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/crates/bridge/src/neovide.rs b/crates/bridge/src/neovide.rs index db6ecb7..e901f27 100644 --- a/crates/bridge/src/neovide.rs +++ b/crates/bridge/src/neovide.rs @@ -5,13 +5,7 @@ use std::{ use anyhow::{Context, Result, bail}; use defold_nvim_core::github; -use flate2::read::GzDecoder; -use tar::Archive; use version_compare::Version; -use zip::ZipArchive; - -#[cfg(target_os = "linux")] -use std::os::unix::fs::PermissionsExt; const OWNER: &str = "neovide"; const REPOSITORY: &str = "neovide"; @@ -107,7 +101,12 @@ pub fn update_or_install() -> Result { let file = File::open(downloaded_file)?; - if cfg!(target_os = "linux") { + #[cfg(target_os = "linux")] + { + use flate2::read::GzDecoder; + use std::os::unix::fs::PermissionsExt; + use tar::Archive; + let tar = GzDecoder::new(file); let mut archive = Archive::new(tar); archive.unpack(&parent_dir)?; @@ -121,7 +120,12 @@ pub fn update_or_install() -> Result { fs::set_permissions(&neovide_path, Permissions::from_mode(0o700))?; fs::rename(neovide_path, path()?)?; - } else if cfg!(target_os = "windows") { + } + + #[cfg(target_os = "windows")] + { + use zip::ZipArchive; + let mut archive = ZipArchive::new(file)?; archive.extract(&parent_dir)?; @@ -132,8 +136,11 @@ pub fn update_or_install() -> Result { } fs::rename(neovide_path, path()?)?; - } else { - bail!("Unsupported OS"); + } + + #[cfg(not(any(target_os = "linux", target_os = "windows")))] + { + bail!("Unsupported operating system"); } fs::write(version_path()?, release.tag_name)?; From 5e8b8d013faa76c013f877232416148ee5621760 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Wed, 17 Dec 2025 12:24:52 +0100 Subject: [PATCH 10/70] only check for updates once a week --- crates/bridge/src/neovide.rs | 10 ++++++++++ lua/defold/service/debugger.lua | 1 + 2 files changed, 11 insertions(+) diff --git a/crates/bridge/src/neovide.rs b/crates/bridge/src/neovide.rs index e901f27..32693bf 100644 --- a/crates/bridge/src/neovide.rs +++ b/crates/bridge/src/neovide.rs @@ -1,6 +1,7 @@ use std::{ fs::{self, File, Permissions}, path::PathBuf, + time::Duration, }; use anyhow::{Context, Result, bail}; @@ -64,10 +65,19 @@ pub fn is_update_available() -> Result { return Ok(false); } + // if the version file is younger than a week dont bother + let last_modified = version_path()?.metadata()?.modified()?; + if last_modified.elapsed()? < Duration::from_hours(24 * 7) { + return Ok(false); + } + let Ok(v) = version() else { return Ok(true); }; + // re-write the file again so that we only check once a week + fs::write(version_path()?, &v)?; + tracing::debug!("Neovide Version {v} installed"); let Some(installed) = Version::from(&v) else { diff --git a/lua/defold/service/debugger.lua b/lua/defold/service/debugger.lua index 98c8622..ca3d30d 100644 --- a/lua/defold/service/debugger.lua +++ b/lua/defold/service/debugger.lua @@ -22,6 +22,7 @@ function M.mobdap_path() return M.path end + -- TODO: check if is available, if not download local res = babashka.run_task_json "mobdap-path" if res.status ~= 200 then From 2ae6afd47521d12b4655c478ce7c661ca7ddfaad Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Wed, 17 Dec 2025 13:07:11 +0100 Subject: [PATCH 11/70] add support for managing mobdap --- .clippy.toml | 8 -- Cargo.toml | 9 ++ crates/bridge/Cargo.toml | 4 - crates/bridge/src/main.rs | 12 +- crates/core/Cargo.toml | 4 + crates/core/src/lib.rs | 2 + crates/core/src/mobdap.rs | 161 +++++++++++++++++++++++++ crates/{bridge => core}/src/neovide.rs | 13 +- crates/sidecar/src/lib.rs | 50 ++------ lua/defold/init.lua | 4 +- lua/defold/service/debugger.lua | 16 ++- lua/defold/service/github.lua | 30 ----- lua/defold/sidecar.lua | 3 +- src/defold/constants.clj | 6 - src/defold/debugger.clj | 56 --------- src/defold/main.clj | 20 +-- src/defold/neovide.clj | 53 -------- 17 files changed, 216 insertions(+), 235 deletions(-) delete mode 100644 .clippy.toml create mode 100644 crates/core/src/mobdap.rs rename crates/{bridge => core}/src/neovide.rs (92%) delete mode 100644 lua/defold/service/github.lua delete mode 100644 src/defold/constants.clj delete mode 100644 src/defold/debugger.clj delete mode 100644 src/defold/neovide.clj diff --git a/.clippy.toml b/.clippy.toml deleted file mode 100644 index 099d14c..0000000 --- a/.clippy.toml +++ /dev/null @@ -1,8 +0,0 @@ -no_deps = true - -[lints.clippy] -correctness = "deny" -complexity = "deny" -perf = "warn" -pedantic = "warn" -too_many_lines = "allow" diff --git a/Cargo.toml b/Cargo.toml index b294f10..833466d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,3 +1,12 @@ [workspace] members = ["crates/sidecar", "crates/bridge", "crates/core"] resolver = "2" + +[workspace.lints.clippy] +correctness = "deny" +complexity = "deny" +perf = "warn" +pedantic = "warn" + +[workspace.lints.rust] +too-many-lines = "allow" diff --git a/crates/bridge/Cargo.toml b/crates/bridge/Cargo.toml index f3daec9..547ac6a 100644 --- a/crates/bridge/Cargo.toml +++ b/crates/bridge/Cargo.toml @@ -8,14 +8,10 @@ anyhow = "1.0.100" clap = { version = "4.5.53", features = ["derive"] } defold-nvim-core = { path = "../core" } dirs = "6.0.0" -flate2 = "1.1.5" netstat2 = "0.11.2" serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.145" -tar = "0.4.44" tracing = "0.1.43" tracing-appender = "0.2.4" tracing-subscriber = "0.3.22" -version-compare = "0.2.1" which = "8.0.0" -zip = "6.0.0" diff --git a/crates/bridge/src/main.rs b/crates/bridge/src/main.rs index b9b4893..5a08288 100644 --- a/crates/bridge/src/main.rs +++ b/crates/bridge/src/main.rs @@ -2,13 +2,15 @@ use std::{env, fs, io, path::absolute}; use anyhow::{Context, Result}; use clap::{Parser, Subcommand, command}; -use defold_nvim_core::focus::{focus_game, focus_neovim}; +use defold_nvim_core::{ + focus::{focus_game, focus_neovim}, + mobdap, neovide, +}; use tracing::Level; use tracing_appender::rolling::daily; use tracing_subscriber::fmt::writer::MakeWriterExt; mod launcher; -mod neovide; mod plugin_config; mod utils; @@ -47,6 +49,8 @@ enum Commands { }, /// Downloads Neovide DownloadNeovide, + /// Downloads Mobdap Debugger + DownloadMobdap, } fn main() -> Result<()> { @@ -94,6 +98,10 @@ fn main() -> Result<()> { let path = neovide::update_or_install()?; tracing::info!("Installed neovide at {path:?}"); } + Commands::DownloadMobdap => { + let path = mobdap::update_or_install()?; + tracing::info!("Installed mobdap at {path:?}"); + } } Ok(()) diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 8fc09fd..aeb0073 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -5,6 +5,10 @@ edition = "2024" [dependencies] anyhow = "1.0.100" +tar = "0.4.44" +version-compare = "0.2.1" +flate2 = "1.1.5" +zip = "6.0.0" dirs = "6.0.0" reqwest = { version = "0.12.26", features = ["blocking", "json"] } rust-ini = "0.21.3" diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 4090822..9bee90c 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -2,4 +2,6 @@ mod cache; pub mod focus; pub mod game_project; pub mod github; +pub mod mobdap; +pub mod neovide; pub mod utils; diff --git a/crates/core/src/mobdap.rs b/crates/core/src/mobdap.rs new file mode 100644 index 0000000..cc9199c --- /dev/null +++ b/crates/core/src/mobdap.rs @@ -0,0 +1,161 @@ +use std::{ + fs::{self, File, Permissions}, + path::PathBuf, + time::Duration, +}; + +use crate::github; +use anyhow::{Context, Result, bail}; +use version_compare::Version; + +const OWNER: &str = "atomicptr"; +const REPOSITORY: &str = "mobdap"; + +#[cfg(all(target_os = "linux", target_arch = "x86_64"))] +const NAME: &str = "mobdap-linux-amd64.tar.gz"; + +#[cfg(all(target_os = "linux", target_arch = "aarch64"))] +const NAME: &str = "mobdap-linux-arm64.tar.gz"; + +#[cfg(all(target_os = "macos", target_arch = "x86_64"))] +const NAME: &str = "mobdap-macos-amd64.tar.gz"; + +#[cfg(all(target_os = "macos", target_arch = "aarch64"))] +const NAME: &str = "mobdap-macos-arm64.tar.gz"; + +#[cfg(target_os = "windows")] +const NAME: &str = "mobdap-windows-amd64.zip"; + +fn path() -> Result { + let dir = dirs::state_dir() + .context("could not get state dir")? + .join("defold.nvim") + .join("bin"); + + fs::create_dir_all(&dir)?; + + let suffix = if cfg!(target_os = "windows") { + ".exe" + } else { + "" + }; + + Ok(dir.join(format!("mobdap{suffix}"))) +} + +fn version_path() -> Result { + let dir = dirs::state_dir() + .context("could not get state dir")? + .join("defold.nvim") + .join("meta"); + + fs::create_dir_all(&dir)?; + + Ok(dir.join("mobdap_version")) +} + +fn version() -> Result { + let file = version_path()?; + + if !file.exists() { + bail!("Version not found"); + } + + Ok(fs::read_to_string(file)?) +} + +pub fn is_update_available() -> Result { + if version_path()?.exists() { + // if the version file is younger than a week dont bother + let last_modified = version_path()?.metadata()?.modified()?; + if last_modified.elapsed()? < Duration::from_hours(24 * 7) { + return Ok(false); + } + } + + let Ok(v) = version() else { + return Ok(true); + }; + + // re-write the file again so that we only check once a week + fs::write(version_path()?, &v)?; + + tracing::debug!("Mobdap Version {v} installed"); + + let Some(installed) = Version::from(&v) else { + return Ok(true); + }; + + let release = github::fetch_release(OWNER, REPOSITORY)?; + + tracing::debug!("Mobdap Version {} is newest", release.tag_name); + + let Some(current) = Version::from(&release.tag_name) else { + return Ok(false); + }; + + Ok(current > installed) +} + +pub fn update_or_install() -> Result { + if !is_update_available()? { + return path(); + } + + let (downloaded_file, release) = github::download_release(OWNER, REPOSITORY, NAME)?; + + tracing::debug!("New Mobdap version found {}", release.tag_name); + + let parent_dir = downloaded_file + .parent() + .map(PathBuf::from) + .context("could not get parent dir")?; + + let file = File::open(downloaded_file)?; + + #[cfg(any(target_os = "linux", target_os = "macos"))] + { + use flate2::read::GzDecoder; + use std::os::unix::fs::PermissionsExt; + use tar::Archive; + + let tar = GzDecoder::new(file); + let mut archive = Archive::new(tar); + archive.unpack(&parent_dir)?; + + let mobdap_path = parent_dir.join("mobdap"); + + if !mobdap_path.exists() { + bail!("Mobdap doesnt exist after unpacking?"); + } + + fs::set_permissions(&mobdap_path, Permissions::from_mode(0o700))?; + + fs::rename(mobdap_path, path()?)?; + } + + #[cfg(target_os = "windows")] + { + use zip::ZipArchive; + + let mut archive = ZipArchive::new(file)?; + archive.extract(&parent_dir)?; + + let mobdap_path = parent_dir.join("mobdap.exe"); + + if !mobdap_path.exists() { + bail!("mobdap doesnt exist after unpacking?"); + } + + fs::rename(mobdap_path, path()?)?; + } + + #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))] + { + bail!("Unsupported operating system"); + } + + fs::write(version_path()?, release.tag_name)?; + + path() +} diff --git a/crates/bridge/src/neovide.rs b/crates/core/src/neovide.rs similarity index 92% rename from crates/bridge/src/neovide.rs rename to crates/core/src/neovide.rs index 32693bf..39335c6 100644 --- a/crates/bridge/src/neovide.rs +++ b/crates/core/src/neovide.rs @@ -5,9 +5,10 @@ use std::{ }; use anyhow::{Context, Result, bail}; -use defold_nvim_core::github; use version_compare::Version; +use crate::github; + const OWNER: &str = "neovide"; const REPOSITORY: &str = "neovide"; @@ -65,10 +66,12 @@ pub fn is_update_available() -> Result { return Ok(false); } - // if the version file is younger than a week dont bother - let last_modified = version_path()?.metadata()?.modified()?; - if last_modified.elapsed()? < Duration::from_hours(24 * 7) { - return Ok(false); + if version_path()?.exists() { + // if the version file is younger than a week dont bother + let last_modified = version_path()?.metadata()?.modified()?; + if last_modified.elapsed()? < Duration::from_hours(24 * 7) { + return Ok(false); + } } let Ok(v) = version() else { diff --git a/crates/sidecar/src/lib.rs b/crates/sidecar/src/lib.rs index 77d95c6..12eb44c 100644 --- a/crates/sidecar/src/lib.rs +++ b/crates/sidecar/src/lib.rs @@ -1,15 +1,10 @@ use anyhow::Context; -use defold_nvim_core::{ - focus, - game_project::GameProject, - github, - utils::{self}, -}; +use defold_nvim_core::mobdap; +use defold_nvim_core::{focus, game_project::GameProject}; use mlua::Value; use mlua::prelude::*; use std::{ - fs::{self, File}, - io, + fs::{self}, path::{PathBuf, absolute}, sync::OnceLock, }; @@ -45,7 +40,6 @@ fn defold_nvim_sidecar(lua: &Lua) -> LuaResult { let exports = lua.create_table()?; exports.set("version", lua.create_string(env!("CARGO_PKG_VERSION"))?)?; - exports.set("sha3", lua.create_function(sha3)?)?; exports.set("read_game_project", lua.create_function(read_game_project)?)?; exports.set("find_editor_port", lua.create_function(find_editor_port)?)?; exports.set("list_commands", lua.create_function(list_commands)?)?; @@ -56,19 +50,11 @@ fn defold_nvim_sidecar(lua: &Lua) -> LuaResult { )?; exports.set("focus_neovim", lua.create_function(focus_neovim)?)?; exports.set("focus_game", lua.create_function(focus_game)?)?; - exports.set("download", lua.create_function(download)?)?; - exports.set( - "fetch_github_release", - lua.create_function(fetch_github_release)?, - )?; + exports.set("mobdap_install", lua.create_function(mobdap_install)?)?; Ok(exports) } -fn sha3(_lua: &Lua, str: String) -> LuaResult { - Ok(utils::sha3(&str)) -} - fn read_game_project(lua: &Lua, path: String) -> LuaResult { let game_project = GameProject::load_from_path(path.into())?; let val = lua.to_value(&game_project)?; @@ -110,26 +96,10 @@ fn focus_game(_lua: &Lua, game_root: String) -> LuaResult<()> { Ok(()) } -fn download(_lua: &Lua, (url, location): (String, String)) -> LuaResult<()> { - let res = reqwest::blocking::get(url); - if let Err(err) = res { - return Err(anyhow::Error::from(err).into()); - } - - let mut res = res.unwrap(); - - if let Err(err) = res.error_for_status_ref() { - return Err(anyhow::Error::from(err).into()); - } - - let mut file = File::create(location)?; - io::copy(&mut res, &mut file)?; - - Ok(()) -} - -fn fetch_github_release(lua: &Lua, (owner, repo): (String, String)) -> LuaResult { - let res = github::fetch_release(&owner, &repo)?; - let res = lua.to_value(&res)?; - Ok(res) +fn mobdap_install(_lua: &Lua, _: ()) -> LuaResult { + let path = mobdap::update_or_install()?; + Ok(path + .to_str() + .context("could not convert path to string")? + .to_string()) } diff --git a/lua/defold/init.lua b/lua/defold/init.lua index 13239b5..56c683c 100644 --- a/lua/defold/init.lua +++ b/lua/defold/init.lua @@ -244,7 +244,9 @@ function M.load_plugin() log.debug "============= defold.nvim: Loaded plugin" log.debug("Sidecar Version:" .. sidecar.version) log.debug("Babashka Path: " .. babashka.bb_path()) - log.debug("Mobdap Path: " .. debugger.mobdap_path()) + if debugger.mobdap_path() then + log.debug("Mobdap Path: " .. debugger.mobdap_path()) + end log.debug("Config: " .. vim.inspect(M.config)) -- register hot reload when saving lua files diff --git a/lua/defold/service/debugger.lua b/lua/defold/service/debugger.lua index ca3d30d..af262c4 100644 --- a/lua/defold/service/debugger.lua +++ b/lua/defold/service/debugger.lua @@ -7,8 +7,8 @@ M.path = nil ---@return string|nil function M.mobdap_path() local os = require "defold.service.os" - local babashka = require "defold.service.babashka" local log = require "defold.service.logger" + local sidecar = require "defold.sidecar" if M.custom_executable then return M.custom_executable @@ -22,16 +22,14 @@ function M.mobdap_path() return M.path end - -- TODO: check if is available, if not download - local res = babashka.run_task_json "mobdap-path" - - if res.status ~= 200 then - log.error("Unable to locate debugger: " .. vim.inspect(res)) - return nil + local ok, res = pcall(sidecar.mobdap_install) + if not ok then + log.error(string.format("Could not install mobdap: %s", res)) + return end - M.path = res.mobdap_path - return res.mobdap_path + M.path = res + return res end ---@param custom_executable string|nil diff --git a/lua/defold/service/github.lua b/lua/defold/service/github.lua deleted file mode 100644 index 0600f1a..0000000 --- a/lua/defold/service/github.lua +++ /dev/null @@ -1,30 +0,0 @@ -local M = {} - ----@param owner string ----@param repository string ----@param current_version string|nil ----@return boolean -function M.is_update_available(owner, repository, current_version) - local log = require "defold.service.logger" - local sidecar = require "defold.sidecar" - - local ok, res = pcall(sidecar.fetch_github_release, owner, repository) - if not ok then - log.error(string.format("Could not check if github repo update is available because: %s", res)) - return false - end - - -- no release exists? - if not res.tag_name then - return false - end - - -- we dont have a version specified yet so everything is new - if current_version == nil then - return true - end - - return vim.version.lt(current_version, res.tag_name) -end - -return M diff --git a/lua/defold/sidecar.lua b/lua/defold/sidecar.lua index 47158af..d9fc018 100644 --- a/lua/defold/sidecar.lua +++ b/lua/defold/sidecar.lua @@ -60,8 +60,7 @@ package.cpath = package.cpath ---@field set_default_editor function(plugin_root: string, launch_config: string) ---@field focus_neovim function(game_root: string) ---@field focus_game function(game_root: string) ----@field download function(url: string, location: string) ----@field fetch_github_release function(owner: string, repo: string) +---@field mobdap_install function(): string ---@type Sidecar local rust_plugin = require "defold_nvim_sidecar" diff --git a/src/defold/constants.clj b/src/defold/constants.clj deleted file mode 100644 index fc2babf..0000000 --- a/src/defold/constants.clj +++ /dev/null @@ -1,6 +0,0 @@ -(ns defold.constants) - -; TODO: the debugger port, get this from a config or something -(def mobdap-port 18172) - -(def base-class-name "com.defold.nvim.%s") diff --git a/src/defold/debugger.clj b/src/defold/debugger.clj deleted file mode 100644 index ca5d103..0000000 --- a/src/defold/debugger.clj +++ /dev/null @@ -1,56 +0,0 @@ -(ns defold.debugger - (:require - [babashka.fs :as fs] - [defold.utils :refer [data-dir download-and-unpack get-os-arch-value - windows?]] - [taoensso.timbre :as log])) - -(def ^:private mobdap-version "0.2.1") - -(def ^:private mobdap-urls - {:linux {:x86 "https://github.com/atomicptr/mobdap/releases/download/v%s/mobdap-linux-amd64.tar.gz" - :arm "https://github.com/atomicptr/mobdap/releases/download/v%s/mobdap-linux-arm64.tar.gz"} - :mac {:x86 "https://github.com/atomicptr/mobdap/releases/download/v%s/mobdap-macos-amd64.tar.gz" - :arm "https://github.com/atomicptr/mobdap/releases/download/v%s/mobdap-macos-arm64.tar.gz"} - :windows {:x86 "https://github.com/atomicptr/mobdap/releases/download/v%s/mobdap-windows-amd64.zip"}}) - -(defn executable-path [] - (if (windows?) - (data-dir "defold.nvim" "bin" "mobdap.exe") - (data-dir "defold.nvim" "bin" "mobdap"))) - -(defn- version-file-path [] - (data-dir "defold.nvim" ".meta" "mobdap.version")) - -(defn- installed-version [] - (let [path (version-file-path)] - (fs/create-dirs (fs/parent path)) - - (when (fs/exists? path) - (slurp path)))) - -(defn- install-mobdap [] - (let [download-url (format (get-os-arch-value mobdap-urls) mobdap-version) - mobdap-file (download-and-unpack download-url) - exec-path (executable-path)] - (fs/create-dirs (fs/parent exec-path)) - (fs/delete-if-exists exec-path) - - (fs/move mobdap-file exec-path) - (spit (version-file-path) mobdap-version) - (log/debug "mobdap: Installed version:" mobdap-version) - - (when-not (windows?) - (fs/set-posix-file-permissions exec-path "rwxr-xr-x")))) - -(defn setup [] - (when (get-os-arch-value mobdap-urls) - (log/debug "mobdap: Currently installed version:" (installed-version)) - - (cond - (not (fs/exists? (executable-path))) (install-mobdap) - (not= (installed-version) mobdap-version) (install-mobdap) - :else (log/debug "mobdap: Nothing to do...")) - - (log/debug "mobdap: Installed at" (executable-path)))) - diff --git a/src/defold/main.clj b/src/defold/main.clj index c521175..74b92e6 100644 --- a/src/defold/main.clj +++ b/src/defold/main.clj @@ -2,9 +2,7 @@ (:require [babashka.fs :as fs] [cheshire.core :as json] - [defold.debugger :as debugger] [defold.logging :as logging] - [defold.neovide :as neovide] [defold.project :as project] [taoensso.timbre :as log])) @@ -28,17 +26,7 @@ type)) (defmethod run :setup [_ config-file] - (try - (let [conf (parse-config config-file)] - (when-not (get-in conf ["plugin_config" "debugger" "custom_executable"]) - (debugger/setup)) - (when (and (= "neovide" (get-in conf ["plugin_config" "launcher" "type"])) - (not (get-in conf ["plugin_config" "launcher" "executable"]))) - (neovide/setup)) - (print-json {:status 200})) - (catch Throwable t - (log/error "Error:" (ex-message t) t) - (print-json {:status 500 :error (ex-message t)})))) + (print-json {:status 200})) (defmethod run :install-dependencies ([_ _ game-project] @@ -49,12 +37,6 @@ (defmethod run :list-dependency-dirs [_ _ game-project] (print-json (project/list-dependency-dirs game-project))) -(defmethod run :mobdap-path [_ _] - (let [path (debugger/executable-path)] - (if (fs/exists? path) - (print-json {:status 200 :mobdap_path path}) - (print-json {:status 500 :error (str "Could not find file: " path)})))) - (defn run-wrapped [& args] (try (apply run args) diff --git a/src/defold/neovide.clj b/src/defold/neovide.clj deleted file mode 100644 index 2eccc1d..0000000 --- a/src/defold/neovide.clj +++ /dev/null @@ -1,53 +0,0 @@ -(ns defold.neovide - (:require - [babashka.fs :as fs] - [defold.utils :refer [data-dir download-and-unpack get-os-arch-value - windows?]] - [taoensso.timbre :as log])) - -(def ^:private neovide-version "0.15.1") - -(def ^:private neovide-urls - ; TODO: add macos support, sadly neovide only offers the executables as .dmg (and then .app) files making the installation kinda cumbersome - {:linux {:x86 "https://github.com/neovide/neovide/releases/download/%s/neovide-linux-x86_64.tar.gz"} - :windows {:x86 "https://github.com/neovide/neovide/releases/download/%s/neovide.exe.zip"}}) - -(defn executable-path [] - (if (windows?) - (data-dir "defold.nvim" "bin" "neovide.exe") - (data-dir "defold.nvim" "bin" "neovide"))) - -(defn- version-file-path [] - (data-dir "defold.nvim" ".meta" "neovide.version")) - -(defn- installed-version [] - (let [path (version-file-path)] - (fs/create-dirs (fs/parent path)) - - (when (fs/exists? path) - (slurp path)))) - -(defn- install-neovide [] - (let [download-url (format (get-os-arch-value neovide-urls) neovide-version) - mobdap-file (download-and-unpack download-url) - exec-path (executable-path)] - (fs/create-dirs (fs/parent exec-path)) - (fs/delete-if-exists exec-path) - - (fs/move mobdap-file exec-path) - (spit (version-file-path) neovide-version) - (log/debug "neovide: Installed version:" neovide-version) - - (when-not (windows?) - (fs/set-posix-file-permissions exec-path "rwxr-xr-x")))) - -(defn setup [] - (when (get-os-arch-value neovide-urls) - (log/debug "neovide: Currently installed version:" (installed-version)) - - (cond - (not (fs/exists? (executable-path))) (install-neovide) - (not= (installed-version) neovide-version) (install-neovide) - :else (log/debug "neovide: Nothing to do....")) - - (log/debug "neovide: Installed at" (executable-path)))) From 7672cb2caa96f23170adf377f5511f93822f4bda Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Wed, 17 Dec 2025 15:34:31 +0100 Subject: [PATCH 12/70] move .cargo/config.toml to crates/sidecar --- {.cargo => crates/sidecar/.cargo}/config.toml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename {.cargo => crates/sidecar/.cargo}/config.toml (100%) diff --git a/.cargo/config.toml b/crates/sidecar/.cargo/config.toml similarity index 100% rename from .cargo/config.toml rename to crates/sidecar/.cargo/config.toml From 1f305467d68559476a785619066320c07c589282 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Wed, 17 Dec 2025 16:21:00 +0100 Subject: [PATCH 13/70] cache editor port to improve editor interaction performance drastically --- crates/bridge/src/main.rs | 4 ++-- crates/core/src/cache.rs | 19 +++++++++---------- crates/core/src/github.rs | 11 ++++++----- crates/core/src/lib.rs | 2 +- crates/sidecar/src/editor.rs | 36 ++++++++++++++++++++++++++++++------ crates/sidecar/src/lib.rs | 17 ++++++++++++----- 6 files changed, 60 insertions(+), 29 deletions(-) diff --git a/crates/bridge/src/main.rs b/crates/bridge/src/main.rs index 5a08288..b62dcd7 100644 --- a/crates/bridge/src/main.rs +++ b/crates/bridge/src/main.rs @@ -7,7 +7,7 @@ use defold_nvim_core::{ mobdap, neovide, }; use tracing::Level; -use tracing_appender::rolling::daily; +use tracing_appender::rolling::never; use tracing_subscriber::fmt::writer::MakeWriterExt; mod launcher; @@ -62,7 +62,7 @@ fn main() -> Result<()> { fs::create_dir_all(&logs)?; let (stdout, _stdout_guard) = tracing_appender::non_blocking(io::stdout()); - let (logfile, _logfile_guard) = tracing_appender::non_blocking(daily(logs, "bridge")); + let (logfile, _logfile_guard) = tracing_appender::non_blocking(never(logs, "bridge.log")); let writer = stdout.and(logfile); diff --git a/crates/core/src/cache.rs b/crates/core/src/cache.rs index 73abed0..9e475c7 100644 --- a/crates/core/src/cache.rs +++ b/crates/core/src/cache.rs @@ -21,25 +21,24 @@ pub fn get(key: &str) -> Option { let path = dir.join(sha3(key)); + tracing::debug!("Cache Key {key} -> {path:?}"); + if path.exists() { let modified = path.metadata().ok()?.modified().ok()?; - if modified.elapsed().ok()? < Duration::from_secs(3600) { + if modified.elapsed().ok()? < Duration::from_hours(1) { return fs::read_to_string(&path).ok(); - } else { - fs::remove_file(&path).ok(); } + + fs::remove_file(&path).ok(); } None } -pub fn set(key: &str, value: &str) { - let Ok(dir) = cache_dir() else { - tracing::error!("Could not get cache dir"); - return; - }; +pub fn set(key: &str, value: &str) -> Result<()> { + let path = cache_dir()?.join(sha3(key)); + fs::write(path, value)?; - let path = dir.join(sha3(key)); - fs::write(path, value).ok(); + Ok(()) } diff --git a/crates/core/src/github.rs b/crates/core/src/github.rs index 8dece10..7a3f835 100644 --- a/crates/core/src/github.rs +++ b/crates/core/src/github.rs @@ -28,17 +28,18 @@ pub fn fetch_release(owner: &str, repo: &str) -> Result { let url = format!("https://api.github.com/repos/{owner}/{repo}/releases/latest"); if let Some(str) = cache::get(&url) - && let Ok(res) = serde_json::from_str(&str) { - tracing::debug!("Serving {url} from cache"); - return Ok(res); - } + && let Ok(res) = serde_json::from_str(&str) + { + tracing::debug!("Serving {url} from cache"); + return Ok(res); + } let client = reqwest::blocking::Client::new(); let res = client.get(&url).header("User-Agent", USER_AGENT).send()?; let release: Release = res.json()?; - cache::set(&url, &serde_json::to_string(&release)?); + cache::set(&url, &serde_json::to_string(&release)?)?; Ok(release) } diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 9bee90c..e6f7c41 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -1,4 +1,4 @@ -mod cache; +pub mod cache; pub mod focus; pub mod game_project; pub mod github; diff --git a/crates/sidecar/src/editor.rs b/crates/sidecar/src/editor.rs index 8dc1c65..56e03fc 100644 --- a/crates/sidecar/src/editor.rs +++ b/crates/sidecar/src/editor.rs @@ -1,6 +1,7 @@ use std::collections::HashMap; use anyhow::{Context, Result, bail}; +use defold_nvim_core::cache; use netstat2::{AddressFamilyFlags, ProtocolFlags, ProtocolSocketInfo, iterate_sockets_info}; use sysinfo::System; @@ -12,6 +13,16 @@ fn command_url(port: u16, command: Option) -> String { } pub fn find_port() -> Option { + const CACHE: &str = "editor-port"; + + if let Some(port) = cache::get(CACHE) + && let Some(port) = port.parse::().ok() + { + return Some(port); + } + + tracing::debug!("Looking for editor port..."); + let sys = System::new_all(); for (pid, proc) in sys.processes() { @@ -31,8 +42,21 @@ pub fn find_port() -> Option { continue; } - for port in ports.unwrap() { - if is_editor(port) { + let ports = ports.unwrap(); + if ports.is_empty() { + continue; + } + + tracing::debug!("Pid {} has ports: {:?}", pid.as_u32(), ports); + + for port in ports { + if is_editor_port(port) { + tracing::debug!("Found editor at {port}"); + + if let Err(err) = cache::set(CACHE, &port.to_string()) { + tracing::error!("Could not cache editor key because: {err:?}"); + } + return Some(port); } } @@ -65,7 +89,7 @@ fn ports_for_pid(pid: u32) -> Result> { Ok(ports) } -fn is_editor(port: u16) -> bool { +pub fn is_editor_port(port: u16) -> bool { reqwest::blocking::Client::new() .head(command_url(port, None)) .send() @@ -85,16 +109,16 @@ pub fn list_commands(port: Option) -> Result> { bail!("could not list commands, status: {:?}", res.status()); } - let content = res.text()?.to_string(); + let content = res.text()?.clone(); serde_json::from_str(&content).map_err(anyhow::Error::from) } -pub fn send_command(port: Option, cmd: String) -> Result<()> { +pub fn send_command(port: Option, cmd: &str) -> Result<()> { let url = command_url( port.or_else(find_port) .context("could not determine editor port")?, - Some(cmd.clone()), + Some(cmd.to_string()), ); let res = reqwest::blocking::Client::new().post(url).send()?; diff --git a/crates/sidecar/src/lib.rs b/crates/sidecar/src/lib.rs index 12eb44c..283a0fc 100644 --- a/crates/sidecar/src/lib.rs +++ b/crates/sidecar/src/lib.rs @@ -9,7 +9,6 @@ use std::{ sync::OnceLock, }; use tracing::Level; -use tracing_appender::rolling::daily; mod editor; mod editor_config; @@ -26,17 +25,25 @@ fn defold_nvim_sidecar(lua: &Lua) -> LuaResult { fs::create_dir_all(&logs).expect("could not create logs dir"); - let rolling = daily(logs, "sidecar"); - tracing_subscriber::fmt() .with_ansi(false) .with_file(true) .with_line_number(true) .with_max_level(Level::DEBUG) - .with_writer(rolling) + .with_writer(tracing_appender::rolling::never(logs, "sidecar.log")) .init(); }); + match register_exports(lua) { + Ok(exports) => Ok(exports), + Err(err) => { + tracing::error!("{lua:?}"); + Err(err) + } + } +} + +fn register_exports(lua: &Lua) -> LuaResult { let exports = lua.create_table()?; exports.set("version", lua.create_string(env!("CARGO_PKG_VERSION"))?)?; @@ -73,7 +80,7 @@ fn list_commands(lua: &Lua, port: Option) -> LuaResult { } fn send_command(_lua: &Lua, (port, cmd): (Option, String)) -> LuaResult<()> { - editor::send_command(port, cmd)?; + editor::send_command(port, &cmd)?; Ok(()) } From d76e47d2012c7ff153a58e118d0a97a5523ee492 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Fri, 19 Dec 2025 09:13:20 +0100 Subject: [PATCH 14/70] remove intermediate lua -> runner json config file --- crates/bridge/src/launcher.rs | 98 ++++++--------------- crates/bridge/src/main.rs | 43 +++++++-- crates/bridge/src/plugin_config.rs | 36 +++----- crates/sidecar/Cargo.toml | 1 + crates/sidecar/assets/run_linux.sh | 2 +- crates/sidecar/assets/run_macos.sh | 2 +- crates/sidecar/assets/run_windows.bat | 2 +- crates/sidecar/src/editor_config.rs | 122 +++++++++++++++++++++----- crates/sidecar/src/lib.rs | 11 ++- lua/defold/init.lua | 18 +--- lua/defold/service/babashka.lua | 6 -- lua/defold/sidecar.lua | 2 +- src/defold/main.clj | 13 +-- 13 files changed, 193 insertions(+), 163 deletions(-) diff --git a/crates/bridge/src/launcher.rs b/crates/bridge/src/launcher.rs index cd6b5ee..1285707 100644 --- a/crates/bridge/src/launcher.rs +++ b/crates/bridge/src/launcher.rs @@ -1,18 +1,16 @@ use std::{ - fs::{self, File}, - io::BufReader, + fs::{self}, path::{Path, PathBuf}, process::Command, }; use anyhow::{Context, Result, bail}; use defold_nvim_core::{focus::focus_neovim, utils::classname}; -use serde::Deserialize; use which::which; use crate::{ neovide, - plugin_config::{LauncherConfig, LauncherType, PluginConfig, SocketType}, + plugin_config::{LauncherType, PluginConfig, SocketType}, utils::{self, is_port_in_use}, }; @@ -23,21 +21,6 @@ const VAR_CLASSNAME: &str = "{CLASSNAME}"; const VAR_ADDRESS: &str = "{ADDR}"; const VAR_REMOTE_CMD: &str = "{REMOTE_CMD}"; -#[derive(Debug, Deserialize)] -pub struct LaunchConfig { - // pub data_dir: PathBuf, - pub plugin_config: PluginConfig, -} - -impl LaunchConfig { - pub fn from_file(path: PathBuf) -> Result { - let file = File::open(path)?; - let reader = BufReader::new(file); - let res = serde_json::from_reader(reader)?; - Ok(res) - } -} - #[derive(Debug)] struct Launcher(PathBuf, Vec); @@ -77,12 +60,11 @@ const DEFAULT_TERMINALS: [(&str, &str, &str); 5] = [ ]; fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { - match cfg.launcher.as_ref().and_then(|cfg| cfg.launcher_type) { + match cfg.launcher_type { Some(LauncherType::Neovide) => { let executable = &cfg - .launcher + .executable .as_ref() - .and_then(|exe| exe.executable.clone()) .map(Into::into) .or_else(|| which("neovide").ok()) .or_else(|| match neovide::update_or_install() { @@ -100,13 +82,9 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { let mut args = Vec::new(); - if let Some(extra_args) = cfg - .launcher - .as_ref() - .map(|cfg| cfg.extra_arguments.clone().unwrap_or_default()) - { + if let Some(extra_args) = &cfg.extra_arguments { for extra_arg in extra_args { - args.push(extra_arg); + args.push(extra_arg.clone()); } } @@ -132,53 +110,35 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { Ok(Launcher(executable.clone(), args)) } Some(LauncherType::Terminal) => { - let executable: Option = cfg - .launcher - .as_ref() - .and_then(|launcher| launcher.executable.clone()) - .map(Into::into); + let executable: Option = cfg.executable.clone().map(Into::into); let executable = if let Some(exe) = executable && exe.exists() { - let class_arg = cfg - .launcher - .as_ref() - .and_then(|l| l.terminal.clone()) - .and_then(|term| term.class_argument.clone()); - - let run_arg = cfg - .launcher - .as_ref() - .and_then(|l| l.terminal.clone()) - .and_then(|term| term.run_argument.clone()); - + let class_arg = &cfg.terminal_class_argument; + let run_arg = &cfg.terminal_run_argument; let mut args = Vec::new(); - if let Some(extra_args) = cfg - .launcher - .as_ref() - .map(|cfg| cfg.extra_arguments.clone().unwrap_or_default()) - { + if let Some(extra_args) = &cfg.extra_arguments { for extra_arg in extra_args { - args.push(extra_arg); + args.push(extra_arg.clone()); } } if let Some(class_arg) = class_arg { if class_arg.ends_with('=') { - args.push(class_arg + VAR_CLASSNAME); + args.push(class_arg.clone() + VAR_CLASSNAME); } else { - args.push(class_arg); + args.push(class_arg.clone()); args.push(VAR_CLASSNAME.to_string()); } } if let Some(run_arg) = run_arg { if run_arg.ends_with('=') { - args.push(run_arg + nvim); + args.push(run_arg.clone() + nvim); } else { - args.push(run_arg); + args.push(run_arg.clone()); args.push(nvim.clone()); } } @@ -196,18 +156,14 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { .or_else(|| { let mut args = Vec::new(); - if let Some(extra_args) = cfg - .launcher - .as_ref() - .map(|cfg| cfg.extra_arguments.clone().unwrap_or_default()) - { + if let Some(extra_args) = &cfg.extra_arguments { for extra_arg in extra_args { - args.push(extra_arg); + args.push(extra_arg.clone()); } } // executable specifies only the name of which terminal we want to use - if let Some(exe_name) = cfg.launcher.as_ref().and_then(|cfg| cfg.executable.clone()) + if let Some(exe_name) = &cfg.executable && let Some((name, class_arg, run_arg)) = DEFAULT_TERMINALS .iter() .find(|(name, _, _)| *name == exe_name) @@ -271,10 +227,8 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { None => { if let Ok(launcher) = create_launcher( &PluginConfig { - launcher: cfg.launcher.as_ref().map(|launcher| LauncherConfig { - launcher_type: Some(LauncherType::Neovide), - ..launcher.clone() - }), + launcher_type: Some(LauncherType::Neovide), + ..cfg.clone() }, nvim, ) { @@ -283,10 +237,8 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { if let Ok(launcher) = create_launcher( &PluginConfig { - launcher: cfg.launcher.as_ref().map(|launcher| LauncherConfig { - launcher_type: Some(LauncherType::Terminal), - ..launcher.clone() - }), + launcher_type: Some(LauncherType::Terminal), + ..cfg.clone() }, nvim, ) { @@ -395,7 +347,7 @@ fn run_netsock(launcher: Launcher, nvim: &str, root_dir: &Path, remote_cmd: &str } pub fn run( - config: LaunchConfig, + plugin_config: PluginConfig, root_dir: PathBuf, file: &Path, line: Option, @@ -405,7 +357,7 @@ pub fn run( .context("could not convert nvim path to string")? .to_string(); - let launcher = create_launcher(&config.plugin_config, &nvim)?; + let launcher = create_launcher(&plugin_config, &nvim)?; let launcher = if cfg!(target_os = "linux") { launcher.apply_var( @@ -435,7 +387,7 @@ pub fn run( let launcher = launcher.apply_var(VAR_REMOTE_CMD, &remote_cmd.clone()); - match config.plugin_config.launcher.and_then(|l| l.socket_type) { + match plugin_config.socket_type { Some(SocketType::Fsock) => run_fsock(launcher, &nvim, &root_dir, &remote_cmd)?, Some(SocketType::Netsock) => run_netsock(launcher, &nvim, &root_dir, &remote_cmd)?, None => { diff --git a/crates/bridge/src/main.rs b/crates/bridge/src/main.rs index b62dcd7..bb8243b 100644 --- a/crates/bridge/src/main.rs +++ b/crates/bridge/src/main.rs @@ -10,6 +10,8 @@ use tracing::Level; use tracing_appender::rolling::never; use tracing_subscriber::fmt::writer::MakeWriterExt; +use crate::plugin_config::{LauncherType, PluginConfig, SocketType}; + mod launcher; mod plugin_config; mod utils; @@ -25,16 +27,31 @@ struct Args { enum Commands { /// Open a file in Neovim or launch a new instance LaunchNeovim { - #[clap(value_name = "LAUNCH_CONFIG", index = 1)] - launch_config: String, + #[arg(long = "launcher-type")] + launcher_type: Option, + + #[arg(long = "socket-type")] + socket_type: Option, + + #[arg(long = "executable")] + executable: Option, + + #[arg(long = "extra-arguments", value_delimiter = ' ', num_args = 0..)] + extra_arguments: Option>, - #[clap(value_name = "GAME_ROOT_DIR", index = 2)] + #[arg(long = "terminal-class-argument")] + terminal_class_argument: Option, + + #[arg(long = "terminal-run-argument")] + terminal_run_argument: Option, + + #[clap(value_name = "GAME_ROOT_DIR", index = 1)] game_root_dir: String, - #[clap(value_name = "FILE", index = 3)] + #[clap(value_name = "FILE", index = 2)] file: String, - #[clap(value_name = "LINE", index = 4)] + #[clap(value_name = "LINE", index = 3)] line: Option, }, /// Focus the currently open instance of Neovim @@ -82,12 +99,24 @@ fn main() -> Result<()> { match args.cmd { Commands::LaunchNeovim { - launch_config, + launcher_type, + socket_type, + executable, + extra_arguments, + terminal_class_argument, + terminal_run_argument, game_root_dir, file, line, } => launcher::run( - launcher::LaunchConfig::from_file(absolute(launch_config)?)?, + PluginConfig { + launcher_type, + socket_type, + executable, + extra_arguments, + terminal_class_argument, + terminal_run_argument, + }, absolute(game_root_dir)?, &absolute(file)?, line, diff --git a/crates/bridge/src/plugin_config.rs b/crates/bridge/src/plugin_config.rs index 3d4b235..f750d57 100644 --- a/crates/bridge/src/plugin_config.rs +++ b/crates/bridge/src/plugin_config.rs @@ -1,41 +1,27 @@ -use serde::Deserialize; - -#[derive(Debug, Deserialize, Clone, Copy)] +#[derive(Debug, clap::ValueEnum, Clone, Copy)] pub enum LauncherType { - #[serde(rename = "neovide")] + #[clap(name = "neovide")] Neovide, - #[serde(rename = "terminal")] + #[clap(name = "terminal")] Terminal, } -#[derive(Debug, Deserialize, Clone, Copy)] +#[derive(Debug, clap::ValueEnum, Clone, Copy)] pub enum SocketType { - #[serde(rename = "fsock")] + #[clap(name = "fsock")] Fsock, - #[serde(rename = "netsock")] + #[clap(name = "netsock")] Netsock, } -#[derive(Debug, Deserialize, Clone)] -pub struct LauncherConfig { - #[serde(rename = "type")] +#[derive(Debug, Clone)] +pub struct PluginConfig { pub launcher_type: Option, - + pub socket_type: Option, pub executable: Option, pub extra_arguments: Option>, - pub terminal: Option, - pub socket_type: Option, -} - -#[derive(Debug, Deserialize, Clone)] -pub struct TerminalConfig { - pub class_argument: Option, - pub run_argument: Option, -} - -#[derive(Debug, Deserialize, Clone)] -pub struct PluginConfig { - pub launcher: Option, + pub terminal_class_argument: Option, + pub terminal_run_argument: Option, } diff --git a/crates/sidecar/Cargo.toml b/crates/sidecar/Cargo.toml index 2dfe6b1..cf66934 100644 --- a/crates/sidecar/Cargo.toml +++ b/crates/sidecar/Cargo.toml @@ -20,6 +20,7 @@ mlua = { version = "0.11.5", features = [ ] } netstat2 = "0.11.2" reqwest = { version = "0.12.24", features = ["blocking", "json"] } +serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.145" sysinfo = "0.37.2" tracing = "0.1.43" diff --git a/crates/sidecar/assets/run_linux.sh b/crates/sidecar/assets/run_linux.sh index 19bec0d..622c0a7 100644 --- a/crates/sidecar/assets/run_linux.sh +++ b/crates/sidecar/assets/run_linux.sh @@ -1,2 +1,2 @@ #!/usr/bin/env bash -{BRIDGE_PATH} launch-neovim "{LAUNCH_CONFIG}" "$(realpath .)" "$1" $2 +{BRIDGE_PATH} launch-neovim {LAUNCH_ARGS} "$(realpath .)" "$1" $2 diff --git a/crates/sidecar/assets/run_macos.sh b/crates/sidecar/assets/run_macos.sh index 29d77ff..e1570a5 100644 --- a/crates/sidecar/assets/run_macos.sh +++ b/crates/sidecar/assets/run_macos.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash export PATH="/usr/bin:/usr/local/bin:$PATH" -{BRIDGE_PATH} launch-neovim "{LAUNCH_CONFIG}" "$(realpath .)" "$1" $2 +{BRIDGE_PATH} launch-neovim {LAUNCH_ARGS} "$(realpath .)" "$1" $2 diff --git a/crates/sidecar/assets/run_windows.bat b/crates/sidecar/assets/run_windows.bat index b0261b4..f880f52 100644 --- a/crates/sidecar/assets/run_windows.bat +++ b/crates/sidecar/assets/run_windows.bat @@ -1,2 +1,2 @@ @echo off -{BRIDGE_PATH} launch-neovim "{LAUNCH_CONFIG}" "%s" "%%CD%%" "%%~1" %%2 +{BRIDGE_PATH} launch-neovim {LAUNCH_ARGS} "%s" "%%CD%%" "%%~1" %%2 diff --git a/crates/sidecar/src/editor_config.rs b/crates/sidecar/src/editor_config.rs index 7f12511..73bb292 100644 --- a/crates/sidecar/src/editor_config.rs +++ b/crates/sidecar/src/editor_config.rs @@ -1,6 +1,91 @@ use anyhow::{Context, Result, bail}; use edn_rs::Edn; -use std::{fs, path::PathBuf, str::FromStr}; +use serde::Deserialize; +use std::{ + fs, + path::{Path, PathBuf}, + str::FromStr, +}; + +#[derive(Debug, Deserialize)] +pub enum LauncherType { + #[serde(rename = "neovide")] + Neovide, + + #[serde(rename = "terminal")] + Terminal, +} + +#[derive(Debug, Deserialize)] +pub enum SocketType { + #[serde(rename = "fsock")] + Fsock, + + #[serde(rename = "netsock")] + Netsock, +} + +#[derive(Debug, Deserialize)] +pub struct TerminalLauncherSettings { + pub class_argument: Option, + pub run_argument: Option, +} + +#[derive(Debug, Deserialize)] +pub struct LauncherSettings { + #[serde(rename = "type")] + pub launcher_type: LauncherType, + pub executable: Option, + pub socket_type: Option, + pub extra_arguments: Option>, + pub terminal: Option, +} + +impl LauncherSettings { + pub fn bridge_cli_args(&self) -> Vec { + let mut args = Vec::new(); + + args.push("--launcher-type".to_string()); + args.push(match self.launcher_type { + LauncherType::Neovide => "neovide".to_string(), + LauncherType::Terminal => "terminal".to_string(), + }); + + if let Some(socket_type) = &self.socket_type { + args.push("--socket-type".to_string()); + args.push(match socket_type { + SocketType::Fsock => "fsock".to_string(), + SocketType::Netsock => "netsock".to_string(), + }); + } + + if let Some(executable) = &self.executable { + args.push("--executable".to_string()); + args.push(executable.clone()); + } + + if let Some(extra_args) = &self.extra_arguments { + args.push("--extra-arguments".to_string()); + for arg in extra_args { + args.push(arg.clone()); + } + } + + if let Some(terminal) = &self.terminal { + if let Some(class_arg) = &terminal.class_argument { + args.push("--terminal-class-argument".to_string()); + args.push(class_arg.clone()); + } + + if let Some(run_arg) = &terminal.run_argument { + args.push("--terminal-run-argument".to_string()); + args.push(run_arg.clone()); + } + } + + args + } +} #[cfg(target_os = "linux")] const RUN_SCRIPT: &str = include_str!("../assets/run_linux.sh"); @@ -23,8 +108,8 @@ const EXE_SUFFIX: &'static str = ".exe"; #[cfg(not(target_os = "windows"))] const EXE_SUFFIX: &str = ""; -fn find_bridge_path(plugin_root: PathBuf) -> Result { - let exe = format!("defold-nvim-bridge{}", EXE_SUFFIX); +fn find_bridge_path(plugin_root: &Path) -> Result { + let exe = format!("defold-nvim-bridge{EXE_SUFFIX}"); if plugin_root.exists() { let candidates = [ @@ -39,18 +124,21 @@ fn find_bridge_path(plugin_root: PathBuf) -> Result { } // TODO: if not lets download it - panic!("not yet implemented!") + bail!("not yet implemented") } -fn create_runner_script(plugin_root: PathBuf, launch_config: PathBuf) -> Result { +fn create_runner_script( + plugin_root: &Path, + launcher_settings: &LauncherSettings, +) -> Result { let dir = dirs::data_dir() .context("could not get data dir")? .join("defold.nvim"); fs::create_dir_all(&dir)?; let script_path = dir.join(format!("run.{SCRIPT_EXT}")); - let bridge_path = find_bridge_path(plugin_root)?; + let launch_args = launcher_settings.bridge_cli_args().join(" "); fs::write( &script_path, @@ -61,12 +149,7 @@ fn create_runner_script(plugin_root: PathBuf, launch_config: PathBuf) -> Result< .to_str() .context("could not convert bridge path")?, ) - .replace( - "{LAUNCH_CONFIG}", - launch_config - .to_str() - .context("could not convert launch config")?, - ), + .replace("{LAUNCH_ARGS}", &launch_args), )?; #[cfg(not(target_os = "windows"))] @@ -78,20 +161,19 @@ fn create_runner_script(plugin_root: PathBuf, launch_config: PathBuf) -> Result< Ok(script_path) } -pub fn set_default_editor(plugin_root: PathBuf, launch_config: PathBuf) -> Result<()> { +pub fn set_default_editor(plugin_root: &Path, launcher_settings: &LauncherSettings) -> Result<()> { if !plugin_root.exists() { - bail!("plugin root '{plugin_root:?}' could not be found"); - } - - if !launch_config.exists() { - bail!("launch config path '{launch_config:?}' could not be found"); + bail!("plugin root '{}' could not be found", plugin_root.display()); } let config_dir = dirs::config_dir().context("could not find config dir")?; let path = config_dir.join("Defold").join("prefs.editor_settings"); if !path.exists() { - bail!("prefs.editor_settings file {path:?} could not be found"); + bail!( + "prefs.editor_settings file {} could not be found", + path.display() + ); } let data = fs::read_to_string(&path)?; @@ -99,7 +181,7 @@ pub fn set_default_editor(plugin_root: PathBuf, launch_config: PathBuf) -> Resul let mut config = Edn::from_str(&data).map_err(|err| anyhow::anyhow!(err.to_string()))?; config[":code"][":custom-editor"] = Edn::Str( - create_runner_script(plugin_root, launch_config)? + create_runner_script(plugin_root, launcher_settings)? .to_str() .context("could not convert path to string")? .to_string(), diff --git a/crates/sidecar/src/lib.rs b/crates/sidecar/src/lib.rs index 283a0fc..ed0fb25 100644 --- a/crates/sidecar/src/lib.rs +++ b/crates/sidecar/src/lib.rs @@ -3,9 +3,10 @@ use defold_nvim_core::mobdap; use defold_nvim_core::{focus, game_project::GameProject}; use mlua::Value; use mlua::prelude::*; +use std::path::PathBuf; use std::{ fs::{self}, - path::{PathBuf, absolute}, + path::absolute, sync::OnceLock, }; use tracing::Level; @@ -85,8 +86,12 @@ fn send_command(_lua: &Lua, (port, cmd): (Option, String)) -> LuaResult<()> Ok(()) } -fn set_default_editor(_lua: &Lua, (plugin_root, launch_config): (String, String)) -> LuaResult<()> { - editor_config::set_default_editor(PathBuf::from(plugin_root), PathBuf::from(launch_config))?; +fn set_default_editor( + lua: &Lua, + (plugin_root, launcher_settings): (String, LuaValue), +) -> LuaResult<()> { + let launcher_settings = lua.from_value(launcher_settings)?; + editor_config::set_default_editor(&PathBuf::from(plugin_root), &launcher_settings)?; Ok(()) } diff --git a/lua/defold/init.lua b/lua/defold/init.lua index 56c683c..11a9a4f 100644 --- a/lua/defold/init.lua +++ b/lua/defold/init.lua @@ -100,11 +100,6 @@ function M.plugin_root() return os.plugin_root() end ----@return string -function M.launch_config() - return vim.fs.joinpath(vim.fn.stdpath "data", "defold.nvim", "config.json") -end - ---@param opts DefoldNvimConfig|nil function M.setup(opts) local babashka = require "defold.service.babashka" @@ -114,20 +109,11 @@ function M.setup(opts) -- TODO: check if sidecar is available, if not download it (which shouldnt be necessary with some pkg managers) - -- persist config for launcher - vim.fn.writefile({ - vim.fn.json_encode { - data_dir = vim.fn.stdpath "data", - bb_path = babashka.bb_path(), - plugin_config = M.config, - }, - }, M.launch_config()) - -- add setup defold command vim.api.nvim_create_user_command("SetupDefold", function() local sidecar = require "defold.sidecar" - local ok, err = pcall(sidecar.set_default_editor, M.plugin_root(), M.launch_config()) + local ok, err = pcall(sidecar.set_default_editor, M.plugin_root(), M.config.launcher) if not ok then log.error(string.format("Could not set default editor because: %s", err)) end @@ -180,7 +166,7 @@ function M.setup(opts) if M.config.defold.set_default_editor then local sidecar = require "defold.sidecar" - local ok, err = pcall(sidecar.set_default_editor, M.plugin_root(), M.launch_config()) + local ok, err = pcall(sidecar.set_default_editor, M.plugin_root(), M.config.launcher) if not ok then log.error(string.format("Could not set default editor because: %s", err)) diff --git a/lua/defold/service/babashka.lua b/lua/defold/service/babashka.lua index 8320833..b40a644 100644 --- a/lua/defold/service/babashka.lua +++ b/lua/defold/service/babashka.lua @@ -109,10 +109,6 @@ function M.bb_edn_path() return vim.fs.joinpath(plugin_root, "bb.edn") end -function M.config_path() - return vim.fs.joinpath(vim.fn.stdpath "data", "defold.nvim", "config.json") -end - ---@param custom_executable string|nil ---@return boolean function M.setup(custom_executable) @@ -140,8 +136,6 @@ function M.run_task(task, args) local args_to_send = {} - table.insert(args_to_send, M.config_path()) - for _, arg in ipairs(args or {}) do table.insert(args_to_send, arg) end diff --git a/lua/defold/sidecar.lua b/lua/defold/sidecar.lua index d9fc018..0178e2a 100644 --- a/lua/defold/sidecar.lua +++ b/lua/defold/sidecar.lua @@ -57,7 +57,7 @@ package.cpath = package.cpath ---@field find_editor_port function(): integer ---@field list_commands function(port: integer|nil): table ---@field send_command function(port: integer|nil, cmd: string) ----@field set_default_editor function(plugin_root: string, launch_config: string) +---@field set_default_editor function(plugin_root: string, launcher_config: LauncherSettings) ---@field focus_neovim function(game_root: string) ---@field focus_game function(game_root: string) ---@field mobdap_install function(): string diff --git a/src/defold/main.clj b/src/defold/main.clj index 74b92e6..b60592e 100644 --- a/src/defold/main.clj +++ b/src/defold/main.clj @@ -1,6 +1,5 @@ (ns defold.main (:require - [babashka.fs :as fs] [cheshire.core :as json] [defold.logging :as logging] [defold.project :as project] @@ -15,26 +14,22 @@ (defn- print-json [m] (print (json/generate-string m))) -(defn parse-config [path] - (assert (fs/exists? path) (format "assert that '%s' exists" path)) - (json/parse-string (slurp path))) - (defmulti run (fn [type & args] (setup-logging! type) (log/info (format "Run command '%s' with args: %s" type args)) type)) -(defmethod run :setup [_ config-file] +(defmethod run :setup [_] (print-json {:status 200})) (defmethod run :install-dependencies - ([_ _ game-project] + ([_ game-project] (print-json (project/install-dependencies game-project false))) - ([_ _ game-project force-redownload] + ([_ game-project force-redownload] (print-json (project/install-dependencies game-project force-redownload)))) -(defmethod run :list-dependency-dirs [_ _ game-project] +(defmethod run :list-dependency-dirs [_ game-project] (print-json (project/list-dependency-dirs game-project))) (defn run-wrapped [& args] From 4796617e88c86346bf5e26bdf0b7a60184f4b7c1 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Fri, 19 Dec 2025 09:22:29 +0100 Subject: [PATCH 15/70] add editor port validation to lua --- crates/sidecar/src/lib.rs | 6 ++++++ lua/defold/init.lua | 7 +++---- lua/defold/sidecar.lua | 1 + 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/crates/sidecar/src/lib.rs b/crates/sidecar/src/lib.rs index ed0fb25..88b37b3 100644 --- a/crates/sidecar/src/lib.rs +++ b/crates/sidecar/src/lib.rs @@ -50,6 +50,7 @@ fn register_exports(lua: &Lua) -> LuaResult { exports.set("version", lua.create_string(env!("CARGO_PKG_VERSION"))?)?; exports.set("read_game_project", lua.create_function(read_game_project)?)?; exports.set("find_editor_port", lua.create_function(find_editor_port)?)?; + exports.set("is_editor_port", lua.create_function(is_editor_port)?)?; exports.set("list_commands", lua.create_function(list_commands)?)?; exports.set("send_command", lua.create_function(send_command)?)?; exports.set( @@ -74,6 +75,11 @@ fn find_editor_port(_lua: &Lua, _: ()) -> LuaResult { Ok(port) } +#[allow(clippy::unnecessary_wraps)] +fn is_editor_port(_lua: &Lua, port: u16) -> LuaResult { + Ok(editor::is_editor_port(port)) +} + fn list_commands(lua: &Lua, port: Option) -> LuaResult { let commands = editor::list_commands(port)?; diff --git a/lua/defold/init.lua b/lua/defold/init.lua index 11a9a4f..866e57e 100644 --- a/lua/defold/init.lua +++ b/lua/defold/init.lua @@ -190,13 +190,12 @@ end ---@return integer function M.editor_port() - if M.prev_editor_port then - -- TODO: validate port + local sidecar = require "defold.sidecar" + + if M.prev_editor_port and sidecar.is_editor_port(M.prev_editor_port) then return M.prev_editor_port end - local sidecar = require "defold.sidecar" - local ok, res = pcall(sidecar.find_editor_port) if ok then diff --git a/lua/defold/sidecar.lua b/lua/defold/sidecar.lua index 0178e2a..c486421 100644 --- a/lua/defold/sidecar.lua +++ b/lua/defold/sidecar.lua @@ -55,6 +55,7 @@ package.cpath = package.cpath ---@field sha3 function(input: string): string ---@field read_game_project function(path: string): GameProject ---@field find_editor_port function(): integer +---@field is_editor_port function(port: integer): boolean ---@field list_commands function(port: integer|nil): table ---@field send_command function(port: integer|nil, cmd: string) ---@field set_default_editor function(plugin_root: string, launcher_config: LauncherSettings) From 8c958b2b57956bd70baf6c9129aa3c8ffe99924a Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Fri, 19 Dec 2025 09:55:21 +0100 Subject: [PATCH 16/70] add debug settings --- crates/bridge/src/main.rs | 14 ++++++--- crates/sidecar/src/editor_config.rs | 7 +++++ crates/sidecar/src/lib.rs | 45 +++++++++++++++++++++++++---- lua/defold/init.lua | 11 ++++++- lua/defold/sidecar.lua | 2 +- 5 files changed, 67 insertions(+), 12 deletions(-) diff --git a/crates/bridge/src/main.rs b/crates/bridge/src/main.rs index bb8243b..1c9f823 100644 --- a/crates/bridge/src/main.rs +++ b/crates/bridge/src/main.rs @@ -19,6 +19,9 @@ mod utils; #[derive(Parser, Debug)] // #[command(version)] struct Args { + #[arg(long = "debug")] + debug: bool, + #[command(subcommand)] cmd: Commands, } @@ -71,6 +74,8 @@ enum Commands { } fn main() -> Result<()> { + let args = Args::parse(); + let logs = dirs::cache_dir() .context("could not get cache dir")? .join("defold.nvim") @@ -86,15 +91,16 @@ fn main() -> Result<()> { tracing_subscriber::fmt() .with_file(true) .with_line_number(true) - .with_max_level(Level::DEBUG) + .with_max_level(if args.debug { + Level::DEBUG + } else { + Level::INFO + }) .with_writer(writer) .init(); tracing::info!("Starting defold.nvim bridge",); tracing::debug!("CLI: {}", env::args().collect::>().join(" ")); - - let args = Args::parse(); - tracing::debug!("Clap: {args:?}"); match args.cmd { diff --git a/crates/sidecar/src/editor_config.rs b/crates/sidecar/src/editor_config.rs index 73bb292..d2dcaae 100644 --- a/crates/sidecar/src/editor_config.rs +++ b/crates/sidecar/src/editor_config.rs @@ -39,12 +39,19 @@ pub struct LauncherSettings { pub socket_type: Option, pub extra_arguments: Option>, pub terminal: Option, + pub debug: Option, } impl LauncherSettings { pub fn bridge_cli_args(&self) -> Vec { let mut args = Vec::new(); + if let Some(debug) = self.debug + && debug + { + args.push("--debug".to_string()); + } + args.push("--launcher-type".to_string()); args.push(match self.launcher_type { LauncherType::Neovide => "neovide".to_string(), diff --git a/crates/sidecar/src/lib.rs b/crates/sidecar/src/lib.rs index 88b37b3..f8ce652 100644 --- a/crates/sidecar/src/lib.rs +++ b/crates/sidecar/src/lib.rs @@ -9,29 +9,42 @@ use std::{ path::absolute, sync::OnceLock, }; -use tracing::Level; +use tracing::level_filters::LevelFilter; +use tracing_subscriber::layer::SubscriberExt; +use tracing_subscriber::reload; +use tracing_subscriber::util::SubscriberInitExt; mod editor; mod editor_config; static LOG_INIT: OnceLock<()> = OnceLock::new(); +static LOG_RELOAD_HANDLE: OnceLock> = + OnceLock::new(); #[mlua::lua_module] fn defold_nvim_sidecar(lua: &Lua) -> LuaResult { LOG_INIT.get_or_init(|| { - let logs = dirs::cache_dir() + let log_dir = dirs::cache_dir() .expect("could not get cache dir") .join("defold.nvim") .join("logs"); - fs::create_dir_all(&logs).expect("could not create logs dir"); + fs::create_dir_all(&log_dir).expect("could not create logs dir"); - tracing_subscriber::fmt() + let (filter, handle) = reload::Layer::new(LevelFilter::INFO); + LOG_RELOAD_HANDLE + .set(handle) + .expect("Logger handle already initialized"); + + let file_layer = tracing_subscriber::fmt::layer() .with_ansi(false) .with_file(true) .with_line_number(true) - .with_max_level(Level::DEBUG) - .with_writer(tracing_appender::rolling::never(logs, "sidecar.log")) + .with_writer(tracing_appender::rolling::never(log_dir, "sidecar.log")); + + tracing_subscriber::registry() + .with(filter) + .with(file_layer) .init(); }); @@ -48,6 +61,7 @@ fn register_exports(lua: &Lua) -> LuaResult { let exports = lua.create_table()?; exports.set("version", lua.create_string(env!("CARGO_PKG_VERSION"))?)?; + exports.set("set_log_level", lua.create_function(set_log_level)?)?; exports.set("read_game_project", lua.create_function(read_game_project)?)?; exports.set("find_editor_port", lua.create_function(find_editor_port)?)?; exports.set("is_editor_port", lua.create_function(is_editor_port)?)?; @@ -64,6 +78,25 @@ fn register_exports(lua: &Lua) -> LuaResult { Ok(exports) } +fn set_log_level(_lua: &Lua, level: String) -> LuaResult<()> { + let new_filter = match level.to_lowercase().as_str() { + "debug" => LevelFilter::DEBUG, + "info" => LevelFilter::INFO, + "error" => LevelFilter::ERROR, + _ => LevelFilter::INFO, + }; + + let handle = LOG_RELOAD_HANDLE + .get() + .context("could not get log handle")?; + + handle + .modify(|f| *f = new_filter) + .map_err(anyhow::Error::from)?; + + Ok(()) +} + fn read_game_project(lua: &Lua, path: String) -> LuaResult { let game_project = GameProject::load_from_path(path.into())?; let val = lua.to_value(&game_project)?; diff --git a/lua/defold/init.lua b/lua/defold/init.lua index 866e57e..a7a85a8 100644 --- a/lua/defold/init.lua +++ b/lua/defold/init.lua @@ -11,6 +11,7 @@ local root_markers = { "game.project" } ---@field socket_type "fsock"|"netsock"|nil Run Neovims RPC protocol over file socket or network. Nil means it will be picked automatic (fsock on Unix, network on Windows) ---@field extra_arguments table|nil Extra arguments passed to the `executable` (or neovide) ---@field terminal TerminalLauncherSettings|nil Settings for running via terminal +---@field debug boolean|nil Enable debug settings for the bridge cli ---@class TerminalLauncherSettings ---@field class_argument string|nil Argument to define the class name, usually something like "--class=" @@ -35,6 +36,7 @@ local root_markers = { "game.project" } ---@field babashka BabashkaSettings Settings for the integrated Babashka interpreter ---@field keymaps table|nil Settings for key -> action mappings ---@field force_plugin_enabled boolean Force the plugin to be always enabled (even if we can't find the game.project file) +---@field debug boolean Enable debug settings for the plugin ---@type DefoldNvimConfig local default_config = { @@ -69,6 +71,7 @@ local default_config = { }, force_plugin_enabled = false, + debug = false, } local M = {} @@ -109,10 +112,16 @@ function M.setup(opts) -- TODO: check if sidecar is available, if not download it (which shouldnt be necessary with some pkg managers) + if M.config.debug then + M.config.launcher.debug = true + + local sidecar = require "defold.sidecar" + sidecar.set_log_level "debug" + end + -- add setup defold command vim.api.nvim_create_user_command("SetupDefold", function() local sidecar = require "defold.sidecar" - local ok, err = pcall(sidecar.set_default_editor, M.plugin_root(), M.config.launcher) if not ok then log.error(string.format("Could not set default editor because: %s", err)) diff --git a/lua/defold/sidecar.lua b/lua/defold/sidecar.lua index c486421..ff1edf8 100644 --- a/lua/defold/sidecar.lua +++ b/lua/defold/sidecar.lua @@ -52,7 +52,7 @@ package.cpath = package.cpath ---@class Sidecar ---@field version string ----@field sha3 function(input: string): string +---@field set_log_level function(level: "debug"|"info"|"error") ---@field read_game_project function(path: string): GameProject ---@field find_editor_port function(): integer ---@field is_editor_port function(port: integer): boolean From 14f9997e4bea0ea4d127b3b7b161e809817efc82 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Fri, 19 Dec 2025 11:01:40 +0100 Subject: [PATCH 17/70] add ability to download releases by arbitrary match functions and add clear downloads func --- crates/core/src/github.rs | 36 +++++++++++++++++++++++++++++++++--- 1 file changed, 33 insertions(+), 3 deletions(-) diff --git a/crates/core/src/github.rs b/crates/core/src/github.rs index 7a3f835..99706bb 100644 --- a/crates/core/src/github.rs +++ b/crates/core/src/github.rs @@ -44,7 +44,28 @@ pub fn fetch_release(owner: &str, repo: &str) -> Result { Ok(release) } -pub fn download_release(owner: &str, repo: &str, name: &str) -> Result<(PathBuf, Release)> { +pub fn clear_downloads(owner: &str, repo: &str) -> Result<()> { + let temp = temp_dir() + .join("defold.nvim") + .join("download") + .join(owner) + .join(repo); + + tracing::debug!("Deleting {}...", &temp.display()); + + fs::remove_dir_all(temp)?; + + Ok(()) +} + +pub fn download_release_matching( + owner: &str, + repo: &str, + matching: F, +) -> Result<(PathBuf, Release)> +where + F: Fn(&Asset) -> bool, +{ let temp = temp_dir() .join("defold.nvim") .join("download") @@ -54,12 +75,16 @@ pub fn download_release(owner: &str, repo: &str, name: &str) -> Result<(PathBuf, let release = fetch_release(owner, repo)?; - let Some(asset) = release.assets.iter().find(|asset| asset.name == name) else { - bail!("Could not find asset {name} at {owner}/{repo}"); + let Some(asset) = release.assets.iter().find(|asset| matching(asset)) else { + bail!("Could not find asset for {owner}/{repo}"); }; let download_file = temp.join(&asset.name); + if download_file.exists() { + return Ok((download_file, release)); + } + let mut res = reqwest::blocking::get(&asset.browser_download_url)?; res.error_for_status_ref()?; @@ -68,3 +93,8 @@ pub fn download_release(owner: &str, repo: &str, name: &str) -> Result<(PathBuf, Ok((download_file, release)) } + +pub fn download_release(owner: &str, repo: &str, name: &str) -> Result<(PathBuf, Release)> { + download_release_matching(owner, repo, |asset| asset.name == name) + .map_err(|_| anyhow::anyhow!("Could not find asset {name} for {owner}/{repo}")) +} From 2e0e76db50a63cabba99f6af8ed62ff7aa34a762 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Fri, 19 Dec 2025 11:03:15 +0100 Subject: [PATCH 18/70] remove temp files for neovide and mobdap after download --- crates/core/src/mobdap.rs | 2 ++ crates/core/src/neovide.rs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/crates/core/src/mobdap.rs b/crates/core/src/mobdap.rs index cc9199c..af9e330 100644 --- a/crates/core/src/mobdap.rs +++ b/crates/core/src/mobdap.rs @@ -157,5 +157,7 @@ pub fn update_or_install() -> Result { fs::write(version_path()?, release.tag_name)?; + github::clear_downloads(OWNER, REPOSITORY)?; + path() } diff --git a/crates/core/src/neovide.rs b/crates/core/src/neovide.rs index 39335c6..4895129 100644 --- a/crates/core/src/neovide.rs +++ b/crates/core/src/neovide.rs @@ -158,5 +158,7 @@ pub fn update_or_install() -> Result { fs::write(version_path()?, release.tag_name)?; + github::clear_downloads(OWNER, REPOSITORY)?; + path() } From d0d346ff8cb8c309cd8e5e2e73ad0c18326e4514 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Fri, 19 Dec 2025 11:04:14 +0100 Subject: [PATCH 19/70] add ability to download defold annotations --- crates/bridge/src/main.rs | 17 +++- crates/core/src/defold_annotations.rs | 120 ++++++++++++++++++++++++++ crates/core/src/lib.rs | 2 + crates/core/src/project.rs | 47 ++++++++++ crates/sidecar/src/lib.rs | 16 +++- lua/defold/sidecar.lua | 1 + 6 files changed, 200 insertions(+), 3 deletions(-) create mode 100644 crates/core/src/defold_annotations.rs create mode 100644 crates/core/src/project.rs diff --git a/crates/bridge/src/main.rs b/crates/bridge/src/main.rs index 1c9f823..217e229 100644 --- a/crates/bridge/src/main.rs +++ b/crates/bridge/src/main.rs @@ -4,7 +4,7 @@ use anyhow::{Context, Result}; use clap::{Parser, Subcommand, command}; use defold_nvim_core::{ focus::{focus_game, focus_neovim}, - mobdap, neovide, + mobdap, neovide, project, }; use tracing::Level; use tracing_appender::rolling::never; @@ -71,6 +71,14 @@ enum Commands { DownloadNeovide, /// Downloads Mobdap Debugger DownloadMobdap, + /// Install dependencies + InstallDependencies { + #[clap(long = "force-redownload")] + force_redownload: bool, + + #[clap(value_name = "GAME_ROOT_DIR", index = 1)] + game_root_dir: String, + }, } fn main() -> Result<()> { @@ -137,6 +145,13 @@ fn main() -> Result<()> { let path = mobdap::update_or_install()?; tracing::info!("Installed mobdap at {path:?}"); } + Commands::InstallDependencies { + force_redownload, + game_root_dir, + } => { + project::install_dependencies(&absolute(game_root_dir)?, force_redownload)?; + tracing::info!("Installed dependencies"); + } } Ok(()) diff --git a/crates/core/src/defold_annotations.rs b/crates/core/src/defold_annotations.rs new file mode 100644 index 0000000..4ed24dc --- /dev/null +++ b/crates/core/src/defold_annotations.rs @@ -0,0 +1,120 @@ +use std::{ + fs::{self, File}, + path::PathBuf, + time::Duration, +}; + +use anyhow::{Context, Result, bail}; +use version_compare::Version; +use zip::ZipArchive; + +use crate::{github, project}; + +const OWNER: &str = "astrochili"; +const REPOSITORY: &str = "defold-annotations"; + +pub fn dir() -> Result { + let dir = project::deps_root()?.join("defold"); + fs::create_dir_all(&dir)?; + Ok(dir) +} + +fn version_path() -> Result { + let dir = dirs::state_dir() + .context("could not get state dir")? + .join("defold.nvim") + .join("meta"); + + fs::create_dir_all(&dir)?; + + Ok(dir.join("defold_annotations_version")) +} + +fn version() -> Result { + let file = version_path()?; + + if !file.exists() { + bail!("Version not found"); + } + + Ok(fs::read_to_string(file)?) +} + +fn is_update_available() -> Result { + let defold_dir = dir()?; + + if !defold_dir.exists() { + return Ok(true); + } + + if version_path()?.exists() { + // if the version file is younger than a week dont bother + let last_modified = version_path()?.metadata()?.modified()?; + if last_modified.elapsed()? < Duration::from_hours(24 * 7) { + return Ok(false); + } + } + + let Ok(v) = version() else { + return Ok(true); + }; + + // re-write the file again so that we only check once a week + fs::write(version_path()?, &v)?; + + tracing::debug!("Defold Annotations Version {v} installed"); + + let Some(installed) = Version::from(&v) else { + return Ok(true); + }; + + let release = github::fetch_release(OWNER, REPOSITORY)?; + + tracing::debug!("Defold Annotations Version {} is newest", release.tag_name); + + let Some(current) = Version::from(&release.tag_name) else { + return Ok(false); + }; + + Ok(current > installed) +} + +pub fn install() -> Result<()> { + if !is_update_available()? { + return Ok(()); + } + + tracing::info!("Updating Defold annotations..."); + + let defold_dir = dir()?; + fs::remove_dir_all(&defold_dir)?; + + let (download_path, release) = github::download_release_matching(OWNER, REPOSITORY, |asset| { + asset.name.starts_with("defold_api_") + })?; + + let parent_dir = download_path.parent().context("could not get parent dir")?; + + tracing::info!("Found version {}", release.tag_name); + + let file = File::open(&download_path)?; + + let mut archive = ZipArchive::new(file)?; + archive.extract(parent_dir)?; + + let defold_api_dir = parent_dir.join("defold_api"); + + if !defold_api_dir.exists() { + bail!( + "Defold Annotations could not be found after downloading at {}", + defold_api_dir.display() + ); + } + + fs::rename(defold_api_dir, defold_dir)?; + fs::write(version_path()?, release.tag_name)?; + + github::clear_downloads(OWNER, REPOSITORY)?; + + Ok(()) +} diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index e6f7c41..a6753ed 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -1,7 +1,9 @@ pub mod cache; +pub mod defold_annotations; pub mod focus; pub mod game_project; pub mod github; pub mod mobdap; pub mod neovide; +pub mod project; pub mod utils; diff --git a/crates/core/src/project.rs b/crates/core/src/project.rs new file mode 100644 index 0000000..68f8e68 --- /dev/null +++ b/crates/core/src/project.rs @@ -0,0 +1,47 @@ +use std::{ + fs, + path::{Path, PathBuf}, +}; + +use crate::{defold_annotations, game_project::GameProject, utils::sha3}; +use anyhow::{Context, Result, bail}; + +pub fn deps_root() -> Result { + let dir = dirs::state_dir() + .context("could not get state dir")? + .join("defold.nvim") + .join("deps"); + fs::create_dir_all(&dir)?; + Ok(dir) +} + +fn deps_dir(game_root: &Path) -> Result { + let hash = sha3(&game_root.display().to_string()); + let ident = hash.get(..8).context("could not create hash")?; + let dir = deps_root()?.join(ident); + fs::create_dir_all(&dir)?; + Ok(dir) +} + +pub fn install_dependencies(game_root: &Path, force_redownload: bool) -> Result<()> { + let game_project_path = game_root.join("game.project"); + + if !game_project_path.exists() { + bail!( + "Could not find game.project file at {}", + game_project_path.display() + ); + } + + defold_annotations::install()?; + + // TODO: delete dependencies if force redownload is set + // TODO: download dependencies + // TODO: check for unused dependencies + + // let game_project = GameProject::load_from_path(game_project_path); + // println!("{game_project:?}"); + // println!("{:?}", deps_dir(game_root)); + + Ok(()) +} diff --git a/crates/sidecar/src/lib.rs b/crates/sidecar/src/lib.rs index f8ce652..f1332ad 100644 --- a/crates/sidecar/src/lib.rs +++ b/crates/sidecar/src/lib.rs @@ -1,6 +1,6 @@ use anyhow::Context; -use defold_nvim_core::mobdap; use defold_nvim_core::{focus, game_project::GameProject}; +use defold_nvim_core::{mobdap, project}; use mlua::Value; use mlua::prelude::*; use std::path::PathBuf; @@ -74,14 +74,18 @@ fn register_exports(lua: &Lua) -> LuaResult { exports.set("focus_neovim", lua.create_function(focus_neovim)?)?; exports.set("focus_game", lua.create_function(focus_game)?)?; exports.set("mobdap_install", lua.create_function(mobdap_install)?)?; + exports.set( + "install_dependencies", + lua.create_function(install_dependencies)?, + )?; Ok(exports) } +#[allow(clippy::needless_pass_by_value)] fn set_log_level(_lua: &Lua, level: String) -> LuaResult<()> { let new_filter = match level.to_lowercase().as_str() { "debug" => LevelFilter::DEBUG, - "info" => LevelFilter::INFO, "error" => LevelFilter::ERROR, _ => LevelFilter::INFO, }; @@ -154,3 +158,11 @@ fn mobdap_install(_lua: &Lua, _: ()) -> LuaResult { .context("could not convert path to string")? .to_string()) } + +fn install_dependencies( + _lua: &Lua, + (game_root, force_redownload): (String, Option), +) -> LuaResult<()> { + project::install_dependencies(&absolute(game_root)?, force_redownload.unwrap_or_default())?; + Ok(()) +} diff --git a/lua/defold/sidecar.lua b/lua/defold/sidecar.lua index ff1edf8..e55e8c9 100644 --- a/lua/defold/sidecar.lua +++ b/lua/defold/sidecar.lua @@ -62,6 +62,7 @@ package.cpath = package.cpath ---@field focus_neovim function(game_root: string) ---@field focus_game function(game_root: string) ---@field mobdap_install function(): string +---@field install_dependencies function(game_root: string, force_redownload: boolean|nil) ---@type Sidecar local rust_plugin = require "defold_nvim_sidecar" From 598f78937698fb30ba7704647d79f6834f073f1b Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Fri, 19 Dec 2025 13:31:23 +0100 Subject: [PATCH 20/70] add project dependency management (without .script_api support) --- .cljfmt.edn | 2 - .github/workflows/update-defold-api.yml | 28 ---- bb.edn | 7 - crates/bridge/Cargo.toml | 2 - crates/bridge/src/main.rs | 20 ++- crates/core/Cargo.toml | 2 + crates/core/src/defold_annotations.rs | 5 +- crates/core/src/focus.rs | 2 +- crates/core/src/game_project.rs | 24 ++- crates/core/src/github.rs | 11 +- crates/core/src/project.rs | 199 ++++++++++++++++++++++-- crates/core/src/utils.rs | 64 ++++++++ crates/sidecar/src/lib.rs | 19 ++- lua/defold/config/lsp.lua | 8 +- lua/defold/init.lua | 13 -- lua/defold/project.lua | 39 ++--- lua/defold/service/babashka.lua | 153 ------------------ lua/defold/service/os.lua | 1 - lua/defold/sidecar.lua | 1 + scripts/update-defold-api.clj | 58 ------- src/defold/logging.clj | 19 --- src/defold/main.clj | 39 ----- src/defold/mini.clj | 170 -------------------- src/defold/project.clj | 116 -------------- src/defold/utils.clj | 136 ---------------- 25 files changed, 334 insertions(+), 804 deletions(-) delete mode 100644 .cljfmt.edn delete mode 100644 .github/workflows/update-defold-api.yml delete mode 100644 bb.edn delete mode 100644 lua/defold/service/babashka.lua delete mode 100755 scripts/update-defold-api.clj delete mode 100644 src/defold/logging.clj delete mode 100644 src/defold/main.clj delete mode 100644 src/defold/mini.clj delete mode 100644 src/defold/project.clj delete mode 100644 src/defold/utils.clj diff --git a/.cljfmt.edn b/.cljfmt.edn deleted file mode 100644 index c261f94..0000000 --- a/.cljfmt.edn +++ /dev/null @@ -1,2 +0,0 @@ -{:indent-line-comments? true - :sort-ns-references? true} diff --git a/.github/workflows/update-defold-api.yml b/.github/workflows/update-defold-api.yml deleted file mode 100644 index bb2e55b..0000000 --- a/.github/workflows/update-defold-api.yml +++ /dev/null @@ -1,28 +0,0 @@ -on: - schedule: - - cron: "0 0 * * *" - workflow_dispatch: {} - -name: Update Defold API - -jobs: - update-defold-api: - name: Update Defold API - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: just-sultanov/setup-babashka@v2 - with: - version: '1.12.206' - - id: update-api - run: | - echo "DEFOLD_API_VERSION=$(bb scripts/update-defold-api.clj)" >> $GITHUB_OUTPUT - - name: Create Pull Request - uses: peter-evans/create-pull-request@v7 - with: - title: "Updated Defold API to ${{ steps.update-api.outputs.DEFOLD_API_VERSION }}" - commit-message: "Updated Defold API ${{ steps.update-api.outputs.DEFOLD_API_VERSION }}" - branch: "update/defold-api" - delete-branch: true - assignees: atomicptr - diff --git a/bb.edn b/bb.edn deleted file mode 100644 index 809da70..0000000 --- a/bb.edn +++ /dev/null @@ -1,7 +0,0 @@ -{:min-bb-version "1.12.0" - :paths ["src"] - :tasks {:requires ([defold.main :as defold]) - setup {:task (apply defold/run-wrapped :setup *command-line-args*)} - install-dependencies {:task (apply defold/run-wrapped :install-dependencies *command-line-args*)} - list-dependency-dirs {:task (apply defold/run-wrapped :list-dependency-dirs *command-line-args*)} - mobdap-path {:task (apply defold/run-wrapped :mobdap-path *command-line-args*)}}} diff --git a/crates/bridge/Cargo.toml b/crates/bridge/Cargo.toml index 547ac6a..67cb3de 100644 --- a/crates/bridge/Cargo.toml +++ b/crates/bridge/Cargo.toml @@ -9,8 +9,6 @@ clap = { version = "4.5.53", features = ["derive"] } defold-nvim-core = { path = "../core" } dirs = "6.0.0" netstat2 = "0.11.2" -serde = { version = "1.0.228", features = ["derive"] } -serde_json = "1.0.145" tracing = "0.1.43" tracing-appender = "0.2.4" tracing-subscriber = "0.3.22" diff --git a/crates/bridge/src/main.rs b/crates/bridge/src/main.rs index 217e229..f28ba91 100644 --- a/crates/bridge/src/main.rs +++ b/crates/bridge/src/main.rs @@ -71,7 +71,7 @@ enum Commands { DownloadNeovide, /// Downloads Mobdap Debugger DownloadMobdap, - /// Install dependencies + /// Install dependencies for game InstallDependencies { #[clap(long = "force-redownload")] force_redownload: bool, @@ -79,6 +79,11 @@ enum Commands { #[clap(value_name = "GAME_ROOT_DIR", index = 1)] game_root_dir: String, }, + /// List dependencies of game + ListDependencies { + #[clap(value_name = "GAME_ROOT_DIR", index = 1)] + game_root_dir: String, + }, } fn main() -> Result<()> { @@ -149,8 +154,17 @@ fn main() -> Result<()> { force_redownload, game_root_dir, } => { - project::install_dependencies(&absolute(game_root_dir)?, force_redownload)?; - tracing::info!("Installed dependencies"); + let root_dir = absolute(&game_root_dir)?; + + project::install_dependencies(&root_dir, force_redownload)?; + tracing::info!("Finished installing dependencies for {game_root_dir}",); + } + Commands::ListDependencies { game_root_dir } => { + let root_dir = absolute(&game_root_dir)?; + + for dir in project::list_dependency_dirs(&root_dir)? { + tracing::info!("{}", dir.display()); + } } } diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index aeb0073..ada5ee0 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -18,3 +18,5 @@ sha3 = "0.10.8" strum = { version = "0.27.2", features = ["derive"] } tracing = "0.1.43" which = "8.0.0" +url = "2.5.7" +walkdir = "2.5.0" diff --git a/crates/core/src/defold_annotations.rs b/crates/core/src/defold_annotations.rs index 4ed24dc..080830b 100644 --- a/crates/core/src/defold_annotations.rs +++ b/crates/core/src/defold_annotations.rs @@ -15,7 +15,6 @@ const REPOSITORY: &str = "defold-annotations"; pub fn dir() -> Result { let dir = project::deps_root()?.join("defold"); - fs::create_dir_all(&dir)?; Ok(dir) } @@ -87,7 +86,9 @@ pub fn install() -> Result<()> { tracing::info!("Updating Defold annotations..."); let defold_dir = dir()?; - fs::remove_dir_all(&defold_dir)?; + if defold_dir.exists() { + fs::remove_dir_all(&defold_dir)?; + } let (download_path, release) = github::download_release_matching(OWNER, REPOSITORY, |asset| { asset.name.starts_with("defold_api_") diff --git a/crates/core/src/focus.rs b/crates/core/src/focus.rs index 660c331..b208ab5 100644 --- a/crates/core/src/focus.rs +++ b/crates/core/src/focus.rs @@ -178,7 +178,7 @@ pub fn focus_game(root_dir: PathBuf) -> Result<()> { bail!("Could not find game.project file in {root_dir:?}: Not a valid Defold directory"); } - let game_project = GameProject::load_from_path(root_dir.join("game.project"))?; + let game_project = GameProject::load_from_path(&root_dir.join("game.project"))?; if cfg!(target_os = "linux") { return switch(SwitcherType::Title(game_project.title)); diff --git a/crates/core/src/game_project.rs b/crates/core/src/game_project.rs index 176059a..8351150 100644 --- a/crates/core/src/game_project.rs +++ b/crates/core/src/game_project.rs @@ -1,19 +1,25 @@ -use std::{fs, path::PathBuf}; +use std::{fs, path::Path}; use anyhow::{Context, Result, bail}; use ini::Ini; use serde::Serialize; +#[derive(Debug, Serialize)] +pub struct Library { + pub include_dirs: Vec, +} + #[derive(Debug, Serialize)] pub struct GameProject { pub title: String, pub dependencies: Vec, + pub library: Option, } impl GameProject { - pub fn load_from_path(path: PathBuf) -> Result { + pub fn load_from_path(path: &Path) -> Result { if !path.exists() { - bail!("game.project file {path:?} could not be found"); + bail!("game.project file {} could not be found", path.display()); } GameProject::try_from(fs::read_to_string(path)?) @@ -41,9 +47,21 @@ impl TryFrom for GameProject { .map(|(_, v)| v.to_string()) .collect(); + let mut library = None; + + if let Some(library_section) = proj.section(Some("library")) { + library = Some(Library { + include_dirs: library_section + .get("include_dirs") + .map(|s| s.split(',').map(|s| s.trim().to_string()).collect()) + .unwrap_or_default(), + }); + } + Ok(GameProject { title, dependencies, + library, }) } } diff --git a/crates/core/src/github.rs b/crates/core/src/github.rs index 99706bb..6a6db91 100644 --- a/crates/core/src/github.rs +++ b/crates/core/src/github.rs @@ -1,14 +1,13 @@ use std::{ env::temp_dir, - fs::{self, File}, - io, + fs::{self}, path::PathBuf, }; use anyhow::{Result, bail}; use serde::{Deserialize, Serialize}; -use crate::cache; +use crate::{cache, utils}; const USER_AGENT: &str = "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36"; @@ -85,11 +84,7 @@ where return Ok((download_file, release)); } - let mut res = reqwest::blocking::get(&asset.browser_download_url)?; - res.error_for_status_ref()?; - - let mut file = File::create(&download_file)?; - io::copy(&mut res, &mut file)?; + utils::download_to(&asset.browser_download_url, &download_file)?; Ok((download_file, release)) } diff --git a/crates/core/src/project.rs b/crates/core/src/project.rs index 68f8e68..2126e7c 100644 --- a/crates/core/src/project.rs +++ b/crates/core/src/project.rs @@ -1,10 +1,22 @@ use std::{ - fs, + fs::{self, File}, path::{Path, PathBuf}, }; -use crate::{defold_annotations, game_project::GameProject, utils::sha3}; +use crate::{ + defold_annotations, + game_project::GameProject, + utils::{self, sha3}, +}; use anyhow::{Context, Result, bail}; +use walkdir::WalkDir; +use zip::ZipArchive; + +fn ident(string: &str) -> Result { + let hash = sha3(string); + let ident_str = hash.get(..8).context("could not create hash")?; + Ok(ident_str.to_string()) +} pub fn deps_root() -> Result { let dir = dirs::state_dir() @@ -16,13 +28,37 @@ pub fn deps_root() -> Result { } fn deps_dir(game_root: &Path) -> Result { - let hash = sha3(&game_root.display().to_string()); - let ident = hash.get(..8).context("could not create hash")?; - let dir = deps_root()?.join(ident); + let dir = deps_root()? + .join("project") + .join(ident(&game_root.display().to_string())?); fs::create_dir_all(&dir)?; Ok(dir) } +pub fn list_dependency_dirs(game_root: &Path) -> Result> { + let mut deps = Vec::new(); + + if let Ok(annotations) = defold_annotations::dir() + && annotations.exists() + { + deps.push(annotations); + } + + if let Ok(dep_dirs) = deps_dir(game_root) { + for entry in fs::read_dir(dep_dirs)? { + let path = entry?.path(); + + if !path.is_dir() { + continue; + } + + deps.push(path); + } + } + + Ok(deps) +} + pub fn install_dependencies(game_root: &Path, force_redownload: bool) -> Result<()> { let game_project_path = game_root.join("game.project"); @@ -35,13 +71,154 @@ pub fn install_dependencies(game_root: &Path, force_redownload: bool) -> Result< defold_annotations::install()?; - // TODO: delete dependencies if force redownload is set - // TODO: download dependencies - // TODO: check for unused dependencies + let proj_deps_dir = deps_dir(game_root)?; + + if force_redownload { + fs::remove_dir_all(&proj_deps_dir)?; + } + + let game_project = GameProject::load_from_path(&game_project_path)?; + + for dep_url in &game_project.dependencies { + if let Err(err) = install_dependency(dep_url, &proj_deps_dir) { + tracing::error!("Could not download dependency {dep_url} because: {err:?}"); + } + } + + // delete unused dirs + let dep_dirs = fs::read_dir(&proj_deps_dir)? + .filter_map(Result::ok) + .map(|f| f.path()) + .filter(|f| f.is_dir()); + + 'outer: for dir in dep_dirs { + let Some(name) = dir + .file_name() + .and_then(|s| s.to_str()) + .map(std::string::ToString::to_string) + else { + continue; + }; + + for dep in &game_project.dependencies { + let dep_ident = ident(dep)?; + + if name == dep_ident { + continue 'outer; + } + } + + tracing::debug!("Removing unused dependency dir {}", dir.display()); + fs::remove_dir_all(dir)?; + } + + utils::delete_empty_dirs_from(game_root)?; + + Ok(()) +} + +fn install_dependency(url: &str, project_deps_dir: &Path) -> Result<()> { + let target_dir = project_deps_dir.join(ident(url)?); + + if target_dir.exists() { + tracing::debug!("Dependency {url} does already exist, skipping..."); + return Ok(()); + } - // let game_project = GameProject::load_from_path(game_project_path); - // println!("{game_project:?}"); - // println!("{:?}", deps_dir(game_root)); + tracing::info!("Downloading {url} to {}...", target_dir.display()); + + let downloaded_file = utils::download(url)?; + + tracing::debug!("Downloaded to {}", downloaded_file.display()); + + let parent_dir = downloaded_file + .parent() + .context("could not get parent dir of downloaded file")?; + + let file = File::open(&downloaded_file)?; + let mut archive = ZipArchive::new(file)?; + archive.extract(parent_dir)?; + + tracing::debug!("Extracted to {}", parent_dir.display()); + + let game_project_file = find_game_project(parent_dir)?; + let game_root = game_project_file + .parent() + .context("could not get parent dir of game.project")?; + + let game_project = GameProject::load_from_path(&game_project_file)?; + + let Some(library) = &game_project.library else { + utils::clear_download(url)?; + bail!("Dependency {url} does not contain key library.include_dirs"); + }; + + if library.include_dirs.is_empty() { + utils::clear_download(url)?; + bail!("Dependency {url} does not contain any library.include_dirs"); + } + + for include_dir in &library.include_dirs { + let include_dir_path = game_root.join(include_dir); + let include_dir_target = target_dir.join(include_dir); + + if !include_dir_path.exists() { + tracing::warn!( + "Dependency {url} has specified include dir {include_dir} but it doesn't actually exist at {}. Skipping...", + include_dir_path.display() + ); + continue; + } + + // TODO: compile script api files + + copy_files(&include_dir_path, &include_dir_target, "lua")?; + } + + utils::clear_download(url)?; + + Ok(()) +} + +fn find_game_project(root_dir: &Path) -> Result { + for file in WalkDir::new(root_dir) + .into_iter() + .filter_map(Result::ok) + .map(walkdir::DirEntry::into_path) + .filter(|p| p.is_file()) + { + if let Some(filename) = file.file_name().and_then(|s| s.to_str()) + && filename == "game.project" + { + tracing::debug!("Found game.project at {}", file.display()); + return Ok(file); + } + } + + bail!("Could not find game.project file in {}", root_dir.display()); +} + +fn find_files_with_ext(root_dir: &Path, ext: &str) -> Vec { + WalkDir::new(root_dir) + .into_iter() + .filter_map(Result::ok) + .filter(|e| e.file_type().is_file()) + .filter(|e| e.path().extension().and_then(|s| s.to_str()) == Some(ext)) + .map(|e| e.path().to_owned()) + .collect() +} + +fn copy_files(from_dir: &Path, to_dir: &Path, ext: &str) -> Result<()> { + for file in find_files_with_ext(from_dir, ext) { + let relative_path = file.strip_prefix(from_dir)?; + let target_path = to_dir.join(relative_path); + let target_parent = target_path.parent().context("could not get path parent")?; + + fs::create_dir_all(target_parent)?; + + tracing::debug!("Copying {} to {}", file.display(), target_path.display()); + fs::copy(file, target_path)?; + } Ok(()) } diff --git a/crates/core/src/utils.rs b/crates/core/src/utils.rs index b476b64..ee28ae2 100644 --- a/crates/core/src/utils.rs +++ b/crates/core/src/utils.rs @@ -1,5 +1,14 @@ +use std::{ + env::temp_dir, + fs::{self, File}, + io, + path::{Path, PathBuf}, +}; + use anyhow::{Context, Result}; use sha3::{Digest, Sha3_256}; +use url::Url; +use walkdir::WalkDir; pub fn sha3(str: &str) -> String { let mut hasher = Sha3_256::new(); @@ -19,3 +28,58 @@ pub fn project_id(root_dir: &str) -> Result { pub fn classname(root_dir: &str) -> Result { Ok(format!("com.defold.nvim.{}", project_id(root_dir)?)) } + +pub fn download(url: &str) -> Result { + let download_dir = temp_dir() + .join("defold.nvim") + .join("download") + .join(sha3(url).get(..8).context("could not make hash")?); + fs::create_dir_all(&download_dir)?; + + let parsed_url = Url::parse(url)?; + + let filename = parsed_url + .path_segments() + .and_then(std::iter::Iterator::last) + .unwrap_or("file"); + + let download_file = download_dir.join(filename); + + download_to(url, &download_file)?; + + Ok(download_file) +} + +pub fn clear_download(url: &str) -> Result<()> { + let download_root_dir = temp_dir().join("defold.nvim").join("download"); + let download_dir = download_root_dir.join(sha3(url).get(..8).context("could not make hash")?); + fs::remove_dir_all(download_dir)?; + delete_empty_dirs_from(&download_root_dir)?; + Ok(()) +} + +pub fn download_to(url: &str, path: &Path) -> Result<()> { + tracing::debug!("Downloading {url} to {}...", path.display()); + + let mut res = reqwest::blocking::get(url)?; + res.error_for_status_ref()?; + + let mut file = File::create(path)?; + io::copy(&mut res, &mut file)?; + Ok(()) +} + +pub fn delete_empty_dirs_from(root_dir: &Path) -> Result<()> { + for entry in WalkDir::new(root_dir) + .contents_first(true) + .into_iter() + .filter_map(Result::ok) + { + let path = entry.path(); + + if path.is_dir() && fs::read_dir(path)?.next().is_none() { + fs::remove_dir(path)?; + } + } + Ok(()) +} diff --git a/crates/sidecar/src/lib.rs b/crates/sidecar/src/lib.rs index f1332ad..31170be 100644 --- a/crates/sidecar/src/lib.rs +++ b/crates/sidecar/src/lib.rs @@ -78,6 +78,10 @@ fn register_exports(lua: &Lua) -> LuaResult { "install_dependencies", lua.create_function(install_dependencies)?, )?; + exports.set( + "list_dependency_dirs", + lua.create_function(list_dependency_dirs)?, + )?; Ok(exports) } @@ -102,7 +106,7 @@ fn set_log_level(_lua: &Lua, level: String) -> LuaResult<()> { } fn read_game_project(lua: &Lua, path: String) -> LuaResult { - let game_project = GameProject::load_from_path(path.into())?; + let game_project = GameProject::load_from_path(&absolute(path)?)?; let val = lua.to_value(&game_project)?; Ok(val) } @@ -166,3 +170,16 @@ fn install_dependencies( project::install_dependencies(&absolute(game_root)?, force_redownload.unwrap_or_default())?; Ok(()) } + +fn list_dependency_dirs(_lua: &Lua, game_root: String) -> LuaResult> { + let deps = project::list_dependency_dirs(&absolute(game_root)?)? + .into_iter() + .map(|p| { + p.to_str() + .context("could not convert path to string") + .unwrap() + .to_string() + }) + .collect(); + Ok(deps) +} diff --git a/lua/defold/config/lsp.lua b/lua/defold/config/lsp.lua index b5fc1a7..f19b2b0 100644 --- a/lua/defold/config/lsp.lua +++ b/lua/defold/config/lsp.lua @@ -1,18 +1,12 @@ local project = require "defold.project" -local libs = { project.defold_api_path() } - -for _, lib in ipairs(project.dependency_api_paths()) do - table.insert(libs, lib) -end - return { Lua = { runtime = { version = "LuaJIT", }, workspace = { - library = libs, + library = project.dependency_api_paths(), }, diagnostics = { globals = { diff --git a/lua/defold/init.lua b/lua/defold/init.lua index a7a85a8..6cd6f96 100644 --- a/lua/defold/init.lua +++ b/lua/defold/init.lua @@ -22,9 +22,6 @@ local root_markers = { "game.project" } ---@field custom_executable string|nil Use a custom executable for the debugger ---@field custom_arguments table|nil Custom arguments for the debugger ----@class BabashkaSettings Settings for the integrated Babashka interpreter ----@field custom_executable string|nil Use a custom executable for babashka - ---@class Keymap ---@field mode string|string[] ---@field mapping string @@ -33,7 +30,6 @@ local root_markers = { "game.project" } ---@field defold DefoldEditorSettings Settings for the Defold Game Engine ---@field launcher LauncherSettings Settings for the Neovim launcher run by Defold ---@field debugger DebuggerSettings Settings for the integrated debugger ----@field babashka BabashkaSettings Settings for the integrated Babashka interpreter ---@field keymaps table|nil Settings for key -> action mappings ---@field force_plugin_enabled boolean Force the plugin to be always enabled (even if we can't find the game.project file) ---@field debug boolean Enable debug settings for the plugin @@ -59,10 +55,6 @@ local default_config = { custom_arguments = nil, }, - babashka = { - custom_executable = nil, - }, - keymaps = { build = { mode = { "n", "i" }, @@ -105,7 +97,6 @@ end ---@param opts DefoldNvimConfig|nil function M.setup(opts) - local babashka = require "defold.service.babashka" local log = require "defold.service.logger" M.config = vim.tbl_deep_extend("force", default_config, opts or {}) @@ -171,8 +162,6 @@ function M.setup(opts) }) vim.defer_fn(function() - babashka.setup(M.config.babashka.custom_executable) - if M.config.defold.set_default_editor then local sidecar = require "defold.sidecar" local ok, err = pcall(sidecar.set_default_editor, M.plugin_root(), M.config.launcher) @@ -229,7 +218,6 @@ function M.load_plugin() vim.filetype.add(require("defold.config.filetype").full) local sidecar = require "defold.sidecar" - local babashka = require "defold.service.babashka" local debugger = require "defold.service.debugger" local editor = require "defold.editor" local log = require "defold.service.logger" @@ -237,7 +225,6 @@ function M.load_plugin() log.debug "============= defold.nvim: Loaded plugin" log.debug("Sidecar Version:" .. sidecar.version) - log.debug("Babashka Path: " .. babashka.bb_path()) if debugger.mobdap_path() then log.debug("Mobdap Path: " .. debugger.mobdap_path()) end diff --git a/lua/defold/project.lua b/lua/defold/project.lua index 7a0a379..47d695b 100644 --- a/lua/defold/project.lua +++ b/lua/defold/project.lua @@ -1,6 +1,6 @@ local M = {} -local function game_project_file() +local function game_project_root() local log = require "defold.service.logger" local root = vim.fs.root(0, { "game.project" }) @@ -10,39 +10,30 @@ local function game_project_file() return {} end - return '"' .. vim.fs.joinpath(root, "game.project") .. '"' -end - ----@return string -function M.defold_api_path() - local os = require "defold.service.os" - - local plugin_root = os.plugin_root() - return vim.fs.joinpath(plugin_root, "resources", "defold_api") + return root end function M.dependency_api_paths() - local babashka = require "defold.service.babashka" + local log = require "defold.service.logger" + local sidecar = require "defold.sidecar" - local res = babashka.run_task_json("list-dependency-dirs", { game_project_file() }) - return res.dirs + local ok, res = pcall(sidecar.list_dependency_dirs, game_project_root()) + if not ok then + log.error(string.format("Could not get dependency paths because: %s", res)) + return {} + end + + return res end ---@param force_redownload boolean function M.install_dependencies(force_redownload) - local babashka = require "defold.service.babashka" local log = require "defold.service.logger" + local sidecar = require "defold.sidecar" - local args = { game_project_file() } - - if force_redownload then - table.insert(args, "force") - end - - local res = babashka.run_task_json("install-dependencies", args) - - if res.error then - log.error(string.format("Could not install dependencies, because: %s", res.error)) + local ok, res = pcall(sidecar.install_dependencies, game_project_root(), force_redownload or false) + if not ok then + log.error(string.format("Could not install dependencies because: %s", res)) return end end diff --git a/lua/defold/service/babashka.lua b/lua/defold/service/babashka.lua deleted file mode 100644 index b40a644..0000000 --- a/lua/defold/service/babashka.lua +++ /dev/null @@ -1,153 +0,0 @@ -local bb_version = "1.12.207" -local bb_url = "https://github.com/babashka/babashka/releases/download/v%s/babashka-%s-%s-%s.%s" - -local M = {} - -M.custom_executable = nil - ----@return string|nil -function M.local_bb_path() - local os = require "defold.service.os" - local log = require "defold.service.logger" - - local meta_data_path = vim.fs.joinpath(vim.fn.stdpath "data", "defold.nvim", "meta.json") - local bb_path = vim.fs.joinpath(vim.fn.stdpath "data", "defold.nvim", "bin", "bb") - - if os.is_windows() then - bb_path = bb_path .. ".exe" - end - - local meta_data = nil - - if os.file_exists(meta_data_path) then - local content = vim.fn.readfile(meta_data_path) - meta_data = vim.fn.json_decode(table.concat(content)) - end - - if meta_data and meta_data.bb_version == bb_version and os.file_exists(bb_path) then - return bb_path - end - - meta_data = meta_data or {} - - log.info(string.format("defold.nvim: Downloading babashka %s", bb_version)) - - vim.fn.mkdir(vim.fs.dirname(bb_path), "p") - - local file_ending = "tar.gz" - - if os.is_windows() then - file_ending = "zip" - end - - local url = string.format(bb_url, bb_version, bb_version, os.name(), os.architecture(), file_ending) - - local download_path = bb_path .. "." .. file_ending - - os.download(url, download_path) - - if not os.file_exists(download_path) then - log.error(string.format("Unable to download '%s' to '%s', something went wrong", url, download_path)) - return nil - end - - if not os.is_windows() then - os.exec(string.format("tar -xf '%s' -C '%s'", download_path, vim.fs.dirname(bb_path))) - os.make_executable(bb_path) - else - os.exec( - string.format( - 'powershell -Command "Expand-Archive -Path %s -DestinationPath %s"', - download_path, - vim.fs.dirname(bb_path) - ) - ) - end - - if os.file_exists(download_path) then - vim.fs.rm(download_path) - end - - if not os.file_exists(bb_path) then - log.error( - string.format( - "Could not install '%s' (downloaded from: '%s', unpacked from '%s'), something went wrong", - bb_path, - url, - download_path - ) - ) - return nil - end - - meta_data.bb_version = bb_version - local json = vim.fn.json_encode(meta_data) - vim.fn.writefile({ json }, meta_data_path) - - return bb_path -end - ----@return string|nil -function M.bb_path() - local os = require "defold.service.os" - - if M.custom_executable then - return M.custom_executable - end - - if os.command_exists "bb" then - return vim.fn.exepath "bb" - end - - return M.local_bb_path() -end - -function M.bb_edn_path() - local os = require "defold.service.os" - - local plugin_root = os.plugin_root() - return vim.fs.joinpath(plugin_root, "bb.edn") -end - ----@param custom_executable string|nil ----@return boolean -function M.setup(custom_executable) - local log = require "defold.service.logger" - - M.custom_executable = custom_executable - - -- make sure bb is available - M.bb_path() - - local res = M.run_task_json("setup", {}) - if res.status ~= 200 then - log.error "Could not initialize babashka, check error logs" - return false - end - - return true -end - -function M.run_task(task, args) - local os = require "defold.service.os" - local log = require "defold.service.logger" - - log.debug(string.format("Run Babashka task: %s %s", task, vim.inspect(args))) - - local args_to_send = {} - - for _, arg in ipairs(args or {}) do - table.insert(args_to_send, arg) - end - - local params = table.concat(args_to_send, " ") - local cmd = string.format('%s --config "%s" run %s %s', M.bb_path(), M.bb_edn_path(), task, params) - return os.exec(cmd) -end - -function M.run_task_json(task, args) - local res = M.run_task(task, args) - return vim.json.decode(res) -end - -return M diff --git a/lua/defold/service/os.lua b/lua/defold/service/os.lua index 095127d..761d016 100644 --- a/lua/defold/service/os.lua +++ b/lua/defold/service/os.lua @@ -20,7 +20,6 @@ end function M.name() local os_name = vim.loop.os_uname().sysname:lower() - -- babashka uses macos and not darwin, so we'll do the same if os_name == "darwin" then return "macos" elseif string.find(os_name, "windows") then diff --git a/lua/defold/sidecar.lua b/lua/defold/sidecar.lua index e55e8c9..75e85aa 100644 --- a/lua/defold/sidecar.lua +++ b/lua/defold/sidecar.lua @@ -63,6 +63,7 @@ package.cpath = package.cpath ---@field focus_game function(game_root: string) ---@field mobdap_install function(): string ---@field install_dependencies function(game_root: string, force_redownload: boolean|nil) +---@field list_dependency_dirs function(game_root: string): string[] ---@type Sidecar local rust_plugin = require "defold_nvim_sidecar" diff --git a/scripts/update-defold-api.clj b/scripts/update-defold-api.clj deleted file mode 100755 index 3a1edb3..0000000 --- a/scripts/update-defold-api.clj +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env bb - -(require '[babashka.http-client :as http] - '[babashka.fs :as fs] - '[cheshire.core :as json] - '[clojure.string :as string] - '[clojure.java.io :as io]) - -(def base-url "https://api.github.com/repos/astrochili/defold-annotations") -(def releases-url (str base-url "/releases")) - -(defn snake-to-kebab-case [s] - (assert (string? s)) - (string/replace s #"_" "-")) - -(defn transform-key-fn [key] - (-> key - snake-to-kebab-case - keyword)) - -(defn fetch-releases [] - (let [res (http/get releases-url) - body (:body res)] - (json/parse-string body transform-key-fn))) - -(defn fetch-newest-release [] - (-> (fetch-releases) - first)) - -(defn download-file [url to-path] - (try - (let [res (http/get url {:as :bytes})] - (with-open [os (io/output-stream to-path)] - (.write os (:body res)))) - (catch Exception e - (println "ERROR: Could not download" url "to path" to-path) - (println e) - (fs/delete-if-exists to-path)))) - -(def root-dir (-> *file* - (fs/parent) - (fs/parent))) - -(def resources-dir (fs/file root-dir "resources")) -(def api-dir (fs/file resources-dir "defold_api")) - -(let [release (fetch-newest-release) - _ (print (:tag-name release)) - tempfile (fs/file (fs/temp-dir) "api.zip") - _ (fs/delete-tree tempfile) - url (-> release - :assets - first - :browser-download-url)] - (download-file url tempfile) - (fs/delete-tree api-dir) - (fs/unzip tempfile resources-dir)) - diff --git a/src/defold/logging.clj b/src/defold/logging.clj deleted file mode 100644 index 908a894..0000000 --- a/src/defold/logging.clj +++ /dev/null @@ -1,19 +0,0 @@ -(ns defold.logging - (:require - [babashka.fs :as fs] - [defold.utils :refer [cache-dir]] - [taoensso.timbre :as log] - [taoensso.timbre.appenders.core :as appenders])) - -(defn- setup! [log-to-stdout?] - (let [file (cache-dir "defold.nvim" "bb.log")] - (fs/create-dirs (fs/parent file)) - (log/merge-config! {:level :debug - :appenders {:println {:enabled? log-to-stdout?} - :spit (appenders/spit-appender {:fname (cache-dir "defold.nvim" "bb.log")})}}))) - -(defn setup-with-stdout-logging! [] - (setup! true)) - -(defn setup-with-file-logging-only! [] - (setup! false)) diff --git a/src/defold/main.clj b/src/defold/main.clj deleted file mode 100644 index b60592e..0000000 --- a/src/defold/main.clj +++ /dev/null @@ -1,39 +0,0 @@ -(ns defold.main - (:require - [cheshire.core :as json] - [defold.logging :as logging] - [defold.project :as project] - [taoensso.timbre :as log])) - -(defn- setup-logging! [type] - (case type - (:launch-neovim) (logging/setup-with-stdout-logging!) - - (logging/setup-with-file-logging-only!))) - -(defn- print-json [m] - (print (json/generate-string m))) - -(defmulti run - (fn [type & args] - (setup-logging! type) - (log/info (format "Run command '%s' with args: %s" type args)) - type)) - -(defmethod run :setup [_] - (print-json {:status 200})) - -(defmethod run :install-dependencies - ([_ game-project] - (print-json (project/install-dependencies game-project false))) - ([_ game-project force-redownload] - (print-json (project/install-dependencies game-project force-redownload)))) - -(defmethod run :list-dependency-dirs [_ game-project] - (print-json (project/list-dependency-dirs game-project))) - -(defn run-wrapped [& args] - (try - (apply run args) - (catch Throwable t - (log/error (ex-message t) t)))) diff --git a/src/defold/mini.clj b/src/defold/mini.clj deleted file mode 100644 index 740d0a7..0000000 --- a/src/defold/mini.clj +++ /dev/null @@ -1,170 +0,0 @@ -; This file is part of mini - One file, no dependency .ini parse for Clojure -; -; Github: https://github.com/atomicptr/mini -; -; Copyright (c) 2025 Christopher Kaster -; -; 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. - -(ns defold.mini - (:require - [clojure.string :as string])) - -(set! *warn-on-reflection* true) - -(defn- lexer-error [message pos] - (throw (ex-info (format "Lexer Error: %s" message) {:position pos}))) - -(defn- lexer [source] - (let [source (string/trim source) - data (vec source) - len (count data)] - (loop [current 0 - tokens [] - state :key] - (if (>= current len) - (conj tokens [:eof]) - (let [c (nth data current) - [token new-current] - (case c - (\space \return \tab) [nil (inc current)] - \newline [[:newline current] (inc current)] - \[ [[:lbracket current] (inc current)] - \] [[:rbracket current] (inc current)] - \= [[:equal current] (inc current)] - \. [[:dot current] (inc current)] - \; (loop [curr (inc current)] - (if (or (>= curr len) (= \newline (nth data curr))) - [[:newline curr] (inc curr)] - (recur (inc curr)))) - - ; parse strings - \" (let [start (inc current) - [end found] - (loop [curr start] - (if (>= curr len) - [curr false] - (if (= \" (nth data curr)) - [curr true] - (recur (inc curr)))))] - (if found - [[:string (string/join (subvec data start end)) current] - (inc end)] - (lexer-error "Unterminated string" current))) - - (let [start current - stop-chars (if (= state :key) - #{\= \; \newline \] \[} - #{\; \newline \] \[}) - [ident-str new-current] - (loop [curr current - chars []] - (if (or (>= curr len) - (stop-chars (nth data curr))) - [(string/join chars) curr] - (recur (inc curr) (conj chars (nth data curr)))))] - [[:ident (string/trim ident-str) start] new-current])) - next-state (if token - (let [tok-type (first token)] - (cond - (= tok-type :equal) :value - (#{:newline :rbracket} tok-type) :key - :else state)) - state)] - (recur new-current (if token (conj tokens token) tokens) next-state)))))) - -(defn- parser-error [message pos] - (throw (ex-info (format "Parser Error: %s" message) {:position pos}))) - -(defn- expect-token [tokens token-type] - (let [token (first tokens) - toktype (first token) - pos (last token)] - (cond - (empty? tokens) - (parser-error "Unexpected end of file" pos) - - (and (not (coll? token-type)) - (not= toktype token-type)) - (parser-error (format "Expected token %s but found %s instead" token-type toktype) pos) - - (and (coll? token-type) - (not (some #(= toktype %) token-type))) - (parser-error (format "Expected token %s but found %s instead" token-type toktype) pos) - - :else - (rest tokens)))) - -(defn- consume-token - ([tokens] (consume-token tokens nil)) - ([tokens token-type] - (let [token (first tokens) - value (second token)] - [value (if token-type - (expect-token tokens token-type) - (rest tokens))]))) - -(defn- peek-token [tokens] - (if (empty? tokens) - nil - (ffirst tokens))) - -(defn- parse-tokens [tokens] - (loop [tokens tokens - result {} - current-section nil - error nil] - (when error - (parser-error (:msg error) (:pos error))) - (if (empty? tokens) - result - (let [token (first tokens) - [token-type value] token - pos (last token)] - (case token-type - :lbracket (let [[ident tokens] (consume-token (rest tokens) :ident) - tokens (expect-token tokens :rbracket) - tokens (expect-token tokens [:newline :eof])] - (recur tokens result (string/split ident #"\.") nil)) - - (:ident :string) - (let [k value - tokens (expect-token (rest tokens) :equal) - [v tokens] (case (peek-token tokens) - :ident (consume-token tokens) - :string (consume-token tokens) - :newline [nil tokens] - (parser-error (format "Unexpected token (line): %s" (ffirst tokens)) (last (first tokens)))) - tokens (expect-token tokens [:newline :eof])] - (if current-section - (recur tokens (assoc-in result (concat current-section [k]) v) current-section nil) - (recur tokens (assoc result k v) current-section nil))) - - :newline - (recur (rest tokens) result current-section nil) - - :eof result - - (recur (rest tokens) result nil {:msg (format "Unexpected token: %s" token-type) - :pos pos})))))) - -(defn parse-string - "Parses .ini string to a Clojure map" - [^String source] - (parse-tokens (lexer source))) diff --git a/src/defold/project.clj b/src/defold/project.clj deleted file mode 100644 index 80df363..0000000 --- a/src/defold/project.clj +++ /dev/null @@ -1,116 +0,0 @@ -(ns defold.project - (:require - [babashka.fs :as fs] - [clojure.string :as string] - [defold.script-api-compiler :as script-api-compiler] - [defold.utils :as utils :refer [load-ini]] - [taoensso.timbre :as log]) - (:import - [java.nio.charset StandardCharsets] - [java.security MessageDigest])) - -(defn- get-dependencies [ini] - (->> - (seq (get ini "project")) - (map #(when (string/starts-with? (first %) "dependencies") (second %))) - (filter some?))) - -(defn- sha3 [input-string] - (let [digest (MessageDigest/getInstance "SHA3-256") - bytes (.getBytes input-string StandardCharsets/UTF_8) - hashed-bytes (.digest digest bytes)] - (apply str (map #(format "%02x" (bit-and % 0xFF)) hashed-bytes)))) - -(defn- make-ident [string] - (let [hash (sha3 string)] - (subs hash 0 8))) - -(defn- cache-dir [ident] - (let [path (utils/cache-dir "defold.nvim" "cache" ident)] - (fs/create-dirs path) - path)) - -(defn- deps-dir [ident] - (let [path (utils/data-dir "defold.nvim" "deps" ident)] - (fs/create-dirs path) - path)) - -(defn- download-file [base-dir file-url] - (let [ext (fs/extension file-url) - file-ident (make-ident file-url) - filename (str file-ident "." ext) - zip-dir (str (fs/path base-dir file-ident)) - download-path (str (fs/path base-dir filename))] - (when (not (fs/exists? download-path)) - (utils/download-file file-url download-path)) - (when (not (fs/exists? zip-dir)) - (fs/create-dirs zip-dir) - (fs/unzip download-path zip-dir {:replace-existing true})))) - -(defn- find-game-project-files [in-dir] - (fs/glob in-dir "**/game.project")) - -(defn- find-include-dirs [in-dir] - (let [game-projects (find-game-project-files in-dir)] - (flatten (map (fn [game-project] - (let [project-dir (fs/parent game-project) - config (load-ini game-project) - libs (get-in config ["library" "include_dirs"])] - (map #(str (fs/path project-dir %)) (string/split libs #",")))) - game-projects)))) - -(defn- copy-files [from-dir to-dir extensions] - (doseq [file (flatten (map #(fs/glob from-dir (str "**." %)) extensions))] - (let [rel-file (fs/relativize from-dir file) - target-file (fs/path to-dir rel-file) - target-dir (fs/parent target-file)] - (fs/create-dirs target-dir) - (fs/copy file target-file)))) - -(defn- find-script-api-files [in-dir] - (fs/glob in-dir "**.script_api")) - -(defn- compile-script-api-file [file to-path] - (let [parent (fs/parent to-path) - output (with-out-str (script-api-compiler/run (str file)))] - (fs/create-dirs parent) - (spit (fs/file to-path) output))) - -(defn- replace-ext [path new-ext] - (str (fs/strip-ext path) "." new-ext)) - -(defn install-dependencies [game-project-file force-redownload] - (try (when (not (fs/exists? game-project-file)) - (throw (ex-info (str "Could not find game.project file at: " game-project-file) {}))) - (when force-redownload - (fs/delete-tree deps-dir) - (fs/delete-tree cache-dir)) - (let [ident (make-ident game-project-file) - ini (load-ini game-project-file) - deps (get-dependencies ini) - deps-dir (deps-dir ident) - cache-dir (cache-dir ident)] - (doseq [url deps] - (let [url-ident (make-ident url) - cache-path (fs/path cache-dir url-ident) - deps-path (fs/path deps-dir url-ident)] - (when (not (fs/exists? deps-path)) - (download-file cache-dir url) - (doseq [include-dir (find-include-dirs cache-path)] - (copy-files include-dir (fs/path deps-path (fs/file-name include-dir)) ["lua"]) - (let [script-api-files (find-script-api-files include-dir)] - (doseq [script-api-file script-api-files] - (compile-script-api-file script-api-file (str (fs/path deps-path (replace-ext (fs/file-name script-api-file) "lua"))))))) - (fs/delete-tree cache-path)))) - {"success" true}) - (catch Exception e - (let [msg (ex-message e)] - (log/error "Error: " msg e) - {"error" (ex-message e)})))) - -(defn list-dependency-dirs [game-project-file] - (let [ident (make-ident game-project-file) - deps-dir (deps-dir ident) - dirs (map str (fs/list-dir deps-dir))] - {"dirs" dirs})) - diff --git a/src/defold/utils.clj b/src/defold/utils.clj deleted file mode 100644 index 5f8f325..0000000 --- a/src/defold/utils.clj +++ /dev/null @@ -1,136 +0,0 @@ -(ns defold.utils - (:require - [babashka.fs :as fs] - [babashka.http-client :as http] - [babashka.process :refer [shell]] - [clojure.java.io :as io] - [clojure.string :as string] - [defold.mini :as mini] - [taoensso.timbre :as log])) - -(defn command-exists? [cmd] - (some? (fs/which cmd))) - -(defn determine-os [] - (let [os-name (string/lower-case (System/getProperty "os.name"))] - (cond - (string/includes? os-name "linux") :linux - (string/includes? os-name "mac") :mac - (string/includes? os-name "windows") :windows - :else :unknown))) - -(defn determine-arch [] - (let [arch-name (string/lower-case (System/getProperty "os.arch"))] - (case arch-name - ("amd64" "x86" "x86_64") :x86 - ("arm" "aarch64") :arm - :else :unknown))) - -(defn get-os-arch-value [m] - (get-in m [(determine-os) (determine-arch)])) - -(defn linux? [] - (= (determine-os) :linux)) - -(defn windows? [] - (= (determine-os) :windows)) - -(defn config-dir - ([] (case (determine-os) - :linux (str (fs/xdg-config-home)) - :mac (str (fs/path (fs/home) "Library" "Preferences")) - :windows (str (fs/path (System/getenv "APPDATA"))) - :unknown (str (fs/path (fs/home) ".config")))) - ([& path] (str (apply fs/path (config-dir) path)))) - -(defn data-dir - ([] (case (determine-os) - :linux (str (fs/xdg-data-home)) - :mac (str (fs/path (fs/home) "Library")) - :windows (str (fs/path (System/getenv "APPDATA"))) - :unknown (str (fs/path (fs/home) ".data")))) - ([& path] (str (apply fs/path (data-dir) path)))) - -(defn cache-dir - ([] (case (determine-os) - :linux (str (fs/xdg-cache-home)) - :mac (str (fs/path (fs/home) "Library" "Caches")) - :windows (str (fs/path (System/getenv "TEMP"))) - :unknown (str (fs/path (fs/home) ".cache")))) - ([& path] (str (apply fs/path (cache-dir) path)))) - -(defn sha3 [s] - (let [md (.getInstance java.security.MessageDigest "SHA3-256") - bytes (.getBytes s)] - (.update md bytes) - (apply str (map #(format "%02x" %) (.digest md))))) - -(defn escape-spaces [s] - (string/escape s {\space "\\ "})) - -(defn- remove-ansi-codes [s] - (string/replace s #"\x1B\[([0-9A-Za-z;?])*[\w@]" "")) - -(defn run-shell [& cmd] - (let [cmd (vec (filter some? cmd))] - (log/info "run-shell:" cmd) - (let [res (apply shell {:out :string :err :string} cmd) - out (remove-ansi-codes (:out res)) - err (remove-ansi-codes (:err res))] - (when (and (some? out) (not-empty out)) - (log/debug "run-shell result:" out)) - (when (and (some? err) (not-empty err)) - (log/error "run-shell error:" err)) - res))) - -(defn download-file [url path] - (let [response (http/get url {:as :stream})] - (with-open [in (:body response) - out (io/output-stream path)] - (io/copy in out)))) - -(defn download-and-unpack [download-url] - (let [temp-dir (str (fs/create-temp-dir {:prefix "defold.nvim"})) - temp-file (str (fs/create-temp-file {:prefix "defold.nvim"})) - file-type (cond - (string/ends-with? download-url ".tar.gz") :tar - (string/ends-with? download-url ".zip") :zip - :else :unknown) - temp-file (case file-type - :tar (str temp-file ".tar.gz") - :zip (str temp-file ".zip"))] - - (fs/create-dirs temp-dir) - - (log/debug "Downloading" download-url "to" temp-file) - (download-file download-url temp-file) - - (case file-type - :tar (run-shell "tar" "-xvf" temp-file "-C" temp-dir) - :zip (fs/unzip temp-file temp-dir)) - - (fs/delete-if-exists temp-file) - - (-> - temp-dir - (fs/glob "**") - first - str))) - -(defn seq-replace-var [coll search replace-with] - (map #(if (= % search) replace-with %) coll)) - -(defn merge-seq-setters [coll] - (loop [result [] [x y & rest] coll] - (if (nil? x) - result - (if (and y (= \= (last x))) - (recur (conj result (str x y)) rest) - (recur (conj result x) (cons y rest)))))) - -(defn project-id [project-root] - (subs (sha3 project-root) 0 8)) - -(defn load-ini [path] - (log/debug "Loading .ini file" (str path)) - (mini/parse-string (slurp (str path)))) From 6b8f21c1d20486bc2efe87028dc489e22d5c1f87 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Fri, 19 Dec 2025 13:58:18 +0100 Subject: [PATCH 21/70] move editor and editor_config code into core, add find-editor-port and send-command sub commands to bridge --- crates/bridge/src/main.rs | 21 +++++++++++++++++++ crates/core/Cargo.toml | 3 +++ crates/{sidecar => core}/assets/run_linux.sh | 0 crates/{sidecar => core}/assets/run_macos.sh | 0 .../{sidecar => core}/assets/run_windows.bat | 0 crates/{sidecar => core}/src/editor.rs | 2 +- crates/{sidecar => core}/src/editor_config.rs | 1 + crates/core/src/lib.rs | 2 ++ crates/sidecar/Cargo.toml | 6 ------ crates/sidecar/src/lib.rs | 5 +---- 10 files changed, 29 insertions(+), 11 deletions(-) rename crates/{sidecar => core}/assets/run_linux.sh (100%) rename crates/{sidecar => core}/assets/run_macos.sh (100%) rename crates/{sidecar => core}/assets/run_windows.bat (100%) rename crates/{sidecar => core}/src/editor.rs (99%) rename crates/{sidecar => core}/src/editor_config.rs (99%) diff --git a/crates/bridge/src/main.rs b/crates/bridge/src/main.rs index f28ba91..a77106d 100644 --- a/crates/bridge/src/main.rs +++ b/crates/bridge/src/main.rs @@ -3,6 +3,7 @@ use std::{env, fs, io, path::absolute}; use anyhow::{Context, Result}; use clap::{Parser, Subcommand, command}; use defold_nvim_core::{ + editor, focus::{focus_game, focus_neovim}, mobdap, neovide, project, }; @@ -84,6 +85,16 @@ enum Commands { #[clap(value_name = "GAME_ROOT_DIR", index = 1)] game_root_dir: String, }, + /// Finds the open editor port + FindEditorPort, + /// Sends a command to the editor + SendCommand { + #[clap(long = "port")] + port: Option, + + #[clap(value_name = "COMMAND", index = 1)] + command: String, + }, } fn main() -> Result<()> { @@ -166,6 +177,16 @@ fn main() -> Result<()> { tracing::info!("{}", dir.display()); } } + Commands::FindEditorPort => { + if let Some(port) = editor::find_port() { + tracing::info!("Editor is running at port {port}"); + } else { + tracing::info!("Could not find editor port, is the editor open?"); + } + } + Commands::SendCommand { port, command } => { + editor::send_command(port, &command)?; + } } Ok(()) diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index ada5ee0..7b94048 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -20,3 +20,6 @@ tracing = "0.1.43" which = "8.0.0" url = "2.5.7" walkdir = "2.5.0" +edn-rs = "0.18.0" +netstat2 = "0.11.2" +sysinfo = "0.37.2" diff --git a/crates/sidecar/assets/run_linux.sh b/crates/core/assets/run_linux.sh similarity index 100% rename from crates/sidecar/assets/run_linux.sh rename to crates/core/assets/run_linux.sh diff --git a/crates/sidecar/assets/run_macos.sh b/crates/core/assets/run_macos.sh similarity index 100% rename from crates/sidecar/assets/run_macos.sh rename to crates/core/assets/run_macos.sh diff --git a/crates/sidecar/assets/run_windows.bat b/crates/core/assets/run_windows.bat similarity index 100% rename from crates/sidecar/assets/run_windows.bat rename to crates/core/assets/run_windows.bat diff --git a/crates/sidecar/src/editor.rs b/crates/core/src/editor.rs similarity index 99% rename from crates/sidecar/src/editor.rs rename to crates/core/src/editor.rs index 56e03fc..2f70b17 100644 --- a/crates/sidecar/src/editor.rs +++ b/crates/core/src/editor.rs @@ -1,7 +1,7 @@ use std::collections::HashMap; +use crate::cache; use anyhow::{Context, Result, bail}; -use defold_nvim_core::cache; use netstat2::{AddressFamilyFlags, ProtocolFlags, ProtocolSocketInfo, iterate_sockets_info}; use sysinfo::System; diff --git a/crates/sidecar/src/editor_config.rs b/crates/core/src/editor_config.rs similarity index 99% rename from crates/sidecar/src/editor_config.rs rename to crates/core/src/editor_config.rs index d2dcaae..b024db6 100644 --- a/crates/sidecar/src/editor_config.rs +++ b/crates/core/src/editor_config.rs @@ -43,6 +43,7 @@ pub struct LauncherSettings { } impl LauncherSettings { + #[must_use] pub fn bridge_cli_args(&self) -> Vec { let mut args = Vec::new(); diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index a6753ed..2a5e392 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -1,5 +1,7 @@ pub mod cache; pub mod defold_annotations; +pub mod editor; +pub mod editor_config; pub mod focus; pub mod game_project; pub mod github; diff --git a/crates/sidecar/Cargo.toml b/crates/sidecar/Cargo.toml index cf66934..1460bbc 100644 --- a/crates/sidecar/Cargo.toml +++ b/crates/sidecar/Cargo.toml @@ -11,18 +11,12 @@ crate-type = ["cdylib"] anyhow = "1.0.100" defold-nvim-core = { path = "../core" } dirs = "6.0.0" -edn-rs = "0.18.0" mlua = { version = "0.11.5", features = [ "module", "luajit", "serde", "anyhow", ] } -netstat2 = "0.11.2" -reqwest = { version = "0.12.24", features = ["blocking", "json"] } -serde = { version = "1.0.228", features = ["derive"] } -serde_json = "1.0.145" -sysinfo = "0.37.2" tracing = "0.1.43" tracing-appender = "0.2.4" tracing-subscriber = "0.3.22" diff --git a/crates/sidecar/src/lib.rs b/crates/sidecar/src/lib.rs index 31170be..a253dd0 100644 --- a/crates/sidecar/src/lib.rs +++ b/crates/sidecar/src/lib.rs @@ -1,6 +1,6 @@ use anyhow::Context; +use defold_nvim_core::{editor, editor_config, mobdap, project}; use defold_nvim_core::{focus, game_project::GameProject}; -use defold_nvim_core::{mobdap, project}; use mlua::Value; use mlua::prelude::*; use std::path::PathBuf; @@ -14,9 +14,6 @@ use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::reload; use tracing_subscriber::util::SubscriberInitExt; -mod editor; -mod editor_config; - static LOG_INIT: OnceLock<()> = OnceLock::new(); static LOG_RELOAD_HANDLE: OnceLock> = OnceLock::new(); From 007b11af559a76feace266d6210d2ef724f7e4d6 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Fri, 19 Dec 2025 14:02:48 +0100 Subject: [PATCH 22/70] move run script to the same place we download our executables --- crates/core/src/editor_config.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/core/src/editor_config.rs b/crates/core/src/editor_config.rs index b024db6..a2c47e5 100644 --- a/crates/core/src/editor_config.rs +++ b/crates/core/src/editor_config.rs @@ -139,9 +139,10 @@ fn create_runner_script( plugin_root: &Path, launcher_settings: &LauncherSettings, ) -> Result { - let dir = dirs::data_dir() + let dir = dirs::state_dir() .context("could not get data dir")? - .join("defold.nvim"); + .join("defold.nvim") + .join("bin"); fs::create_dir_all(&dir)?; let script_path = dir.join(format!("run.{SCRIPT_EXT}")); From da99958d3617ce789f6dab78a625a490c35a1686 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Fri, 19 Dec 2025 14:03:14 +0100 Subject: [PATCH 23/70] make find_port validate the cached port --- crates/core/src/editor.rs | 1 + lua/defold/init.lua | 18 +++++------------- 2 files changed, 6 insertions(+), 13 deletions(-) diff --git a/crates/core/src/editor.rs b/crates/core/src/editor.rs index 2f70b17..a98d327 100644 --- a/crates/core/src/editor.rs +++ b/crates/core/src/editor.rs @@ -17,6 +17,7 @@ pub fn find_port() -> Option { if let Some(port) = cache::get(CACHE) && let Some(port) = port.parse::().ok() + && is_editor_port(port) { return Some(port); } diff --git a/lua/defold/init.lua b/lua/defold/init.lua index 6cd6f96..8af796c 100644 --- a/lua/defold/init.lua +++ b/lua/defold/init.lua @@ -74,9 +74,6 @@ M.loaded = false ---@type DefoldNvimConfig M.config = default_config ----@type integer|nil -M.prev_editor_port = nil - ---Returns true if we are in a defold project ---@return boolean function M.is_defold_project() @@ -190,21 +187,16 @@ end function M.editor_port() local sidecar = require "defold.sidecar" - if M.prev_editor_port and sidecar.is_editor_port(M.prev_editor_port) then - return M.prev_editor_port - end - local ok, res = pcall(sidecar.find_editor_port) if ok then - ---@cast res integer - M.prev_editor_port = res - else - local log = require "defold.service.logger" - log.error(string.format("Could not find editor port, because: %s", res)) + return res end - return M.prev_editor_port + local log = require "defold.service.logger" + log.error(string.format("Could not find editor port, because: %s", res)) + + return -1 end function M.load_plugin() From eaa3b3c6b447deb57ce3ce2815c02c474490193f Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Fri, 19 Dec 2025 22:20:43 +0100 Subject: [PATCH 24/70] fix bridge debug flag blocking launch via defold --- crates/core/assets/run_linux.sh | 2 +- crates/core/assets/run_macos.sh | 2 +- crates/core/assets/run_windows.bat | 2 +- crates/core/src/editor_config.rs | 18 +++++++++++------- 4 files changed, 14 insertions(+), 10 deletions(-) diff --git a/crates/core/assets/run_linux.sh b/crates/core/assets/run_linux.sh index 622c0a7..04c1d36 100644 --- a/crates/core/assets/run_linux.sh +++ b/crates/core/assets/run_linux.sh @@ -1,2 +1,2 @@ #!/usr/bin/env bash -{BRIDGE_PATH} launch-neovim {LAUNCH_ARGS} "$(realpath .)" "$1" $2 +{BRIDGE_PATH} {DEBUG_FLAG} launch-neovim {LAUNCH_ARGS} "$(realpath .)" "$1" $2 diff --git a/crates/core/assets/run_macos.sh b/crates/core/assets/run_macos.sh index e1570a5..31d4e14 100644 --- a/crates/core/assets/run_macos.sh +++ b/crates/core/assets/run_macos.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash export PATH="/usr/bin:/usr/local/bin:$PATH" -{BRIDGE_PATH} launch-neovim {LAUNCH_ARGS} "$(realpath .)" "$1" $2 +{BRIDGE_PATH} {DEBUG_FLAG} launch-neovim {LAUNCH_ARGS} "$(realpath .)" "$1" $2 diff --git a/crates/core/assets/run_windows.bat b/crates/core/assets/run_windows.bat index f880f52..36f45dc 100644 --- a/crates/core/assets/run_windows.bat +++ b/crates/core/assets/run_windows.bat @@ -1,2 +1,2 @@ @echo off -{BRIDGE_PATH} launch-neovim {LAUNCH_ARGS} "%s" "%%CD%%" "%%~1" %%2 +{BRIDGE_PATH} {DEBUG_FLAG} launch-neovim {LAUNCH_ARGS} "%s" "%%CD%%" "%%~1" %%2 diff --git a/crates/core/src/editor_config.rs b/crates/core/src/editor_config.rs index a2c47e5..f8c4e89 100644 --- a/crates/core/src/editor_config.rs +++ b/crates/core/src/editor_config.rs @@ -47,12 +47,6 @@ impl LauncherSettings { pub fn bridge_cli_args(&self) -> Vec { let mut args = Vec::new(); - if let Some(debug) = self.debug - && debug - { - args.push("--debug".to_string()); - } - args.push("--launcher-type".to_string()); args.push(match self.launcher_type { LauncherType::Neovide => "neovide".to_string(), @@ -158,7 +152,17 @@ fn create_runner_script( .to_str() .context("could not convert bridge path")?, ) - .replace("{LAUNCH_ARGS}", &launch_args), + .replace("{LAUNCH_ARGS}", &launch_args) + .replace( + "{DEBUG_FLAG}", + if let Some(debug) = launcher_settings.debug + && debug + { + "--debug" + } else { + "" + }, + ), )?; #[cfg(not(target_os = "windows"))] From 232be907a864411750c18b902fa16ffc17747557 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sat, 20 Dec 2025 13:06:20 +0100 Subject: [PATCH 25/70] add script api compiler with bridge integration --- crates/bridge/src/main.rs | 23 +- crates/core/Cargo.toml | 5 + crates/core/fixtures/astar.script_api | 238 +++++++ crates/core/fixtures/facebook.script_api | 771 +++++++++++++++++++++++ crates/core/fixtures/webview.script_api | 232 +++++++ crates/core/src/lib.rs | 1 + crates/core/src/script_api.rs | 480 ++++++++++++++ src/defold/script_api_compiler.clj | 155 ----- 8 files changed, 1748 insertions(+), 157 deletions(-) create mode 100644 crates/core/fixtures/astar.script_api create mode 100644 crates/core/fixtures/facebook.script_api create mode 100644 crates/core/fixtures/webview.script_api create mode 100644 crates/core/src/script_api.rs delete mode 100644 src/defold/script_api_compiler.clj diff --git a/crates/bridge/src/main.rs b/crates/bridge/src/main.rs index a77106d..7a92db3 100644 --- a/crates/bridge/src/main.rs +++ b/crates/bridge/src/main.rs @@ -1,11 +1,14 @@ -use std::{env, fs, io, path::absolute}; +use std::{ + env, fs, io, + path::{PathBuf, absolute}, +}; use anyhow::{Context, Result}; use clap::{Parser, Subcommand, command}; use defold_nvim_core::{ editor, focus::{focus_game, focus_neovim}, - mobdap, neovide, project, + mobdap, neovide, project, script_api, }; use tracing::Level; use tracing_appender::rolling::never; @@ -95,6 +98,11 @@ enum Commands { #[clap(value_name = "COMMAND", index = 1)] command: String, }, + /// Compile `.script_api` file and return the resulting `.lua` in stdout + CompileScriptApi { + #[clap(value_name = "SCRIPT_API_FILE", index = 1)] + input: PathBuf, + }, } fn main() -> Result<()> { @@ -187,6 +195,17 @@ fn main() -> Result<()> { Commands::SendCommand { port, command } => { editor::send_command(port, &command)?; } + Commands::CompileScriptApi { input } => { + if !input.exists() { + println!("File {} could not be found", input.display()); + return Ok(()); + } + + let data = fs::read_to_string(input)?; + let res = script_api::compile(&data)?; + + println!("{res}"); + } } Ok(()) diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 7b94048..cdc4735 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -23,3 +23,8 @@ walkdir = "2.5.0" edn-rs = "0.18.0" netstat2 = "0.11.2" sysinfo = "0.37.2" +serde_yaml = "0.9.34" +textwrap = "0.16.2" + +[dev-dependencies] +pretty_assertions = "1.4.1" diff --git a/crates/core/fixtures/astar.script_api b/crates/core/fixtures/astar.script_api new file mode 100644 index 0000000..810fc4c --- /dev/null +++ b/crates/core/fixtures/astar.script_api @@ -0,0 +1,238 @@ +- name: astar + type: table + desc: A* Path Finding + members: + - name: new_map_id + desc: Get a new self-incrementing map_id. + type: function + returns: + - name: map_id + type: number + desc: ID of a new map. + - name: delete_map + desc: Delete a map by its ID. This will free the memory used by the map. + type: function + parameters: + - name: map_id + type: number + desc: ID of the map to delete. Has to be an integer from 0 to 2^16 - 1. + - name: setup + desc: Initial setup. You have to setup the astar before calling any other methods. + type: function + parameters: + - name: map_width + type: number + desc: Width of your map. This is generally width of your tilemap. + - name: map_height + type: number + desc: Height of your map. This is generally width of your tilemap. + - name: direction + type: number + desc: Movement direction (astar.DIRECTION_FOUR or astar.DIRECTION_EIGHT) + + `astar.DIRECTION_FOUR` - on a square grid that allows 4 directions of movement using Manhattan distance. + + `astar.DIRECTION_EIGHT` - on a square grid that allows 8 directions of movement using Euclidean distance. + - name: allocate + type: number + desc: + How many states should be internally allocated at a time. This can be hard to get correct. The higher the value, the more memory Patfinder will use. + + - If you have a small map (a few thousand states?) it may make sense to pass in the maximum value. This will cache everything, and MicroPather will only need one main memory allocation. For a chess board, allocate would be set to 8x8 (64) + + - If your map is large, something like 1/4 the number of possible states is good. + + - If your state space is huge, use a multiple (5-10x) of the normal path. "Occasionally" call `astar.reset_cache()` to free unused memory. + - name: typical_adjacent + type: number + desc: Used to determine cache size. The typical number of adjacent states to a given state. (On a chessboard, 8.) Higher values use a little more memory. + - name: cache[optional] + type: boolean + desc: Turn on path caching. Uses more memory (yet again) but at a huge speed advantage if you may call the pather with the same path or sub-path, which is common for pathing over maps in games. Default is `true` + - name: use_zero[optional] + type: boolean + desc: Toggle start index 0 or 1 for tables and tile positions. Also you can set it by call `astar.use_zero()`. Default is `false` + + - name: map_vflip[optional] + type: boolean + desc: Flips the map vertically. This doesn't flip the coordinates. Also you can set it by call `astar.map_vflip()`. Default is `false` + - name: map_id[optional] + type: number + desc: ID of the map. Has to be an integer from 0 to 2^16 - 1. Default is `0`. + examples: + - desc: |- + ```lua + + local map_width = 5 + local map_height = 4 + local direction = astar.DIRECTION_EIGHT + local allocate = map_width * map_height + local typical_adjacent = 8 + local cache = true -- Optional. Default is true + local flip_map = false -- Optional. Default is false + + astar.setup("my_map", map_width, map_height, direction, allocate, typical_adjacent, cache, flip_map) + + ``` + - name: use_zero + desc: Toggle start index 0 or 1 for tables and tile positions. + + If set to `false`, [astar.solve](#astarsolvestart_x-start_y-end_x-end_y), [astar.solve_near](#astarsolve_nearstart_x-start_y-max_cost), [astar.get_at](#astarget_atx-y), [astar.set_at](#astarset_atx-y-value) methods expect positions start with 1 and returns table indexes from **1**. + + Default is `false` = 1 + + parameters: + - name: toggle + type: boolean + desc: true/false + - name: map_id[optional] + type: number + desc: ID of the map. Has to be an integer from 0 to 2^16 - 1. Default is `0`. + + - name: set_map + desc: Set your map data. + + * Setting new map data reset the current cache. + type: function + parameters: + - name: world + type: table + desc: Your tilemap data. Keep it simple as much as you can. + - name: map_id[optional] + type: number + desc: ID of the map. Has to be an integer from 0 to 2^16 - 1. Default is `0`. + + - name: set_costs + desc: Set costs for walkable tiles on your `world` table. This table keys determine the walkable area. + + Table's sum must be the `astar.DIRECTION_FOUR` or `astar.DIRECTION_EIGHT`. + type: function + parameters: + - name: costs + type: table + desc: Table of costs for directions. + - name: map_id[optional] + type: number + desc: ID of the map. Has to be an integer from 0 to 2^16 - 1. Default is `0`. + + - name: solve + desc: Solves the path. + type: function + parameters: + - name: start_x + type: number + desc: Start tile X. + - name: start_y + type: number + desc: Start tile Y. + - name: end_x + type: number + desc: End tile X. + - name: end_y + type: number + desc: End tile Y. + - name: map_id[optional] + type: number + desc: ID of the map. Has to be an integer from 0 to 2^16 - 1. Default is `0`. + returns: + - name: result + type: number + desc: Result code (`astar.SOLVED`, `astar.NO_SOLUTION`, `astar.START_END_SAME`). + - name: size + type: number + desc: Size of the path. + - name: total_cost + type: number + desc: Total cost of the path. + - name: path + type: table + desc: Table with x and y coordinates. First value is the given start point. + + - name: solve_near + desc: Finds the neighbours according to a given cost. + type: function + parameters: + - name: start_x + type: number + desc: Start tile X. + - name: start_y + type: number + desc: Start tile Y. + - name: max_cost + type: number + desc: Maximum cost for finding neighbours. + - name: map_id[optional] + type: number + desc: ID of the map. Has to be an integer from 0 to 2^16 - 1. Default is `0`. + returns: + - name: near_result + type: number + desc: Result code (`astar.SOLVED`, `astar.NO_SOLUTION`, `astar.START_END_SAME`). + - name: near_size + type: number + desc: Size of the found neighbours. + - name: nears + type: table + desc: Table with x and y coordinates. First value is the given start point. + + - name: reset_cache + desc: If your state space is huge, occasionally call `astar.reset_cache()` to free unused memory. + type: function + parameters: + - name: map_id[optional] + type: number + desc: ID of the map. Has to be an integer from 0 to 2^16 - 1. Default is `0`. + - name: get_at + desc: Returns the value from the map array by coordinates. + type: function + parameters: + - name: x + type: number + desc: Tile X. + - name: y + type: number + desc: Tile Y. + - name: map_id[optional] + type: number + desc: ID of the map. Has to be an integer from 0 to 2^16 - 1. Default is `0`. + returns: + - name: value + type: number + desc: The value at the specified coordinates. + + - name: set_at + desc: Set your value to the map array by coordinates. + type: function + parameters: + - name: x + type: number + desc: Tile X. + - name: y + type: number + desc: Tile Y. + - name: value + type: number + desc: The value to set. + - name: map_id[optional] + type: number + desc: ID of the map. Has to be an integer from 0 to 2^16 - 1. Default is `0`. + + - name: SOLVED + type: number + desc: Represents a solved path result. + + - name: NO_SOLUTION + type: number + desc: Represents a result where no path is found. + + - name: START_END_SAME + type: number + desc: Represents a result where the start and end points are the same. + + - name: DIRECTION_FOUR + type: number + desc: Represents the four-direction movement option. + + - name: DIRECTION_EIGHT + type: number + desc: Represents the eight-direction movement option. diff --git a/crates/core/fixtures/facebook.script_api b/crates/core/fixtures/facebook.script_api new file mode 100644 index 0000000..abdac05 --- /dev/null +++ b/crates/core/fixtures/facebook.script_api @@ -0,0 +1,771 @@ +- name: facebook + type: table + desc: Functions and constants for interacting with Facebook APIs + + members: + +#***************************************************************************************************** + + - name: login_with_permissions + type: function + desc: Login to Facebook and request a set of publish permissions. + + The user is prompted to authorize the application using the login dialog of the specific + platform. Even if the user is already logged in to Facebook this function can still be used to request additional publish permissions. + + A comprehensive list of permissions can be found in the [Facebook permissions](https://developers.facebook.com/docs/facebook-login/permissions) documentation, + as well as in their [guide to best practices for login management](https://developers.facebook.com/docs/facebook-login/best-practices). + + parameters: + - name: permissions + type: table + desc: table with the requested publish permission strings. + - name: audience + type: number + desc: The audience that should be able to see the publications. + Can be any of + + - `facebook.AUDIENCE_NONE` + + - `facebook.AUDIENCE_ONLYME` + + - `facebook.AUDIENCE_FRIENDS` + + - `facebook.AUDIENCE_EVERYONE` + + - name: callback + type: function + desc: Callback function that is executed when the permission request dialog is closed. + parameters: + - name: self + type: object + desc: The context of the calling script + + - name: data + type: table + desc: A table that contains the response + + examples: + - desc: |- + Log in to Facebook with a set of publish permissions + ```lua + local permissions = {"publish_actions"} + facebook.login_with_permissions(permissions, facebook.AUDIENCE_FRIENDS, function(self, data) + if (data.status == facebook.STATE_OPEN and data.error == nil) then + print("Successfully logged into Facebook") + pprint(facebook.permissions()) + else + print("Failed to get permissions (" .. data.status .. ")") + pprint(data) + end + end) + ``` + + Log in to Facebook with a set of read permissions + ```lua + local permissions = {"public_profile", "email", "user_friends"} + facebook.login_with_read_permissions(permissions, facebook.AUDIENCE_EVERYONE, function(self, data) + if (data.status == facebook.STATE_OPEN and data.error == nil) then + print("Successfully logged into Facebook") + pprint(facebook.permissions()) + else + print("Failed to get permissions (" .. data.status .. ")") + pprint(data) + end + end) + ``` + +#***************************************************************************************************** + + - name: login_with_tracking_preference + type: function + desc: iOS ONLY. Login to Facebook and request a set of permissions. Allows developers to signal that a login is limited in terms of tracking users. + + The user is prompted to authorize the application using the login dialog of the specific + platform. Even if the user is already logged in to Facebook this function can still be used to request additional publish permissions. + + A comprehensive list of permissions can be found in the [Facebook permissions](https://developers.facebook.com/docs/facebook-login/permissions) documentation, + as well as in their [guide to best practices for login management](https://developers.facebook.com/docs/facebook-login/best-practices). + For Limited Login the list of permissions can be found in the [Permissions in Limited Login](https://developers.facebook.com/docs/facebook-login/limited-login/permissions) documentation. + + parameters: + - name: login_tracking + type: number + desc: The tracking type for the login. + Can be any of + + - `facebook.LOGIN_TRACKING_LIMITED` + + - `facebook.LOGIN_TRACKING_ENABLED` + - name: permissions + type: table + desc: table with the requested publish permission strings. + - name: crypto_nonce + type: string + desc: Nonce that the configuration was created with. A unique nonce will be used if none is provided to the factory method. + + - name: callback + type: function + desc: Callback function that is executed when the permission request dialog is closed. + parameters: + - name: self + type: object + desc: The context of the calling script + + - name: data + type: table + desc: A table that contains the response + + examples: + - desc: |- + Log in to Facebook with a set of publish permissions + ```lua + local permissions = {"publish_actions"} + facebook.login_with_permissions(permissions, facebook.AUDIENCE_FRIENDS, function(self, data) + if (data.status == facebook.STATE_OPEN and data.error == nil) then + print("Successfully logged into Facebook") + pprint(facebook.permissions()) + else + print("Failed to get permissions (" .. data.status .. ")") + pprint(data) + end + end) + ``` + + Log in to Facebook with a set of read permissions + ```lua + local permissions = {"public_profile", "email", "user_friends"} + facebook.login_with_tracking_preference(facebook.LOGIN_TRACKING_LIMITED, permissions, "customcryptononce", function(self, data) + if (data.status == facebook.STATE_OPEN and data.error == nil) then + print("Successfully logged into Facebook") + pprint(facebook.permissions()) + else + print("Failed to get permissions (" .. data.status .. ")") + pprint(data) + end + end) + ``` + +#***************************************************************************************************** + + - name: logout + type: function + desc: Logout from Facebook + +#***************************************************************************************************** + + - name: set_default_audience + type: function + desc: iOS ONLY. The audience that should be able to see the publications. Should be called before `facebook.login_with_tracking_preference()`; + Can be any of + + - `facebook.AUDIENCE_NONE` + + - `facebook.AUDIENCE_ONLYME` + + - `facebook.AUDIENCE_FRIENDS` + + - `facebook.AUDIENCE_EVERYONE` +#***************************************************************************************************** + + - name: get_current_authentication_token + type: function + desc: iOS ONLY. Get the current AuthenticationToken. + + This function returns the currently stored authentication token after a previous + successful login. If it returns nil no access token exists and you need + to perform a login to get the wanted permissions. + + returns: + - name: authentication_token + type: string + desc: the authentication token or nil if the user is not logged in + +#***************************************************************************************************** + + - name: get_current_profile + type: function + desc: iOS ONLY. Get the users [FBSDKProfile.currentProfile](https://developers.facebook.com/docs/facebook-login/limited-login/ios/). + [Reading From Profile Helper Class](https://developers.facebook.com/docs/facebook-login/limited-login/permissions/profile-helper) + + returns: + - name: current_profile + type: table + desc: After your application receives the logged-in user’s authentication token, you can use + this function to read information that user has granted to your application. + +#**************************************************************************************************** + + - name: init + type: function + desc: Initialize Facebook SDK (if facebook.autoinit is 0 in game.project) + +#***************************************************************************************************** + + - name: access_token + type: function + desc: Get the current Facebook access token. + + This function returns the currently stored access token after a previous + successful login. If it returns nil no access token exists and you need + to perform a login to get the wanted permissions. + + returns: + - name: token + type: string + desc: the access token or nil if the user is not logged in + + examples: + - desc: |- + Get the current access token, then use it to perform a graph API request. + + ```lua + local function get_name_callback(self, id, response) + -- do something with the response + end + function init(self) + -- assuming we are already logged in. + local token = facebook.access_token() + if token then + local url = "https://graph.facebook.com/me/?access_token=".. token + http.request(url, "GET", get_name_callback) + end + end + ``` + +#***************************************************************************************************** + + - name: permissions + type: function + desc: Get the currently granted permissions. + + This function returns a table with all the currently granted permission strings. + + returns: + - name: permissions + type: table + desc: The permissions + + examples: + - desc: |- + Check the currently granted permissions for a particular permission + + ```lua + for _,permission in ipairs(facebook.permissions()) do + if permission == "user_likes" then + -- "user_likes" granted... + break + end + end + ``` + +#***************************************************************************************************** + + - name: post_event + type: function + desc: Post an event to Facebook Analytics + + This function will post an event to Facebook Analytics where it can be used in the Facebook Insights system. + + parameters: + - name: event + type: [number, string] + desc: An event can either be one of the predefined constants below or a text string + which can be used to define a custom event that is registered with Facebook Analytics. + + - `facebook.EVENT_ACHIEVED_LEVEL` + - `facebook.EVENT_ADDED_PAYMENT_INFO` + - `facebook.EVENT_ADDED_TO_CART` + - `facebook.EVENT_ADDED_TO_WISHLIST` + - `facebook.EVENT_COMPLETED_REGISTRATION` + - `facebook.EVENT_COMPLETED_TUTORIAL` + - `facebook.EVENT_INITIATED_CHECKOUT` + - `facebook.EVENT_PURCHASED` + - `facebook.EVENT_RATED` + - `facebook.EVENT_SEARCHED` + - `facebook.EVENT_SPENT_CREDITS` + - `facebook.EVENT_TIME_BETWEEN_SESSIONS` + - `facebook.EVENT_UNLOCKED_ACHIEVEMENT` + - `facebook.EVENT_VIEWED_CONTENT` + + - name: value + type: number + desc: A numeric value for the event. This should represent the value of the event, such as the level achieved, + price for an item or number of orcs killed. + + - name: params + type: table + optional: true + desc: Optional table with parameters and their values. A key in the table can + either be one of the predefined constants below or a text which can be used to define a custom parameter. + + - `facebook.PARAM_CONTENT_ID` + - `facebook.PARAM_CONTENT_TYPE` + - `facebook.PARAM_CURRENCY` + - `facebook.PARAM_DESCRIPTION` + - `facebook.PARAM_LEVEL` + - `facebook.PARAM_MAX_RATING_VALUE` + - `facebook.PARAM_NUM_ITEMS` + - `facebook.PARAM_PAYMENT_INFO_AVAILABLE` + - `facebook.PARAM_REGISTRATION_METHOD` + - `facebook.PARAM_SEARCH_STRING` + - `facebook.PARAM_SOURCE_APPLICATION` + - `facebook.PARAM_SUCCESS` + + examples: + - desc: |- + Post a spent credits event to Facebook Analytics + + ```lua + params = {[facebook.PARAM_LEVEL] = 30, [facebook.PARAM_NUM_ITEMS] = 2} + facebook.post_event(facebook.EVENT_SPENT_CREDITS, 25, params) + ``` + +#***************************************************************************************************** + + - name: enable_event_usage + type: function + desc: Enable event usage with Facebook Analytics + This function will enable event usage for Facebook Analytics which means + that Facebook will be able to use event data for ad-tracking. + + [icon:attention] Event usage cannot be controlled and is always enabled for the + Facebook Canvas platform, therefore this function has no effect on Facebook + Canvas. + +#***************************************************************************************************** + + - name: disable_event_usage + type: function + desc: Disable event usage with Facebook Analytics + This function will disable event usage for Facebook Analytics which means + that Facebook won't be able to use event data for ad-tracking. Events will + still be sent to Facebook for insights. + + [icon:attention] Event usage cannot be controlled and is always enabled for the + Facebook Canvas platform, therefore this function has no effect on Facebook + Canvas. + +#***************************************************************************************************** + + - name: enable_advertiser_tracking + type: function + desc: Enable advertiser tracking + This function will set AdvertiserTrackingEnabled (the 'ATE' flag) to true on iOS, to inform + Audience Network to use the data to deliver personalized ads for users on iOS 14 and above. + +#***************************************************************************************************** + + - name: disable_advertiser_tracking + type: function + desc: Disable advertiser tracking + This function will set AdvertiserTrackingEnabled (the 'ATE' flag) to false on iOS, to inform + Audience Network not to use the data to deliver personalized ads for users on iOS 14 and above. + +#***************************************************************************************************** + + - name: show_dialog + type: function + desc: Show facebook web dialog + + Display a Facebook web dialog of the type specified in the `dialog` parameter. + + + The `param` table should be set up according to the requirements of each dialog + type. Note that some parameters are mandatory. Below is the list of available dialogs and + where to find Facebook's developer documentation on parameters and response data. + + + `"apprequests"` + + Shows a Game Request dialog. Game Requests allows players to invite their friends to play a + game. Available parameters + + - [type:string] `title` + + - [type:string] `message` + + - [type:number] `action_type` + + - [type:number] `filters` + + - [type:string] `data` + + - [type:string] `object_id` + + - [type:table] `suggestions` + + - [type:table] `recipients` + + - [type:string] `to` + + + + On success, the "result" table parameter passed to the callback function will include the following fields + + - [type:string] `request_id` + + - [type:table] `to` + + + [Details for each parameter](https://developers.facebook.com/docs/games/services/gamerequests/v2.6#dialogparameters) + + + `"feed"` + + The Feed Dialog allows people to publish individual stories to their timeline. + + + - [type:string] `caption` + + - [type:string] `description` + + - [type:string] `picture` + + - [type:string] `link` + + - [type:table] `people_ids` + + - [type:string] `place_id` + + - [type:string] `ref` + + + + On success, the "result" table parameter passed to the callback function will include the following fields + + - [type:string] `post_id` + + + [Details for each parameter](https://developers.facebook.com/docs/sharing/reference/feed-dialog/v2.6#params) + + + `"appinvite"` + + The App Invite dialog is available only on iOS and Android. + Note that the `url` parameter + corresponds to the appLinkURL (iOS) and setAppLinkUrl (Android) properties. + + - [type:string] `url` + + - [type:string] `preview_image` + + [Details for each parameter](https://developers.facebook.com/docs/reference/ios/current/class/FBSDKAppInviteContent) + + parameters: + - name: dialog + type: string + desc: Dialog to show + + - `"apprequests"` + + - `"feed"` + + - `"appinvite"` + + - name: param + type: table + desc: table with dialog parameters + + - name: callback + type: function + desc: Callback function that is called when the dialog is closed. + parameters: + - name: self + type: object + desc: The context of the calling script + + - name: result + type: table + desc: table with dialog specific results. See above. + + - name: error + type: table + desc: Error message. If there is no error, `error` is `nil`. + + examples: + - desc: |- + Show a dialog allowing the user to share a post to their timeline + + ```lua + local function fb_share(self, result, error) + if error then + -- something did not go right... + else + -- do something sensible + end + end + function init(self) + -- assuming we have logged in with publish permissions + local param = { link = "http://www.mygame.com",picture="http://www.mygame.com/image.jpg" } + facebook.show_dialog("feed", param, fb_share) + end + ``` + +#***************************************************************************************************** + + - name: get_version + type: function + desc: Get the version of the Facebook SDK used by the extension. + + returns: + - name: version + type: string + desc: The Facebook SDK version + +#***************************************************************************************************** + + - name: deferred_deep_link + type: function + desc: Allows receiving deferred deep link URL and parameters. + + [More info about Referred Deep Links](https://developers.facebook.com/docs/app-ads/deep-linking/) + + parameters: + - name: callback + type: function + desc: Callback function that is called when information is ready. + parameters: + - name: self + type: object + desc: The context of the calling script + + - name: result + type: table + desc: table with a deferred deep link information + members: + - name: ref + type: string + desc: ref for this App Link. + + - name: extras + type: table + desc: the full set of arguments for this app link. Properties like target uri & ref are typically picked out of this set of arguments. + + - name: target_url + type: string + desc: target uri for this App Link. + + - name: error + type: table + desc: Error message. If there is no error, `error` is `nil`. + + examples: + - desc: |- + Show a dialog allowing the user to share a post to their timeline + + ```lua + local function deferred_deep_link_callback(self, result, error) + if error then + print(error.error) + else + pprint(result) + end + end + + function init(self) + facebook.deferred_deep_link(deferred_deep_link_callback) + end + ``` + +#***************************************************************************************************** + + - name: STATE_OPEN + type: number + desc: The Facebook login session is open + + - name: STATE_CLOSED_LOGIN_FAILED + type: number + desc: The Facebook login session has closed because login failed + + - name: GAMEREQUEST_ACTIONTYPE_NONE + type: number + desc: Game request action type "none" for "apprequests" dialog + + - name: GAMEREQUEST_ACTIONTYPE_SEND + type: number + desc: Game request action type "send" for "apprequests" dialog + + - name: GAMEREQUEST_ACTIONTYPE_ASKFOR + type: number + desc: Game request action type "askfor" for "apprequests" dialog + + - name: GAMEREQUEST_ACTIONTYPE_TURN + type: number + desc: Game request action type "turn" for "apprequests" dialog + + - name: GAMEREQUEST_FILTER_NONE + type: number + desc: Game request filter type "none" for "apprequests" dialog + + - name: GAMEREQUEST_FILTER_APPUSERS + type: number + desc: Game request filter type "app_users" for "apprequests" dialog + + - name: GAMEREQUEST_FILTER_APPNONUSERS + type: number + desc: Game request filter type "app_non_users" for "apprequests" dialog + + - name: EVENT_ACHIEVED_LEVEL + type: number + desc: Log this event when the user has achieved a level + + - name: EVENT_ADDED_PAYMENT_INFO + type: number + desc: Log this event when the user has entered their payment info + + - name: EVENT_ADDED_TO_CART + type: number + desc: Log this event when the user has added an item to their cart + The value_to_sum passed to facebook.post_event should be the item's price. + + - name: EVENT_ADDED_TO_WISHLIST + type: number + desc: Log this event when the user has added an item to their wish list + The value_to_sum passed to facebook.post_event should be the item's price. + + - name: EVENT_COMPLETED_REGISTRATION + type: number + desc: Log this event when a user has completed registration with the app + + - name: EVENT_COMPLETED_TUTORIAL + type: number + desc: Log this event when the user has completed a tutorial in the app + + - name: EVENT_INITIATED_CHECKOUT + type: number + desc: Log this event when the user has entered the checkout process + The value_to_sum passed to facebook.post_event should be the total price in + the cart. + + - name: EVENT_PURCHASED + type: number + desc: Log this event when the user has completed a purchase. + The value_to_sum passed to facebook.post_event should be the numeric rating. + + - name: EVENT_RATED + type: number + desc: Log this event when the user has rated an item in the app + + - name: EVENT_SEARCHED + type: number + desc: Log this event when a user has performed a search within the app + + - name: EVENT_SPENT_CREDITS + type: number + desc: Log this event when the user has spent app credits + The value_to_sum passed to facebook.post_event should be the number of credits spent. + + - name: EVENT_TIME_BETWEEN_SESSIONS + type: number + desc: Log this event when measuring the time between user sessions + + - name: EVENT_UNLOCKED_ACHIEVEMENT + type: number + desc: Log this event when the user has unlocked an achievement in the app + + - name: EVENT_VIEWED_CONTENT + type: number + desc: Log this event when a user has viewed a form of content in the app + + - name: PARAM_CONTENT_ID + type: number + desc: Parameter key used to specify an ID for the content being logged about + + The parameter key could be an EAN, article identifier, etc., depending + on the nature of the app. + + - name: PARAM_CONTENT_TYPE + type: number + desc: Parameter key used to specify a generic content type/family for the logged event + + The key is an arbitrary type/family (e.g. "music", "photo", "video") depending + on the nature of the app. + + - name: PARAM_CURRENCY + type: number + desc: Parameter key used to specify currency used with logged event + + Use a currency value key, e.g. "USD", "EUR", "GBP" etc. + See See ISO-4217 for specific values. + + - name: PARAM_DESCRIPTION + type: number + desc: Parameter key used to specify a description appropriate to the event being logged + + Use this for app specific event description, for instance the name of the achievement + unlocked in the facebook.EVENT_UNLOCKED_ACHIEVEMENT event. + + - name: PARAM_LEVEL + type: number + desc: Parameter key used to specify the level achieved + + - name: PARAM_MAX_RATING_VALUE + type: number + desc: Parameter key used to specify the maximum rating available + + Set to specify the max rating available for the facebook.EVENT_RATED event. + E.g., "5" or "10". + + - name: PARAM_NUM_ITEMS + type: number + desc: Parameter key used to specify how many items are being processed + + Set to specify the number of items being processed for an + facebook.EVENT_INITIATED_CHECKOUT or facebook.EVENT_PURCHASED event. + + - name: PARAM_PAYMENT_INFO_AVAILABLE + type: number + desc: Parameter key used to specify whether payment info is available + + Set to specify if payment info is available for the + facebook.EVENT_INITIATED_CHECKOUT event. + + - name: PARAM_REGISTRATION_METHOD + type: number + desc: Parameter key used to specify method user has used to register for the app + + Set to specify what registation method a user used for the app, e.g. + "Facebook", "email", "Twitter", etc. + + - name: PARAM_SEARCH_STRING + type: number + desc: Parameter key used to specify user search string + + Set this to the the string that the user provided for a search + operation. + + - name: PARAM_SOURCE_APPLICATION + type: number + desc: Parameter key used to specify source application package + + - name: PARAM_SUCCESS + type: number + desc: Parameter key used to specify activity success + + Set this key to indicate whether the activity being logged about was + successful or not. + + - name: AUDIENCE_NONE + type: number + desc: Publish permission to reach no audience + + - name: AUDIENCE_ONLYME + type: number + desc: Publish permission to reach only me (private to current user) + + - name: AUDIENCE_FRIENDS + type: number + desc: Publish permission to reach user friends + + - name: AUDIENCE_EVERYONE + type: number + desc: Publish permission to reach everyone + + - name: LOGIN_TRACKING_LIMITED + type: number + desc: Login tracking Limited + + - name: LOGIN_TRACKING_ENABLED + type: number + desc: Login tracking enabled diff --git a/crates/core/fixtures/webview.script_api b/crates/core/fixtures/webview.script_api new file mode 100644 index 0000000..1d42e88 --- /dev/null +++ b/crates/core/fixtures/webview.script_api @@ -0,0 +1,232 @@ +- name: webview + type: table + desc: Functions and constants for interacting with webview APIs + members: + + - name: create + type: function + desc: Creates a webview instance. + It can show HTML pages as well as evaluate + Javascript. The view remains hidden until the first call. There can exist a + maximum of 4 webviews at the same time. + + On iOS, the callback will never get a `webview.CALLBACK_RESULT_EVAL_ERROR`, + due to the iOS SDK implementation." + parameters: + - type: function + parameters: + - name: self + type: object + desc: The calling script + + - name: webview_id + type: number + desc: The webview id + + - name: request_id + type: number + desc: The request id + + - name: type + type: enum + desc: The type of callback + + - `webview.CALLBACK_RESULT_URL_OK` + + - `webview.CALLBACK_RESULT_URL_ERROR` + + - `webview.CALLBACK_RESULT_URL_LOADING` + + - `webview.CALLBACK_RESULT_EVAL_OK` + + - `webview.CALLBACK_RESULT_EVAL_ERROR` + + - name: data + type: table + desc: A table holding the data + fields: + - name: url + type: string + desc: The url used in the `webview.open()` call. `nil` otherwise. + - name: result + type: string + desc: "Holds the result of either: a failed url open, a successful + eval request or a failed eval. `nil` otherwise." + + name: callback + desc: "A callback which receives info about finished requests taking the + following parameters:" + return: + type: number + desc: The id number of the webview + + examples: + - desc: |- + ```lua + local function webview_callback(self, webview_id, request_id, type, data) + if type == webview.CALLBACK_RESULT_URL_OK then + -- the page is now loaded, let's show it + webview.set_visible(webview_id, 1) + elseif type == webview.CALLBACK_RESULT_URL_ERROR then + print("Failed to load url: " .. data["url"]) + print("Error: " .. data["error"]) + elseif type == webview.CALLBACK_RESULT_URL_LOADING then + -- a page is loading + -- return false to prevent it from loading + -- return true or nil to continue loading the page + if data.url ~= "https://www.defold.com/" then + return false + end + elseif type == webview.CALLBACK_RESULT_EVAL_OK then + print("Eval ok. Result: " .. data['result']) + elseif type == webview.CALLBACK_RESULT_EVAL_ERROR then + print("Eval not ok. Request # " .. request_id) + end + end + local webview_id = webview.create(webview_callback) + ``` + + - name: destroy + type: function + desc: Destroys an instance of a webview. + parameters: + - name: webview_id + type: number + desc: The webview id (returned by the `webview.create()` call) + + - name: open + type: function + desc: Opens a web page in the webview, using an URL. + Once the request is done, the callback (registered in `webview.create()`) is invoked. + parameters: + - name: webview_id + type: number + desc: The webview id + - name: url + type: string + desc: The URL to open + - name: options + type: table + desc: "A table of options for the request. Currently it holds these options:" + fields: + - name: hidden + type: boolean + desc: If true, the webview will stay hidden (default=false) + - name: headers + type: table + desc: A table of header keys and values + - name: transparent + type: boolean + desc: If true, the webview background will be transparent (default=false) + examples: + - desc: |- + ```lua + local request_id = webview.open(webview_id, "http://www.defold.com", {hidden = true}) + ``` + + - name: open_raw + type: function + desc: Opens a web page in the webview, using HTML data. + Once the request is done, the callback (registered in `webview.create()`) is invoked. + parameters: + - name: webview_id + type: number + desc: The webview id + - name: html + type: string + desc: The HTML data to display + - name: options + type: table + desc: "A table of options for the request. See `webview.open()`" + examples: + - desc: |- + ```lua + local html = sys.load_resource("/main/data/test.html") + local request_id = webview.open_raw(webview_id, html, {hidden = true}) + ``` + + - name: eval + type: function + desc: Evaluates JavaScript within the context of the currently loaded page (if any). + Once the request is done, the callback (registered in `webview.create()`) is invoked. The callback will get the result in the `data["result"]` field. + parameters: + - name: webview_id + type: number + desc: The webview id + - name: code + type: string + desc: The JavaScript code to evaluate + return: + type: number + desc: The id number of the request + examples: + - desc: |- + ```lua + local request_id = webview.eval(webview_id, "GetMyFormData()") + ``` + + - name: set_transparent + type: function + desc: Set transparency of webview background + parameters: + - name: webview_id + type: number + desc: The webview id + - name: transparent + type: boolean + desc: If `true`, the webview background becomes transparent, otherwise opaque. + + - name: set_visible + type: function + desc: Shows or hides a webview + parameters: + - name: webview_id + type: number + desc: The webview id + - name: visible + type: number + desc: If `0`, hides the webview. If non zero, shows the view + + - name: is_visible + type: function + desc: Returns the visibility state of the webview. + parameters: + - name: webview_id + type: number + desc: The webview id + return: + type: number + desc: Returns `0` if not visible, `1` if it is visible + + - name: set_position + type: function + desc: Sets the position and size of the webview + parameters: + - name: webview_id + type: number + desc: The webview id + - name: x + type: number + desc: The x position of the webview + - name: y + type: number + desc: The y position of the webview + - name: width + type: number + desc: The width of the webview (-1 to match screen width) + - name: height + type: number + desc: The height of the webview (-1 to match screen height) + +#***************************************************************************************************** + + - name: CALLBACK_RESULT_URL_OK + type: number + - name: CALLBACK_RESULT_URL_ERROR + type: number + - name: CALLBACK_RESULT_URL_LOADING + type: number + - name: CALLBACK_RESULT_EVAL_OK + type: number + - name: CALLBACK_RESULT_EVAL_ERROR + type: number diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 2a5e392..269b9c8 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -8,4 +8,5 @@ pub mod github; pub mod mobdap; pub mod neovide; pub mod project; +pub mod script_api; pub mod utils; diff --git a/crates/core/src/script_api.rs b/crates/core/src/script_api.rs new file mode 100644 index 0000000..e7e0518 --- /dev/null +++ b/crates/core/src/script_api.rs @@ -0,0 +1,480 @@ +use anyhow::{Result, bail}; +use serde::Deserialize; +use std::fmt::Write; + +const LINE_WRAP: usize = 100; + +#[derive(Debug, Deserialize, Clone)] +#[serde(untagged)] +enum Type { + Single(String), + Multiple(Vec), +} + +impl Type { + pub fn type_name(&self) -> String { + match self { + Type::Single(s) => s.clone(), + Type::Multiple(items) => format!("[{}]", items.join(", ")), + } + } + + pub fn is_single(&self, t: &str) -> bool { + match self { + Type::Single(s) => s == t, + Type::Multiple(_) => false, + } + } +} + +#[derive(Debug, Deserialize, Default)] +struct Value { + pub name: Option, + pub desc: Option, + + #[serde(rename = "type")] + pub vtype: Option, + + pub members: Option>, + pub examples: Option>, + pub parameters: Option>, + + #[serde(rename = "return")] + pub returnx: Option>, + + pub returns: Option>, +} + +const TYPE_TABLE: &str = "table"; +const TYPE_FUNCTION: &str = "function"; + +impl Value { + pub fn is_field(&self) -> bool { + match &self.vtype { + Some(Type::Single(s)) => { + let s = s.as_str(); + !matches!(s, TYPE_FUNCTION | TYPE_TABLE) + } + Some(Type::Multiple(_)) => true, + None => false, + } + } + + pub fn fully_qualified_name(&self, parents: &Vec<&Value>) -> Result { + let mut fqn = String::new(); + + for p in parents { + write!(fqn, "{}.", p.name.clone().expect("parent must have name"))?; + } + + write!(fqn, "{}", self.name.clone().expect("must have name"))?; + + Ok(fqn) + } + + pub fn param_name(&self) -> String { + self.name + .clone() + .expect("called param_name on element without name") + .trim_end_matches("[optional]") + .to_string() + } + + pub fn param_type(&self) -> String { + format!( + "{}{}", + self.vtype + .as_ref() + .expect("called param_type on element without type") + .type_name(), + if self.is_optional() { "|nil" } else { "" } + ) + } + + pub fn is_optional(&self) -> bool { + self.name + .clone() + .expect("called param_name on element without name") + .ends_with("[optional]") + } +} + +fn split_lines(input: &str, line_width: usize, first_line_cutoff: usize) -> Vec { + let mut result = Vec::new(); + + let first_line_options = textwrap::Options::new(line_width - first_line_cutoff); + let first_line_wrapped = textwrap::wrap(input, &first_line_options); + + if let Some(first) = first_line_wrapped.first() { + result.push(first.to_string()); + let remaining_text = &input[first.len()..].trim_start(); + + if !remaining_text.is_empty() { + let rest_options = textwrap::Options::new(line_width); + let rest_wrapped = textwrap::wrap(remaining_text, &rest_options); + + for line in rest_wrapped { + result.push(line.into_owned()); + } + } + } + + result +} + +fn compile_table(out: &mut String, table: &Value, parents: &Vec<&Value>) -> Result<()> { + if let Some(vtype) = &table.vtype + && !vtype.is_single(TYPE_TABLE) + { + bail!("compile_table can only be called with tables"); + } + + if let Some(desc) = &table.desc { + writeln!(out, "---{desc}")?; + } + + if let Some(vtype) = &table.vtype { + writeln!( + out, + "---@class {} : {}", + table.name.clone().expect("table must have name"), + vtype.type_name(), + )?; + } else { + writeln!( + out, + "---@class {}", + table.name.clone().expect("table must have name"), + )?; + } + + if let Some(members) = &table.members { + for m in members.iter().filter(|m| m.is_field()) { + compile_field(out, m)?; + } + } + + if parents.is_empty() { + writeln!( + out, + "local {} = {{}}", + table.name.clone().expect("table must have name") + )?; + } else { + writeln!(out, "{} = {{}}", table.fully_qualified_name(&parents)?)?; + } + + if let Some(members) = &table.members { + for m in members { + let Some(vtype) = &m.vtype else { + continue; + }; + + if vtype.is_single(TYPE_TABLE) { + writeln!(out)?; + compile_table(out, m, &{ + let mut tmp = parents.clone(); + tmp.extend_from_slice(&[table]); + tmp + })?; + } else if vtype.is_single(TYPE_FUNCTION) { + writeln!(out)?; + compile_function(out, m, &{ + let mut tmp = parents.clone(); + tmp.extend_from_slice(&[table]); + tmp + })?; + } + + // we already handled others + } + } + + if parents.is_empty() { + writeln!(out)?; + writeln!( + out, + "return {}", + table.name.clone().expect("table must have name") + )?; + } + + Ok(()) +} + +fn compile_field(out: &mut String, field: &Value) -> Result<()> { + assert!(field.is_field()); + + let prefix = format!( + "---@field public {} {} ", + field.name.clone().expect("field must have name"), + field + .vtype + .as_ref() + .expect("field must have type") + .type_name(), + ); + + if let Some(desc) = &field.desc { + let lines = split_lines(desc, LINE_WRAP - 3, prefix.len()); + + write!(out, "{prefix}")?; + + if let Some(first) = lines.first() { + writeln!(out, "{first}")?; + } + + for line in lines.iter().skip(1) { + writeln!(out, "---{line}")?; + } + } else { + writeln!(out, "{prefix}")?; + } + + Ok(()) +} + +fn compile_function(out: &mut String, function: &Value, parents: &Vec<&Value>) -> Result<()> { + if let Some(desc) = &function.desc { + let lines = split_lines(desc, LINE_WRAP - 3, 0); + + for line in lines { + write!(out, "---")?; + + if line.starts_with('-') { + write!(out, " ")?; + } + + writeln!(out, "{line}")?; + } + } + + if let Some(examples) = &function.examples { + writeln!(out, "---")?; + writeln!(out, "--- Examples:")?; + writeln!(out, "---")?; + + let prefix = "--- "; + + for example in examples { + if example.desc.is_none() { + continue; + } + + let desc = example.desc.clone().unwrap(); + + let lines = split_lines(&desc, LINE_WRAP, prefix.len()); + + for line in lines { + writeln!(out, "{prefix}{line}")?; + } + } + + writeln!(out, "---")?; + } + + if let Some(params) = &function.parameters { + for param in params { + compile_parameter(out, param)?; + } + } + + if let Some(returnx) = &function.returnx { + compile_return(out, returnx)?; + } + + if let Some(returns) = &function.returns { + for ret in returns { + compile_return(out, ret)?; + } + } + + let params = if let Some(params) = &function.parameters { + params + .iter() + .map(Value::param_name) + .collect::>() + .join(", ") + } else { + String::new() + }; + + if !parents.is_empty() { + writeln!( + out, + "function {}({params}) end", + function.fully_qualified_name(&parents)?, + )?; + } else { + writeln!( + out, + "function {}({params}) end", + function.name.clone().unwrap() + )?; + } + + Ok(()) +} + +fn compile_return(out: &mut String, ret: &Value) -> Result<()> { + let prefix = format!( + "---@return {}", + ret.vtype + .clone() + .expect("return must have type") + .type_name() + ); + + if let Some(desc) = &ret.desc { + let lines = split_lines(desc, LINE_WRAP, prefix.len() + 1); + write!(out, "{prefix} ")?; + + if let Some(first) = lines.first() { + writeln!(out, "{first}")?; + } + + for line in lines.iter().skip(1) { + if line.starts_with('-') { + writeln!(out, "--- {line}")?; + continue; + } + writeln!(out, "---{line}")?; + } + } + + Ok(()) +} + +fn compile_parameter(out: &mut String, param: &Value) -> Result<()> { + let mut prefix = format!("---@param {} ", param.param_name()); + + if let Some(vtype) = ¶m.vtype + && vtype.is_single(TYPE_FUNCTION) + { + write!( + out, + "{prefix}fun({})", + if let Some(params) = ¶m.parameters { + params + .iter() + .map(|p| format!("{}: {}", p.param_name(), p.param_type())) + .collect::>() + .join(", ") + } else { + String::new() + } + )?; + + if let Some(returnx) = ¶m.returnx { + write!( + out, + ": {}", + returnx + .vtype + .clone() + .expect("return must have type") + .type_name() + )?; + } + + if let Some(returns) = ¶m.returns { + write!( + out, + ": {}", + returns + .iter() + .filter_map(|r| r.vtype.clone()) + .map(|t| t.type_name()) + .collect::>() + .join("|") + )?; + } + + writeln!(out)?; + } else { + write!(prefix, "{} ", param.param_type(),)?; + + if let Some(desc) = ¶m.desc { + let lines = split_lines(desc, LINE_WRAP - 3, prefix.len()); + write!(out, "{prefix}")?; + + if let Some(first) = lines.first() { + writeln!(out, "{first}")?; + } + + for line in lines.iter().skip(1) { + if line.starts_with('-') { + writeln!(out, "--- {line}")?; + continue; + } + + writeln!(out, "---{line}")?; + } + } + } + + Ok(()) +} + +fn parse(input: &str) -> Result> { + Ok(serde_yaml::from_str(input)?) +} + +pub fn compile(input: &str) -> Result { + let tables = parse(input)?; + + let mut out = String::new(); + + writeln!(out, "-- Generated by atomicptr/defold.nvim")?; + writeln!(out)?; + + if tables.len() == 1 { + compile_table(&mut out, tables.first().unwrap(), &Vec::new())?; + return Ok(out); + } + + // if we have multiple tables, create a fake wrapper "M" table + let wrapper = Value { + name: Some(String::from("M")), + members: Some(tables), + ..Default::default() + }; + + compile_table(&mut out, &wrapper, &Vec::new())?; + + Ok(out) +} + +#[cfg(test)] +mod tests { + use super::*; + use pretty_assertions::assert_eq; + + const DEFOLD_ASTAR_EXAMPLE: &str = include_str!("../fixtures/astar.script_api"); + const DEFOLD_FACEBOOK_EXAMPLE: &str = include_str!("../fixtures/facebook.script_api"); + const DEFOLD_WEBVIEW_EXAMPLE: &str = include_str!("../fixtures/webview.script_api"); + + #[test] + fn test_parse_astar() { + let astar = parse(DEFOLD_ASTAR_EXAMPLE).expect("expect parse to succeed"); + assert_eq!(1, astar.len()); + let astar = astar.first().expect("should have one"); + assert_eq!(Some("astar".to_string()), astar.name); + } + + #[test] + fn test_parse_facebook() { + let facebook = parse(DEFOLD_FACEBOOK_EXAMPLE).expect("expect parse to succeed"); + assert_eq!(1, facebook.len()); + let facebook = facebook.first().expect("should have one"); + assert_eq!(Some("facebook".to_string()), facebook.name); + } + + #[test] + fn test_parse_webview() { + let webview = parse(DEFOLD_WEBVIEW_EXAMPLE).expect("expect parse to succeed"); + assert_eq!(1, webview.len()); + let webview = webview.first().expect("should have one"); + assert_eq!(Some("webview".to_string()), webview.name); + } +} diff --git a/src/defold/script_api_compiler.clj b/src/defold/script_api_compiler.clj deleted file mode 100644 index 8d892b2..0000000 --- a/src/defold/script_api_compiler.clj +++ /dev/null @@ -1,155 +0,0 @@ -(ns defold.script-api-compiler - (:require [clj-yaml.core :as yaml] - [clojure.string :as string])) - -(defn- optional? [name] - (string/ends-with? name "[optional]")) - -(defn- get-param-name [p] - (-> p - :name - (string/replace #"\[optional\]" ""))) - -(defn- get-param-type [p] - (if (optional? (:name p)) - (format "%s|nil" (:type p)) - (:type p))) - -(defn- split-long-string [s max-len] - (loop [remaining-s s - result []] - (if (<= (count remaining-s) max-len) - (conj result remaining-s) - (let [segment (subs remaining-s 0 max-len) - last-space-idx (.lastIndexOf segment " ") - split-idx (if (pos? last-space-idx) - last-space-idx - max-len)] - (recur (subs remaining-s (inc split-idx)) - (conj result (subs segment 0 split-idx))))))) - -(defn- split-lines [text] - (->> text - string/split-lines - (map #(split-long-string % 80)) - flatten)) - -(defn- compile-comment-description - ([description] (compile-comment-description description "")) - ([description prefix] - (let [lines (split-lines description)] - (if (= (count lines) 1) - (println description) - (do (println (first lines)) - (doseq [line (rest lines)] - (println (str "---" prefix line)))))))) - -(defn- compile-param-comment [param] - (cond - (= (:type param) "function") - (do - (print "---@param" - (get-param-name param) - (str "fun(" (string/join ", " (map #(str (get-param-name %) ": " (get-param-type %)) (:parameters param))) ")")) - (when (:returns param) - (print ":" (string/join "|" (map :type (:returns param))))) - (println "")) - - :else - (do - (print "---@param" (get-param-name param) (get-param-type param)) - (when (:desc param) - (print " ") - (compile-comment-description (:desc param) " "))))) - -(defn- compile-return-comment [return] - (print "---@return" (:type return)) - (when (:name return) - (print (str " " (:name return)))) - (when (:desc return) - (print " ") - (compile-comment-description (:desc return) " "))) - -(defn- compile-params-list [parameters] - (string/join ", " (map get-param-name parameters))) - -(defn- compile-function [function parent] - (println "") - - (when (:desc function) - (doseq [line (split-lines (:desc function))] - (if (string/starts-with? line "-") - (println (str "--- " line)) - (println (str "---" line))))) - - (when (:examples function) - (println "---") - (println "--- Examples:") - (println "---") - (doseq [example (:examples function)] - (doseq [line (split-lines (:desc example))] - (println (str "--- " line))) - (println "---"))) - - (doseq [param (:parameters function)] - (compile-param-comment param)) - - (doseq [return (:returns function)] - (compile-return-comment return)) - - (println (str "function " parent "." (:name function) "(" (compile-params-list (:parameters function)) ") end"))) - -(defn- compile-field [field] - (print "---@field public" (:name field) (:type field)) - (when (:desc field) - (print " @") - (compile-comment-description (:desc field) " "))) - -(defn- is-field? [member] - (case (:type member) - "function" false - "table" false - true)) - -(defn- compile-table [table parent] - (when (:desc table) - (println (str "---" (:desc table)))) - - (when (:type table) - (println "---@type" (:type table))) - - (println "---@class" (:name table)) - - ; parse fields - (doseq [field (filter is-field? (:members table))] - (compile-field field)) - - (if (nil? parent) - (println (:name table) "= {}") - (println (str parent "." (:name table) " = {}"))) - - ; parse members - (doseq [member (:members table)] - (case (:type member) - "function" (compile-function member (:name table)) - "table" (compile-table member (:name table)) - nil)) - - (when (nil? parent) - (println "") - (println "return" (:name table)))) - -(defn run [script-api-file] - (when (nil? script-api-file) - (println "Error: .script_api file was not specified") - (System/exit 1)) - - (let [data (slurp script-api-file) - script-api (yaml/parse-string data)] - (println "-- Generated by atomicptr/defold.nvim") - (println "") - (println "---@meta") - (println "") - - (doseq [module script-api] - (compile-table module nil)))) From 2ec010ec4f90ba7f75465844b165f246e522c8cc Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sat, 20 Dec 2025 13:18:05 +0100 Subject: [PATCH 26/70] add .script_api compilation --- crates/core/src/project.rs | 31 +++++++++++++++++++++++++++++-- crates/core/src/script_api.rs | 2 +- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/crates/core/src/project.rs b/crates/core/src/project.rs index 2126e7c..95f6e5a 100644 --- a/crates/core/src/project.rs +++ b/crates/core/src/project.rs @@ -1,11 +1,13 @@ use std::{ fs::{self, File}, + io::Write, path::{Path, PathBuf}, }; use crate::{ defold_annotations, game_project::GameProject, + script_api, utils::{self, sha3}, }; use anyhow::{Context, Result, bail}; @@ -170,8 +172,7 @@ fn install_dependency(url: &str, project_deps_dir: &Path) -> Result<()> { continue; } - // TODO: compile script api files - + compile_script_api_files(&include_dir_path)?; copy_files(&include_dir_path, &include_dir_target, "lua")?; } @@ -208,6 +209,32 @@ fn find_files_with_ext(root_dir: &Path, ext: &str) -> Vec { .collect() } +fn compile_script_api_files(from_dir: &Path) -> Result<()> { + let files = find_files_with_ext(from_dir, "script_api"); + + for file in &files { + let Some(stem) = file.file_stem() else { + continue; + }; + + let target_path = file.with_file_name(format!("{}.lua", stem.to_str().unwrap())); + + tracing::debug!( + "Compiling {} to {}...", + file.display(), + target_path.display() + ); + + let input = fs::read_to_string(file)?; + let output = script_api::compile(&input)?; + + let mut file = File::create(target_path)?; + file.write_all(output.as_bytes())?; + } + + Ok(()) +} + fn copy_files(from_dir: &Path, to_dir: &Path, ext: &str) -> Result<()> { for file in find_files_with_ext(from_dir, ext) { let relative_path = file.strip_prefix(from_dir)?; diff --git a/crates/core/src/script_api.rs b/crates/core/src/script_api.rs index e7e0518..5b473bb 100644 --- a/crates/core/src/script_api.rs +++ b/crates/core/src/script_api.rs @@ -2,7 +2,7 @@ use anyhow::{Result, bail}; use serde::Deserialize; use std::fmt::Write; -const LINE_WRAP: usize = 100; +const LINE_WRAP: usize = 120; #[derive(Debug, Deserialize, Clone)] #[serde(untagged)] From cf5cb9caa7c0bda393b4e796c7a3ec646c4050f2 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sat, 20 Dec 2025 19:28:08 +0100 Subject: [PATCH 27/70] update lsp library dirs when using :DefoldFetch --- lua/defold/config/lsp.lua | 42 ++++++++++++++++++++------------------- lua/defold/init.lua | 18 ++++++++++++++++- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/lua/defold/config/lsp.lua b/lua/defold/config/lsp.lua index f19b2b0..12d04cc 100644 --- a/lua/defold/config/lsp.lua +++ b/lua/defold/config/lsp.lua @@ -1,23 +1,25 @@ -local project = require "defold.project" +return function() + local project = require "defold.project" -return { - Lua = { - runtime = { - version = "LuaJIT", - }, - workspace = { - library = project.dependency_api_paths(), - }, - diagnostics = { - globals = { - "final", - "fixed_update", - "init", - "on_input", - "on_message", - "on_reload", - "update", + return { + Lua = { + runtime = { + version = "LuaJIT", + }, + workspace = { + library = project.dependency_api_paths(), + }, + diagnostics = { + globals = { + "final", + "fixed_update", + "init", + "on_input", + "on_message", + "on_reload", + "update", + }, }, }, - }, -} + } +end diff --git a/lua/defold/init.lua b/lua/defold/init.lua index 8af796c..3d8a1b2 100644 --- a/lua/defold/init.lua +++ b/lua/defold/init.lua @@ -92,6 +92,20 @@ function M.plugin_root() return os.plugin_root() end +local function update_lua_lsp_paths() + local clients = vim.lsp.get_clients { name = "lua_ls" } + + local lsp_config = (require "defold.config.lsp")() + + for _, client in ipairs(clients) do + client.settings = vim.tbl_deep_extend("force", client.settings or {}, lsp_config) + + client:notify("workspace/didChangeConfiguration", { + settings = client.settings, + }) + end +end + ---@param opts DefoldNvimConfig|nil function M.setup(opts) local log = require "defold.service.logger" @@ -145,7 +159,7 @@ function M.setup(opts) return end - local lsp_config = require "defold.config.lsp" + local lsp_config = (require "defold.config.lsp")() log.debug(string.format("For %s, loaded lsp config %s", root, vim.inspect(lsp_config))) @@ -276,6 +290,8 @@ function M.load_plugin() editor.send_command(M.editor_port(), "fetch-libraries", true) project.install_dependencies(opt.bang) + + update_lua_lsp_paths() end, { bang = true, nargs = 0, desc = "Fetch & create Defold project dependency annotations" }) -- integrate the debugger into dap From 2911e66f5a6acca141bbaa654a0ebac2a02686cf Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sat, 20 Dec 2025 20:02:16 +0100 Subject: [PATCH 28/70] move data into data dir (XDG_DATA_HOME or eqv) --- crates/core/src/defold_annotations.rs | 2 +- crates/core/src/editor_config.rs | 2 +- crates/core/src/mobdap.rs | 4 +-- crates/core/src/neovide.rs | 4 +-- crates/core/src/project.rs | 2 +- lua/defold/service/logger.lua | 7 ++++- lua/defold/service/os.lua | 42 +++++++++++++++++++++++++++ 7 files changed, 55 insertions(+), 8 deletions(-) diff --git a/crates/core/src/defold_annotations.rs b/crates/core/src/defold_annotations.rs index 080830b..377d9fe 100644 --- a/crates/core/src/defold_annotations.rs +++ b/crates/core/src/defold_annotations.rs @@ -19,7 +19,7 @@ pub fn dir() -> Result { } fn version_path() -> Result { - let dir = dirs::state_dir() + let dir = dirs::data_dir() .context("could not get state dir")? .join("defold.nvim") .join("meta"); diff --git a/crates/core/src/editor_config.rs b/crates/core/src/editor_config.rs index f8c4e89..62d608d 100644 --- a/crates/core/src/editor_config.rs +++ b/crates/core/src/editor_config.rs @@ -133,7 +133,7 @@ fn create_runner_script( plugin_root: &Path, launcher_settings: &LauncherSettings, ) -> Result { - let dir = dirs::state_dir() + let dir = dirs::data_dir() .context("could not get data dir")? .join("defold.nvim") .join("bin"); diff --git a/crates/core/src/mobdap.rs b/crates/core/src/mobdap.rs index af9e330..afad56e 100644 --- a/crates/core/src/mobdap.rs +++ b/crates/core/src/mobdap.rs @@ -27,7 +27,7 @@ const NAME: &str = "mobdap-macos-arm64.tar.gz"; const NAME: &str = "mobdap-windows-amd64.zip"; fn path() -> Result { - let dir = dirs::state_dir() + let dir = dirs::data_dir() .context("could not get state dir")? .join("defold.nvim") .join("bin"); @@ -44,7 +44,7 @@ fn path() -> Result { } fn version_path() -> Result { - let dir = dirs::state_dir() + let dir = dirs::data_dir() .context("could not get state dir")? .join("defold.nvim") .join("meta"); diff --git a/crates/core/src/neovide.rs b/crates/core/src/neovide.rs index 4895129..3e1eaef 100644 --- a/crates/core/src/neovide.rs +++ b/crates/core/src/neovide.rs @@ -22,7 +22,7 @@ const NAME: &'static str = "not supported"; const NAME: &'static str = "neovide.exe.zip"; fn path() -> Result { - let dir = dirs::state_dir() + let dir = dirs::data_dir() .context("could not get state dir")? .join("defold.nvim") .join("bin"); @@ -39,7 +39,7 @@ fn path() -> Result { } fn version_path() -> Result { - let dir = dirs::state_dir() + let dir = dirs::data_dir() .context("could not get state dir")? .join("defold.nvim") .join("meta"); diff --git a/crates/core/src/project.rs b/crates/core/src/project.rs index 95f6e5a..d0cb7de 100644 --- a/crates/core/src/project.rs +++ b/crates/core/src/project.rs @@ -21,7 +21,7 @@ fn ident(string: &str) -> Result { } pub fn deps_root() -> Result { - let dir = dirs::state_dir() + let dir = dirs::data_dir() .context("could not get state dir")? .join("defold.nvim") .join("deps"); diff --git a/lua/defold/service/logger.lua b/lua/defold/service/logger.lua index b90ac93..deb5593 100644 --- a/lua/defold/service/logger.lua +++ b/lua/defold/service/logger.lua @@ -17,7 +17,12 @@ local function level2string(level) end local function log(level, message) - local log_file = vim.fs.joinpath(vim.fn.stdpath "data", "defold.nvim", "plugin.log") + local osm = require "defold.service.os" + + local logdir = vim.fs.joinpath(osm.cache_dir(), "logs") + vim.fn.mkdir(logdir, "p") + + local log_file = vim.fs.joinpath(logdir, "plugin.log") local timestamp = os.date "%Y-%m-%d %H:%M:%S" local level_str = level2string(level) diff --git a/lua/defold/service/os.lua b/lua/defold/service/os.lua index 761d016..f79e7ed 100644 --- a/lua/defold/service/os.lua +++ b/lua/defold/service/os.lua @@ -29,10 +29,20 @@ function M.name() return os_name end +---Test if the OS is Linux +---@return boolean +function M.is_linux() + return M.name() == "linux" +end + +---Test if the OS is Windows +---@return boolean function M.is_windows() return M.name() == "windows" end +---Test if the OS is MacOS +---@return boolean function M.is_macos() return M.name() == "macos" end @@ -123,4 +133,36 @@ function M.plugin_root() return res end +---Returns `Data` directory +---@return string +function M.data_dir() + if M.is_linux() then + if vim.env.XDG_DATA_HOME then + local dir = vim.fs.joinpath(vim.env.XDG_DATA_HOME, "defold.nvim") + vim.fn.mkdir(dir, "p") + return dir + end + end + + local dir = vim.fs.joinpath(vim.fn.stdpath "data", "defold.nvim") + vim.fn.mkdir(dir, "p") + return dir +end + +---Returns `Cache` directory +---@return string +function M.cache_dir() + if M.is_linux() then + if vim.env.XDG_CACHE_HOME then + local dir = vim.fs.joinpath(vim.env.XDG_CACHE_HOME, "defold.nvim") + vim.fn.mkdir(dir, "p") + return dir + end + end + + local dir = vim.fs.joinpath(vim.fn.stdpath "cache", "defold.nvim") + vim.fn.mkdir(dir, "p") + return dir +end + return M From f0b568a0637dce91bace72c6b77c1fab12aa00c3 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sat, 20 Dec 2025 20:58:40 +0100 Subject: [PATCH 29/70] add experimental github action for releases --- .github/workflows/release.yml | 75 +++++++++++++++++++++++++++++++++++ Cargo.toml | 5 +++ 2 files changed, 80 insertions(+) create mode 100644 .github/workflows/release.yml diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..84d2ac1 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,75 @@ +name: Release + +on: + push: + tags: ["v*"] + +permissions: + contents: write + +jobs: + release: + name: Release ${{ matrix.config.name }} + runs-on: ${{ matrix.config.os }} + strategy: + matrix: + config: + - name: Linux x86 + os: ubuntu-latest + src: + sidecar: target/release/libdefold_nvim_sidecar.so + bridge: target/release/defold-nvim-bridge + dist: + sidecar: target/release/linux-x86-libdefold_nvim_sidecar.so + bridge: target/release/linux-x86-defold-nvim-bridge + - name: MacOS x86 + os: macos-15-intel + src: + sidecar: target/release/libdefold_nvim_sidecar.dylib + bridge: target/release/defold-nvim-bridge + dist: + sidecar: target/release/macos-x86-libdefold_nvim_sidecar.so + bridge: target/release/macos-x86-defold-nvim-bridge + - name: MacOS ARM + os: macos-latest + src: + sidecar: target/release/libdefold_nvim_sidecar.dylib + bridge: target/release/defold-nvim-bridge + dist: + sidecar: target/release/macos-arm-libdefold_nvim_sidecar.so + bridge: target/release/macos-arm-defold-nvim-bridge + - name: Windows x86 + os: windows-latest + src: + sidecar: target/release/defold_nvim_sidecar.dll + bridge: target/release/defold-nvim-bridge.exe + dist: + sidecar: target/release/windows-x86-libdefold_nvim_sidecar.so + bridge: target/release/windows-x86-defold-nvim-bridge + + steps: + - uses: actions/checkout@v5 + - uses: actions-rust-lang/setup-rust-toolchain@v1 + with: + rustflags: "" + + - name: build ${{ matrix.config.name }} + run: cargo build --release + + - name: prepare ${{ matrix.config.name }} + run: | + # rename builds to be unique + mv "${{ matrix.config.src.sidecar }}" "${{ matrix.config.dist.sidecar }}" + mv "${{ matrix.config.src.bridge }}" "${{ matrix.config.dist.bridge }}" + # generate sha256 sum + shasum -a 256 "${{ matrix.config.dist.sidecar }}" > "${{ matrix.config.dist.sidecar }}.sha256" + shasum -a 256 "${{ matrix.config.dist.bridge }}" > "${{ matrix.config.dist.bridge }}.sha256" + + - name: upload ${{ matrix.config.name }} + uses: softprops/action-gh-release@v2 + with: + files: | + ${{ matrix.config.dist.sidecar }} + ${{ matrix.config.dist.bridge }} + ${{ matrix.config.dist.sidecar }}.sha256 + ${{ matrix.config.dist.bridge }}.sha256 diff --git a/Cargo.toml b/Cargo.toml index 833466d..92976fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,8 @@ pedantic = "warn" [workspace.lints.rust] too-many-lines = "allow" + +[profile.release] +strip = true +opt-level = 3 +lto = true From 00f8caa7e9a8d06aabee36ed5f8e845166bd2441 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sat, 20 Dec 2025 21:42:17 +0100 Subject: [PATCH 30/70] update deps --- .gitignore | 2 +- Cargo.lock | 3187 ++++++++++++++++++++++++++++++++++++++++ crates/core/Cargo.toml | 2 +- 3 files changed, 3189 insertions(+), 2 deletions(-) create mode 100644 Cargo.lock diff --git a/.gitignore b/.gitignore index 0676706..8b8123c 100644 --- a/.gitignore +++ b/.gitignore @@ -106,7 +106,7 @@ target/ # Remove Cargo.lock from gitignore if creating an executable, leave it for libraries # More information here https://doc.rust-lang.org/cargo/guide/cargo-toml-vs-cargo-lock.html -Cargo.lock +# Cargo.lock # These are backup files generated by rustfmt **/*.rs.bk diff --git a/Cargo.lock b/Cargo.lock new file mode 100644 index 0000000..279fb1e --- /dev/null +++ b/Cargo.lock @@ -0,0 +1,3187 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "adler2" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if", + "cipher", + "cpufeatures", +] + +[[package]] +name = "aho-corasick" +version = "1.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +dependencies = [ + "memchr", +] + +[[package]] +name = "anstream" +version = "0.6.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" +dependencies = [ + "anstyle", + "anstyle-parse", + "anstyle-query", + "anstyle-wincon", + "colorchoice", + "is_terminal_polyfill", + "utf8parse", +] + +[[package]] +name = "anstyle" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" + +[[package]] +name = "anstyle-parse" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" +dependencies = [ + "utf8parse", +] + +[[package]] +name = "anstyle-query" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "anstyle-wincon" +version = "3.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" +dependencies = [ + "anstyle", + "once_cell_polyfill", + "windows-sys 0.61.2", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "base64" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "bindgen" +version = "0.72.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" +dependencies = [ + "bitflags 2.10.0", + "cexpr", + "clang-sys", + "itertools", + "log", + "prettyplease", + "proc-macro2", + "quote", + "regex", + "rustc-hash", + "shlex", + "syn 2.0.111", +] + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "block-buffer" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +dependencies = [ + "generic-array", +] + +[[package]] +name = "bstr" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63044e1ae8e69f3b5a92c736ca6269b8d12fa7efe39bf34ddb06d102cf0e2cab" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "bumpalo" +version = "3.19.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" + +[[package]] +name = "byteorder" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "bzip2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c" +dependencies = [ + "libbz2-rs-sys", +] + +[[package]] +name = "cc" +version = "1.2.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9f50d563227a1c37cc0a263f64eca3334388c01c5e4c4861a9def205c614383c" +dependencies = [ + "find-msvc-tools", + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cexpr" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" +dependencies = [ + "nom", +] + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" +dependencies = [ + "crypto-common", + "inout", +] + +[[package]] +name = "clang-sys" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" +dependencies = [ + "glob", + "libc", + "libloading", +] + +[[package]] +name = "clap" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8" +dependencies = [ + "clap_builder", + "clap_derive", +] + +[[package]] +name = "clap_builder" +version = "4.5.53" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00" +dependencies = [ + "anstream", + "anstyle", + "clap_lex", + "strsim", +] + +[[package]] +name = "clap_derive" +version = "4.5.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "clap_lex" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" + +[[package]] +name = "colorchoice" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" + +[[package]] +name = "const-random" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" +dependencies = [ + "const-random-macro", +] + +[[package]] +name = "const-random-macro" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" +dependencies = [ + "getrandom 0.2.16", + "once_cell", + "tiny-keccak", +] + +[[package]] +name = "constant_time_eq" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9710d3b3739c2e349eb44fe848ad0b7c8cb1e42bd87ee49371df2f7acaf3e675" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crc32fast" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-channel" +version = "0.5.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" + +[[package]] +name = "crypto-common" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "deflate64" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26bf8fc351c5ed29b5c2f0cbbac1b209b74f60ecd62e675a998df72c49af5204" + +[[package]] +name = "defold-nvim-bridge" +version = "0.1.0" +dependencies = [ + "anyhow", + "clap", + "defold-nvim-core", + "dirs", + "netstat2", + "tracing", + "tracing-appender", + "tracing-subscriber", + "which", +] + +[[package]] +name = "defold-nvim-core" +version = "0.1.0" +dependencies = [ + "anyhow", + "dirs", + "edn-rs", + "flate2", + "netstat2", + "pretty_assertions", + "reqwest", + "rust-ini", + "serde", + "serde_json", + "serde_yaml", + "sha3", + "strum", + "sysinfo", + "tar", + "textwrap", + "tracing", + "url", + "version-compare", + "walkdir", + "which", + "zip", +] + +[[package]] +name = "defold-nvim-sidecar" +version = "0.1.0" +dependencies = [ + "anyhow", + "defold-nvim-core", + "dirs", + "mlua", + "tracing", + "tracing-appender", + "tracing-subscriber", +] + +[[package]] +name = "deranged" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ececcb659e7ba858fb4f10388c250a7252eb0a27373f1a72b8748afdd248e587" +dependencies = [ + "powerfmt", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys", +] + +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", +] + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "dlv-list" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" +dependencies = [ + "const-random", +] + +[[package]] +name = "edn-rs" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f976aae7770c1b708fa4dbc0187ff1e58cfe805ec3d855903ec344e3d6b52c6" +dependencies = [ + "ordered-float 4.6.0", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "env_home" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" + +[[package]] +name = "equivalent" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" + +[[package]] +name = "erased-serde" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e8918065695684b2b0702da20382d5ae6065cf3327bc2d6436bd49a71ce9f3" +dependencies = [ + "serde", + "serde_core", + "typeid", +] + +[[package]] +name = "errno" +version = "0.3.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +dependencies = [ + "libc", + "windows-sys 0.61.2", +] + +[[package]] +name = "fastrand" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" + +[[package]] +name = "filetime" +version = "0.2.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc0505cd1b6fa6580283f6bdf70a73fcf4aba1184038c90902b92b3dd0df63ed" +dependencies = [ + "cfg-if", + "libc", + "libredox", + "windows-sys 0.60.2", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "flate2" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" +dependencies = [ + "crc32fast", + "libz-rs-sys", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "form_urlencoded" +version = "1.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-core", + "futures-io", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generic-array" +version = "0.14.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +dependencies = [ + "typenum", + "version_check", +] + +[[package]] +name = "getrandom" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" +dependencies = [ + "cfg-if", + "libc", + "wasi", +] + +[[package]] +name = "getrandom" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "h2" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "hashbrown" +version = "0.14.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" + +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" + +[[package]] +name = "hyper" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +dependencies = [ + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "727805d60e7938b76b826a6ef209eb70eaa1812794f9424d4a4e2d740662df5f" +dependencies = [ + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "system-configuration", + "tokio", + "tower-service", + "tracing", + "windows-registry", +] + +[[package]] +name = "icu_collections" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +dependencies = [ + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" + +[[package]] +name = "icu_properties" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" + +[[package]] +name = "icu_provider" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +dependencies = [ + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "idna" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "indexmap" +version = "2.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2" +dependencies = [ + "equivalent", + "hashbrown 0.16.1", +] + +[[package]] +name = "inout" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" +dependencies = [ + "generic-array", +] + +[[package]] +name = "ipnet" +version = "2.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f867b9d1d896b67beb18518eda36fdb77a32ea590de864f1325b294a6d14397" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +dependencies = [ + "getrandom 0.3.4", + "libc", +] + +[[package]] +name = "js-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "keccak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ecc2af9a1119c51f12a14607e783cb977bde58bc069ff0c3da1095e635d70654" +dependencies = [ + "cpufeatures", +] + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "libbz2-rs-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c4a545a15244c7d945065b5d392b2d2d7f21526fba56ce51467b06ed445e8f7" + +[[package]] +name = "libc" +version = "0.2.178" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "37c93d8daa9d8a012fd8ab92f088405fb202ea0b6ab73ee2482ae66af4f42091" + +[[package]] +name = "libloading" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" +dependencies = [ + "cfg-if", + "windows-link 0.2.1", +] + +[[package]] +name = "libredox" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df15f6eac291ed1cf25865b1ee60399f57e7c227e7f51bdbd4c5270396a9ed50" +dependencies = [ + "bitflags 2.10.0", + "libc", + "redox_syscall 0.6.0", +] + +[[package]] +name = "libz-rs-sys" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15413ef615ad868d4d65dce091cb233b229419c7c0c4bcaa746c0901c49ff39c" +dependencies = [ + "zlib-rs", +] + +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + +[[package]] +name = "litemap" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" + +[[package]] +name = "lock_api" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" +dependencies = [ + "scopeguard", +] + +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "lzma-rust2" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48172246aa7c3ea28e423295dd1ca2589a24617cc4e588bb8cfe177cb2c54d95" +dependencies = [ + "crc", + "sha2", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "mlua" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "935ac67539907efcd7198137eb7358e052555f77fe1b2916600a2249351f2b33" +dependencies = [ + "anyhow", + "bstr", + "either", + "erased-serde", + "libc", + "mlua-sys", + "mlua_derive", + "num-traits", + "parking_lot", + "rustc-hash", + "rustversion", + "serde", + "serde-value", +] + +[[package]] +name = "mlua-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c968af21bf6b19fc9ca8e7b85ee16f86e4c9e3d0591de101a5608086bda0ad8" +dependencies = [ + "cc", + "cfg-if", + "pkg-config", +] + +[[package]] +name = "mlua_derive" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "465bddde514c4eb3b50b543250e97c1d4b284fa3ef7dc0ba2992c77545dbceb2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "native-tls" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "netlink-packet-core" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72724faf704479d67b388da142b186f916188505e7e0b26719019c525882eda4" +dependencies = [ + "anyhow", + "byteorder", + "netlink-packet-utils", +] + +[[package]] +name = "netlink-packet-sock-diag" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a495cb1de50560a7cd12fdcf023db70eec00e340df81be31cedbbfd4aadd6b76" +dependencies = [ + "anyhow", + "bitflags 1.3.2", + "byteorder", + "libc", + "netlink-packet-core", + "netlink-packet-utils", + "smallvec", +] + +[[package]] +name = "netlink-packet-utils" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ede8a08c71ad5a95cdd0e4e52facd37190977039a4704eb82a283f713747d34" +dependencies = [ + "anyhow", + "byteorder", + "paste", + "thiserror 1.0.69", +] + +[[package]] +name = "netlink-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16c903aa70590cb93691bf97a767c8d1d6122d2cc9070433deb3bbf36ce8bd23" +dependencies = [ + "bytes", + "libc", + "log", +] + +[[package]] +name = "netstat2" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "496f264d3ead4870d6b366deb9d20597592d64aac2a907f3e7d07c2325ba4663" +dependencies = [ + "bindgen", + "bitflags 2.10.0", + "byteorder", + "netlink-packet-core", + "netlink-packet-sock-diag", + "netlink-packet-utils", + "netlink-sys", + "num-derive", + "num-traits", + "thiserror 2.0.17", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "ntapi" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c70f219e21142367c70c0b30c6a9e3a14d55b4d12a204d897fbec83a0363f081" +dependencies = [ + "winapi", +] + +[[package]] +name = "nu-ansi-term" +version = "0.50.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-derive" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "objc2-io-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33fafba39597d6dc1fb709123dfa8289d39406734be322956a69f0931c73bb15" +dependencies = [ + "libc", + "objc2-core-foundation", +] + +[[package]] +name = "once_cell" +version = "1.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "once_cell_polyfill" +version = "1.70.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" + +[[package]] +name = "openssl" +version = "0.10.75" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" +dependencies = [ + "bitflags 2.10.0", + "cfg-if", + "foreign-types", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[package]] +name = "option-ext" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" + +[[package]] +name = "ordered-float" +version = "2.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68f19d67e5a2795c94e73e0bb1cc1a7edeb2e28efd39e2e1c9b7a40c1108b11c" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-float" +version = "4.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7bb71e1b3fa6ca1c61f383464aaf2bb0e2f8e772a1f01d486832464de363b951" +dependencies = [ + "num-traits", +] + +[[package]] +name = "ordered-multimap" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" +dependencies = [ + "dlv-list", + "hashbrown 0.14.5", +] + +[[package]] +name = "parking_lot" +version = "0.12.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" +dependencies = [ + "cfg-if", + "libc", + "redox_syscall 0.5.18", + "smallvec", + "windows-link 0.2.1", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "percent-encoding" +version = "2.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pin-utils" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" + +[[package]] +name = "pkg-config" +version = "0.3.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" + +[[package]] +name = "potential_utf" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +dependencies = [ + "zerovec", +] + +[[package]] +name = "powerfmt" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" + +[[package]] +name = "ppmd-rust" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d558c559f0450f16f2a27a1f017ef38468c1090c9ce63c8e51366232d53717b4" + +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "prettyplease" +version = "0.2.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" +dependencies = [ + "proc-macro2", + "syn 2.0.111", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + +[[package]] +name = "redox_syscall" +version = "0.5.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "redox_syscall" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec96166dafa0886eb81fe1c0a388bece180fbef2135f97c1e2cf8302e74b43b5" +dependencies = [ + "bitflags 2.10.0", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.16", + "libredox", + "thiserror 2.0.17", +] + +[[package]] +name = "regex" +version = "1.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" + +[[package]] +name = "reqwest" +version = "0.12.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" +dependencies = [ + "base64", + "bytes", + "encoding_rs", + "futures-channel", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "js-sys", + "log", + "mime", + "native-tls", + "percent-encoding", + "pin-project-lite", + "rustls-pki-types", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tokio-native-tls", + "tower", + "tower-http", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", +] + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" +dependencies = [ + "cc", + "cfg-if", + "getrandom 0.2.16", + "libc", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "rust-ini" +version = "0.21.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7" +dependencies = [ + "cfg-if", + "ordered-multimap", +] + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" + +[[package]] +name = "rustix" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd15f8a2c5551a84d56efdc1cd049089e409ac19a3072d5037a17fd70719ff3e" +dependencies = [ + "bitflags 2.10.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.61.2", +] + +[[package]] +name = "rustls" +version = "0.23.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" +dependencies = [ + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-pki-types" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +dependencies = [ + "zeroize", +] + +[[package]] +name = "rustls-webpki" +version = "0.103.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ffdfa2f5286e2247234e03f680868ac2815974dc39e00ea15adc445d0aafe52" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" + +[[package]] +name = "ryu" +version = "1.0.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62049b2877bf12821e8f9ad256ee38fdc31db7387ec2d3b3f403024de2034aea" + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "schannel" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.10.0", + "core-foundation", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] + +[[package]] +name = "serde-value" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3a1a3341211875ef120e117ea7fd5228530ae7e7036a779fdc9117be6b3282c" +dependencies = [ + "ordered-float 2.10.1", + "serde", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "serde_json" +version = "1.0.145" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", + "serde_core", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" +dependencies = [ + "cfg-if", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha3" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75872d278a8f37ef87fa0ddbda7802605cb18344497949862c0d4dcb291eba60" +dependencies = [ + "digest", + "keccak", +] + +[[package]] +name = "sharded-slab" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +dependencies = [ + "lazy_static", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "smawk" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" + +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + +[[package]] +name = "stable_deref_trait" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "strsim" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "1.0.109" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.111" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + +[[package]] +name = "synstructure" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "sysinfo" +version = "0.37.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16607d5caffd1c07ce073528f9ed972d88db15dd44023fa57142963be3feb11f" +dependencies = [ + "libc", + "memchr", + "ntapi", + "objc2-core-foundation", + "objc2-io-kit", + "windows", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.10.0", + "core-foundation", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "tar" +version = "0.4.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[package]] +name = "tempfile" +version = "3.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" +dependencies = [ + "fastrand", + "getrandom 0.3.4", + "once_cell", + "rustix", + "windows-sys 0.61.2", +] + +[[package]] +name = "textwrap" +version = "0.16.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" +dependencies = [ + "smawk", + "unicode-linebreak", + "unicode-width", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +dependencies = [ + "thiserror-impl 2.0.17", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "thread_local" +version = "1.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "time" +version = "0.3.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e7d9e3bb61134e77bde20dd4825b97c010155709965fedf0f49bb138e52a9d" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40868e7c1d2f0b8d73e4a8c7f0ff63af4f6d19be117e90bd73eb1d62cf831c6b" + +[[package]] +name = "time-macros" +version = "0.2.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30cfb0125f12d9c277f35663a0a33f8c30190f4e4574868a330595412d34ebf3" +dependencies = [ + "num-conv", + "time-core", +] + +[[package]] +name = "tiny-keccak" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" +dependencies = [ + "crunchy", +] + +[[package]] +name = "tinystr" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.61.2", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-http" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +dependencies = [ + "bitflags 2.10.0", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" + +[[package]] +name = "tower-service" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" + +[[package]] +name = "tracing" +version = "0.1.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" +dependencies = [ + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-appender" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" +dependencies = [ + "crossbeam-channel", + "thiserror 2.0.17", + "time", + "tracing-subscriber", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-log" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +dependencies = [ + "log", + "once_cell", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" +dependencies = [ + "nu-ansi-term", + "sharded-slab", + "smallvec", + "thread_local", + "tracing-core", + "tracing-log", +] + +[[package]] +name = "try-lock" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" + +[[package]] +name = "typeid" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" + +[[package]] +name = "typenum" +version = "1.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "unicode-linebreak" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" + +[[package]] +name = "unicode-width" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" + +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + +[[package]] +name = "untrusted" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" + +[[package]] +name = "url" +version = "2.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version-compare" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "836d9622d604feee9e5de25ac10e3ea5f2d65b41eac0d9ce72eb5deae707ce7c" +dependencies = [ + "cfg-if", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn 2.0.111", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.106" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "web-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b32828d774c412041098d182a8b38b16ea816958e07cf40eec2bc080ae137ac" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "which" +version = "8.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3fabb953106c3c8eea8306e4393700d7657561cb43122571b172bbfb7c7ba1d" +dependencies = [ + "env_home", + "rustix", + "winsafe", +] + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" + +[[package]] +name = "windows" +version = "0.61.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +dependencies = [ + "windows-collections", + "windows-core", + "windows-future", + "windows-link 0.1.3", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +dependencies = [ + "windows-core", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement", + "windows-interface", + "windows-link 0.1.3", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-future" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +dependencies = [ + "windows-core", + "windows-link 0.1.3", + "windows-threading", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "windows-link" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-numerics" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +dependencies = [ + "windows-core", + "windows-link 0.1.3", +] + +[[package]] +name = "windows-registry" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" +dependencies = [ + "windows-link 0.2.1", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link 0.2.1", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link 0.2.1", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows-threading" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" +dependencies = [ + "windows-link 0.1.3", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "winsafe" +version = "0.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d135d17ab770252ad95e9a872d365cf3090e3be864a34ab46f48555993efc904" + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + +[[package]] +name = "writeable" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" + +[[package]] +name = "xattr" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" +dependencies = [ + "libc", + "rustix", +] + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "yoke" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +dependencies = [ + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", + "synstructure", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", + "synstructure", +] + +[[package]] +name = "zeroize" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" +dependencies = [ + "zeroize_derive", +] + +[[package]] +name = "zeroize_derive" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "zerotrie" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + +[[package]] +name = "zip" +version = "7.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdd8a47718a4ee5fe78e07667cd36f3de80e7c2bfe727c7074245ffc7303c037" +dependencies = [ + "aes", + "arbitrary", + "bzip2", + "constant_time_eq", + "crc32fast", + "deflate64", + "flate2", + "generic-array", + "getrandom 0.3.4", + "hmac", + "indexmap", + "lzma-rust2", + "memchr", + "pbkdf2", + "ppmd-rust", + "sha1", + "time", + "zeroize", + "zopfli", + "zstd", +] + +[[package]] +name = "zlib-rs" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51f936044d677be1a1168fae1d03b583a285a5dd9d8cbf7b24c23aa1fc775235" + +[[package]] +name = "zopfli" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f05cd8797d63865425ff89b5c4a48804f35ba0ce8d125800027ad6017d2b5249" +dependencies = [ + "bumpalo", + "crc32fast", + "log", + "simd-adler32", +] + +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index cdc4735..7813857 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -8,7 +8,7 @@ anyhow = "1.0.100" tar = "0.4.44" version-compare = "0.2.1" flate2 = "1.1.5" -zip = "6.0.0" +zip = "7.0.0" dirs = "6.0.0" reqwest = { version = "0.12.26", features = ["blocking", "json"] } rust-ini = "0.21.3" From b638875f5eb94fd69c0803662f51187eb281f740 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sat, 20 Dec 2025 21:47:24 +0100 Subject: [PATCH 31/70] remove fail fast --- .github/workflows/release.yml | 14 ++++++++------ .github/workflows/test.yml | 1 + 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 84d2ac1..0632322 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -12,11 +12,12 @@ jobs: name: Release ${{ matrix.config.name }} runs-on: ${{ matrix.config.os }} strategy: + fail-fast: false matrix: config: - name: Linux x86 os: ubuntu-latest - src: + artifact: sidecar: target/release/libdefold_nvim_sidecar.so bridge: target/release/defold-nvim-bridge dist: @@ -24,7 +25,7 @@ jobs: bridge: target/release/linux-x86-defold-nvim-bridge - name: MacOS x86 os: macos-15-intel - src: + artifact: sidecar: target/release/libdefold_nvim_sidecar.dylib bridge: target/release/defold-nvim-bridge dist: @@ -32,7 +33,7 @@ jobs: bridge: target/release/macos-x86-defold-nvim-bridge - name: MacOS ARM os: macos-latest - src: + artifact: sidecar: target/release/libdefold_nvim_sidecar.dylib bridge: target/release/defold-nvim-bridge dist: @@ -40,7 +41,7 @@ jobs: bridge: target/release/macos-arm-defold-nvim-bridge - name: Windows x86 os: windows-latest - src: + artifact: sidecar: target/release/defold_nvim_sidecar.dll bridge: target/release/defold-nvim-bridge.exe dist: @@ -59,8 +60,9 @@ jobs: - name: prepare ${{ matrix.config.name }} run: | # rename builds to be unique - mv "${{ matrix.config.src.sidecar }}" "${{ matrix.config.dist.sidecar }}" - mv "${{ matrix.config.src.bridge }}" "${{ matrix.config.dist.bridge }}" + mv "${{ matrix.config.artifact.sidecar }}" "${{ matrix.config.dist.sidecar }}" + mv "${{ matrix.config.artifact.bridge }}" "${{ matrix.config.dist.bridge }}" + # generate sha256 sum shasum -a 256 "${{ matrix.config.dist.sidecar }}" > "${{ matrix.config.dist.sidecar }}.sha256" shasum -a 256 "${{ matrix.config.dist.bridge }}" > "${{ matrix.config.dist.bridge }}.sha256" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 39fb0e6..fa6ea94 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,6 +9,7 @@ jobs: name: Test on ${{ matrix.os }} runs-on: ${{ matrix.os }} strategy: + fail-fast: false matrix: os: - ubuntu-latest From b374043c7ff8d63df304d9ae55677958eb3d80b9 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sat, 20 Dec 2025 21:52:57 +0100 Subject: [PATCH 32/70] build macos with deployment target --- .github/workflows/release.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0632322..37e963c 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -55,8 +55,13 @@ jobs: rustflags: "" - name: build ${{ matrix.config.name }} + if: ${{ !contains(matrix.config.os, 'macos') }} run: cargo build --release + - name: build ${{ matrix.config.name }} + if: ${{ contains(matrix.config.os, 'macos') }} + run: MACOSX_DEPLOYMENT_TARGET="13" cargo build --release + - name: prepare ${{ matrix.config.name }} run: | # rename builds to be unique From 7a874e004c13554ffa59c84e1b6827893385b111 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sat, 20 Dec 2025 22:28:46 +0100 Subject: [PATCH 33/70] move macos specific build args to build.rs --- .github/workflows/release.yml | 5 ----- crates/bridge/src/main.rs | 2 +- crates/sidecar/.cargo/config.toml | 11 ----------- crates/sidecar/build.rs | 7 +++++++ 4 files changed, 8 insertions(+), 17 deletions(-) delete mode 100644 crates/sidecar/.cargo/config.toml create mode 100644 crates/sidecar/build.rs diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 37e963c..0632322 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -55,13 +55,8 @@ jobs: rustflags: "" - name: build ${{ matrix.config.name }} - if: ${{ !contains(matrix.config.os, 'macos') }} run: cargo build --release - - name: build ${{ matrix.config.name }} - if: ${{ contains(matrix.config.os, 'macos') }} - run: MACOSX_DEPLOYMENT_TARGET="13" cargo build --release - - name: prepare ${{ matrix.config.name }} run: | # rename builds to be unique diff --git a/crates/bridge/src/main.rs b/crates/bridge/src/main.rs index 7a92db3..ba67ee7 100644 --- a/crates/bridge/src/main.rs +++ b/crates/bridge/src/main.rs @@ -4,7 +4,7 @@ use std::{ }; use anyhow::{Context, Result}; -use clap::{Parser, Subcommand, command}; +use clap::{Parser, Subcommand}; use defold_nvim_core::{ editor, focus::{focus_game, focus_neovim}, diff --git a/crates/sidecar/.cargo/config.toml b/crates/sidecar/.cargo/config.toml deleted file mode 100644 index d47f983..0000000 --- a/crates/sidecar/.cargo/config.toml +++ /dev/null @@ -1,11 +0,0 @@ -[target.x86_64-apple-darwin] -rustflags = [ - "-C", "link-arg=-undefined", - "-C", "link-arg=dynamic_lookup", -] - -[target.aarch64-apple-darwin] -rustflags = [ - "-C", "link-arg=-undefined", - "-C", "link-arg=dynamic_lookup", -] diff --git a/crates/sidecar/build.rs b/crates/sidecar/build.rs new file mode 100644 index 0000000..4fb88f0 --- /dev/null +++ b/crates/sidecar/build.rs @@ -0,0 +1,7 @@ +fn main() { + let target_os = std::env::var("CARGO_CFG_TARGET_OS").unwrap(); + if target_os == "macos" { + println!("cargo:rustc-link-arg=-undefined"); + println!("cargo:rustc-link-arg=dynamic_lookup"); + } +} From 888baa989d87ef45c2f59b834de2ef1c659156ec Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sat, 20 Dec 2025 23:43:41 +0100 Subject: [PATCH 34/70] fix artifact names, remove libssl dependency (use rustls), implement auto downloading of sidecar --- .github/workflows/release.yml | 6 +- Cargo.lock | 461 +++++++++++++--------------------- crates/core/Cargo.toml | 6 +- lua/defold/service/github.lua | 50 ++++ lua/defold/service/os.lua | 57 ++++- lua/defold/sidecar.lua | 97 +++++-- 6 files changed, 365 insertions(+), 312 deletions(-) create mode 100644 lua/defold/service/github.lua diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0632322..317bd48 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -29,7 +29,7 @@ jobs: sidecar: target/release/libdefold_nvim_sidecar.dylib bridge: target/release/defold-nvim-bridge dist: - sidecar: target/release/macos-x86-libdefold_nvim_sidecar.so + sidecar: target/release/macos-x86-libdefold_nvim_sidecar.dylib bridge: target/release/macos-x86-defold-nvim-bridge - name: MacOS ARM os: macos-latest @@ -37,7 +37,7 @@ jobs: sidecar: target/release/libdefold_nvim_sidecar.dylib bridge: target/release/defold-nvim-bridge dist: - sidecar: target/release/macos-arm-libdefold_nvim_sidecar.so + sidecar: target/release/macos-arm-libdefold_nvim_sidecar.dylib bridge: target/release/macos-arm-defold-nvim-bridge - name: Windows x86 os: windows-latest @@ -45,7 +45,7 @@ jobs: sidecar: target/release/defold_nvim_sidecar.dll bridge: target/release/defold-nvim-bridge.exe dist: - sidecar: target/release/windows-x86-libdefold_nvim_sidecar.so + sidecar: target/release/windows-x86-defold_nvim_sidecar.dll bridge: target/release/windows-x86-defold-nvim-bridge steps: diff --git a/Cargo.lock b/Cargo.lock index 279fb1e..958bd7b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -216,6 +216,12 @@ version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +[[package]] +name = "cfg_aliases" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" + [[package]] name = "cipher" version = "0.4.4" @@ -309,22 +315,6 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" -[[package]] -name = "core-foundation" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "core-foundation-sys" -version = "0.8.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" - [[package]] name = "cpufeatures" version = "0.2.17" @@ -544,15 +534,6 @@ version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" -[[package]] -name = "encoding_rs" -version = "0.8.35" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" -dependencies = [ - "cfg-if", -] - [[package]] name = "env_home" version = "0.1.0" @@ -586,12 +567,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - [[package]] name = "filetime" version = "0.2.26" @@ -621,27 +596,6 @@ dependencies = [ "miniz_oxide", ] -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foreign-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" -dependencies = [ - "foreign-types-shared", -] - -[[package]] -name = "foreign-types-shared" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - [[package]] name = "form_urlencoded" version = "1.2.2" @@ -718,8 +672,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", + "js-sys", "libc", "wasi", + "wasm-bindgen", ] [[package]] @@ -729,9 +685,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi", "wasip2", + "wasm-bindgen", ] [[package]] @@ -740,25 +698,6 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" -[[package]] -name = "h2" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c0b69cfcb4e1b9f1bf2f53f95f766e4661169728ec61cd3fe5a0166f2d1386" -dependencies = [ - "atomic-waker", - "bytes", - "fnv", - "futures-core", - "futures-sink", - "http", - "indexmap", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "hashbrown" version = "0.14.5" @@ -835,7 +774,6 @@ dependencies = [ "bytes", "futures-channel", "futures-core", - "h2", "http", "http-body", "httparse", @@ -861,22 +799,7 @@ dependencies = [ "tokio", "tokio-rustls", "tower-service", -] - -[[package]] -name = "hyper-tls" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" -dependencies = [ - "bytes", - "http-body-util", - "hyper", - "hyper-util", - "native-tls", - "tokio", - "tokio-native-tls", - "tower-service", + "webpki-roots", ] [[package]] @@ -898,11 +821,9 @@ dependencies = [ "percent-encoding", "pin-project-lite", "socket2", - "system-configuration", "tokio", "tower-service", "tracing", - "windows-registry", ] [[package]] @@ -1167,6 +1088,12 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "lzma-rust2" version = "0.15.4" @@ -1183,12 +1110,6 @@ version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - [[package]] name = "minimal-lexical" version = "0.2.1" @@ -1259,23 +1180,6 @@ dependencies = [ "syn 2.0.111", ] -[[package]] -name = "native-tls" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" -dependencies = [ - "libc", - "log", - "openssl", - "openssl-probe", - "openssl-sys", - "schannel", - "security-framework", - "security-framework-sys", - "tempfile", -] - [[package]] name = "netlink-packet-core" version = "0.7.0" @@ -1428,50 +1332,6 @@ version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" -[[package]] -name = "openssl" -version = "0.10.75" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08838db121398ad17ab8531ce9de97b244589089e290a384c900cb9ff7434328" -dependencies = [ - "bitflags 2.10.0", - "cfg-if", - "foreign-types", - "libc", - "once_cell", - "openssl-macros", - "openssl-sys", -] - -[[package]] -name = "openssl-macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - -[[package]] -name = "openssl-probe" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" - -[[package]] -name = "openssl-sys" -version = "0.9.111" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321" -dependencies = [ - "cc", - "libc", - "pkg-config", - "vcpkg", -] - [[package]] name = "option-ext" version = "0.2.0" @@ -1590,6 +1450,15 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d558c559f0450f16f2a27a1f017ef38468c1090c9ce63c8e51366232d53717b4" +[[package]] +name = "ppv-lite86" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] + [[package]] name = "pretty_assertions" version = "1.4.1" @@ -1619,6 +1488,61 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quinn" +version = "0.11.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.17", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.17", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", +] + [[package]] name = "quote" version = "1.0.42" @@ -1634,6 +1558,35 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.4", +] + [[package]] name = "redox_syscall" version = "0.5.18" @@ -1700,31 +1653,28 @@ checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" dependencies = [ "base64", "bytes", - "encoding_rs", "futures-channel", "futures-core", "futures-util", - "h2", "http", "http-body", "http-body-util", "hyper", "hyper-rustls", - "hyper-tls", "hyper-util", "js-sys", "log", - "mime", - "native-tls", "percent-encoding", "pin-project-lite", + "quinn", + "rustls", "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", "tokio", - "tokio-native-tls", + "tokio-rustls", "tower", "tower-http", "tower-service", @@ -1732,6 +1682,7 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", + "webpki-roots", ] [[package]] @@ -1784,6 +1735,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "533f54bc6a7d4f647e46ad909549eda97bf5afc1585190ef692b4286b198bd8f" dependencies = [ "once_cell", + "ring", "rustls-pki-types", "rustls-webpki", "subtle", @@ -1796,6 +1748,7 @@ version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" dependencies = [ + "web-time", "zeroize", ] @@ -1831,44 +1784,12 @@ dependencies = [ "winapi-util", ] -[[package]] -name = "schannel" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" -dependencies = [ - "windows-sys 0.61.2", -] - [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.10.0", - "core-foundation", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc1f0cbffaac4852523ce30d8bd3c5cdc873501d96ff467ca09b6767bb8cd5c0" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "serde" version = "1.0.228" @@ -2123,27 +2044,6 @@ dependencies = [ "windows", ] -[[package]] -name = "system-configuration" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" -dependencies = [ - "bitflags 2.10.0", - "core-foundation", - "system-configuration-sys", -] - -[[package]] -name = "system-configuration-sys" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" -dependencies = [ - "core-foundation-sys", - "libc", -] - [[package]] name = "tar" version = "0.4.44" @@ -2155,19 +2055,6 @@ dependencies = [ "xattr", ] -[[package]] -name = "tempfile" -version = "3.23.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d31c77bdf42a745371d260a26ca7163f1e0924b64afa0b688e61b5a9fa02f16" -dependencies = [ - "fastrand", - "getrandom 0.3.4", - "once_cell", - "rustix", - "windows-sys 0.61.2", -] - [[package]] name = "textwrap" version = "0.16.2" @@ -2278,6 +2165,21 @@ dependencies = [ "zerovec", ] +[[package]] +name = "tinyvec" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +dependencies = [ + "tinyvec_macros", +] + +[[package]] +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + [[package]] name = "tokio" version = "1.48.0" @@ -2292,16 +2194,6 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "tokio-native-tls" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" -dependencies = [ - "native-tls", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.26.4" @@ -2312,19 +2204,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-util" -version = "0.7.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" -dependencies = [ - "bytes", - "futures-core", - "futures-sink", - "pin-project-lite", - "tokio", -] - [[package]] name = "tower" version = "0.5.2" @@ -2517,12 +2396,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" -[[package]] -name = "vcpkg" -version = "0.2.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - [[package]] name = "version-compare" version = "0.2.1" @@ -2637,6 +2510,25 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2878ef029c47c6e8cf779119f20fcf52bde7ad42a731b2a304bc221df17571e" +dependencies = [ + "rustls-pki-types", +] + [[package]] name = "which" version = "8.0.0" @@ -2710,8 +2602,8 @@ dependencies = [ "windows-implement", "windows-interface", "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", + "windows-result", + "windows-strings", ] [[package]] @@ -2769,17 +2661,6 @@ dependencies = [ "windows-link 0.1.3", ] -[[package]] -name = "windows-registry" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720" -dependencies = [ - "windows-link 0.2.1", - "windows-result 0.4.1", - "windows-strings 0.5.1", -] - [[package]] name = "windows-result" version = "0.3.4" @@ -2789,15 +2670,6 @@ dependencies = [ "windows-link 0.1.3", ] -[[package]] -name = "windows-result" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" -dependencies = [ - "windows-link 0.2.1", -] - [[package]] name = "windows-strings" version = "0.4.2" @@ -2807,15 +2679,6 @@ dependencies = [ "windows-link 0.1.3", ] -[[package]] -name = "windows-strings" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" -dependencies = [ - "windows-link 0.2.1", -] - [[package]] name = "windows-sys" version = "0.52.0" @@ -3038,6 +2901,26 @@ dependencies = [ "synstructure", ] +[[package]] +name = "zerocopy" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd74ec98b9250adb3ca554bdde269adf631549f51d8a8f8f0a10b50f1cb298c3" +dependencies = [ + "zerocopy-derive", +] + +[[package]] +name = "zerocopy-derive" +version = "0.8.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a8d209fdf45cf5138cbb5a506f6b52522a25afccc534d1475dad8e31105c6a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "zerofrom" version = "0.1.6" diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 7813857..b01d9ea 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -10,7 +10,11 @@ version-compare = "0.2.1" flate2 = "1.1.5" zip = "7.0.0" dirs = "6.0.0" -reqwest = { version = "0.12.26", features = ["blocking", "json"] } +reqwest = { version = "0.12.26", default-features = false, features = [ + "blocking", + "json", + "rustls-tls", +] } rust-ini = "0.21.3" serde = { version = "1.0.228", features = ["derive"] } serde_json = "1.0.145" diff --git a/lua/defold/service/github.lua b/lua/defold/service/github.lua new file mode 100644 index 0000000..ea91cdc --- /dev/null +++ b/lua/defold/service/github.lua @@ -0,0 +1,50 @@ +local M = {} + +---Fetch the latest github release of owner/repository +---@param owner string +---@param repository string +---@return table|nil +function M.fetch_release(owner, repository) + local os = require "defold.service.os" + local log = require "defold.service.logger" + local url = string.format("https://api.github.com/repos/%s/%s/releases/latest", owner, repository) + local release = os.fetch_json(url) + + if not release then + log.error(string.format("Unable to fetch github release %s/%s", owner, repository)) + return nil + end + + log.debug(string.format("Fetched %s/%s version: %s", owner, repository, release.tag_name)) + return release +end + +---Fetch latest github release and download asset with `name`. Returns path to file or nil on error +---@param owner string +---@param repository string +---@param name string +---@return string|nil +---@return table|nil +function M.download_release(owner, repository, name) + local osm = require "defold.service.os" + local log = require "defold.service.logger" + local release = M.fetch_release(owner, repository) + + if not release then + log.error(string.format("Unable to download github release %s/%s file: %s", owner, repository, name)) + return nil, nil + end + + for _, asset in ipairs(release.assets) do + if asset.name == name then + local tmp = os.tmpname() + osm.download(asset.browser_download_url, tmp) + return tmp, release + end + end + + log.error(string.format("Unable to find github release %s/%s file: %s", owner, repository, name)) + return nil, nil +end + +return M diff --git a/lua/defold/service/os.lua b/lua/defold/service/os.lua index f79e7ed..bbf876b 100644 --- a/lua/defold/service/os.lua +++ b/lua/defold/service/os.lua @@ -1,5 +1,8 @@ local M = {} +local user_agent = + "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36" + ---@param command string ---@return string function M.exec(command) @@ -68,23 +71,55 @@ function M.download(url, to_path) log.debug(string.format("Downloading '%s' to '%s'", url, to_path)) if M.is_windows() then - M.exec(string.format('powershell -Command "Invoke-WebRequest -Uri %s -OutFile %s"', url, to_path)) + M.exec( + string.format( + 'powershell -Command "Invoke-WebRequest -Uri %s -UserAgent "%s" -OutFile %s"', + url, + user_agent, + to_path + ) + ) return end if M.command_exists "curl" then - M.exec(string.format("curl -L -s -o '%s' %s", to_path, url)) + M.exec(string.format("curl -L -s --user-agent '%s' -o '%s' %s", user_agent, to_path, url)) return end if M.command_exists "wget" then - M.exec(string.format("wget -q -O '%s' %s", to_path, url)) + M.exec(string.format("wget -q --user-agent '%s' -O '%s' %s", user_agent, to_path, url)) return end log.error "Could not find a command to download something like 'curl', 'wget' or 'powershell'" end +---Fetch json via url and return it as a lua table. Returns nil on error +---@param url string +---@return table|nil +function M.fetch_json(url) + local log = require "defold.service.logger" + + log.debug(string.format("Fetching JSON from '%s'", url)) + + local tmp_path = os.tmpname() + M.download(url, tmp_path) + + local file = io.open(tmp_path, "r") + if not file then + log.error "Failed to open downloaded json file" + os.remove(tmp_path) + return nil + end + + local data = file:read "*a" + file:close() + os.remove(tmp_path) + + return vim.json.decode(data) +end + ---Move file from location a to b ---@param from_path string ---@param to_path string @@ -165,4 +200,20 @@ function M.cache_dir() return dir end +---Writes (and overwrites if present) `content` to `path` +---@param path string +---@param content string +function M.write(path, content) + local file = io.open(path, "w") + + if not file then + local log = require "defold.service.logger" + log.error(string.format("Could not open file for writing: %s", path)) + return + end + + file:write(content) + file:close() +end + return M diff --git a/lua/defold/sidecar.lua b/lua/defold/sidecar.lua index 75e85aa..8b69400 100644 --- a/lua/defold/sidecar.lua +++ b/lua/defold/sidecar.lua @@ -1,3 +1,28 @@ +local github_owner = "atomicptr" +local github_repository = "defold.nvim" +local github_file_name = { + linux = { + amd64 = "linux-x86-libdefold_nvim_sidecar.so", + }, + macos = { + amd64 = "macos-x86-libdefold_nvim_sidecar.dylib", + aarch64 = "macos-arm-libdefold_nvim_sidecar.dylib", + }, + windows = { + amd64 = "windows-x86-defold_nvim_sidecar.dll", + }, +} + +local function lib_name() + local os = require "defold.service.os" + + if os.is_windows() then + return "defold_nvim_sidecar" + end + + return "libdefold_nvim_sidecar" +end + local function lib_extension() local os = require "defold.service.os" @@ -10,31 +35,71 @@ local function lib_extension() end end +---Download latest sidecar release, install it at DATA_DIR/lib and return the lib path +---@return string|nil +local function download_release() + local log = require "defold.service.logger" + local os = require "defold.service.os" + local github = require "defold.service.github" + + local filename = (github_file_name[os.name()] or {})[os.architecture()] + + if not filename then + log.error(string.format("unsupported platform: %s using %s", os.name(), os.architecture())) + return nil + end + + local file, release = github.download_release(github_owner, github_repository, filename) + if not file or not release then + return nil + end + + local lib_dir = vim.fs.joinpath(os.data_dir(), "lib") + vim.fn.mkdir(lib_dir, "p") + + os.move(file, vim.fs.joinpath(lib_dir, lib_name() .. lib_extension())) + + local meta_dir = vim.fs.joinpath(os.data_dir(), "meta") + vim.fn.mkdir(meta_dir, "p") + + -- write version to file + os.write(vim.fs.joinpath(meta_dir, "sidecar_version"), release.tag_name) + + return lib_dir +end + local function find_rust_lib_rootdir() local os = require "defold.service.os" - local plugin_root = os.plugin_root() local file_name = string.format("defold_nvim_sidecar%s", lib_extension()) local file_name_alt = string.format("libdefold_nvim_sidecar%s", lib_extension()) + local plugin_root = os.plugin_root() + local lib_dir = vim.fs.joinpath(os.data_dir(), "lib") + + -- if + -- -- check local debug build first + -- os.file_exists(vim.fs.joinpath(plugin_root, "target", "debug", file_name)) + -- or os.file_exists(vim.fs.joinpath(plugin_root, "target", "debug", file_name_alt)) + -- then + -- return vim.fs.joinpath(plugin_root, "target", "debug") + -- elseif + -- -- check local release build second + -- os.file_exists(vim.fs.joinpath(plugin_root, "release", "debug", file_name)) + -- or os.file_exists(vim.fs.joinpath(plugin_root, "release", "debug", file_name_alt)) + -- then + -- return vim.fs.joinpath(plugin_root, "target", "release") + -- elseif if - os.file_exists(vim.fs.joinpath(plugin_root, file_name)) - or os.file_exists(vim.fs.joinpath(plugin_root, file_name_alt)) - then - return plugin_root - elseif - os.file_exists(vim.fs.joinpath(plugin_root, "target", "debug", file_name)) - or os.file_exists(vim.fs.joinpath(plugin_root, "target", "debug", file_name_alt)) - then - return vim.fs.joinpath(plugin_root, "target", "debug") - elseif - os.file_exists(vim.fs.joinpath(plugin_root, "release", "debug", file_name)) - or os.file_exists(vim.fs.joinpath(plugin_root, "release", "debug", file_name_alt)) + -- and the actual properly installed path last + os.file_exists(vim.fs.joinpath(lib_dir, file_name)) + or os.file_exists(vim.fs.joinpath(lib_dir, file_name_alt)) then - return vim.fs.joinpath(plugin_root, "target", "release") + -- TODO: check if version is outdated, if yes replace + return lib_dir else - -- TODO: add auto download - error "Error: Could not find rust lib" + -- and if that also doesnt exist... download it + return download_release() end end From d9c0a62b98b84490329e67f8155f9aef01995f50 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sat, 20 Dec 2025 23:48:15 +0100 Subject: [PATCH 35/70] restore loading debug builds --- lua/defold/sidecar.lua | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/lua/defold/sidecar.lua b/lua/defold/sidecar.lua index 8b69400..4522f00 100644 --- a/lua/defold/sidecar.lua +++ b/lua/defold/sidecar.lua @@ -77,20 +77,19 @@ local function find_rust_lib_rootdir() local plugin_root = os.plugin_root() local lib_dir = vim.fs.joinpath(os.data_dir(), "lib") - -- if - -- -- check local debug build first - -- os.file_exists(vim.fs.joinpath(plugin_root, "target", "debug", file_name)) - -- or os.file_exists(vim.fs.joinpath(plugin_root, "target", "debug", file_name_alt)) - -- then - -- return vim.fs.joinpath(plugin_root, "target", "debug") - -- elseif - -- -- check local release build second - -- os.file_exists(vim.fs.joinpath(plugin_root, "release", "debug", file_name)) - -- or os.file_exists(vim.fs.joinpath(plugin_root, "release", "debug", file_name_alt)) - -- then - -- return vim.fs.joinpath(plugin_root, "target", "release") - -- elseif if + -- check local debug build first + os.file_exists(vim.fs.joinpath(plugin_root, "target", "debug", file_name)) + or os.file_exists(vim.fs.joinpath(plugin_root, "target", "debug", file_name_alt)) + then + return vim.fs.joinpath(plugin_root, "target", "debug") + elseif + -- check local release build second + os.file_exists(vim.fs.joinpath(plugin_root, "release", "debug", file_name)) + or os.file_exists(vim.fs.joinpath(plugin_root, "release", "debug", file_name_alt)) + then + return vim.fs.joinpath(plugin_root, "target", "release") + elseif -- and the actual properly installed path last os.file_exists(vim.fs.joinpath(lib_dir, file_name)) or os.file_exists(vim.fs.joinpath(lib_dir, file_name_alt)) From 0adac55eee09191fdae6631952284b2d258eb1e1 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 01:09:42 +0100 Subject: [PATCH 36/70] add build step to lazy.nvim setup --- README.md | 5 ++++- lua/defold/init.lua | 5 +++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3456166..3814408 100644 --- a/README.md +++ b/README.md @@ -88,7 +88,10 @@ You're in luck, powershell is surprisingly capable so there is nothing else need "mfussenegger/nvim-dap", }, - version = "*", + -- This makes sure the native library downloads at installation + build = function() + require("defold").download() + end, opts = { -- config options, see below diff --git a/lua/defold/init.lua b/lua/defold/init.lua index 3d8a1b2..2ca7016 100644 --- a/lua/defold/init.lua +++ b/lua/defold/init.lua @@ -213,6 +213,11 @@ function M.editor_port() return -1 end +---Makes sure the native library `sidecar` gets loaded +function M.download() + require "defold.sidecar" +end + function M.load_plugin() if M.loaded then return From b8fab5ba344d7493fe7bd47064d0337fe92fadec Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 01:19:58 +0100 Subject: [PATCH 37/70] fix initialization only working when opts are supplied --- lua/defold/init.lua | 4 ++++ plugin/defold.lua | 1 + 2 files changed, 5 insertions(+) create mode 100644 plugin/defold.lua diff --git a/lua/defold/init.lua b/lua/defold/init.lua index 2ca7016..2227ce6 100644 --- a/lua/defold/init.lua +++ b/lua/defold/init.lua @@ -108,6 +108,10 @@ end ---@param opts DefoldNvimConfig|nil function M.setup(opts) + if M.loaded then + return + end + local log = require "defold.service.logger" M.config = vim.tbl_deep_extend("force", default_config, opts or {}) diff --git a/plugin/defold.lua b/plugin/defold.lua new file mode 100644 index 0000000..ed90286 --- /dev/null +++ b/plugin/defold.lua @@ -0,0 +1 @@ +require("defold").setup() From c8d6dbac2402fdc40ac895f3cde6540211192f68 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 01:27:23 +0100 Subject: [PATCH 38/70] add support for zigbuild and build linux with glibc 2.31 --- .github/workflows/release.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 317bd48..0d295ad 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,6 +17,7 @@ jobs: config: - name: Linux x86 os: ubuntu-latest + target: x86_64-unknown-linux-gnu.2.31 artifact: sidecar: target/release/libdefold_nvim_sidecar.so bridge: target/release/defold-nvim-bridge @@ -25,6 +26,7 @@ jobs: bridge: target/release/linux-x86-defold-nvim-bridge - name: MacOS x86 os: macos-15-intel + target: x86_64-apple-darwin artifact: sidecar: target/release/libdefold_nvim_sidecar.dylib bridge: target/release/defold-nvim-bridge @@ -33,6 +35,7 @@ jobs: bridge: target/release/macos-x86-defold-nvim-bridge - name: MacOS ARM os: macos-latest + target: aarch64-apple-darwin artifact: sidecar: target/release/libdefold_nvim_sidecar.dylib bridge: target/release/defold-nvim-bridge @@ -41,6 +44,7 @@ jobs: bridge: target/release/macos-arm-defold-nvim-bridge - name: Windows x86 os: windows-latest + target: x86_64-pc-windows-gnu artifact: sidecar: target/release/defold_nvim_sidecar.dll bridge: target/release/defold-nvim-bridge.exe @@ -55,7 +59,9 @@ jobs: rustflags: "" - name: build ${{ matrix.config.name }} - run: cargo build --release + run: | + cargo install cargo-zigbuild + cargo zigbuild --release --target ${{ matrix.config.target }} - name: prepare ${{ matrix.config.name }} run: | From 4d530085786192b98ea18fb1f06ee827f80f9e75 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 01:29:26 +0100 Subject: [PATCH 39/70] add zig to pipeline --- .github/workflows/release.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 0d295ad..cff19c9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -54,6 +54,7 @@ jobs: steps: - uses: actions/checkout@v5 + - uses: mlugg/setup-zig@v2 - uses: actions-rust-lang/setup-rust-toolchain@v1 with: rustflags: "" From f86f23c1d9fed9d838870efe4582503866bac42c Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 01:41:11 +0100 Subject: [PATCH 40/70] only build linux with zigbuild --- .github/workflows/release.yml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cff19c9..b0a5043 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -17,16 +17,14 @@ jobs: config: - name: Linux x86 os: ubuntu-latest - target: x86_64-unknown-linux-gnu.2.31 artifact: - sidecar: target/release/libdefold_nvim_sidecar.so - bridge: target/release/defold-nvim-bridge + sidecar: target/x86_64-unknown-linux-gnu/release/libdefold_nvim_sidecar.so + bridge: target/x86_64-unknown-linux-gnu/release/defold-nvim-bridge dist: sidecar: target/release/linux-x86-libdefold_nvim_sidecar.so bridge: target/release/linux-x86-defold-nvim-bridge - name: MacOS x86 os: macos-15-intel - target: x86_64-apple-darwin artifact: sidecar: target/release/libdefold_nvim_sidecar.dylib bridge: target/release/defold-nvim-bridge @@ -35,7 +33,6 @@ jobs: bridge: target/release/macos-x86-defold-nvim-bridge - name: MacOS ARM os: macos-latest - target: aarch64-apple-darwin artifact: sidecar: target/release/libdefold_nvim_sidecar.dylib bridge: target/release/defold-nvim-bridge @@ -44,7 +41,6 @@ jobs: bridge: target/release/macos-arm-defold-nvim-bridge - name: Windows x86 os: windows-latest - target: x86_64-pc-windows-gnu artifact: sidecar: target/release/defold_nvim_sidecar.dll bridge: target/release/defold-nvim-bridge.exe @@ -60,9 +56,18 @@ jobs: rustflags: "" - name: build ${{ matrix.config.name }} + if: contains(matrix.config.os, 'ubuntu') run: | cargo install cargo-zigbuild - cargo zigbuild --release --target ${{ matrix.config.target }} + cargo zigbuild --release --target x86_64-unknown-linux-gnu.2.31 + + - name: build ${{ matrix.config.name }} + if: contains(matrix.config.os, 'macos') + run: MACOSX_DEPLOYMENT_TARGET="13" cargo build --release + + - name: build ${{ matrix.config.name }} + if: contains(matrix.config.os, 'windows') + run: cargo build --release - name: prepare ${{ matrix.config.name }} run: | From 48aed33d9d7dd171e80f4bd7752f846dbb6a8d87 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 09:04:44 +0100 Subject: [PATCH 41/70] add bridge download/management --- crates/core/src/bridge.rs | 142 +++++++++++++++++++++++++++++++ crates/core/src/editor_config.rs | 29 +------ crates/core/src/lib.rs | 1 + crates/sidecar/src/lib.rs | 12 ++- lua/defold/init.lua | 16 +++- lua/defold/sidecar.lua | 1 + 6 files changed, 172 insertions(+), 29 deletions(-) create mode 100644 crates/core/src/bridge.rs diff --git a/crates/core/src/bridge.rs b/crates/core/src/bridge.rs new file mode 100644 index 0000000..bf0c30a --- /dev/null +++ b/crates/core/src/bridge.rs @@ -0,0 +1,142 @@ +use anyhow::{Context, Result, bail}; +use std::{ + fs::{self}, + path::{Path, PathBuf}, + time::Duration, +}; +use version_compare::Version; + +use crate::github; + +#[cfg(target_os = "windows")] +const EXE_SUFFIX: &'static str = ".exe"; + +#[cfg(not(target_os = "windows"))] +const EXE_SUFFIX: &str = ""; + +#[cfg(target_os = "linux")] +const NAME: &str = "linux-x86-defold-nvim-bridge"; + +#[cfg(all(target_os = "macos", target_arch = "x86_64"))] +const NAME: &str = "macos-x86-defold-nvim-bridge"; + +#[cfg(all(target_os = "macos", target_arch = "aarch64"))] +const NAME: &str = "macos-arm-defold-nvim-bridge"; + +#[cfg(target_os = "windows")] +const NAME: &str = "windows-x86-defold-nvim-bridge"; + +const OWNER: &str = "atomicptr"; +const REPOSITORY: &str = "defold.nvim"; + +pub fn path(plugin_root: &Path) -> Result { + let exe = exe_name(); + + if plugin_root.exists() { + let candidates = [ + plugin_root.join(&exe), + plugin_root.join("target").join("debug").join(&exe), + plugin_root.join("target").join("release").join(&exe), + ]; + + if let Some(bridge_path) = candidates.into_iter().find(|p| p.exists()) { + return Ok(bridge_path); + } + } + + install() +} + +fn exe_name() -> String { + format!("defold-nvim-bridge{EXE_SUFFIX}") +} + +fn local_path() -> Result { + let dir = dirs::data_dir() + .context("could not get state dir")? + .join("defold.nvim") + .join("bin"); + + fs::create_dir_all(&dir)?; + + Ok(dir.join(exe_name())) +} + +fn version_path() -> Result { + let dir = dirs::data_dir() + .context("could not get state dir")? + .join("defold.nvim") + .join("meta"); + + fs::create_dir_all(&dir)?; + + Ok(dir.join("bridge_version")) +} + +fn version() -> Result { + let file = version_path()?; + + if !file.exists() { + bail!("Version not found"); + } + + Ok(fs::read_to_string(file)?) +} + +fn is_update_available() -> Result { + if version_path()?.exists() { + // if the version file is younger than a week dont bother + let last_modified = version_path()?.metadata()?.modified()?; + if last_modified.elapsed()? < Duration::from_hours(24 * 7) { + return Ok(false); + } + } + + let Ok(v) = version() else { + return Ok(true); + }; + + // re-write the file again so that we only check once a week + fs::write(version_path()?, &v)?; + + tracing::debug!("Bridge Version {v} installed"); + + let Some(installed) = Version::from(&v) else { + return Ok(true); + }; + + let release = github::fetch_release(OWNER, REPOSITORY)?; + + tracing::debug!("Bridge Version {} is newest", release.tag_name); + + let Some(current) = Version::from(&release.tag_name) else { + return Ok(false); + }; + + Ok(current > installed) +} + +fn install() -> Result { + let path = local_path()?; + + if path.exists() && !is_update_available()? { + return local_path(); + } + + let (downloaded_file, release) = github::download_release(OWNER, REPOSITORY, NAME)?; + + tracing::debug!("New Bridge version found {}", release.tag_name); + + fs::rename(downloaded_file, &path)?; + fs::write(version_path()?, release.tag_name)?; + + #[cfg(any(target_os = "linux", target_os = "macos"))] + { + use std::{fs::Permissions, os::unix::fs::PermissionsExt}; + fs::set_permissions(&path, Permissions::from_mode(0o700))?; + } + + github::clear_downloads(OWNER, REPOSITORY)?; + + Ok(path) +} diff --git a/crates/core/src/editor_config.rs b/crates/core/src/editor_config.rs index 62d608d..ad3c723 100644 --- a/crates/core/src/editor_config.rs +++ b/crates/core/src/editor_config.rs @@ -7,6 +7,8 @@ use std::{ str::FromStr, }; +use crate::bridge; + #[derive(Debug, Deserialize)] pub enum LauncherType { #[serde(rename = "neovide")] @@ -104,31 +106,6 @@ const SCRIPT_EXT: &'static str = "bat"; #[cfg(not(target_os = "windows"))] const SCRIPT_EXT: &str = "sh"; -#[cfg(target_os = "windows")] -const EXE_SUFFIX: &'static str = ".exe"; - -#[cfg(not(target_os = "windows"))] -const EXE_SUFFIX: &str = ""; - -fn find_bridge_path(plugin_root: &Path) -> Result { - let exe = format!("defold-nvim-bridge{EXE_SUFFIX}"); - - if plugin_root.exists() { - let candidates = [ - plugin_root.join(&exe), - plugin_root.join("target").join("debug").join(&exe), - plugin_root.join("target").join("release").join(&exe), - ]; - - if let Some(bridge_path) = candidates.into_iter().find(|p| p.exists()) { - return Ok(bridge_path); - } - } - - // TODO: if not lets download it - bail!("not yet implemented") -} - fn create_runner_script( plugin_root: &Path, launcher_settings: &LauncherSettings, @@ -140,7 +117,7 @@ fn create_runner_script( fs::create_dir_all(&dir)?; let script_path = dir.join(format!("run.{SCRIPT_EXT}")); - let bridge_path = find_bridge_path(plugin_root)?; + let bridge_path = bridge::path(plugin_root)?; let launch_args = launcher_settings.bridge_cli_args().join(" "); fs::write( diff --git a/crates/core/src/lib.rs b/crates/core/src/lib.rs index 269b9c8..b22f388 100644 --- a/crates/core/src/lib.rs +++ b/crates/core/src/lib.rs @@ -1,3 +1,4 @@ +pub mod bridge; pub mod cache; pub mod defold_annotations; pub mod editor; diff --git a/crates/sidecar/src/lib.rs b/crates/sidecar/src/lib.rs index a253dd0..b353518 100644 --- a/crates/sidecar/src/lib.rs +++ b/crates/sidecar/src/lib.rs @@ -1,5 +1,5 @@ use anyhow::Context; -use defold_nvim_core::{editor, editor_config, mobdap, project}; +use defold_nvim_core::{bridge, editor, editor_config, mobdap, project}; use defold_nvim_core::{focus, game_project::GameProject}; use mlua::Value; use mlua::prelude::*; @@ -68,6 +68,7 @@ fn register_exports(lua: &Lua) -> LuaResult { "set_default_editor", lua.create_function(set_default_editor)?, )?; + exports.set("find_bridge_path", lua.create_function(find_bridge_path)?)?; exports.set("focus_neovim", lua.create_function(focus_neovim)?)?; exports.set("focus_game", lua.create_function(focus_game)?)?; exports.set("mobdap_install", lua.create_function(mobdap_install)?)?; @@ -140,6 +141,15 @@ fn set_default_editor( Ok(()) } +fn find_bridge_path(_lua: &Lua, plugin_root: String) -> LuaResult { + let path = bridge::path(&absolute(plugin_root)?)?; + + Ok(path + .to_str() + .context("could not convert path to string")? + .to_string()) +} + fn focus_neovim(_lua: &Lua, game_root: String) -> LuaResult<()> { focus::focus_neovim(absolute(game_root)?)?; diff --git a/lua/defold/init.lua b/lua/defold/init.lua index 2227ce6..6de1501 100644 --- a/lua/defold/init.lua +++ b/lua/defold/init.lua @@ -219,7 +219,13 @@ end ---Makes sure the native library `sidecar` gets loaded function M.download() - require "defold.sidecar" + local log = require "defold.service.logger" + local sidecar = require "defold.sidecar" + + local ok, err = pcall(sidecar.find_bridge_path, M.plugin_root()) + if not ok then + log.error(string.format("Could not setup bridge: %s", err)) + end end function M.load_plugin() @@ -239,7 +245,13 @@ function M.load_plugin() local project = require "defold.project" log.debug "============= defold.nvim: Loaded plugin" - log.debug("Sidecar Version:" .. sidecar.version) + log.debug("Sidecar Version: " .. sidecar.version) + + local bridge_ok, bridge_path = pcall(sidecar.find_bridge_path, M.plugin_root()) + if bridge_ok then + log.debug("Bridge Path: " .. bridge_path) + end + if debugger.mobdap_path() then log.debug("Mobdap Path: " .. debugger.mobdap_path()) end diff --git a/lua/defold/sidecar.lua b/lua/defold/sidecar.lua index 4522f00..d9a9ff7 100644 --- a/lua/defold/sidecar.lua +++ b/lua/defold/sidecar.lua @@ -123,6 +123,7 @@ package.cpath = package.cpath ---@field list_commands function(port: integer|nil): table ---@field send_command function(port: integer|nil, cmd: string) ---@field set_default_editor function(plugin_root: string, launcher_config: LauncherSettings) +---@field find_bridge_path function(plugin_root: string): string ---@field focus_neovim function(game_root: string) ---@field focus_game function(game_root: string) ---@field mobdap_install function(): string From a654a0a913691161c30c6914578c20f07e465b03 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 09:19:12 +0100 Subject: [PATCH 42/70] add better panic handling --- crates/sidecar/src/lib.rs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/crates/sidecar/src/lib.rs b/crates/sidecar/src/lib.rs index b353518..bd092b0 100644 --- a/crates/sidecar/src/lib.rs +++ b/crates/sidecar/src/lib.rs @@ -43,6 +43,28 @@ fn defold_nvim_sidecar(lua: &Lua) -> LuaResult { .with(filter) .with(file_layer) .init(); + + std::panic::set_hook(Box::new(|panic_info| { + let payload = panic_info.payload(); + let message = if let Some(s) = payload.downcast_ref::<&str>() { + (*s).to_string() + } else if let Some(s) = payload.downcast_ref::() { + s.clone() + } else { + "Unknown panic payload".to_string() + }; + + let location = panic_info.location().map_or_else( + || "unknown location".to_string(), + |l| format!("{}:{}:{}", l.file(), l.line(), l.column()), + ); + + tracing::error!( + panic_message = %message, + panic_location = %location, + "SIDECAR PANIC" + ); + })); }); match register_exports(lua) { From 295d282bf21a8fa030cf178d97f9f0cdafc90c43 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 10:14:26 +0100 Subject: [PATCH 43/70] check for errors when calling set_log_level --- lua/defold/init.lua | 7 ++++--- lua/defold/service/os.lua | 1 - 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lua/defold/init.lua b/lua/defold/init.lua index 6de1501..7ce5c19 100644 --- a/lua/defold/init.lua +++ b/lua/defold/init.lua @@ -116,13 +116,14 @@ function M.setup(opts) M.config = vim.tbl_deep_extend("force", default_config, opts or {}) - -- TODO: check if sidecar is available, if not download it (which shouldnt be necessary with some pkg managers) - if M.config.debug then M.config.launcher.debug = true local sidecar = require "defold.sidecar" - sidecar.set_log_level "debug" + local ok, err = pcall(sidecar.set_log_level, "debug") + if not ok then + log.error(string.format("Could not setup sidecar: %s", err)) + end end -- add setup defold command diff --git a/lua/defold/service/os.lua b/lua/defold/service/os.lua index bbf876b..e2bbee3 100644 --- a/lua/defold/service/os.lua +++ b/lua/defold/service/os.lua @@ -164,7 +164,6 @@ function M.plugin_root() end local res = vim.fs.dirname(vim.fs.dirname(vim.fs.dirname(vim.fs.dirname(string.sub(script_path, 2))))) - log.debug("Plugin root: " .. res) return res end From 8ba3c5cfb33b31df850b4d86459ed408e9509e9e Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 10:37:04 +0100 Subject: [PATCH 44/70] make launcher type optional as intended --- crates/core/src/editor_config.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/core/src/editor_config.rs b/crates/core/src/editor_config.rs index ad3c723..80aedf7 100644 --- a/crates/core/src/editor_config.rs +++ b/crates/core/src/editor_config.rs @@ -36,7 +36,7 @@ pub struct TerminalLauncherSettings { #[derive(Debug, Deserialize)] pub struct LauncherSettings { #[serde(rename = "type")] - pub launcher_type: LauncherType, + pub launcher_type: Option, pub executable: Option, pub socket_type: Option, pub extra_arguments: Option>, @@ -51,8 +51,8 @@ impl LauncherSettings { args.push("--launcher-type".to_string()); args.push(match self.launcher_type { - LauncherType::Neovide => "neovide".to_string(), - LauncherType::Terminal => "terminal".to_string(), + Some(LauncherType::Neovide) | None => "neovide".to_string(), + Some(LauncherType::Terminal) => "terminal".to_string(), }); if let Some(socket_type) = &self.socket_type { From fc960dd297c278e41208f27f37b654a7c7a0973d Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 10:38:29 +0100 Subject: [PATCH 45/70] switch to debug builds as release just crashes neovim on error --- .github/workflows/release.yml | 38 +++++++++++++++++------------------ Cargo.toml | 5 ----- 2 files changed, 19 insertions(+), 24 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b0a5043..3ddc535 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,35 +18,35 @@ jobs: - name: Linux x86 os: ubuntu-latest artifact: - sidecar: target/x86_64-unknown-linux-gnu/release/libdefold_nvim_sidecar.so - bridge: target/x86_64-unknown-linux-gnu/release/defold-nvim-bridge + sidecar: target/x86_64-unknown-linux-gnu/debug/libdefold_nvim_sidecar.so + bridge: target/x86_64-unknown-linux-gnu/debug/defold-nvim-bridge dist: - sidecar: target/release/linux-x86-libdefold_nvim_sidecar.so - bridge: target/release/linux-x86-defold-nvim-bridge + sidecar: target/debug/linux-x86-libdefold_nvim_sidecar.so + bridge: target/debug/linux-x86-defold-nvim-bridge - name: MacOS x86 os: macos-15-intel artifact: - sidecar: target/release/libdefold_nvim_sidecar.dylib - bridge: target/release/defold-nvim-bridge + sidecar: target/debug/libdefold_nvim_sidecar.dylib + bridge: target/debug/defold-nvim-bridge dist: - sidecar: target/release/macos-x86-libdefold_nvim_sidecar.dylib - bridge: target/release/macos-x86-defold-nvim-bridge + sidecar: target/debug/macos-x86-libdefold_nvim_sidecar.dylib + bridge: target/debug/macos-x86-defold-nvim-bridge - name: MacOS ARM os: macos-latest artifact: - sidecar: target/release/libdefold_nvim_sidecar.dylib - bridge: target/release/defold-nvim-bridge + sidecar: target/debug/libdefold_nvim_sidecar.dylib + bridge: target/debug/defold-nvim-bridge dist: - sidecar: target/release/macos-arm-libdefold_nvim_sidecar.dylib - bridge: target/release/macos-arm-defold-nvim-bridge + sidecar: target/debug/macos-arm-libdefold_nvim_sidecar.dylib + bridge: target/debug/macos-arm-defold-nvim-bridge - name: Windows x86 os: windows-latest artifact: - sidecar: target/release/defold_nvim_sidecar.dll - bridge: target/release/defold-nvim-bridge.exe + sidecar: target/debug/defold_nvim_sidecar.dll + bridge: target/debug/defold-nvim-bridge.exe dist: - sidecar: target/release/windows-x86-defold_nvim_sidecar.dll - bridge: target/release/windows-x86-defold-nvim-bridge + sidecar: target/debug/windows-x86-defold_nvim_sidecar.dll + bridge: target/debug/windows-x86-defold-nvim-bridge steps: - uses: actions/checkout@v5 @@ -59,15 +59,15 @@ jobs: if: contains(matrix.config.os, 'ubuntu') run: | cargo install cargo-zigbuild - cargo zigbuild --release --target x86_64-unknown-linux-gnu.2.31 + cargo zigbuild --target x86_64-unknown-linux-gnu.2.31 - name: build ${{ matrix.config.name }} if: contains(matrix.config.os, 'macos') - run: MACOSX_DEPLOYMENT_TARGET="13" cargo build --release + run: MACOSX_DEPLOYMENT_TARGET="13" cargo build - name: build ${{ matrix.config.name }} if: contains(matrix.config.os, 'windows') - run: cargo build --release + run: cargo build - name: prepare ${{ matrix.config.name }} run: | diff --git a/Cargo.toml b/Cargo.toml index 92976fa..833466d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,8 +10,3 @@ pedantic = "warn" [workspace.lints.rust] too-many-lines = "allow" - -[profile.release] -strip = true -opt-level = 3 -lto = true From b6417d0caa494337431c4e5cbc3e4ee9596b77bb Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 10:51:37 +0100 Subject: [PATCH 46/70] Revert "switch to debug builds as release just crashes neovim on error" This reverts commit fc960dd297c278e41208f27f37b654a7c7a0973d. --- .github/workflows/release.yml | 38 +++++++++++++++++------------------ Cargo.toml | 5 +++++ 2 files changed, 24 insertions(+), 19 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 3ddc535..b0a5043 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -18,35 +18,35 @@ jobs: - name: Linux x86 os: ubuntu-latest artifact: - sidecar: target/x86_64-unknown-linux-gnu/debug/libdefold_nvim_sidecar.so - bridge: target/x86_64-unknown-linux-gnu/debug/defold-nvim-bridge + sidecar: target/x86_64-unknown-linux-gnu/release/libdefold_nvim_sidecar.so + bridge: target/x86_64-unknown-linux-gnu/release/defold-nvim-bridge dist: - sidecar: target/debug/linux-x86-libdefold_nvim_sidecar.so - bridge: target/debug/linux-x86-defold-nvim-bridge + sidecar: target/release/linux-x86-libdefold_nvim_sidecar.so + bridge: target/release/linux-x86-defold-nvim-bridge - name: MacOS x86 os: macos-15-intel artifact: - sidecar: target/debug/libdefold_nvim_sidecar.dylib - bridge: target/debug/defold-nvim-bridge + sidecar: target/release/libdefold_nvim_sidecar.dylib + bridge: target/release/defold-nvim-bridge dist: - sidecar: target/debug/macos-x86-libdefold_nvim_sidecar.dylib - bridge: target/debug/macos-x86-defold-nvim-bridge + sidecar: target/release/macos-x86-libdefold_nvim_sidecar.dylib + bridge: target/release/macos-x86-defold-nvim-bridge - name: MacOS ARM os: macos-latest artifact: - sidecar: target/debug/libdefold_nvim_sidecar.dylib - bridge: target/debug/defold-nvim-bridge + sidecar: target/release/libdefold_nvim_sidecar.dylib + bridge: target/release/defold-nvim-bridge dist: - sidecar: target/debug/macos-arm-libdefold_nvim_sidecar.dylib - bridge: target/debug/macos-arm-defold-nvim-bridge + sidecar: target/release/macos-arm-libdefold_nvim_sidecar.dylib + bridge: target/release/macos-arm-defold-nvim-bridge - name: Windows x86 os: windows-latest artifact: - sidecar: target/debug/defold_nvim_sidecar.dll - bridge: target/debug/defold-nvim-bridge.exe + sidecar: target/release/defold_nvim_sidecar.dll + bridge: target/release/defold-nvim-bridge.exe dist: - sidecar: target/debug/windows-x86-defold_nvim_sidecar.dll - bridge: target/debug/windows-x86-defold-nvim-bridge + sidecar: target/release/windows-x86-defold_nvim_sidecar.dll + bridge: target/release/windows-x86-defold-nvim-bridge steps: - uses: actions/checkout@v5 @@ -59,15 +59,15 @@ jobs: if: contains(matrix.config.os, 'ubuntu') run: | cargo install cargo-zigbuild - cargo zigbuild --target x86_64-unknown-linux-gnu.2.31 + cargo zigbuild --release --target x86_64-unknown-linux-gnu.2.31 - name: build ${{ matrix.config.name }} if: contains(matrix.config.os, 'macos') - run: MACOSX_DEPLOYMENT_TARGET="13" cargo build + run: MACOSX_DEPLOYMENT_TARGET="13" cargo build --release - name: build ${{ matrix.config.name }} if: contains(matrix.config.os, 'windows') - run: cargo build + run: cargo build --release - name: prepare ${{ matrix.config.name }} run: | diff --git a/Cargo.toml b/Cargo.toml index 833466d..92976fa 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,3 +10,8 @@ pedantic = "warn" [workspace.lints.rust] too-many-lines = "allow" + +[profile.release] +strip = true +opt-level = 3 +lto = true From 4d23e1d8feb7e9caa3dca00e5ca41a556c9ba292 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 10:53:04 +0100 Subject: [PATCH 47/70] switch to cross for linux builds --- .github/workflows/release.yml | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index b0a5043..ee2dc62 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -50,7 +50,6 @@ jobs: steps: - uses: actions/checkout@v5 - - uses: mlugg/setup-zig@v2 - uses: actions-rust-lang/setup-rust-toolchain@v1 with: rustflags: "" @@ -58,8 +57,8 @@ jobs: - name: build ${{ matrix.config.name }} if: contains(matrix.config.os, 'ubuntu') run: | - cargo install cargo-zigbuild - cargo zigbuild --release --target x86_64-unknown-linux-gnu.2.31 + cargo install cargo-cross + cargo cross build --release --target x86_64-unknown-linux-gnu - name: build ${{ matrix.config.name }} if: contains(matrix.config.os, 'macos') From 75cd9c9eee664bded44619cb6a358bc81909a833 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 11:19:00 +0100 Subject: [PATCH 48/70] fix cross device link error on ubuntu --- crates/core/src/bridge.rs | 4 ++-- crates/core/src/defold_annotations.rs | 4 ++-- crates/core/src/mobdap.rs | 6 ++++-- crates/core/src/neovide.rs | 6 ++++-- crates/core/src/utils.rs | 16 ++++++++++++++++ 5 files changed, 28 insertions(+), 8 deletions(-) diff --git a/crates/core/src/bridge.rs b/crates/core/src/bridge.rs index bf0c30a..9d021a9 100644 --- a/crates/core/src/bridge.rs +++ b/crates/core/src/bridge.rs @@ -6,7 +6,7 @@ use std::{ }; use version_compare::Version; -use crate::github; +use crate::{github, utils}; #[cfg(target_os = "windows")] const EXE_SUFFIX: &'static str = ".exe"; @@ -127,7 +127,7 @@ fn install() -> Result { tracing::debug!("New Bridge version found {}", release.tag_name); - fs::rename(downloaded_file, &path)?; + utils::move_file(&downloaded_file, &path)?; fs::write(version_path()?, release.tag_name)?; #[cfg(any(target_os = "linux", target_os = "macos"))] diff --git a/crates/core/src/defold_annotations.rs b/crates/core/src/defold_annotations.rs index 377d9fe..80bca10 100644 --- a/crates/core/src/defold_annotations.rs +++ b/crates/core/src/defold_annotations.rs @@ -8,7 +8,7 @@ use anyhow::{Context, Result, bail}; use version_compare::Version; use zip::ZipArchive; -use crate::{github, project}; +use crate::{github, project, utils}; const OWNER: &str = "astrochili"; const REPOSITORY: &str = "defold-annotations"; @@ -112,7 +112,7 @@ pub fn install() -> Result<()> { ); } - fs::rename(defold_api_dir, defold_dir)?; + utils::move_file(&defold_api_dir, &defold_dir)?; fs::write(version_path()?, release.tag_name)?; github::clear_downloads(OWNER, REPOSITORY)?; diff --git a/crates/core/src/mobdap.rs b/crates/core/src/mobdap.rs index afad56e..1749c4b 100644 --- a/crates/core/src/mobdap.rs +++ b/crates/core/src/mobdap.rs @@ -119,6 +119,8 @@ pub fn update_or_install() -> Result { use std::os::unix::fs::PermissionsExt; use tar::Archive; + use crate::utils; + let tar = GzDecoder::new(file); let mut archive = Archive::new(tar); archive.unpack(&parent_dir)?; @@ -131,7 +133,7 @@ pub fn update_or_install() -> Result { fs::set_permissions(&mobdap_path, Permissions::from_mode(0o700))?; - fs::rename(mobdap_path, path()?)?; + utils::move_file(&mobdap_path, &path()?)?; } #[cfg(target_os = "windows")] @@ -147,7 +149,7 @@ pub fn update_or_install() -> Result { bail!("mobdap doesnt exist after unpacking?"); } - fs::rename(mobdap_path, path()?)?; + utils::move_file(&mobdap_path, &path()?)?; } #[cfg(not(any(target_os = "linux", target_os = "macos", target_os = "windows")))] diff --git a/crates/core/src/neovide.rs b/crates/core/src/neovide.rs index 3e1eaef..2fc7108 100644 --- a/crates/core/src/neovide.rs +++ b/crates/core/src/neovide.rs @@ -120,6 +120,8 @@ pub fn update_or_install() -> Result { use std::os::unix::fs::PermissionsExt; use tar::Archive; + use crate::utils; + let tar = GzDecoder::new(file); let mut archive = Archive::new(tar); archive.unpack(&parent_dir)?; @@ -132,7 +134,7 @@ pub fn update_or_install() -> Result { fs::set_permissions(&neovide_path, Permissions::from_mode(0o700))?; - fs::rename(neovide_path, path()?)?; + utils::move_file(&neovide_path, &path()?)?; } #[cfg(target_os = "windows")] @@ -148,7 +150,7 @@ pub fn update_or_install() -> Result { bail!("Neovide doesnt exist after unpacking?"); } - fs::rename(neovide_path, path()?)?; + utils::move_file(&neovide_path, &path()?)?; } #[cfg(not(any(target_os = "linux", target_os = "windows")))] diff --git a/crates/core/src/utils.rs b/crates/core/src/utils.rs index ee28ae2..8056e73 100644 --- a/crates/core/src/utils.rs +++ b/crates/core/src/utils.rs @@ -83,3 +83,19 @@ pub fn delete_empty_dirs_from(root_dir: &Path) -> Result<()> { } Ok(()) } + +// Apparently fs::rename doesnt work on tmpfs +pub fn move_file(from: &Path, to: &Path) -> Result<()> { + if let Err(err) = fs::rename(from, to) { + // EXDEV: Cross-device link + if err.raw_os_error() == Some(18) { + fs::copy(from, to)?; + fs::remove_file(from)?; + return Ok(()); + } + + return Err(err.into()); + } + + Ok(()) +} From 07c5629e84fe5febc0618dd5523e3f924316deaa Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 11:23:54 +0100 Subject: [PATCH 49/70] fix builds on windows --- crates/core/src/mobdap.rs | 3 +-- crates/core/src/neovide.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/crates/core/src/mobdap.rs b/crates/core/src/mobdap.rs index 1749c4b..5155222 100644 --- a/crates/core/src/mobdap.rs +++ b/crates/core/src/mobdap.rs @@ -5,6 +5,7 @@ use std::{ }; use crate::github; +use crate::utils; use anyhow::{Context, Result, bail}; use version_compare::Version; @@ -119,8 +120,6 @@ pub fn update_or_install() -> Result { use std::os::unix::fs::PermissionsExt; use tar::Archive; - use crate::utils; - let tar = GzDecoder::new(file); let mut archive = Archive::new(tar); archive.unpack(&parent_dir)?; diff --git a/crates/core/src/neovide.rs b/crates/core/src/neovide.rs index 2fc7108..eba5207 100644 --- a/crates/core/src/neovide.rs +++ b/crates/core/src/neovide.rs @@ -8,6 +8,7 @@ use anyhow::{Context, Result, bail}; use version_compare::Version; use crate::github; +use crate::utils; const OWNER: &str = "neovide"; const REPOSITORY: &str = "neovide"; @@ -120,8 +121,6 @@ pub fn update_or_install() -> Result { use std::os::unix::fs::PermissionsExt; use tar::Archive; - use crate::utils; - let tar = GzDecoder::new(file); let mut archive = Archive::new(tar); archive.unpack(&parent_dir)?; From f9172dc02784524b63c8ae4be91e11c419487a16 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 11:33:29 +0100 Subject: [PATCH 50/70] implement file/dir move via fs_extra --- Cargo.lock | 7 +++++++ crates/core/Cargo.toml | 1 + crates/core/src/utils.rs | 17 ++++++++--------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 958bd7b..5f733f1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -408,6 +408,7 @@ dependencies = [ "dirs", "edn-rs", "flate2", + "fs_extra", "netstat2", "pretty_assertions", "reqwest", @@ -605,6 +606,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures-channel" version = "0.3.31" diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index b01d9ea..c5aa190 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -29,6 +29,7 @@ netstat2 = "0.11.2" sysinfo = "0.37.2" serde_yaml = "0.9.34" textwrap = "0.16.2" +fs_extra = "1.3.0" [dev-dependencies] pretty_assertions = "1.4.1" diff --git a/crates/core/src/utils.rs b/crates/core/src/utils.rs index 8056e73..bcd07ff 100644 --- a/crates/core/src/utils.rs +++ b/crates/core/src/utils.rs @@ -6,6 +6,10 @@ use std::{ }; use anyhow::{Context, Result}; +use fs_extra::{ + dir::{self, move_dir}, + file, +}; use sha3::{Digest, Sha3_256}; use url::Url; use walkdir::WalkDir; @@ -86,15 +90,10 @@ pub fn delete_empty_dirs_from(root_dir: &Path) -> Result<()> { // Apparently fs::rename doesnt work on tmpfs pub fn move_file(from: &Path, to: &Path) -> Result<()> { - if let Err(err) = fs::rename(from, to) { - // EXDEV: Cross-device link - if err.raw_os_error() == Some(18) { - fs::copy(from, to)?; - fs::remove_file(from)?; - return Ok(()); - } - - return Err(err.into()); + if from.is_dir() { + dir::move_dir(from, to, &dir::CopyOptions::new().overwrite(true))?; + } else { + file::move_file(from, to, &file::CopyOptions::new().overwrite(true))?; } Ok(()) From 2a62ef9056579016a54cfddcce0a26d6af5eb4f6 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 11:38:45 +0100 Subject: [PATCH 51/70] apparently fs_extra doesnt create dirs --- crates/core/src/utils.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/core/src/utils.rs b/crates/core/src/utils.rs index bcd07ff..31248c5 100644 --- a/crates/core/src/utils.rs +++ b/crates/core/src/utils.rs @@ -90,6 +90,10 @@ pub fn delete_empty_dirs_from(root_dir: &Path) -> Result<()> { // Apparently fs::rename doesnt work on tmpfs pub fn move_file(from: &Path, to: &Path) -> Result<()> { + if let Some(parent) = to.parent() { + fs::create_dir_all(parent)?; + } + if from.is_dir() { dir::move_dir(from, to, &dir::CopyOptions::new().overwrite(true))?; } else { From 97daca0396e267293d98520eaa20e28caa2b04c9 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 11:56:31 +0100 Subject: [PATCH 52/70] enable anyhow backtrace --- Cargo.lock | 52 +++++++++++++++++++++++++++++++++++++-- crates/bridge/Cargo.toml | 2 +- crates/core/Cargo.toml | 2 +- crates/sidecar/Cargo.toml | 2 +- 4 files changed, 53 insertions(+), 5 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5f733f1..b0d88dd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "addr2line" +version = "0.25.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" +dependencies = [ + "gimli", +] + [[package]] name = "adler2" version = "2.0.1" @@ -83,6 +92,9 @@ name = "anyhow" version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +dependencies = [ + "backtrace", +] [[package]] name = "arbitrary" @@ -105,6 +117,21 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "backtrace" +version = "0.3.76" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +dependencies = [ + "addr2line", + "cfg-if", + "libc", + "miniz_oxide", + "object", + "rustc-demangle", + "windows-link 0.2.1", +] + [[package]] name = "base64" version = "0.22.1" @@ -699,6 +726,12 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "gimli" +version = "0.32.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" + [[package]] name = "glob" version = "0.3.3" @@ -987,9 +1020,9 @@ dependencies = [ [[package]] name = "itoa" -version = "1.0.15" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" +checksum = "7ee5b5339afb4c41626dde77b7a611bd4f2c202b897852b4bcf5d03eddc61010" [[package]] name = "jobserver" @@ -1327,6 +1360,15 @@ dependencies = [ "objc2-core-foundation", ] +[[package]] +name = "object" +version = "0.37.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" +dependencies = [ + "memchr", +] + [[package]] name = "once_cell" version = "1.21.3" @@ -1716,6 +1758,12 @@ dependencies = [ "ordered-multimap", ] +[[package]] +name = "rustc-demangle" +version = "0.1.26" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f7d92ca342cea22a06f2121d944b4fd82af56988c270852495420f961d4ace" + [[package]] name = "rustc-hash" version = "2.1.1" diff --git a/crates/bridge/Cargo.toml b/crates/bridge/Cargo.toml index 67cb3de..23d5bec 100644 --- a/crates/bridge/Cargo.toml +++ b/crates/bridge/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -anyhow = "1.0.100" +anyhow = { version = "1.0.100", features = ["backtrace"] } clap = { version = "4.5.53", features = ["derive"] } defold-nvim-core = { path = "../core" } dirs = "6.0.0" diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index c5aa190..5026cfe 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -4,7 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] -anyhow = "1.0.100" +anyhow = { version = "1.0.100", features = ["backtrace"] } tar = "0.4.44" version-compare = "0.2.1" flate2 = "1.1.5" diff --git a/crates/sidecar/Cargo.toml b/crates/sidecar/Cargo.toml index 1460bbc..28bd789 100644 --- a/crates/sidecar/Cargo.toml +++ b/crates/sidecar/Cargo.toml @@ -8,7 +8,7 @@ path = "src/lib.rs" crate-type = ["cdylib"] [dependencies] -anyhow = "1.0.100" +anyhow = { version = "1.0.100", features = ["backtrace"] } defold-nvim-core = { path = "../core" } dirs = "6.0.0" mlua = { version = "0.11.5", features = [ From 32c9f2477613f98f6079f071536a891d6a8f7f13 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 12:54:36 +0100 Subject: [PATCH 53/70] add tracing::instrument to lua functions to block mlua from removing context --- crates/core/src/github.rs | 8 ++++++-- crates/core/src/utils.rs | 5 +---- crates/sidecar/src/lib.rs | 16 +++++++++++++++- 3 files changed, 22 insertions(+), 7 deletions(-) diff --git a/crates/core/src/github.rs b/crates/core/src/github.rs index 6a6db91..2360f06 100644 --- a/crates/core/src/github.rs +++ b/crates/core/src/github.rs @@ -26,10 +26,12 @@ pub struct Release { pub fn fetch_release(owner: &str, repo: &str) -> Result { let url = format!("https://api.github.com/repos/{owner}/{repo}/releases/latest"); + tracing::debug!("Github: Fetching release {owner}/{repo}"); + if let Some(str) = cache::get(&url) - && let Ok(res) = serde_json::from_str(&str) + && let Ok(res) = serde_json::from_str::(&str) { - tracing::debug!("Serving {url} from cache"); + tracing::debug!("Github: Found {}, serving from cache", res.tag_name); return Ok(res); } @@ -38,6 +40,8 @@ pub fn fetch_release(owner: &str, repo: &str) -> Result { let release: Release = res.json()?; + tracing::debug!("Github: Found {}", release.tag_name); + cache::set(&url, &serde_json::to_string(&release)?)?; Ok(release) diff --git a/crates/core/src/utils.rs b/crates/core/src/utils.rs index 31248c5..ef02cbd 100644 --- a/crates/core/src/utils.rs +++ b/crates/core/src/utils.rs @@ -6,10 +6,7 @@ use std::{ }; use anyhow::{Context, Result}; -use fs_extra::{ - dir::{self, move_dir}, - file, -}; +use fs_extra::{dir, file}; use sha3::{Digest, Sha3_256}; use url::Url; use walkdir::WalkDir; diff --git a/crates/sidecar/src/lib.rs b/crates/sidecar/src/lib.rs index bd092b0..f6f786f 100644 --- a/crates/sidecar/src/lib.rs +++ b/crates/sidecar/src/lib.rs @@ -9,6 +9,7 @@ use std::{ path::absolute, sync::OnceLock, }; +use tracing::instrument; use tracing::level_filters::LevelFilter; use tracing_subscriber::layer::SubscriberExt; use tracing_subscriber::reload; @@ -70,7 +71,7 @@ fn defold_nvim_sidecar(lua: &Lua) -> LuaResult { match register_exports(lua) { Ok(exports) => Ok(exports), Err(err) => { - tracing::error!("{lua:?}"); + tracing::error!("Register Error: {lua:?}"); Err(err) } } @@ -107,6 +108,7 @@ fn register_exports(lua: &Lua) -> LuaResult { } #[allow(clippy::needless_pass_by_value)] +#[instrument(level = "debug", err(Debug), skip_all)] fn set_log_level(_lua: &Lua, level: String) -> LuaResult<()> { let new_filter = match level.to_lowercase().as_str() { "debug" => LevelFilter::DEBUG, @@ -125,34 +127,40 @@ fn set_log_level(_lua: &Lua, level: String) -> LuaResult<()> { Ok(()) } +#[instrument(level = "debug", err(Debug), skip_all)] fn read_game_project(lua: &Lua, path: String) -> LuaResult { let game_project = GameProject::load_from_path(&absolute(path)?)?; let val = lua.to_value(&game_project)?; Ok(val) } +#[instrument(level = "debug", err(Debug), skip_all)] fn find_editor_port(_lua: &Lua, _: ()) -> LuaResult { let port = editor::find_port().context("could not find port")?; Ok(port) } #[allow(clippy::unnecessary_wraps)] +#[instrument(level = "debug", err(Debug), skip_all)] fn is_editor_port(_lua: &Lua, port: u16) -> LuaResult { Ok(editor::is_editor_port(port)) } +#[instrument(level = "debug", err(Debug), skip_all)] fn list_commands(lua: &Lua, port: Option) -> LuaResult { let commands = editor::list_commands(port)?; lua.create_table_from(commands) } +#[instrument(level = "debug", err(Debug), skip_all)] fn send_command(_lua: &Lua, (port, cmd): (Option, String)) -> LuaResult<()> { editor::send_command(port, &cmd)?; Ok(()) } +#[instrument(level = "debug", err(Debug), skip_all)] fn set_default_editor( lua: &Lua, (plugin_root, launcher_settings): (String, LuaValue), @@ -163,6 +171,7 @@ fn set_default_editor( Ok(()) } +#[instrument(level = "debug", err(Debug), skip_all)] fn find_bridge_path(_lua: &Lua, plugin_root: String) -> LuaResult { let path = bridge::path(&absolute(plugin_root)?)?; @@ -172,18 +181,21 @@ fn find_bridge_path(_lua: &Lua, plugin_root: String) -> LuaResult { .to_string()) } +#[instrument(level = "debug", err(Debug), skip_all)] fn focus_neovim(_lua: &Lua, game_root: String) -> LuaResult<()> { focus::focus_neovim(absolute(game_root)?)?; Ok(()) } +#[instrument(level = "debug", err(Debug), skip_all)] fn focus_game(_lua: &Lua, game_root: String) -> LuaResult<()> { focus::focus_game(absolute(game_root)?)?; Ok(()) } +#[instrument(level = "debug", err(Debug), skip_all)] fn mobdap_install(_lua: &Lua, _: ()) -> LuaResult { let path = mobdap::update_or_install()?; Ok(path @@ -192,6 +204,7 @@ fn mobdap_install(_lua: &Lua, _: ()) -> LuaResult { .to_string()) } +#[instrument(level = "debug", err(Debug), skip_all)] fn install_dependencies( _lua: &Lua, (game_root, force_redownload): (String, Option), @@ -200,6 +213,7 @@ fn install_dependencies( Ok(()) } +#[instrument(level = "debug", err(Debug), skip_all)] fn list_dependency_dirs(_lua: &Lua, game_root: String) -> LuaResult> { let deps = project::list_dependency_dirs(&absolute(game_root)?)? .into_iter() From 6726d6274a0f6fe4c054ae7375eb888199c4c622 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 13:20:24 +0100 Subject: [PATCH 54/70] fix fs_extra::dir::move_dir not working as expected --- crates/core/src/utils.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/crates/core/src/utils.rs b/crates/core/src/utils.rs index ef02cbd..b18995b 100644 --- a/crates/core/src/utils.rs +++ b/crates/core/src/utils.rs @@ -92,7 +92,15 @@ pub fn move_file(from: &Path, to: &Path) -> Result<()> { } if from.is_dir() { - dir::move_dir(from, to, &dir::CopyOptions::new().overwrite(true))?; + if to.exists() { + fs::remove_dir_all(to)?; + } + + dir::move_dir( + from, + to, + &dir::CopyOptions::new().overwrite(true).copy_inside(true), + )?; } else { file::move_file(from, to, &file::CopyOptions::new().overwrite(true))?; } From 0754f670ad2fb5c3556b3795aa6b7a99a735dcb7 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 13:52:04 +0100 Subject: [PATCH 55/70] log bridge usage errors into log, dont show tracing in stdout anymore --- Cargo.toml | 2 +- crates/bridge/src/main.rs | 38 +++++++++++++++++++++++++------------- crates/core/Cargo.toml | 3 +++ 3 files changed, 29 insertions(+), 14 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 92976fa..1ba9aa5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,6 +12,6 @@ pedantic = "warn" too-many-lines = "allow" [profile.release] -strip = true +strip = false opt-level = 3 lto = true diff --git a/crates/bridge/src/main.rs b/crates/bridge/src/main.rs index ba67ee7..e7b8f12 100644 --- a/crates/bridge/src/main.rs +++ b/crates/bridge/src/main.rs @@ -1,5 +1,5 @@ use std::{ - env, fs, io, + env, fs, path::{PathBuf, absolute}, }; @@ -12,7 +12,6 @@ use defold_nvim_core::{ }; use tracing::Level; use tracing_appender::rolling::never; -use tracing_subscriber::fmt::writer::MakeWriterExt; use crate::plugin_config::{LauncherType, PluginConfig, SocketType}; @@ -106,7 +105,15 @@ enum Commands { } fn main() -> Result<()> { - let args = Args::parse(); + let mut err = None; + + let args = match Args::try_parse() { + Ok(args) => Some(args), + Err(e) => { + err = Some(e); + None + } + }; let logs = dirs::cache_dir() .context("could not get cache dir")? @@ -115,26 +122,31 @@ fn main() -> Result<()> { fs::create_dir_all(&logs)?; - let (stdout, _stdout_guard) = tracing_appender::non_blocking(io::stdout()); let (logfile, _logfile_guard) = tracing_appender::non_blocking(never(logs, "bridge.log")); - let writer = stdout.and(logfile); - tracing_subscriber::fmt() .with_file(true) .with_line_number(true) - .with_max_level(if args.debug { + .with_max_level(if args.as_ref().is_some_and(|args| args.debug) { Level::DEBUG } else { Level::INFO }) - .with_writer(writer) + .with_writer(logfile) + .with_ansi(false) .init(); tracing::info!("Starting defold.nvim bridge",); tracing::debug!("CLI: {}", env::args().collect::>().join(" ")); tracing::debug!("Clap: {args:?}"); + if let Some(err) = err { + tracing::error!("Clap Error {}", err.to_string()); + err.exit(); + } + + let args = args.unwrap(); + match args.cmd { Commands::LaunchNeovim { launcher_type, @@ -163,11 +175,11 @@ fn main() -> Result<()> { Commands::FocusGame { game_root_dir } => focus_game(absolute(game_root_dir)?)?, Commands::DownloadNeovide => { let path = neovide::update_or_install()?; - tracing::info!("Installed neovide at {path:?}"); + println!("Installed neovide at {}", path.display()); } Commands::DownloadMobdap => { let path = mobdap::update_or_install()?; - tracing::info!("Installed mobdap at {path:?}"); + println!("Installed mobdap at {}", path.display()); } Commands::InstallDependencies { force_redownload, @@ -176,7 +188,7 @@ fn main() -> Result<()> { let root_dir = absolute(&game_root_dir)?; project::install_dependencies(&root_dir, force_redownload)?; - tracing::info!("Finished installing dependencies for {game_root_dir}",); + println!("Finished installing dependencies for {game_root_dir}",); } Commands::ListDependencies { game_root_dir } => { let root_dir = absolute(&game_root_dir)?; @@ -187,9 +199,9 @@ fn main() -> Result<()> { } Commands::FindEditorPort => { if let Some(port) = editor::find_port() { - tracing::info!("Editor is running at port {port}"); + println!("Editor is running at port {port}"); } else { - tracing::info!("Could not find editor port, is the editor open?"); + println!("Could not find editor port, is the editor open?"); } } Commands::SendCommand { port, command } => { diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 5026cfe..3789f7b 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -33,3 +33,6 @@ fs_extra = "1.3.0" [dev-dependencies] pretty_assertions = "1.4.1" + +[package.metadata.cargo-machete] +ignored = ["rust-ini"] From 70d3ebe84b6df7ac99423285f91aa5d585f66302 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 16:28:38 +0100 Subject: [PATCH 56/70] add untested support for neovide download on mac --- Cargo.lock | 123 ++++++++++++++++++++++++++++++++++++- crates/bridge/src/main.rs | 1 - crates/core/Cargo.toml | 3 + crates/core/src/neovide.rs | 26 +++++--- 4 files changed, 140 insertions(+), 13 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index b0d88dd..5fa1b6d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,6 +2,15 @@ # It is not intended for manual editing. version = 4 +[[package]] +name = "adc" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8d1f134dbe49adda618d0eced7120463e75e899b4676dcd7fc28f4daaa0d785" +dependencies = [ + "byteorder", +] + [[package]] name = "addr2line" version = "0.25.1" @@ -138,6 +147,15 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] + [[package]] name = "bindgen" version = "0.72.1" @@ -207,6 +225,16 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" +[[package]] +name = "bzip2" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] + [[package]] name = "bzip2" version = "0.6.1" @@ -216,6 +244,16 @@ dependencies = [ "libbz2-rs-sys", ] +[[package]] +name = "bzip2-sys" +version = "0.1.13+1.0.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "cc" version = "1.2.50" @@ -433,6 +471,7 @@ version = "0.1.0" dependencies = [ "anyhow", "dirs", + "dmgwiz", "edn-rs", "flate2", "fs_extra", @@ -547,6 +586,26 @@ dependencies = [ "const-random", ] +[[package]] +name = "dmgwiz" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7091fb6813de08ee6a2e97592355e4f00c84dc3444a8f7e5807b7ea87094007f" +dependencies = [ + "adc", + "bincode", + "byteorder", + "bzip2 0.4.4", + "flate2", + "itertools", + "lzfse", + "num-derive 0.4.2", + "num-traits", + "plist", + "quick-xml 0.37.5", + "serde", +] + [[package]] name = "edn-rs" version = "0.18.0" @@ -1134,6 +1193,24 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" +[[package]] +name = "lzfse" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21b45f9990844d28a06dc7b7a3ffe3657e5379c8e3c0d6ae43a22e98a2256a0b" +dependencies = [ + "lzfse-sys", +] + +[[package]] +name = "lzfse-sys" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23c80418f1cfc4601f8ef2055dcea77eebea47d2edc74ee0dadb005dd3a9f604" +dependencies = [ + "cc", +] + [[package]] name = "lzma-rust2" version = "0.15.4" @@ -1282,7 +1359,7 @@ dependencies = [ "netlink-packet-sock-diag", "netlink-packet-utils", "netlink-sys", - "num-derive", + "num-derive 0.3.3", "num-traits", "thiserror 2.0.17", ] @@ -1332,6 +1409,17 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.111", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -1478,6 +1566,19 @@ version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +[[package]] +name = "plist" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" +dependencies = [ + "base64", + "indexmap", + "quick-xml 0.38.4", + "serde", + "time", +] + [[package]] name = "potential_utf" version = "0.1.4" @@ -1537,6 +1638,24 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quick-xml" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +dependencies = [ + "memchr", +] + +[[package]] +name = "quick-xml" +version = "0.38.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +dependencies = [ + "memchr", +] + [[package]] name = "quinn" version = "0.11.9" @@ -3058,7 +3177,7 @@ checksum = "bdd8a47718a4ee5fe78e07667cd36f3de80e7c2bfe727c7074245ffc7303c037" dependencies = [ "aes", "arbitrary", - "bzip2", + "bzip2 0.6.1", "constant_time_eq", "crc32fast", "deflate64", diff --git a/crates/bridge/src/main.rs b/crates/bridge/src/main.rs index e7b8f12..0f1944d 100644 --- a/crates/bridge/src/main.rs +++ b/crates/bridge/src/main.rs @@ -20,7 +20,6 @@ mod plugin_config; mod utils; #[derive(Parser, Debug)] -// #[command(version)] struct Args { #[arg(long = "debug")] debug: bool, diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 3789f7b..596f4b8 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -31,6 +31,9 @@ serde_yaml = "0.9.34" textwrap = "0.16.2" fs_extra = "1.3.0" +[target.'cfg(target_os = "macos")'.dependencies] +dmgwiz = { version = "1.1.0", default-features = false } + [dev-dependencies] pretty_assertions = "1.4.1" diff --git a/crates/core/src/neovide.rs b/crates/core/src/neovide.rs index eba5207..b49539a 100644 --- a/crates/core/src/neovide.rs +++ b/crates/core/src/neovide.rs @@ -16,8 +16,11 @@ const REPOSITORY: &str = "neovide"; #[cfg(target_os = "linux")] const NAME: &str = "neovide-linux-x86_64.tar.gz"; -#[cfg(target_os = "macos")] -const NAME: &'static str = "not supported"; +#[cfg(all(target_os = "macos", target_arch = "x86_64"))] +const NAME: &str = "Neovide-x86_64-apple-darwin.dmg"; + +#[cfg(all(target_os = "macos", target_arch = "aarch64"))] +const NAME: &str = "Neovide-aarch64-apple-darwin.dmg"; #[cfg(target_os = "windows")] const NAME: &'static str = "neovide.exe.zip"; @@ -61,12 +64,6 @@ fn version() -> Result { } pub fn is_update_available() -> Result { - // macos is unsupported - if cfg!(target_os = "macos") { - tracing::debug!("MacOS is not supported, no update available"); - return Ok(false); - } - if version_path()?.exists() { // if the version file is younger than a week dont bother let last_modified = version_path()?.metadata()?.modified()?; @@ -152,9 +149,18 @@ pub fn update_or_install() -> Result { utils::move_file(&neovide_path, &path()?)?; } - #[cfg(not(any(target_os = "linux", target_os = "windows")))] + #[cfg(target_os = "macos")] { - bail!("Unsupported operating system"); + use dmg::Attach; + use std::os::unix::fs::PermissionsExt; + + let info = Attach::new(&downloaded_file).with()?; + + let neovide_path = info.mount_point.join("Neovide.app/Contents/MacOS/neovide"); + + fs::copy(neovide_path, target_path)?; + + fs::set_permissions(&target_path, Permissions::from_mode(0o700))?; } fs::write(version_path()?, release.tag_name)?; From 23a9b95ff3fabd95a094463f0d58aad0e97b77a9 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 16:54:06 +0100 Subject: [PATCH 57/70] fix downloads on windows --- lua/defold/service/os.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lua/defold/service/os.lua b/lua/defold/service/os.lua index e2bbee3..3eca437 100644 --- a/lua/defold/service/os.lua +++ b/lua/defold/service/os.lua @@ -73,7 +73,7 @@ function M.download(url, to_path) if M.is_windows() then M.exec( string.format( - 'powershell -Command "Invoke-WebRequest -Uri %s -UserAgent "%s" -OutFile %s"', + 'powershell -Command "Invoke-WebRequest -Uri \\"%s\\" -UserAgent \\"%s\\" -OutFile \\"%s\\""', url, user_agent, to_path From 54bdb1d2a321ec95b5f46555cde883165e25c6ff Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 17:05:20 +0100 Subject: [PATCH 58/70] fix windows run script --- crates/core/assets/run_windows.bat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/core/assets/run_windows.bat b/crates/core/assets/run_windows.bat index 36f45dc..08af098 100644 --- a/crates/core/assets/run_windows.bat +++ b/crates/core/assets/run_windows.bat @@ -1,2 +1,2 @@ @echo off -{BRIDGE_PATH} {DEBUG_FLAG} launch-neovim {LAUNCH_ARGS} "%s" "%%CD%%" "%%~1" %%2 +{BRIDGE_PATH} {DEBUG_FLAG} launch-neovim {LAUNCH_ARGS} "%CD%" "%~1" %2 From c229175bc2ff6d4af7672bdcef5df5e8dd09e5d4 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 17:12:56 +0100 Subject: [PATCH 59/70] maybe fix macos using dmgwiz --- crates/core/src/neovide.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/crates/core/src/neovide.rs b/crates/core/src/neovide.rs index b49539a..cae1456 100644 --- a/crates/core/src/neovide.rs +++ b/crates/core/src/neovide.rs @@ -151,16 +151,21 @@ pub fn update_or_install() -> Result { #[cfg(target_os = "macos")] { - use dmg::Attach; + use dmgwiz::{DmgWiz, Verbosity}; use std::os::unix::fs::PermissionsExt; - let info = Attach::new(&downloaded_file).with()?; + let mut wiz = DmgWiz::from_reader(file, Verbosity::None)?; + wiz.extract_all(&parent_dir)?; - let neovide_path = info.mount_point.join("Neovide.app/Contents/MacOS/neovide"); + let neovide_path = parent_dir + .join("Neovide.app") + .join("Contents") + .join("MacOS") + .join("neovide"); - fs::copy(neovide_path, target_path)?; + fs::copy(&neovide_path, &path()?)?; - fs::set_permissions(&target_path, Permissions::from_mode(0o700))?; + fs::set_permissions(&path()?, Permissions::from_mode(0o700))?; } fs::write(version_path()?, release.tag_name)?; From 7ef69edc38d10cea99ddab7d06abc6a0ebbd45c8 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 17:30:05 +0100 Subject: [PATCH 60/70] replace dmgwiz with dmg --- Cargo.lock | 103 +++---------------------------------- crates/core/Cargo.toml | 2 +- crates/core/src/neovide.rs | 13 ++--- 3 files changed, 16 insertions(+), 102 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5fa1b6d..df6b3f7 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,15 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "adc" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8d1f134dbe49adda618d0eced7120463e75e899b4676dcd7fc28f4daaa0d785" -dependencies = [ - "byteorder", -] - [[package]] name = "addr2line" version = "0.25.1" @@ -147,15 +138,6 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" -[[package]] -name = "bincode" -version = "1.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" -dependencies = [ - "serde", -] - [[package]] name = "bindgen" version = "0.72.1" @@ -225,16 +207,6 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" -[[package]] -name = "bzip2" -version = "0.4.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" -dependencies = [ - "bzip2-sys", - "libc", -] - [[package]] name = "bzip2" version = "0.6.1" @@ -244,16 +216,6 @@ dependencies = [ "libbz2-rs-sys", ] -[[package]] -name = "bzip2-sys" -version = "0.1.13+1.0.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14" -dependencies = [ - "cc", - "pkg-config", -] - [[package]] name = "cc" version = "1.2.50" @@ -471,7 +433,7 @@ version = "0.1.0" dependencies = [ "anyhow", "dirs", - "dmgwiz", + "dmg", "edn-rs", "flate2", "fs_extra", @@ -587,23 +549,13 @@ dependencies = [ ] [[package]] -name = "dmgwiz" -version = "1.1.0" +name = "dmg" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7091fb6813de08ee6a2e97592355e4f00c84dc3444a8f7e5807b7ea87094007f" +checksum = "abc28c350337837f23b4750f774371f63db232338c9f89bdb9eb48523a7c4722" dependencies = [ - "adc", - "bincode", - "byteorder", - "bzip2 0.4.4", - "flate2", - "itertools", - "lzfse", - "num-derive 0.4.2", - "num-traits", + "log", "plist", - "quick-xml 0.37.5", - "serde", ] [[package]] @@ -1193,24 +1145,6 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" -[[package]] -name = "lzfse" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b45f9990844d28a06dc7b7a3ffe3657e5379c8e3c0d6ae43a22e98a2256a0b" -dependencies = [ - "lzfse-sys", -] - -[[package]] -name = "lzfse-sys" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23c80418f1cfc4601f8ef2055dcea77eebea47d2edc74ee0dadb005dd3a9f604" -dependencies = [ - "cc", -] - [[package]] name = "lzma-rust2" version = "0.15.4" @@ -1359,7 +1293,7 @@ dependencies = [ "netlink-packet-sock-diag", "netlink-packet-utils", "netlink-sys", - "num-derive 0.3.3", + "num-derive", "num-traits", "thiserror 2.0.17", ] @@ -1409,17 +1343,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "num-derive" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.111", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -1574,8 +1497,7 @@ checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" dependencies = [ "base64", "indexmap", - "quick-xml 0.38.4", - "serde", + "quick-xml", "time", ] @@ -1638,15 +1560,6 @@ dependencies = [ "unicode-ident", ] -[[package]] -name = "quick-xml" -version = "0.37.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" -dependencies = [ - "memchr", -] - [[package]] name = "quick-xml" version = "0.38.4" @@ -3177,7 +3090,7 @@ checksum = "bdd8a47718a4ee5fe78e07667cd36f3de80e7c2bfe727c7074245ffc7303c037" dependencies = [ "aes", "arbitrary", - "bzip2 0.6.1", + "bzip2", "constant_time_eq", "crc32fast", "deflate64", diff --git a/crates/core/Cargo.toml b/crates/core/Cargo.toml index 596f4b8..08139b4 100644 --- a/crates/core/Cargo.toml +++ b/crates/core/Cargo.toml @@ -32,7 +32,7 @@ textwrap = "0.16.2" fs_extra = "1.3.0" [target.'cfg(target_os = "macos")'.dependencies] -dmgwiz = { version = "1.1.0", default-features = false } +dmg = "0.1.2" [dev-dependencies] pretty_assertions = "1.4.1" diff --git a/crates/core/src/neovide.rs b/crates/core/src/neovide.rs index cae1456..8c26ca0 100644 --- a/crates/core/src/neovide.rs +++ b/crates/core/src/neovide.rs @@ -110,7 +110,7 @@ pub fn update_or_install() -> Result { .map(PathBuf::from) .context("could not get parent dir")?; - let file = File::open(downloaded_file)?; + let file = File::open(&downloaded_file)?; #[cfg(target_os = "linux")] { @@ -151,20 +151,21 @@ pub fn update_or_install() -> Result { #[cfg(target_os = "macos")] { - use dmgwiz::{DmgWiz, Verbosity}; + use dmg::Attach; use std::os::unix::fs::PermissionsExt; - let mut wiz = DmgWiz::from_reader(file, Verbosity::None)?; - wiz.extract_all(&parent_dir)?; + let handle = Attach::new(downloaded_file).with()?; - let neovide_path = parent_dir + tracing::debug!("Mounted .dmg at {:?}", handle.mount_point); + + let neovide_path = handle + .mount_point .join("Neovide.app") .join("Contents") .join("MacOS") .join("neovide"); fs::copy(&neovide_path, &path()?)?; - fs::set_permissions(&path()?, Permissions::from_mode(0o700))?; } From 457261fb44fb7fba74acd4b575e908106d130e5f Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 20:50:21 +0100 Subject: [PATCH 61/70] add ability to enforce sidecar/bridge updates --- crates/core/src/bridge.rs | 33 ++++++++++++------ lua/defold/service/os.lua | 28 +++++++++++++++ lua/defold/sidecar.lua | 72 ++++++++++++++++++++++++++++++++++++--- 3 files changed, 118 insertions(+), 15 deletions(-) diff --git a/crates/core/src/bridge.rs b/crates/core/src/bridge.rs index 9d021a9..38294f9 100644 --- a/crates/core/src/bridge.rs +++ b/crates/core/src/bridge.rs @@ -8,6 +8,8 @@ use version_compare::Version; use crate::{github, utils}; +const MIN_VERSION: &str = "0.0.0"; + #[cfg(target_os = "windows")] const EXE_SUFFIX: &'static str = ".exe"; @@ -84,27 +86,37 @@ fn version() -> Result { } fn is_update_available() -> Result { - if version_path()?.exists() { - // if the version file is younger than a week dont bother - let last_modified = version_path()?.metadata()?.modified()?; - if last_modified.elapsed()? < Duration::from_hours(24 * 7) { - return Ok(false); - } + if !version_path()?.exists() { + return Ok(true); } let Ok(v) = version() else { return Ok(true); }; - // re-write the file again so that we only check once a week - fs::write(version_path()?, &v)?; - tracing::debug!("Bridge Version {v} installed"); let Some(installed) = Version::from(&v) else { + tracing::debug!("Couldnt parse version"); return Ok(true); }; + // if min version is higher, force update + let min_version = Version::from(MIN_VERSION).expect("cant parse min version"); + if installed < min_version { + tracing::debug!("Bridge Min Version {MIN_VERSION} exceeded (current {v})"); + return Ok(true); + } + + // if the version file is younger than a week dont bother + let last_modified = version_path()?.metadata()?.modified()?; + if last_modified.elapsed()? < Duration::from_hours(24 * 7) { + return Ok(false); + } + + // re-write the file again so that we only check once a week + fs::write(version_path()?, &v)?; + let release = github::fetch_release(OWNER, REPOSITORY)?; tracing::debug!("Bridge Version {} is newest", release.tag_name); @@ -120,7 +132,8 @@ fn install() -> Result { let path = local_path()?; if path.exists() && !is_update_available()? { - return local_path(); + tracing::debug!("No update available for {}", path.display()); + return Ok(path); } let (downloaded_file, release) = github::download_release(OWNER, REPOSITORY, NAME)?; diff --git a/lua/defold/service/os.lua b/lua/defold/service/os.lua index 3eca437..8957b0d 100644 --- a/lua/defold/service/os.lua +++ b/lua/defold/service/os.lua @@ -215,4 +215,32 @@ function M.write(path, content) file:close() end +---Checks if a file was updated within `threshold` +---@param path string +---@param threshold integer time in seconds +---@return boolean +function M.was_updated_within(path, threshold) + local stat = vim.uv.fs_stat(path) + if not stat then + return false -- doesnt exist, wasnt updated + end + + local current_time = os.time() + local last_modified = stat.mtime.sec + + return (current_time - last_modified) <= threshold +end + +---@param n integer +---@return integer +function M.hours(n) + return n * 60 * 60 +end + +---@param n integer +---@return integer +function M.days(n) + return n * M.hours(24) +end + return M diff --git a/lua/defold/sidecar.lua b/lua/defold/sidecar.lua index d9a9ff7..73dd5ae 100644 --- a/lua/defold/sidecar.lua +++ b/lua/defold/sidecar.lua @@ -1,3 +1,5 @@ +local min_version = "0.0.0" + local github_owner = "atomicptr" local github_repository = "defold.nvim" local github_file_name = { @@ -35,6 +37,34 @@ local function lib_extension() end end +---@return string +local function version_path() + local os = require "defold.service.os" + + local meta_dir = vim.fs.joinpath(os.data_dir(), "meta") + vim.fn.mkdir(meta_dir, "p") + + return vim.fs.joinpath(meta_dir, "sidecar_version") +end + +---@return string|nil +local function version() + local os = require "defold.service.os" + if not os.file_exists(version_path()) then + return nil + end + + local file = io.open(version_path(), "r") + if not file then + return nil + end + + local data = file:read "*a" + file:close() + + return data +end + ---Download latest sidecar release, install it at DATA_DIR/lib and return the lib path ---@return string|nil local function download_release() @@ -59,17 +89,15 @@ local function download_release() os.move(file, vim.fs.joinpath(lib_dir, lib_name() .. lib_extension())) - local meta_dir = vim.fs.joinpath(os.data_dir(), "meta") - vim.fn.mkdir(meta_dir, "p") - -- write version to file - os.write(vim.fs.joinpath(meta_dir, "sidecar_version"), release.tag_name) + os.write(version_path(), release.tag_name) return lib_dir end local function find_rust_lib_rootdir() local os = require "defold.service.os" + local log = require "defold.service.logger" local file_name = string.format("defold_nvim_sidecar%s", lib_extension()) local file_name_alt = string.format("libdefold_nvim_sidecar%s", lib_extension()) @@ -94,7 +122,41 @@ local function find_rust_lib_rootdir() os.file_exists(vim.fs.joinpath(lib_dir, file_name)) or os.file_exists(vim.fs.joinpath(lib_dir, file_name_alt)) then - -- TODO: check if version is outdated, if yes replace + local curr_version = version() + + if not curr_version then + return download_release() + end + + -- if current version is lower than the minimum version WE GOTTA UPDATE + if vim.version.cmp(curr_version, min_version) < 0 then + log.info( + string.format("Sidecar minimum version %s exceeds our installed version %s", min_version, curr_version) + ) + return download_release() + end + + -- if the version path wasnt updated for a week dont check for new one + if os.was_updated_within(version_path(), os.days(7)) then + return lib_dir + end + + local github = require "defold.service.github" + local release = github.fetch_release(github_owner, github_repository) + + if not release then + return lib_dir + end + + -- if new release + if vim.version.cmp(curr_version, release.tag_name) < 0 then + log.info(string.format("Sidecar new release %s (current %s)", release.tag_name, curr_version)) + return download_release() + end + + -- overwrite file again to delay next check + os.write(version_path(), release.tag_name) + return lib_dir else -- and if that also doesnt exist... download it From eb074f71a8147eb29205b5b3bad73af8f108c114 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 21:06:43 +0100 Subject: [PATCH 62/70] fix macos using different path for Defold preferences --- crates/core/src/editor_config.rs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/crates/core/src/editor_config.rs b/crates/core/src/editor_config.rs index 80aedf7..6ca2735 100644 --- a/crates/core/src/editor_config.rs +++ b/crates/core/src/editor_config.rs @@ -156,7 +156,12 @@ pub fn set_default_editor(plugin_root: &Path, launcher_settings: &LauncherSettin bail!("plugin root '{}' could not be found", plugin_root.display()); } - let config_dir = dirs::config_dir().context("could not find config dir")?; + let config_dir = if cfg!(target_os = "macos") { + // on macos defold stores prefs.editor_settings in ~/Library/Preferences + dirs::preference_dir().context("could not find pref dir")? + } else { + dirs::config_dir().context("could not find config dir")? + }; let path = config_dir.join("Defold").join("prefs.editor_settings"); if !path.exists() { From fdb8a5c30707b6ccb23e64561026520c169a3f94 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 21:19:35 +0100 Subject: [PATCH 63/70] if neovide/mobdap/bridge dont exist always re-download --- crates/core/src/bridge.rs | 4 ++++ crates/core/src/mobdap.rs | 22 ++++++++++++++-------- crates/core/src/neovide.rs | 22 ++++++++++++++-------- 3 files changed, 32 insertions(+), 16 deletions(-) diff --git a/crates/core/src/bridge.rs b/crates/core/src/bridge.rs index 38294f9..35849d1 100644 --- a/crates/core/src/bridge.rs +++ b/crates/core/src/bridge.rs @@ -86,6 +86,10 @@ fn version() -> Result { } fn is_update_available() -> Result { + if !local_path()?.exists() { + return Ok(true); + } + if !version_path()?.exists() { return Ok(true); } diff --git a/crates/core/src/mobdap.rs b/crates/core/src/mobdap.rs index 5155222..2a4fa0f 100644 --- a/crates/core/src/mobdap.rs +++ b/crates/core/src/mobdap.rs @@ -66,23 +66,29 @@ fn version() -> Result { } pub fn is_update_available() -> Result { - if version_path()?.exists() { - // if the version file is younger than a week dont bother - let last_modified = version_path()?.metadata()?.modified()?; - if last_modified.elapsed()? < Duration::from_hours(24 * 7) { - return Ok(false); - } + if !path()?.exists() { + return Ok(true); + } + + if !version_path()?.exists() { + return Ok(true); } let Ok(v) = version() else { return Ok(true); }; + tracing::debug!("Mobdap Version {v} installed"); + + // if the version file is younger than a week dont bother + let last_modified = version_path()?.metadata()?.modified()?; + if last_modified.elapsed()? < Duration::from_hours(24 * 7) { + return Ok(false); + } + // re-write the file again so that we only check once a week fs::write(version_path()?, &v)?; - tracing::debug!("Mobdap Version {v} installed"); - let Some(installed) = Version::from(&v) else { return Ok(true); }; diff --git a/crates/core/src/neovide.rs b/crates/core/src/neovide.rs index 8c26ca0..212fe77 100644 --- a/crates/core/src/neovide.rs +++ b/crates/core/src/neovide.rs @@ -64,23 +64,29 @@ fn version() -> Result { } pub fn is_update_available() -> Result { - if version_path()?.exists() { - // if the version file is younger than a week dont bother - let last_modified = version_path()?.metadata()?.modified()?; - if last_modified.elapsed()? < Duration::from_hours(24 * 7) { - return Ok(false); - } + if !path()?.exists() { + return Ok(true); + } + + if !version_path()?.exists() { + return Ok(true); } let Ok(v) = version() else { return Ok(true); }; + tracing::debug!("Neovide Version {v} installed"); + + // if the version file is younger than a week dont bother + let last_modified = version_path()?.metadata()?.modified()?; + if last_modified.elapsed()? < Duration::from_hours(24 * 7) { + return Ok(false); + } + // re-write the file again so that we only check once a week fs::write(version_path()?, &v)?; - tracing::debug!("Neovide Version {v} installed"); - let Some(installed) = Version::from(&v) else { return Ok(true); }; From 7dcd8158e59f5ed8769c013d44c63e9e67675ee3 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 21:31:32 +0100 Subject: [PATCH 64/70] fix alacritty name, only add class on linux --- crates/bridge/src/launcher.rs | 34 ++++++++++++++++++++++------------ 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/crates/bridge/src/launcher.rs b/crates/bridge/src/launcher.rs index 1285707..5aa9551 100644 --- a/crates/bridge/src/launcher.rs +++ b/crates/bridge/src/launcher.rs @@ -52,7 +52,7 @@ impl Launcher { } const DEFAULT_TERMINALS: [(&str, &str, &str); 5] = [ - ("alacratty", "--class=", "-e"), + ("alacritty", "--class=", "-e"), ("foot", "--app-id=", "-e"), ("ghostty", "--class=", "-e"), ("kitty", "--class=", "-e"), @@ -91,6 +91,7 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { args.push("--neovim-bin".to_string()); args.push(nvim.clone()); + // only add class on linux if cfg!(target_os = "linux") { args.push("--wayland_app_id".to_string()); args.push(VAR_CLASSNAME.to_string()); @@ -125,7 +126,10 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { } } - if let Some(class_arg) = class_arg { + // only add class on linux + if cfg!(target_os = "linux") + && let Some(class_arg) = class_arg + { if class_arg.ends_with('=') { args.push(class_arg.clone() + VAR_CLASSNAME); } else { @@ -169,11 +173,14 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { .find(|(name, _, _)| *name == exe_name) && let Ok(path) = which(name) { - if class_arg.ends_with('=') { - args.push((*class_arg).to_string() + VAR_CLASSNAME); - } else { - args.push((*class_arg).to_string()); - args.push(VAR_CLASSNAME.to_string()); + // only add class on linux + if cfg!(target_os = "linux") { + if class_arg.ends_with('=') { + args.push((*class_arg).to_string() + VAR_CLASSNAME); + } else { + args.push((*class_arg).to_string()); + args.push(VAR_CLASSNAME.to_string()); + } } if run_arg.ends_with('=') { @@ -195,11 +202,14 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { // try finding one of our supported default terminals for (name, class_arg, run_arg) in &DEFAULT_TERMINALS { if let Ok(path) = which(name) { - if class_arg.ends_with('=') { - args.push((*class_arg).to_string() + VAR_CLASSNAME); - } else { - args.push((*class_arg).to_string()); - args.push(VAR_CLASSNAME.to_string()); + // only add class on linux + if cfg!(target_os = "linux") { + if class_arg.ends_with('=') { + args.push((*class_arg).to_string() + VAR_CLASSNAME); + } else { + args.push((*class_arg).to_string()); + args.push(VAR_CLASSNAME.to_string()); + } } if run_arg.ends_with('=') { From cf918fa2135950ed569a8cde594bbe343638c178 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 22:12:55 +0100 Subject: [PATCH 65/70] add tests for game.project --- crates/core/src/game_project.rs | 38 +++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/crates/core/src/game_project.rs b/crates/core/src/game_project.rs index 8351150..7e159e6 100644 --- a/crates/core/src/game_project.rs +++ b/crates/core/src/game_project.rs @@ -65,3 +65,41 @@ impl TryFrom for GameProject { }) } } + +#[cfg(test)] +mod tests { + use crate::game_project::GameProject; + use pretty_assertions::assert_eq; + + #[test] + fn test_parse_game_project() { + let input = r"[project] +title = My Fancy Game +dependencies#1 = https://example.com/dependency1.zip +dependencies#2 = https://example.com/dependency2.zip + +[library] +include_dirs = my_fancy_game"; + + let g: GameProject = input + .to_string() + .try_into() + .expect("could not parse game.project file"); + + assert_eq!("My Fancy Game", g.title); + assert_eq!(2, g.dependencies.len()); + assert_eq!( + vec![ + "https://example.com/dependency1.zip", + "https://example.com/dependency2.zip" + ], + g.dependencies + ); + assert!(g.library.is_some()); + assert_eq!(1, g.library.as_ref().unwrap().include_dirs.len()); + assert_eq!( + vec!["my_fancy_game"], + g.library.as_ref().unwrap().include_dirs + ); + } +} From 143d5f3d73d5e591257ce115cef179194445eaab Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 22:31:48 +0100 Subject: [PATCH 66/70] install neovide on macos as an .app --- crates/bridge/src/launcher.rs | 2 +- crates/bridge/src/main.rs | 4 +- crates/core/src/bridge.rs | 4 +- crates/core/src/defold_annotations.rs | 2 +- crates/core/src/mobdap.rs | 6 +-- crates/core/src/neovide.rs | 54 +++++++++++++++++---------- crates/core/src/project.rs | 2 +- crates/sidecar/src/lib.rs | 2 +- 8 files changed, 45 insertions(+), 31 deletions(-) diff --git a/crates/bridge/src/launcher.rs b/crates/bridge/src/launcher.rs index 5aa9551..e3ef577 100644 --- a/crates/bridge/src/launcher.rs +++ b/crates/bridge/src/launcher.rs @@ -67,7 +67,7 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { .as_ref() .map(Into::into) .or_else(|| which("neovide").ok()) - .or_else(|| match neovide::update_or_install() { + .or_else(|| match neovide::install() { Ok(path) => Some(path), Err(err) => { tracing::error!("Could not download neovide because: {err:?}"); diff --git a/crates/bridge/src/main.rs b/crates/bridge/src/main.rs index 0f1944d..a651405 100644 --- a/crates/bridge/src/main.rs +++ b/crates/bridge/src/main.rs @@ -173,11 +173,11 @@ fn main() -> Result<()> { Commands::FocusNeovim { game_root_dir } => focus_neovim(absolute(game_root_dir)?)?, Commands::FocusGame { game_root_dir } => focus_game(absolute(game_root_dir)?)?, Commands::DownloadNeovide => { - let path = neovide::update_or_install()?; + let path = neovide::install()?; println!("Installed neovide at {}", path.display()); } Commands::DownloadMobdap => { - let path = mobdap::update_or_install()?; + let path = mobdap::install()?; println!("Installed mobdap at {}", path.display()); } Commands::InstallDependencies { diff --git a/crates/core/src/bridge.rs b/crates/core/src/bridge.rs index 35849d1..df3e4f7 100644 --- a/crates/core/src/bridge.rs +++ b/crates/core/src/bridge.rs @@ -55,7 +55,7 @@ fn exe_name() -> String { fn local_path() -> Result { let dir = dirs::data_dir() - .context("could not get state dir")? + .context("could not get data dir")? .join("defold.nvim") .join("bin"); @@ -66,7 +66,7 @@ fn local_path() -> Result { fn version_path() -> Result { let dir = dirs::data_dir() - .context("could not get state dir")? + .context("could not get data dir")? .join("defold.nvim") .join("meta"); diff --git a/crates/core/src/defold_annotations.rs b/crates/core/src/defold_annotations.rs index 80bca10..f4f2e44 100644 --- a/crates/core/src/defold_annotations.rs +++ b/crates/core/src/defold_annotations.rs @@ -20,7 +20,7 @@ pub fn dir() -> Result { fn version_path() -> Result { let dir = dirs::data_dir() - .context("could not get state dir")? + .context("could not get data dir")? .join("defold.nvim") .join("meta"); diff --git a/crates/core/src/mobdap.rs b/crates/core/src/mobdap.rs index 2a4fa0f..e7af09f 100644 --- a/crates/core/src/mobdap.rs +++ b/crates/core/src/mobdap.rs @@ -29,7 +29,7 @@ const NAME: &str = "mobdap-windows-amd64.zip"; fn path() -> Result { let dir = dirs::data_dir() - .context("could not get state dir")? + .context("could not get data dir")? .join("defold.nvim") .join("bin"); @@ -46,7 +46,7 @@ fn path() -> Result { fn version_path() -> Result { let dir = dirs::data_dir() - .context("could not get state dir")? + .context("could not get data dir")? .join("defold.nvim") .join("meta"); @@ -104,7 +104,7 @@ pub fn is_update_available() -> Result { Ok(current > installed) } -pub fn update_or_install() -> Result { +pub fn install() -> Result { if !is_update_available()? { return path(); } diff --git a/crates/core/src/neovide.rs b/crates/core/src/neovide.rs index 212fe77..eced945 100644 --- a/crates/core/src/neovide.rs +++ b/crates/core/src/neovide.rs @@ -1,4 +1,5 @@ use std::{ + env, fs::{self, File, Permissions}, path::PathBuf, time::Duration, @@ -25,26 +26,35 @@ const NAME: &str = "Neovide-aarch64-apple-darwin.dmg"; #[cfg(target_os = "windows")] const NAME: &'static str = "neovide.exe.zip"; -fn path() -> Result { +fn bin_dir() -> Result { let dir = dirs::data_dir() - .context("could not get state dir")? + .context("could not get data dir")? .join("defold.nvim") .join("bin"); - fs::create_dir_all(&dir)?; + Ok(dir) +} - let suffix = if cfg!(target_os = "windows") { - ".exe" - } else { - "" - }; +fn path() -> Result { + let dir = bin_dir()?; - Ok(dir.join(format!("neovide{suffix}"))) + fs::create_dir_all(&dir)?; + + Ok(match env::consts::OS { + "linux" => dir.join("neovide"), + "windows" => dir.join("neovide.exe"), + "macos" => dir + .join("Neovide.app") + .join("Contents") + .join("MacOS") + .join("neovide"), + _ => bail!("Unknown OS {}", env::consts::OS), + }) } fn version_path() -> Result { let dir = dirs::data_dir() - .context("could not get state dir")? + .context("could not get data dir")? .join("defold.nvim") .join("meta"); @@ -102,7 +112,7 @@ pub fn is_update_available() -> Result { Ok(current > installed) } -pub fn update_or_install() -> Result { +pub fn install() -> Result { if !is_update_available()? { return path(); } @@ -158,21 +168,25 @@ pub fn update_or_install() -> Result { #[cfg(target_os = "macos")] { use dmg::Attach; - use std::os::unix::fs::PermissionsExt; + use fs_extra::dir; let handle = Attach::new(downloaded_file).with()?; tracing::debug!("Mounted .dmg at {:?}", handle.mount_point); - let neovide_path = handle - .mount_point - .join("Neovide.app") - .join("Contents") - .join("MacOS") - .join("neovide"); + let neovide_path = handle.mount_point.join("Neovide.app"); + + let target_path = bin_dir()?.join("Neovide.app"); + + if target_path.exists() { + fs::remove_dir_all(&target_path)?; + } - fs::copy(&neovide_path, &path()?)?; - fs::set_permissions(&path()?, Permissions::from_mode(0o700))?; + dir::copy( + &neovide_path, + &target_path, + &dir::CopyOptions::new().overwrite(true).copy_inside(true), + )?; } fs::write(version_path()?, release.tag_name)?; diff --git a/crates/core/src/project.rs b/crates/core/src/project.rs index d0cb7de..40e2a65 100644 --- a/crates/core/src/project.rs +++ b/crates/core/src/project.rs @@ -22,7 +22,7 @@ fn ident(string: &str) -> Result { pub fn deps_root() -> Result { let dir = dirs::data_dir() - .context("could not get state dir")? + .context("could not get data dir")? .join("defold.nvim") .join("deps"); fs::create_dir_all(&dir)?; diff --git a/crates/sidecar/src/lib.rs b/crates/sidecar/src/lib.rs index f6f786f..e419d64 100644 --- a/crates/sidecar/src/lib.rs +++ b/crates/sidecar/src/lib.rs @@ -197,7 +197,7 @@ fn focus_game(_lua: &Lua, game_root: String) -> LuaResult<()> { #[instrument(level = "debug", err(Debug), skip_all)] fn mobdap_install(_lua: &Lua, _: ()) -> LuaResult { - let path = mobdap::update_or_install()?; + let path = mobdap::install()?; Ok(path .to_str() .context("could not convert path to string")? From 629675ff49e1f5e817aad9e7b2fc519d5c34b244 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 23:19:12 +0100 Subject: [PATCH 67/70] fix launching neovim with line number not working --- crates/bridge/src/launcher.rs | 65 +++++++++++++++++++++--------- crates/bridge/src/main.rs | 22 +++++----- crates/core/assets/run_linux.sh | 2 +- crates/core/assets/run_macos.sh | 2 +- crates/core/assets/run_windows.bat | 2 +- crates/core/src/editor_config.rs | 31 +++++++++----- crates/core/src/script_api.rs | 8 ++-- 7 files changed, 83 insertions(+), 49 deletions(-) diff --git a/crates/bridge/src/launcher.rs b/crates/bridge/src/launcher.rs index e3ef577..9c77713 100644 --- a/crates/bridge/src/launcher.rs +++ b/crates/bridge/src/launcher.rs @@ -19,7 +19,8 @@ const ERR_TERMINAL_NOT_FOUND: &str = "Could not find any suitable terminal"; const VAR_CLASSNAME: &str = "{CLASSNAME}"; const VAR_ADDRESS: &str = "{ADDR}"; -const VAR_REMOTE_CMD: &str = "{REMOTE_CMD}"; +const VAR_LINE: &str = "{LINE}"; +const VAR_FILE: &str = "{FILE}"; #[derive(Debug)] struct Launcher(PathBuf, Vec); @@ -106,7 +107,8 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { args.push(VAR_ADDRESS.to_string()); args.push("--remote".to_string()); - args.push(VAR_REMOTE_CMD.to_string()); + args.push(VAR_LINE.to_string()); + args.push(VAR_FILE.to_string()); Ok(Launcher(executable.clone(), args)) } @@ -151,7 +153,8 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { args.push(VAR_ADDRESS.to_string()); args.push("--remote".to_string()); - args.push(VAR_REMOTE_CMD.to_string()); + args.push(VAR_LINE.to_string()); + args.push(VAR_FILE.to_string()); Some(Launcher(exe, args)) } else { @@ -194,7 +197,8 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { args.push(VAR_ADDRESS.to_string()); args.push("--remote".to_string()); - args.push(VAR_REMOTE_CMD.to_string()); + args.push(VAR_LINE.to_string()); + args.push(VAR_FILE.to_string()); return Some(Launcher(path, args)); } @@ -223,7 +227,8 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { args.push(VAR_ADDRESS.to_string()); args.push("--remote".to_string()); - args.push(VAR_REMOTE_CMD.to_string()); + args.push(VAR_LINE.to_string()); + args.push(VAR_FILE.to_string()); return Some(Launcher(path, args)); } @@ -260,7 +265,13 @@ fn create_launcher(cfg: &PluginConfig, nvim: &String) -> Result { } } -fn nvim_open_file_remote(nvim: &str, server: &str, remote_cmd: &str) -> Result<()> { +fn nvim_open_file_remote(nvim: &str, server: &str, file: &str, line: Option) -> Result<()> { + let remote_cmd = if let Some(line) = line { + format!("+{line} {file}") + } else { + file.to_string() + }; + tracing::debug!("Open '{remote_cmd}' via socket: {server}"); let out = Command::new(nvim) @@ -277,7 +288,13 @@ fn nvim_open_file_remote(nvim: &str, server: &str, remote_cmd: &str) -> Result<( Ok(()) } -fn run_fsock(launcher: Launcher, nvim: &str, root_dir: &Path, remote_cmd: &str) -> Result<()> { +fn run_fsock( + launcher: Launcher, + nvim: &str, + root_dir: &Path, + file: &str, + line: Option, +) -> Result<()> { let socket_file = utils::runtime_dir( root_dir .to_str() @@ -302,7 +319,8 @@ fn run_fsock(launcher: Launcher, nvim: &str, root_dir: &Path, remote_cmd: &str) socket_file .to_str() .context("could not convert path to string")?, - remote_cmd, + file, + line, ) { tracing::error!("Failed to communicate with neovim server: {err:?}"); @@ -317,7 +335,13 @@ fn run_fsock(launcher: Launcher, nvim: &str, root_dir: &Path, remote_cmd: &str) Ok(()) } -fn run_netsock(launcher: Launcher, nvim: &str, root_dir: &Path, remote_cmd: &str) -> Result<()> { +fn run_netsock( + launcher: Launcher, + nvim: &str, + root_dir: &Path, + file: &str, + line: Option, +) -> Result<()> { let port_file = utils::runtime_dir( root_dir .to_str() @@ -338,7 +362,7 @@ fn run_netsock(launcher: Launcher, nvim: &str, root_dir: &Path, remote_cmd: &str if is_port_in_use(port) { // if we couldnt communicate with the server despite existing apparently // delete it and start a new instance - if let Err(err) = nvim_open_file_remote(nvim, &socket, remote_cmd) { + if let Err(err) = nvim_open_file_remote(nvim, &socket, file, line) { tracing::error!("Failed to communicate with neovim server: {err:?}"); let new_port = utils::find_free_port(); @@ -357,7 +381,7 @@ fn run_netsock(launcher: Launcher, nvim: &str, root_dir: &Path, remote_cmd: &str } pub fn run( - plugin_config: PluginConfig, + plugin_config: &PluginConfig, root_dir: PathBuf, file: &Path, line: Option, @@ -367,7 +391,7 @@ pub fn run( .context("could not convert nvim path to string")? .to_string(); - let launcher = create_launcher(&plugin_config, &nvim)?; + let launcher = create_launcher(plugin_config, &nvim)?; let launcher = if cfg!(target_os = "linux") { launcher.apply_var( @@ -390,21 +414,22 @@ pub fn run( let file_str = file.to_str().context("could not convert path to string")?; - let remote_cmd = match line { - Some(line) => format!("+{line} {file_str}"), - None => file_str.to_string(), + let launcher = if let Some(line) = line { + launcher.apply_var(VAR_LINE, &format!("+{line}")) + } else { + launcher }; - let launcher = launcher.apply_var(VAR_REMOTE_CMD, &remote_cmd.clone()); + let launcher = launcher.apply_var(VAR_FILE, file_str); match plugin_config.socket_type { - Some(SocketType::Fsock) => run_fsock(launcher, &nvim, &root_dir, &remote_cmd)?, - Some(SocketType::Netsock) => run_netsock(launcher, &nvim, &root_dir, &remote_cmd)?, + Some(SocketType::Fsock) => run_fsock(launcher, &nvim, &root_dir, file_str, line)?, + Some(SocketType::Netsock) => run_netsock(launcher, &nvim, &root_dir, file_str, line)?, None => { if cfg!(target_os = "linux") || cfg!(target_os = "macos") { - run_fsock(launcher, &nvim, &root_dir, &remote_cmd)?; + run_fsock(launcher, &nvim, &root_dir, file_str, line)?; } else { - run_netsock(launcher, &nvim, &root_dir, &remote_cmd)?; + run_netsock(launcher, &nvim, &root_dir, file_str, line)?; } } } diff --git a/crates/bridge/src/main.rs b/crates/bridge/src/main.rs index a651405..c071bca 100644 --- a/crates/bridge/src/main.rs +++ b/crates/bridge/src/main.rs @@ -41,23 +41,23 @@ enum Commands { #[arg(long = "executable")] executable: Option, - #[arg(long = "extra-arguments", value_delimiter = ' ', num_args = 0..)] - extra_arguments: Option>, - #[arg(long = "terminal-class-argument")] terminal_class_argument: Option, #[arg(long = "terminal-run-argument")] terminal_run_argument: Option, - #[clap(value_name = "GAME_ROOT_DIR", index = 1)] + #[clap(value_name = "GAME_ROOT_DIR")] game_root_dir: String, - #[clap(value_name = "FILE", index = 2)] + #[clap(value_name = "FILE")] file: String, - #[clap(value_name = "LINE", index = 3)] + #[clap(value_name = "LINE")] line: Option, + + #[arg(last = true)] + extra_arguments: Option>, }, /// Focus the currently open instance of Neovim FocusNeovim { @@ -126,10 +126,10 @@ fn main() -> Result<()> { tracing_subscriber::fmt() .with_file(true) .with_line_number(true) - .with_max_level(if args.as_ref().is_some_and(|args| args.debug) { - Level::DEBUG - } else { - Level::INFO + .with_max_level(match &args { + Some(args) if args.debug => Level::DEBUG, + Some(_) => Level::INFO, + None => Level::DEBUG, }) .with_writer(logfile) .with_ansi(false) @@ -158,7 +158,7 @@ fn main() -> Result<()> { file, line, } => launcher::run( - PluginConfig { + &PluginConfig { launcher_type, socket_type, executable, diff --git a/crates/core/assets/run_linux.sh b/crates/core/assets/run_linux.sh index 04c1d36..3bd84a6 100644 --- a/crates/core/assets/run_linux.sh +++ b/crates/core/assets/run_linux.sh @@ -1,2 +1,2 @@ #!/usr/bin/env bash -{BRIDGE_PATH} {DEBUG_FLAG} launch-neovim {LAUNCH_ARGS} "$(realpath .)" "$1" $2 +{BRIDGE_PATH} {DEBUG_FLAG} launch-neovim {LAUNCH_PRE_ARGS} "$(realpath .)" "$1" $2 {LAUNCH_POST_ARGS} diff --git a/crates/core/assets/run_macos.sh b/crates/core/assets/run_macos.sh index 31d4e14..2ef7ef4 100644 --- a/crates/core/assets/run_macos.sh +++ b/crates/core/assets/run_macos.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash export PATH="/usr/bin:/usr/local/bin:$PATH" -{BRIDGE_PATH} {DEBUG_FLAG} launch-neovim {LAUNCH_ARGS} "$(realpath .)" "$1" $2 +{BRIDGE_PATH} {DEBUG_FLAG} launch-neovim {LAUNCH_PRE_ARGS} "$(realpath .)" "$1" $2 {LAUNCH_POST_ARGS} diff --git a/crates/core/assets/run_windows.bat b/crates/core/assets/run_windows.bat index 08af098..d09ab0c 100644 --- a/crates/core/assets/run_windows.bat +++ b/crates/core/assets/run_windows.bat @@ -1,2 +1,2 @@ @echo off -{BRIDGE_PATH} {DEBUG_FLAG} launch-neovim {LAUNCH_ARGS} "%CD%" "%~1" %2 +{BRIDGE_PATH} {DEBUG_FLAG} launch-neovim {LAUNCH_PRE_ARGS} "%CD%" "%~1" %2 {LAUNCH_POST_ARGS} diff --git a/crates/core/src/editor_config.rs b/crates/core/src/editor_config.rs index 6ca2735..c40e3b4 100644 --- a/crates/core/src/editor_config.rs +++ b/crates/core/src/editor_config.rs @@ -46,7 +46,7 @@ pub struct LauncherSettings { impl LauncherSettings { #[must_use] - pub fn bridge_cli_args(&self) -> Vec { + pub fn bridge_pre_cli_args(&self) -> Vec { let mut args = Vec::new(); args.push("--launcher-type".to_string()); @@ -68,13 +68,6 @@ impl LauncherSettings { args.push(executable.clone()); } - if let Some(extra_args) = &self.extra_arguments { - args.push("--extra-arguments".to_string()); - for arg in extra_args { - args.push(arg.clone()); - } - } - if let Some(terminal) = &self.terminal { if let Some(class_arg) = &terminal.class_argument { args.push("--terminal-class-argument".to_string()); @@ -89,6 +82,20 @@ impl LauncherSettings { args } + + #[must_use] + pub fn bridge_post_cli_args(&self) -> Vec { + let mut args = Vec::new(); + + if let Some(extra_args) = &self.extra_arguments { + args.push("--".to_string()); + for arg in extra_args { + args.push(arg.clone()); + } + } + + args + } } #[cfg(target_os = "linux")] @@ -118,7 +125,8 @@ fn create_runner_script( let script_path = dir.join(format!("run.{SCRIPT_EXT}")); let bridge_path = bridge::path(plugin_root)?; - let launch_args = launcher_settings.bridge_cli_args().join(" "); + let launch_pre_args = launcher_settings.bridge_pre_cli_args().join(" "); + let launch_post_args = launcher_settings.bridge_post_cli_args().join(" "); fs::write( &script_path, @@ -129,7 +137,7 @@ fn create_runner_script( .to_str() .context("could not convert bridge path")?, ) - .replace("{LAUNCH_ARGS}", &launch_args) + .replace("{LAUNCH_PRE_ARGS}", &launch_pre_args) .replace( "{DEBUG_FLAG}", if let Some(debug) = launcher_settings.debug @@ -139,7 +147,8 @@ fn create_runner_script( } else { "" }, - ), + ) + .replace("{LAUNCH_POST_ARGS}", &launch_post_args), )?; #[cfg(not(target_os = "windows"))] diff --git a/crates/core/src/script_api.rs b/crates/core/src/script_api.rs index 5b473bb..0535f73 100644 --- a/crates/core/src/script_api.rs +++ b/crates/core/src/script_api.rs @@ -161,7 +161,7 @@ fn compile_table(out: &mut String, table: &Value, parents: &Vec<&Value>) -> Resu table.name.clone().expect("table must have name") )?; } else { - writeln!(out, "{} = {{}}", table.fully_qualified_name(&parents)?)?; + writeln!(out, "{} = {{}}", table.fully_qualified_name(parents)?)?; } if let Some(members) = &table.members { @@ -299,17 +299,17 @@ fn compile_function(out: &mut String, function: &Value, parents: &Vec<&Value>) - String::new() }; - if !parents.is_empty() { + if parents.is_empty() { writeln!( out, "function {}({params}) end", - function.fully_qualified_name(&parents)?, + function.name.clone().unwrap() )?; } else { writeln!( out, "function {}({params}) end", - function.name.clone().unwrap() + function.fully_qualified_name(parents)?, )?; } From 0376696443110b618b39b5bbb5485a4f397eb618 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 23:37:56 +0100 Subject: [PATCH 68/70] escape executable path, fix bug with new line syntax --- crates/bridge/src/launcher.rs | 9 ++++++++- crates/core/assets/run_linux.sh | 2 +- crates/core/assets/run_macos.sh | 2 +- crates/core/assets/run_windows.bat | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/crates/bridge/src/launcher.rs b/crates/bridge/src/launcher.rs index 9c77713..02d14d6 100644 --- a/crates/bridge/src/launcher.rs +++ b/crates/bridge/src/launcher.rs @@ -50,6 +50,13 @@ impl Launcher { .collect(), ) } + + fn filter_params(self, f: F) -> Self + where + F: Fn(&String) -> bool, + { + Self(self.0, self.1.into_iter().filter(|s| f(s)).collect()) + } } const DEFAULT_TERMINALS: [(&str, &str, &str); 5] = [ @@ -417,7 +424,7 @@ pub fn run( let launcher = if let Some(line) = line { launcher.apply_var(VAR_LINE, &format!("+{line}")) } else { - launcher + launcher.filter_params(|s| s != VAR_LINE) }; let launcher = launcher.apply_var(VAR_FILE, file_str); diff --git a/crates/core/assets/run_linux.sh b/crates/core/assets/run_linux.sh index 3bd84a6..dcdcadb 100644 --- a/crates/core/assets/run_linux.sh +++ b/crates/core/assets/run_linux.sh @@ -1,2 +1,2 @@ #!/usr/bin/env bash -{BRIDGE_PATH} {DEBUG_FLAG} launch-neovim {LAUNCH_PRE_ARGS} "$(realpath .)" "$1" $2 {LAUNCH_POST_ARGS} +"{BRIDGE_PATH}" {DEBUG_FLAG} launch-neovim {LAUNCH_PRE_ARGS} "$(realpath .)" "$1" $2 {LAUNCH_POST_ARGS} diff --git a/crates/core/assets/run_macos.sh b/crates/core/assets/run_macos.sh index 2ef7ef4..2c028cd 100644 --- a/crates/core/assets/run_macos.sh +++ b/crates/core/assets/run_macos.sh @@ -1,3 +1,3 @@ #!/usr/bin/env bash export PATH="/usr/bin:/usr/local/bin:$PATH" -{BRIDGE_PATH} {DEBUG_FLAG} launch-neovim {LAUNCH_PRE_ARGS} "$(realpath .)" "$1" $2 {LAUNCH_POST_ARGS} +"{BRIDGE_PATH}" {DEBUG_FLAG} launch-neovim {LAUNCH_PRE_ARGS} "$(realpath .)" "$1" $2 {LAUNCH_POST_ARGS} diff --git a/crates/core/assets/run_windows.bat b/crates/core/assets/run_windows.bat index d09ab0c..c06dd34 100644 --- a/crates/core/assets/run_windows.bat +++ b/crates/core/assets/run_windows.bat @@ -1,2 +1,2 @@ @echo off -{BRIDGE_PATH} {DEBUG_FLAG} launch-neovim {LAUNCH_PRE_ARGS} "%CD%" "%~1" %2 {LAUNCH_POST_ARGS} +"{BRIDGE_PATH}" {DEBUG_FLAG} launch-neovim {LAUNCH_PRE_ARGS} "%CD%" "%~1" %2 {LAUNCH_POST_ARGS} From 551b961c3bd14ba273003b89c86a0a1363e39a6e Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 23:47:27 +0100 Subject: [PATCH 69/70] remove defold_api --- resources/defold_api/b2d.body.lua | 268 ---- resources/defold_api/b2d.lua | 29 - resources/defold_api/bit.lua | 108 -- resources/defold_api/buffer.lua | 109 -- resources/defold_api/builtins.lua | 34 - resources/defold_api/camera.lua | 130 -- resources/defold_api/collectionfactory.lua | 88 -- resources/defold_api/collectionproxy.lua | 63 - resources/defold_api/crash.lua | 119 -- resources/defold_api/editor.lua | 798 ------------ resources/defold_api/factory.lua | 80 -- resources/defold_api/font.lua | 42 - resources/defold_api/go.lua | 377 ------ resources/defold_api/graphics.lua | 344 ----- resources/defold_api/gui.lua | 1310 -------------------- resources/defold_api/html5.lua | 39 - resources/defold_api/http.lua | 57 - resources/defold_api/image.lua | 80 -- resources/defold_api/json.lua | 43 - resources/defold_api/label.lua | 32 - resources/defold_api/liveupdate.lua | 157 --- resources/defold_api/meta.lua | 219 ---- resources/defold_api/model.lua | 108 -- resources/defold_api/msg.lua | 56 - resources/defold_api/particlefx.lua | 87 -- resources/defold_api/physics.lua | 315 ----- resources/defold_api/profiler.lua | 136 -- resources/defold_api/render.lua | 543 -------- resources/defold_api/resource.lua | 795 ------------ resources/defold_api/socket.lua | 177 --- resources/defold_api/sound.lua | 151 --- resources/defold_api/sprite.lua | 66 - resources/defold_api/sys.lua | 316 ----- resources/defold_api/tilemap.lua | 113 -- resources/defold_api/timer.lua | 70 -- resources/defold_api/types.lua | 55 - resources/defold_api/vmath.lua | 471 ------- resources/defold_api/window.lua | 130 -- resources/defold_api/zlib.lua | 30 - 39 files changed, 8145 deletions(-) delete mode 100644 resources/defold_api/b2d.body.lua delete mode 100644 resources/defold_api/b2d.lua delete mode 100644 resources/defold_api/bit.lua delete mode 100644 resources/defold_api/buffer.lua delete mode 100644 resources/defold_api/builtins.lua delete mode 100644 resources/defold_api/camera.lua delete mode 100644 resources/defold_api/collectionfactory.lua delete mode 100644 resources/defold_api/collectionproxy.lua delete mode 100644 resources/defold_api/crash.lua delete mode 100644 resources/defold_api/editor.lua delete mode 100644 resources/defold_api/factory.lua delete mode 100644 resources/defold_api/font.lua delete mode 100644 resources/defold_api/go.lua delete mode 100644 resources/defold_api/graphics.lua delete mode 100644 resources/defold_api/gui.lua delete mode 100644 resources/defold_api/html5.lua delete mode 100644 resources/defold_api/http.lua delete mode 100644 resources/defold_api/image.lua delete mode 100644 resources/defold_api/json.lua delete mode 100644 resources/defold_api/label.lua delete mode 100644 resources/defold_api/liveupdate.lua delete mode 100644 resources/defold_api/meta.lua delete mode 100644 resources/defold_api/model.lua delete mode 100644 resources/defold_api/msg.lua delete mode 100644 resources/defold_api/particlefx.lua delete mode 100644 resources/defold_api/physics.lua delete mode 100644 resources/defold_api/profiler.lua delete mode 100644 resources/defold_api/render.lua delete mode 100644 resources/defold_api/resource.lua delete mode 100644 resources/defold_api/socket.lua delete mode 100644 resources/defold_api/sound.lua delete mode 100644 resources/defold_api/sprite.lua delete mode 100644 resources/defold_api/sys.lua delete mode 100644 resources/defold_api/tilemap.lua delete mode 100644 resources/defold_api/timer.lua delete mode 100644 resources/defold_api/types.lua delete mode 100644 resources/defold_api/vmath.lua delete mode 100644 resources/defold_api/window.lua delete mode 100644 resources/defold_api/zlib.lua diff --git a/resources/defold_api/b2d.body.lua b/resources/defold_api/b2d.body.lua deleted file mode 100644 index f862857..0000000 --- a/resources/defold_api/b2d.body.lua +++ /dev/null @@ -1,268 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Box2D b2Body documentation - - Functions for interacting with Box2D bodies. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.b2d.body -b2d.body = {} - ----Apply an angular impulse. ----@param body b2Body body ----@param impulse number impulse the angular impulse in units of kg*m*m/s -function b2d.body.apply_angular_impulse(body, impulse) end - ----Apply a force at a world point. If the force is not ----applied at the center of mass, it will generate a torque and ----affect the angular velocity. This wakes up the body. ----@param body b2Body body ----@param force vector3 the world force vector, usually in Newtons (N). ----@param point vector3 the world position of the point of application. -function b2d.body.apply_force(body, force, point) end - ----Apply a force to the center of mass. This wakes up the body. ----@param body b2Body body ----@param force vector3 the world force vector, usually in Newtons (N). -function b2d.body.apply_force_to_center(body, force) end - ----Apply an impulse at a point. This immediately modifies the velocity. ----It also modifies the angular velocity if the point of application ----is not at the center of mass. This wakes up the body. ----@param body b2Body body ----@param impulse vector3 the world impulse vector, usually in N-seconds or kg-m/s. ----@param point vector3 the world position of the point of application. -function b2d.body.apply_linear_impulse(body, impulse, point) end - ----Apply a torque. This affects the angular velocity ----without affecting the linear velocity of the center of mass. ----This wakes up the body. ----@param body b2Body body ----@param torque number torque about the z-axis (out of the screen), usually in N-m. -function b2d.body.apply_torque(body, torque) end - ----You can disable sleeping on this body. If you disable sleeping, the body will be woken. ----@param body b2Body body ----@param enable boolean if false, the body will never sleep, and consume more CPU -function b2d.body.enable_sleep(body, enable) end - ----Get the angle in radians. ----@param body b2Body body ----@return number angle the current world rotation angle in radians. -function b2d.body.get_angle(body) end - ----Get the angular damping of the body. ----@param body b2Body body ----@return number damping the damping -function b2d.body.get_angular_damping(body) end - ----Get the angular velocity. ----@param body b2Body body ----@return number velocity the angular velocity in radians/second. -function b2d.body.get_angular_velocity(body) end - ----Get the gravity scale of the body. ----@param body b2Body body ----@return number scale the scale -function b2d.body.get_gravity_scale(body) end - ----Get the linear damping of the body. ----@param body b2Body body ----@return number damping the damping -function b2d.body.get_linear_damping(body) end - ----Get the linear velocity of the center of mass. ----@param body b2Body body ----@return vector3 velocity the linear velocity of the center of mass. -function b2d.body.get_linear_velocity(body) end - ----Get the world velocity of a local point. ----@param body b2Body body ----@param local_point vector3 a point in local coordinates. ----@return vector3 velocity the world velocity of a point. -function b2d.body.get_linear_velocity_from_local_point(body, local_point) end - ----Get the world linear velocity of a world point attached to this body. ----@param body b2Body body ----@param world_point vector3 a point in world coordinates. ----@return vector3 velocity the world velocity of a point. -function b2d.body.get_linear_velocity_from_world_point(body, world_point) end - ----Get the local position of the center of mass. ----@param body b2Body body ----@return vector3 center Get the local position of the center of mass. -function b2d.body.get_local_center_of_mass(body) end - ----Gets a local point relative to the body's origin given a world point. ----@param body b2Body body ----@param world_point vector3 a point in world coordinates. ----@return vector3 vector the corresponding local point relative to the body's origin. -function b2d.body.get_local_point(body, world_point) end - ----Gets a local vector given a world vector. ----@param body b2Body body ----@param world_vector vector3 a vector in world coordinates. ----@return vector3 vector the corresponding local vector. -function b2d.body.get_local_vector(body, world_vector) end - ----Get the total mass of the body. ----@param body b2Body body ----@return number mass the mass, usually in kilograms (kg). -function b2d.body.get_mass(body) end - ----Get the world body origin position. ----@param body b2Body body ----@return vector3 position the world position of the body's origin. -function b2d.body.get_position(body) end - ----Get the rotational inertia of the body about the local origin. ----@param body b2Body body ----@return number inertia the rotational inertia, usually in kg-m^2. -function b2d.body.get_rotational_inertia(body) end - ----Get the type of this body. ----@param body b2Body body ----@return b2BodyType type the body type -function b2d.body.get_type(body) end - ----Get the parent world of this body. ----@param body b2Body body ----@return b2World world -function b2d.body.get_world(body) end - ----Get the world position of the center of mass. ----@param body b2Body body ----@return vector3 center Get the world position of the center of mass. -function b2d.body.get_world_center_of_mass(body) end - ----Get the world coordinates of a point given the local coordinates. ----@param body b2Body body ----@param local_vector vector3 localPoint a point on the body measured relative the the body's origin. ----@return vector3 vector the same point expressed in world coordinates. -function b2d.body.get_world_point(body, local_vector) end - ----Get the world coordinates of a vector given the local coordinates. ----@param body b2Body body ----@param local_vector vector3 a vector fixed in the body. ----@return vector3 vector the same vector expressed in world coordinates. -function b2d.body.get_world_vector(body, local_vector) end - ----Get the active state of the body. ----@param body b2Body body ----@return boolean enabled is the body active -function b2d.body.is_active(body) end - ----Get the sleeping state of this body. ----@param body b2Body body ----@return boolean enabled true if the body is awake, false if it's sleeping. -function b2d.body.is_awake(body) end - ----Is this body in bullet mode ----@param body b2Body body ----@return boolean enabled true if the body is in bullet mode -function b2d.body.is_bullet(body) end - ----Does this body have fixed rotation? ----@param body b2Body body ----@return boolean enabled is the rotation fixed -function b2d.body.is_fixed_rotation(body) end - ----Is this body allowed to sleep ----@param body b2Body body ----@return boolean enabled true if the body is allowed to sleep -function b2d.body.is_sleeping_enabled(body) end - ----This resets the mass properties to the sum of the mass properties of the fixtures. ----This normally does not need to be called unless you called SetMassData to override ----@param body b2Body body -function b2d.body.reset_mass_data(body) end - ----Set the active state of the body. An inactive body is not ----simulated and cannot be collided with or woken up. ----If you pass a flag of true, all fixtures will be added to the ----broad-phase. ----If you pass a flag of false, all fixtures will be removed from ----the broad-phase and all contacts will be destroyed. ----Fixtures and joints are otherwise unaffected. You may continue ----to create/destroy fixtures and joints on inactive bodies. ----Fixtures on an inactive body are implicitly inactive and will ----not participate in collisions, ray-casts, or queries. ----Joints connected to an inactive body are implicitly inactive. ----An inactive body is still owned by a b2World object and remains ----in the body list. ----@param body b2Body body ----@param enable boolean true if the body should be active -function b2d.body.set_active(body, enable) end - ----Set the angular damping of the body. ----@param body b2Body body ----@param damping number the damping -function b2d.body.set_angular_damping(body, damping) end - ----Set the angular velocity. ----@param body b2Body body ----@param omega number the new angular velocity in radians/second. -function b2d.body.set_angular_velocity(body, omega) end - ----Set the sleep state of the body. A sleeping body has very low CPU cost. ----@param body b2Body body ----@param enable boolean flag set to false to put body to sleep, true to wake it. -function b2d.body.set_awake(body, enable) end - ----Should this body be treated like a bullet for continuous collision detection? ----@param body b2Body body ----@param enable boolean if true, the body will be in bullet mode -function b2d.body.set_bullet(body, enable) end - ----Set this body to have fixed rotation. This causes the mass to be reset. ----@param body b2Body body ----@param enable boolean true if the rotation should be fixed -function b2d.body.set_fixed_rotation(body, enable) end - ----Set the gravity scale of the body. ----@param body b2Body body ----@param scale number the scale -function b2d.body.set_gravity_scale(body, scale) end - ----Set the linear damping of the body. ----@param body b2Body body ----@param damping number the damping -function b2d.body.set_linear_damping(body, damping) end - ----Set the linear velocity of the center of mass. ----@param body b2Body body ----@param velocity vector3 the new linear velocity of the center of mass. -function b2d.body.set_linear_velocity(body, velocity) end - ----Set the position of the body's origin and rotation. ----This breaks any contacts and wakes the other bodies. ----Manipulating a body's transform may cause non-physical behavior. ----@param body b2Body body ----@param position vector3 the world position of the body's local origin. ----@param angle number the world position of the body's local origin. -function b2d.body.set_transform(body, position, angle) end - ----Set the type of this body. This may alter the mass and velocity. ----@param body b2Body body ----@param type b2BodyType the body type -function b2d.body.set_type(body, type) end - ----Dynamic body -b2d.body.B2_DYNAMIC_BODY = nil - ----Kinematic body -b2d.body.B2_KINEMATIC_BODY = nil - ----Static (immovable) body -b2d.body.B2_STATIC_BODY = nil - -return b2d.body \ No newline at end of file diff --git a/resources/defold_api/b2d.lua b/resources/defold_api/b2d.lua deleted file mode 100644 index dbd6941..0000000 --- a/resources/defold_api/b2d.lua +++ /dev/null @@ -1,29 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Box2D documentation - - Functions for interacting with Box2D. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.b2d -b2d = {} - ----Get the Box2D body from a collision object ----@param url string|hash|url the url to the game object collision component ----@return b2Body body the body if successful. Otherwise `nil`. -function b2d.get_body(url) end - ----Get the Box2D world from the current collection ----@return b2World world the world if successful. Otherwise `nil`. -function b2d.get_world() end - -return b2d \ No newline at end of file diff --git a/resources/defold_api/bit.lua b/resources/defold_api/bit.lua deleted file mode 100644 index 132ef1b..0000000 --- a/resources/defold_api/bit.lua +++ /dev/null @@ -1,108 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Bitwise operations API documentation - - [Lua BitOp](http://bitop.luajit.org/api.html) is a C extension module for Lua 5.1/5.2 which adds bitwise operations on numbers. - Lua BitOp is Copyright © 2008-2012 Mike Pall. - Lua BitOp is free software, released under the MIT license (same license as the Lua core). - Lua BitOp is compatible with the built-in bitwise operations in LuaJIT 2.0 and is used - on platforms where Defold runs without LuaJIT. - For clarity the examples assume the definition of a helper function `printx()`. - This prints its argument as an unsigned 32 bit hexadecimal number on all platforms: - ```lua - function printx(x) - print("0x"..bit.tohex(x)) - end - ``` ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.bit -bit = {} - ----Returns the bitwise arithmetic right-shift of its first argument by the number of bits given by the second argument. ----Arithmetic right-shift treats the most-significant bit as a sign bit and replicates it. ----Only the lower 5 bits of the shift count are used (reduces to the range [0..31]). ----@param x number number ----@param n number number of bits ----@return number y bitwise arithmetic right-shifted number -function bit.arshift(x, n) end - ----Returns the bitwise and of all of its arguments. Note that more than two arguments are allowed. ----@param x1 number number ----@param ...? number number(s) ----@return number y bitwise and of the provided arguments -function bit.band(x1, ...) end - ----Returns the bitwise not of its argument. ----@param x number number ----@return number y bitwise not of number x -function bit.bnot(x) end - ----Returns the bitwise or of all of its arguments. Note that more than two arguments are allowed. ----@param x1 number number ----@param ...? number number(s) ----@return number y bitwise or of the provided arguments -function bit.bor(x1, ...) end - ----Swaps the bytes of its argument and returns it. This can be used to convert little-endian 32 bit numbers to big-endian 32 bit numbers or vice versa. ----@param x number number ----@return number y bitwise swapped number -function bit.bswap(x) end - ----Returns the bitwise xor of all of its arguments. Note that more than two arguments are allowed. ----@param x1 number number ----@param ...? number number(s) ----@return number y bitwise xor of the provided arguments -function bit.bxor(x1, ...) end - ----Returns the bitwise logical left-shift of its first argument by the number of bits given by the second argument. ----Logical shifts treat the first argument as an unsigned number and shift in 0-bits. ----Only the lower 5 bits of the shift count are used (reduces to the range [0..31]). ----@param x number number ----@param n number number of bits ----@return number y bitwise logical left-shifted number -function bit.lshift(x, n) end - ----Returns the bitwise left rotation of its first argument by the number of bits given by the second argument. Bits shifted out on one side are shifted back in on the other side. ----Only the lower 5 bits of the rotate count are used (reduces to the range [0..31]). ----@param x number number ----@param n number number of bits ----@return number y bitwise left-rotated number -function bit.rol(x, n) end - ----Returns the bitwise right rotation of its first argument by the number of bits given by the second argument. Bits shifted out on one side are shifted back in on the other side. ----Only the lower 5 bits of the rotate count are used (reduces to the range [0..31]). ----@param x number number ----@param n number number of bits ----@return number y bitwise right-rotated number -function bit.ror(x, n) end - ----Returns the bitwise logical right-shift of its first argument by the number of bits given by the second argument. ----Logical shifts treat the first argument as an unsigned number and shift in 0-bits. ----Only the lower 5 bits of the shift count are used (reduces to the range [0..31]). ----@param x number number ----@param n number number of bits ----@return number y bitwise logical right-shifted number -function bit.rshift(x, n) end - ----Normalizes a number to the numeric range for bit operations and returns it. This function is usually not needed since all bit operations already normalize all of their input arguments. ----@param x number number to normalize ----@return number y normalized number -function bit.tobit(x) end - ----Converts its first argument to a hex string. The number of hex digits is given by the absolute value of the optional second argument. Positive numbers between 1 and 8 generate lowercase hex digits. Negative numbers generate uppercase hex digits. Only the least-significant 4*|n| bits are used. The default is to generate 8 lowercase hex digits. ----@param x number number to convert ----@param n number number of hex digits to return ----@return string s hexadecimal string -function bit.tohex(x, n) end - -return bit \ No newline at end of file diff --git a/resources/defold_api/buffer.lua b/resources/defold_api/buffer.lua deleted file mode 100644 index e702cd2..0000000 --- a/resources/defold_api/buffer.lua +++ /dev/null @@ -1,109 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Buffer API documentation - - Functions for manipulating buffers and streams ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.buffer -buffer = {} - ----Copy all data streams from one buffer to another, element wise. ---- Each of the source streams must have a matching stream in the ----destination buffer. The streams must match in both type and size. ----The source and destination buffer can be the same. ----@param dst buffer_data the destination buffer ----@param dstoffset number the offset to start copying data to ----@param src buffer_data the source data buffer ----@param srcoffset number the offset to start copying data from ----@param count number the number of elements to copy -function buffer.copy_buffer(dst, dstoffset, src, srcoffset, count) end - ----Copy a specified amount of data from one stream to another. ---- The value type and size must match between source and destination streams. ----The source and destination streams can be the same. ----@param dst buffer_stream the destination stream ----@param dstoffset number the offset to start copying data to (measured in value type) ----@param src buffer_stream the source data stream ----@param srcoffset number the offset to start copying data from (measured in value type) ----@param count number the number of values to copy (measured in value type) -function buffer.copy_stream(dst, dstoffset, src, srcoffset, count) end - ----Create a new data buffer containing a specified set of streams. A data buffer ----can contain one or more streams with typed data. This is useful for managing ----compound data, for instance a vertex buffer could contain separate streams for ----vertex position, color, normal etc. ----@param element_count number The number of elements the buffer should hold ----@param declaration { name:hash|string, type:constant, count:number }[] A table where each entry (table) describes a stream ---- ----- hash | string `name`: The name of the stream ----- constant `type`: The data type of the stream ----- number `count`: The number of values each element should hold ---- ----@return buffer_data buffer the new buffer -function buffer.create(element_count, declaration) end - ----Get a copy of all the bytes from a specified stream as a Lua string. ----@param buffer buffer_data the source buffer ----@param stream_name hash the name of the stream ----@return string data the buffer data as a Lua string -function buffer.get_bytes(buffer, stream_name) end - ----Get a named metadata entry from a buffer along with its type. ----@param buf buffer_data the buffer to get the metadata from ----@param metadata_name hash|string name of the metadata entry ----@return number[]|nil values table of metadata values or `nil` if the entry does not exist ----@return constant|nil value_type numeric type of values or `nil` -function buffer.get_metadata(buf, metadata_name) end - ----Get a specified stream from a buffer. ----@param buffer buffer_data the buffer to get the stream from ----@param stream_name hash|string the stream name ----@return buffer_stream stream the data stream -function buffer.get_stream(buffer, stream_name) end - ----Creates or updates a metadata array entry on a buffer. ---- The value type and count given when updating the entry should match those used when first creating it. ----@param buf buffer_data the buffer to set the metadata on ----@param metadata_name hash|string name of the metadata entry ----@param values number[] actual metadata, an array of numeric values ----@param value_type constant type of values when stored -function buffer.set_metadata(buf, metadata_name, values, value_type) end - ----Float, single precision, 4 bytes -buffer.VALUE_TYPE_FLOAT32 = nil - ----Signed integer, 2 bytes -buffer.VALUE_TYPE_INT16 = nil - ----Signed integer, 4 bytes -buffer.VALUE_TYPE_INT32 = nil - ----Signed integer, 8 bytes -buffer.VALUE_TYPE_INT64 = nil - ----Signed integer, 1 byte -buffer.VALUE_TYPE_INT8 = nil - ----Unsigned integer, 2 bytes -buffer.VALUE_TYPE_UINT16 = nil - ----Unsigned integer, 4 bytes -buffer.VALUE_TYPE_UINT32 = nil - ----Unsigned integer, 8 bytes -buffer.VALUE_TYPE_UINT64 = nil - ----Unsigned integer, 1 byte -buffer.VALUE_TYPE_UINT8 = nil - -return buffer \ No newline at end of file diff --git a/resources/defold_api/builtins.lua b/resources/defold_api/builtins.lua deleted file mode 100644 index cc7bfe1..0000000 --- a/resources/defold_api/builtins.lua +++ /dev/null @@ -1,34 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Built-ins API documentation - - Built-in scripting functions. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----All ids in the engine are represented as hashes, so a string needs to be hashed ----before it can be compared with an id. ----@param s string string to hash ----@return hash hash a hashed string -function hash(s) end - ----Returns a hexadecimal representation of a hash value. ----The returned string is always padded with leading zeros. ----@param h hash hash value to get hex string for ----@return string hex hex representation of the hash -function hash_to_hex(h) end - ----Pretty printing of Lua values. This function prints Lua values ----in a manner similar to +print()+, but will also recurse into tables ----and pretty print them. There is a limit to how deep the function ----will recurse. ----@param ... any value to print -function pprint(...) end \ No newline at end of file diff --git a/resources/defold_api/camera.lua b/resources/defold_api/camera.lua deleted file mode 100644 index 4d3cf79..0000000 --- a/resources/defold_api/camera.lua +++ /dev/null @@ -1,130 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Camera API documentation - - Camera functions, messages and constants. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.camera -camera = {} - ----Gets the effective aspect ratio of the camera. If auto aspect ratio is enabled, ----returns the aspect ratio calculated from the current render target dimensions. ----Otherwise returns the manually set aspect ratio. ----@param camera url|number|nil camera id ----@return number aspect_ratio the effective aspect ratio. -function camera.get_aspect_ratio(camera) end - ----Returns whether auto aspect ratio is enabled. When enabled, the camera automatically ----calculates aspect ratio from render target dimensions. When disabled, uses the ----manually set aspect ratio value. ----@param camera url|number|nil camera id ----@return boolean auto_aspect_ratio true if auto aspect ratio is enabled -function camera.get_auto_aspect_ratio(camera) end - ----This function returns a table with all the camera URLs that have been ----registered in the render context. ----@return table cameras a table with all camera URLs -function camera.get_cameras() end - ----get enabled ----@param camera url|number|nil camera id ----@return boolean flag true if the camera is enabled -function camera.get_enabled(camera) end - ----get far z ----@param camera url|number|nil camera id ----@return number far_z the far z. -function camera.get_far_z(camera) end - ----get field of view ----@param camera url|number|nil camera id ----@return number fov the field of view. -function camera.get_fov(camera) end - ----get near z ----@param camera url|number|nil camera id ----@return number near_z the near z. -function camera.get_near_z(camera) end - ----get orthographic zoom mode ----@param camera url|number|nil camera id ----@return number mode one of camera.ORTHO_MODE_FIXED, camera.ORTHO_MODE_AUTO_FIT or ----camera.ORTHO_MODE_AUTO_COVER -function camera.get_orthographic_mode(camera) end - ----get orthographic zoom ----@param camera url|number|nil camera id ----@return number orthographic_zoom the zoom level when the camera uses orthographic projection. -function camera.get_orthographic_zoom(camera) end - ----get projection matrix ----@param camera url|number|nil camera id ----@return matrix4 projection the projection matrix. -function camera.get_projection(camera) end - ----get view matrix ----@param camera url|number|nil camera id ----@return matrix4 view the view matrix. -function camera.get_view(camera) end - ----Sets the manual aspect ratio for the camera. This value is only used when ----auto aspect ratio is disabled. To disable auto aspect ratio and use this ----manual value, call camera.set_auto_aspect_ratio(camera, false). ----@param camera url|number|nil camera id ----@param aspect_ratio number the manual aspect ratio value. -function camera.set_aspect_ratio(camera, aspect_ratio) end - ----Enables or disables automatic aspect ratio calculation. When enabled (true), ----the camera automatically calculates aspect ratio from render target dimensions. ----When disabled (false), uses the manually set aspect ratio value. ----@param camera url|number|nil camera id ----@param auto_aspect_ratio boolean true to enable auto aspect ratio -function camera.set_auto_aspect_ratio(camera, auto_aspect_ratio) end - ----set far z ----@param camera url|number|nil camera id ----@param far_z number the far z. -function camera.set_far_z(camera, far_z) end - ----set field of view ----@param camera url|number|nil camera id ----@param fov number the field of view. -function camera.set_fov(camera, fov) end - ----set near z ----@param camera url|number|nil camera id ----@param near_z number the near z. -function camera.set_near_z(camera, near_z) end - ----set orthographic zoom mode ----@param camera url|number|nil camera id ----@param mode number camera.ORTHO_MODE_FIXED, camera.ORTHO_MODE_AUTO_FIT or camera.ORTHO_MODE_AUTO_COVER -function camera.set_orthographic_mode(camera, mode) end - ----set orthographic zoom ----@param camera url|number|nil camera id ----@param orthographic_zoom number the zoom level when the camera uses orthographic projection. -function camera.set_orthographic_zoom(camera, orthographic_zoom) end - ----Computes zoom so the original display area covers the entire window while preserving aspect ratio. ----Equivalent to using max(window_width/width, window_height/height). -camera.ORTHO_MODE_AUTO_COVER = nil - ----Computes zoom so the original display area (game.project width/height) fits inside the window ----while preserving aspect ratio. Equivalent to using min(window_width/width, window_height/height). -camera.ORTHO_MODE_AUTO_FIT = nil - ----Uses the manually set orthographic zoom value (camera.set_orthographic_zoom). -camera.ORTHO_MODE_FIXED = nil - -return camera \ No newline at end of file diff --git a/resources/defold_api/collectionfactory.lua b/resources/defold_api/collectionfactory.lua deleted file mode 100644 index f24a6a6..0000000 --- a/resources/defold_api/collectionfactory.lua +++ /dev/null @@ -1,88 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Collection factory API documentation - - Functions for controlling collection factory components which are - used to dynamically spawn collections into the runtime. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.collectionfactory -collectionfactory = {} - ----The URL identifies the collectionfactory component that should do the spawning. ----Spawning is instant, but spawned game objects get their first update calls the following frame. The supplied parameters for position, rotation and scale ----will be applied to the whole collection when spawned. ----Script properties in the created game objects can be overridden through ----a properties-parameter table. The table should contain game object ids ----(hash) as keys and property tables as values to be used when initiating each ----spawned game object. ----See go.property for more information on script properties. ----The function returns a table that contains a key for each game object ----id (hash), as addressed if the collection file was top level, and the ----corresponding spawned instance id (hash) as value with a unique path ----prefix added to each instance. ---- Calling collectionfactory.create create on a collection factory that is marked as dynamic without having loaded resources ----using collectionfactory.load will synchronously load and create resources which may affect application performance. ----@param url string|hash|url the collection factory component to be used ----@param position? vector3 position to assign to the newly spawned collection ----@param rotation? quaternion rotation to assign to the newly spawned collection ----@param properties? table table of script properties to propagate to any new game object instances ----@param scale? number|vector3 uniform scaling to apply to the newly spawned collection (must be greater than 0). ----@return table ids a table mapping the id:s from the collection to the new instance id:s -function collectionfactory.create(url, position, rotation, properties, scale) end - ----This returns status of the collection factory. ----Calling this function when the factory is not marked as dynamic loading always returns COMP_COLLECTION_FACTORY_STATUS_LOADED. ----@param url? string|hash|url the collection factory component to get status from ----@return constant status status of the collection factory component ---- ----- `collectionfactory.STATUS_UNLOADED` ----- `collectionfactory.STATUS_LOADING` ----- `collectionfactory.STATUS_LOADED` ---- -function collectionfactory.get_status(url) end - ----Resources loaded are referenced by the collection factory component until the existing (parent) collection is destroyed or collectionfactory.unload is called. ----Calling this function when the factory is not marked as dynamic loading does nothing. ----@param url? string|hash|url the collection factory component to load ----@param complete_function? fun(self, url, result) function to call when resources are loaded. ---- ----`self` ----object The current object. ----`url` ----url url of the collection factory component ----`result` ----boolean True if resource were loaded successfully ---- -function collectionfactory.load(url, complete_function) end - ----Changes the prototype for the collection factory. ----Setting the prototype to "nil" will revert back to the original prototype. ----@param url? string|hash|url the collection factory component ----@param prototype? string|nil the path to the new prototype, or `nil` -function collectionfactory.set_prototype(url, prototype) end - ----This decreases the reference count for each resource loaded with collectionfactory.load. If reference is zero, the resource is destroyed. ----Calling this function when the factory is not marked as dynamic loading does nothing. ----@param url? string|hash|url the collection factory component to unload -function collectionfactory.unload(url) end - ----loaded -collectionfactory.STATUS_LOADED = nil - ----loading -collectionfactory.STATUS_LOADING = nil - ----unloaded -collectionfactory.STATUS_UNLOADED = nil - -return collectionfactory \ No newline at end of file diff --git a/resources/defold_api/collectionproxy.lua b/resources/defold_api/collectionproxy.lua deleted file mode 100644 index 20027a5..0000000 --- a/resources/defold_api/collectionproxy.lua +++ /dev/null @@ -1,63 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Collection proxy API documentation - - Messages for controlling and interacting with collection proxies - which are used to dynamically load collections into the runtime. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.collectionproxy -collectionproxy = {} - ----return an indexed table of resources for a collection proxy where the ----referenced collection has been excluded using LiveUpdate. Each entry is a ----hexadecimal string that represents the data of the specific resource. ----This representation corresponds with the filename for each individual ----resource that is exported when you bundle an application with LiveUpdate ----functionality. ----@param collectionproxy url the collectionproxy to check for resources. ----@return string[] resources the resources, or an empty list if the ----collection was not excluded. -function collectionproxy.get_resources(collectionproxy) end - ----return an array of missing resources for a collection proxy. Each ----entry is a hexadecimal string that represents the data of the specific ----resource. This representation corresponds with the filename for each ----individual resource that is exported when you bundle an application with ----LiveUpdate functionality. It should be considered good practise to always ----check whether or not there are any missing resources in a collection proxy ----before attempting to load the collection proxy. ----@param collectionproxy url the collectionproxy to check for missing ----resources. ----@return string[] resources the missing resources -function collectionproxy.missing_resources(collectionproxy) end - ----The collection should be loaded by the collection proxy. ----Setting the collection to "nil" will revert it back to the original collection. ----The collection proxy shouldn't be loaded and should have the 'Exclude' checkbox checked. ----This functionality is designed to simplify the management of Live Update resources. ----@param url? string|hash|url the collection proxy component ----@param prototype? string|nil the path to the new collection, or `nil` ----@return boolean success collection change was successful ----@return number code one of the collectionproxy.RESULT_* codes if unsuccessful -function collectionproxy.set_collection(url, prototype) end - ----It's impossible to change the collection if the collection is already loaded. -collectionproxy.RESULT_ALREADY_LOADED = nil - ----It's impossible to change the collection while the collection proxy is loading. -collectionproxy.RESULT_LOADING = nil - ----It's impossible to change the collection for a proxy that isn't excluded. -collectionproxy.RESULT_NOT_EXCLUDED = nil - -return collectionproxy \ No newline at end of file diff --git a/resources/defold_api/crash.lua b/resources/defold_api/crash.lua deleted file mode 100644 index fab4c29..0000000 --- a/resources/defold_api/crash.lua +++ /dev/null @@ -1,119 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Crash API documentation - - Native crash logging functions and constants. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.crash -crash = {} - ----A table is returned containing the addresses of the call stack. ----@param handle number crash dump handle ----@return table backtrace table containing the backtrace -function crash.get_backtrace(handle) end - ----The format of read text blob is platform specific ----and not guaranteed ----but can be useful for manual inspection. ----@param handle number crash dump handle ----@return string blob string with the platform specific data -function crash.get_extra_data(handle) end - ----The function returns a table containing entries with sub-tables that ----have fields 'name' and 'address' set for all loaded modules. ----@param handle number crash dump handle ----@return { name:string, address:string }[] modules module table -function crash.get_modules(handle) end - ----read signal number from a crash report ----@param handle number crash dump handle ----@return number signal signal number -function crash.get_signum(handle) end - ----reads a system field from a loaded crash dump ----@param handle number crash dump handle ----@param index number system field enum. Must be less than crash.SYSFIELD_MAX ----@return string|nil value value recorded in the crash dump, or `nil` if it didn't exist -function crash.get_sys_field(handle, index) end - ----reads user field from a loaded crash dump ----@param handle number crash dump handle ----@param index number user data slot index ----@return string value user data value recorded in the crash dump -function crash.get_user_field(handle, index) end - ----The crash dump will be removed from disk upon a successful ----load, so loading is one-shot. ----@return number|nil handle handle to the loaded dump, or `nil` if no dump was found -function crash.load_previous() end - ----releases a previously loaded crash dump ----@param handle number handle to loaded crash dump -function crash.release(handle) end - ----Crashes occuring before the path is set will be stored to a default engine location. ----@param path string file path to use -function crash.set_file_path(path) end - ----Store a user value that will get written to a crash dump when ----a crash occurs. This can be user id:s, breadcrumb data etc. ----There are 32 slots indexed from 0. Each slot stores at most 255 characters. ----@param index number slot index. 0-indexed ----@param value string string value to store -function crash.set_user_field(index, value) end - ----Performs the same steps as if a crash had just occured but ----allows the program to continue. ----The generated dump can be read by crash.load_previous -function crash.write_dump() end - ----android build fingerprint -crash.SYSFIELD_ANDROID_BUILD_FINGERPRINT = nil - ----system device language as reported by sys.get_sys_info -crash.SYSFIELD_DEVICE_LANGUAGE = nil - ----device model as reported by sys.get_sys_info -crash.SYSFIELD_DEVICE_MODEL = nil - ----engine version as hash -crash.SYSFIELD_ENGINE_HASH = nil - ----engine version as release number -crash.SYSFIELD_ENGINE_VERSION = nil - ----system language as reported by sys.get_sys_info -crash.SYSFIELD_LANGUAGE = nil - ----device manufacturer as reported by sys.get_sys_info -crash.SYSFIELD_MANUFACTURER = nil - ----The max number of sysfields. -crash.SYSFIELD_MAX = nil - ----system name as reported by sys.get_sys_info -crash.SYSFIELD_SYSTEM_NAME = nil - ----system version as reported by sys.get_sys_info -crash.SYSFIELD_SYSTEM_VERSION = nil - ----system territory as reported by sys.get_sys_info -crash.SYSFIELD_TERRITORY = nil - ----The max number of user fields. -crash.USERFIELD_MAX = nil - ----The max size of a single user field. -crash.USERFIELD_SIZE = nil - -return crash \ No newline at end of file diff --git a/resources/defold_api/editor.lua b/resources/defold_api/editor.lua deleted file mode 100644 index 412fc7c..0000000 --- a/resources/defold_api/editor.lua +++ /dev/null @@ -1,798 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Editor scripting documentation ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.editor -editor = {} - ----Error message the signifies bundle abort that is not an error -editor.bundle.abort_message = nil - ----prefs schema for common bundle variants -editor.bundle.common_variant_schema = nil - ----prefs schema for desktop bundle variants -editor.bundle.desktop_variant_schema = nil - ----A string, SHA1 of Defold editor -editor.editor_sha1 = nil - ----A string, SHA1 of Defold engine -editor.engine_sha1 = nil - ----Editor platform id. ----A `string`, either: ----- `"x86_64-win32"` ----- `"x86_64-macos"` ----- `"arm64-macos"` ----- `"x86_64-linux"` -editor.platform = nil - ----`"global"` -editor.prefs.SCOPE.GLOBAL = nil - ----`"project"` -editor.prefs.SCOPE.PROJECT = nil - ----`"bottom"` -editor.ui.ALIGNMENT.BOTTOM = nil - ----`"bottom-left"` -editor.ui.ALIGNMENT.BOTTOM_LEFT = nil - ----`"bottom-right"` -editor.ui.ALIGNMENT.BOTTOM_RIGHT = nil - ----`"center"` -editor.ui.ALIGNMENT.CENTER = nil - ----`"left"` -editor.ui.ALIGNMENT.LEFT = nil - ----`"right"` -editor.ui.ALIGNMENT.RIGHT = nil - ----`"top"` -editor.ui.ALIGNMENT.TOP = nil - ----`"top-left"` -editor.ui.ALIGNMENT.TOP_LEFT = nil - ----`"top-right"` -editor.ui.ALIGNMENT.TOP_RIGHT = nil - ----`"error"` -editor.ui.COLOR.ERROR = nil - ----`"hint"` -editor.ui.COLOR.HINT = nil - ----`"override"` -editor.ui.COLOR.OVERRIDE = nil - ----`"text"` -editor.ui.COLOR.TEXT = nil - ----`"warning"` -editor.ui.COLOR.WARNING = nil - ----`"dialog"` -editor.ui.HEADING_STYLE.DIALOG = nil - ----`"form"` -editor.ui.HEADING_STYLE.FORM = nil - ----`"h1"` -editor.ui.HEADING_STYLE.H1 = nil - ----`"h2"` -editor.ui.HEADING_STYLE.H2 = nil - ----`"h3"` -editor.ui.HEADING_STYLE.H3 = nil - ----`"h4"` -editor.ui.HEADING_STYLE.H4 = nil - ----`"h5"` -editor.ui.HEADING_STYLE.H5 = nil - ----`"h6"` -editor.ui.HEADING_STYLE.H6 = nil - ----`"clear"` -editor.ui.ICON.CLEAR = nil - ----`"minus"` -editor.ui.ICON.MINUS = nil - ----`"open-resource"` -editor.ui.ICON.OPEN_RESOURCE = nil - ----`"plus"` -editor.ui.ICON.PLUS = nil - ----`"error"` -editor.ui.ISSUE_SEVERITY.ERROR = nil - ----`"warning"` -editor.ui.ISSUE_SEVERITY.WARNING = nil - ----`"horizontal"` -editor.ui.ORIENTATION.HORIZONTAL = nil - ----`"vertical"` -editor.ui.ORIENTATION.VERTICAL = nil - ----`"large"` -editor.ui.PADDING.LARGE = nil - ----`"medium"` -editor.ui.PADDING.MEDIUM = nil - ----`"none"` -editor.ui.PADDING.NONE = nil - ----`"small"` -editor.ui.PADDING.SMALL = nil - ----`"large"` -editor.ui.SPACING.LARGE = nil - ----`"medium"` -editor.ui.SPACING.MEDIUM = nil - ----`"none"` -editor.ui.SPACING.NONE = nil - ----`"small"` -editor.ui.SPACING.SMALL = nil - ----`"center"` -editor.ui.TEXT_ALIGNMENT.CENTER = nil - ----`"justify"` -editor.ui.TEXT_ALIGNMENT.JUSTIFY = nil - ----`"left"` -editor.ui.TEXT_ALIGNMENT.LEFT = nil - ----`"right"` -editor.ui.TEXT_ALIGNMENT.RIGHT = nil - ----A string, version name of Defold -editor.version = nil - ----Editor's HTTP server local url -http.server.local_url = nil - ----Editor's HTTP server port -http.server.port = nil - ----Editor's HTTP server url -http.server.url = nil - ----`"deflated"` compression method -zip.METHOD.DEFLATED = nil - ----`"stored"` compression method, i.e. no compression -zip.METHOD.STORED = nil - ----`"error"`, any conflict aborts extraction -zip.ON_CONFLICT.ERROR = nil - ----`"skip"`, existing file is overwritten -zip.ON_CONFLICT.OVERWRITE = nil - ----`"skip"`, existing file is preserved -zip.ON_CONFLICT.SKIP = nil - ----Run bob the builder program ----For the full documentation of the available commands and options, see the bob manual. ----@param options? table table of command line options for bob, without the leading dashes (`--`). You can use snake_case instead of kebab-case for option keys. Only long option names are supported (i.e. `output`, not `o`). Supported value types are strings, integers and booleans. If an option takes no arguments, use a boolean (i.e. `true`). If an option may be repeated, you can use an array of values. ----@param ...? string bob commands, e.g. `"resolve"` or `"build"` -function editor.bob(options, ...) end - ----Open a URL in the default browser or a registered application ----@param url string http(s) or file URL -function editor.browse(url) end - ----Immutably set a key to value in a table ----@param table table the table ----@param key any the key ----@param value any the value ----@return table table New table if it should be changed by assoc, or the input table otherwise -function editor.bundle.assoc(table, key, value) end - ----Immutably set a value to a nested path in a table ----@param table table the table ----@param keys any[] ] the keys ----@param value any the value ----@return table table New table if it should be changed by assoc_in, or the input table otherwise -function editor.bundle.assoc_in(table, keys, value) end - ----Helper function for creating a check box component ----@param config table config table ----@param set_config function config setter ----@param key string config key for the selected value ----@param text string check box label text ----@param rest_props? table extra props for `editor.ui.check_box` ----@return editor.component check_box UI component -function editor.bundle.check_box(config, set_config, key, text, rest_props) end - ----Create a grid row for the common boolean settings ----@param config table config map with common boolean keys ----@param set_config function config setter ----@return editor.component[] row ] grid row -function editor.bundle.check_boxes_grid_row(config, set_config) end - ----Create bundle command definition ----@param label string Command label, as presented in the UI ----@param id string Command id, e.g. `"bundle-my-platform"`, used for re-bundling ----@param fn function bundle function, will receive a `requested_dialog` boolean argument ----@param rest? table extra keys for the command definition, e.g. `active` -function editor.bundle.command(label, id, fn, rest) end - ----Create a grid row for the common variant setting ----@param config table config map with `variant` key ----@param set_config function config setter ----@return editor.component[] row ] grid row -function editor.bundle.common_variant_grid_row(config, set_config) end - ----Get bundle config, optionally showing a dialog to edit the config ----@param requested_dialog boolean whether the user explicitly requested a dialog ----@param prefs_key string preference key used for loading the bundle config ----@param dialog_component editor.component UI component for the dialog, will receive `config` and (optional) `errors` props ----@param errors_fn? function error checking predicate, takes config as an argument; if returns truthy value, it will be passed as a prop to `dialog_component` ----@return any config -function editor.bundle.config(requested_dialog, prefs_key, dialog_component, errors_fn) end - ----Helper function for constructing prefs schema for new bundle dialogs ----@param variant_schema editor.schema bundle variant schema ----@param properties? table|nil extra config properties ----@return editor.schema schema full bundle schema, defines a project-scoped object schema with the following keys:`variant`the provided variant schema`texture_compression string`either `enabled`, `disabled` or `editor``with_symbols boolean``build_report boolean``liveupdate boolean``contentless boolean` -function editor.bundle.config_schema(variant_schema, properties) end - ----Create bob bundle ----@param config table bundle config ----@param output_directory string bundle output directory ----@param extra_bob_opts table extra bob opts, presumably produced from config -function editor.bundle.create(config, output_directory, extra_bob_opts) end - ----Create a grid row for the desktop variant setting ----@param config table config table with `variant` key ----@param set_config function config setter ----@return editor.component[] row ] grid row -function editor.bundle.desktop_variant_grid_row(config, set_config) end - ----Helper function for creating a bundle dialog component ----@param heading string dialog heading text ----@param config table config map with common boolean keys ----@param hint string|nil dialog hint text ----@param error string|nil dialog error text ----@param rows editor.component[] []] grid rows of UI elements, dialog content ----@return editor.component dialog UI component -function editor.bundle.dialog(heading, config, hint, error, rows) end - ----Helper function for creating an external file field component ----@param config table config map with common boolean keys ----@param set_config function config setter ----@param key string config key for the set ----@param error? string error message ----@param rest_props? table extra props for `editor.ui.external_file_field` ----@return editor.component external_file_field UI component -function editor.bundle.external_file_field(config, set_config, key, error, rest_props) end - ----Return a 2-element array that represents a single grid row in a bundle dialog ----@param text string|nil optional string label ----@param content editor.component|editor.component[] ] either a single UI component, or an array of components (will be laid out vertically) ----@return editor.component[] row ] a single grid row -function editor.bundle.grid_row(text, content) end - ----Make stringifier function that first performs the label lookup in a provided table ----@param table table table from values to their corresponding string representation ----@return function to_string stringifier function -function editor.bundle.make_to_string_lookup(table) end - ----Get bundle output directory, optionally showing a directory selection dialog ----@param requested_dialog boolean whether the user explicitly requested a dialog ----@param output_subdir string output subdir, usually a platform name -function editor.bundle.output_directory(requested_dialog, output_subdir) end - ----Helper function for creating a select box component ----@param config table config table ----@param set_config function config setter ----@param key string config key for the selected value ----@param options any[] ] select box options ----@param to_string function option stringifier ----@param rest_props? table extra props for `editor.ui.select_box` ----@return editor.component select_box UI component -function editor.bundle.select_box(config, set_config, key, options, to_string, rest_props) end - ----Helper function for creating a check box for an enum value of set config key ----@param config table config map with common boolean keys ----@param set_config function config setter ----@param key string config key for the set ----@param element string enum value in the set ----@param text string check box label text ----@param error? string error message ----@return editor.component check_box UI component -function editor.bundle.set_element_check_box(config, set_config, key, element, text, error) end - ----Create a grid row for the texture compression setting ----@param config table config map with `texture_compression` key ----@param set_config function config setter ----@return editor.component[] row ] grid row -function editor.bundle.texture_compression_grid_row(config, set_config) end - ----Check if `editor.tx.add()` (as well as `editor.tx.clear()` and `editor.tx.remove()`) transaction with this property won't throw an error ----@param node string|userdata Either resource path (e.g. `"/main/game.script"`), or internal node id passed to the script by the editor ----@param property string Either `"path"`, `"text"`, or a property from the Outline view (hover the label to see its editor script name) ----@return boolean value -function editor.can_add(node, property) end - ----Check if you can get this property so `editor.get()` won't throw an error ----@param node string|userdata Either resource path (e.g. `"/main/game.script"`), or internal node id passed to the script by the editor ----@param property string Either `"path"`, `"text"`, or a property from the Outline view (hover the label to see its editor script name) ----@return boolean value -function editor.can_get(node, property) end - ----Check if `editor.tx.reorder()` transaction with this property won't throw an error ----@param node string|userdata Either resource path (e.g. `"/main/game.script"`), or internal node id passed to the script by the editor ----@param property string Either `"path"`, `"text"`, or a property from the Outline view (hover the label to see its editor script name) ----@return boolean value -function editor.can_reorder(node, property) end - ----Check if `editor.tx.reset()` transaction with this property won't throw an error ----@param node string|userdata Either resource path (e.g. `"/main/game.script"`), or internal node id passed to the script by the editor ----@param property string Either `"path"`, `"text"`, or a property from the Outline view (hover the label to see its editor script name) ----@return boolean value -function editor.can_reset(node, property) end - ----Check if `editor.tx.set()` transaction with this property won't throw an error ----@param node string|userdata Either resource path (e.g. `"/main/game.script"`), or internal node id passed to the script by the editor ----@param property string Either `"path"`, `"text"`, or a property from the Outline view (hover the label to see its editor script name) ----@return boolean value -function editor.can_set(node, property) end - ----Create an editor command ----@param opts table A table with the following keys:`label string`required, user-visible command name`locations string[]`required, a non-empty list of locations where the command is displayed in the editor, values are either `"Edit"`, `"View"`, `"Project"`, `"Debug"` (the editor menubar), `"Assets"` (the assets pane), or `"Outline"` (the outline pane)`query table`optional, a query that both controls the command availability and provides additional information to the command handler functions; a table with the following keys:`selection table`current selection, a table with the following keys:`type string`either `"resource"` (selected resource) or `"outline"` (selected outline node)`cardinality string`either `"one"` (will use first selected item) or `"many"` (will use all selected items)`argument table`the command argument`id string`optional, keyword identifier that may be used for assigning a shortcut to a command; should be a dot-separated identifier string, e.g. `"my-extension.do-stuff"``active function`optional function that additionally checks if a command is active in the current context; will receive opts table with values populated by the query; should be fast to execute since the editor might invoke it in response to UI interactions (on key typed, mouse clicked)`run function`optional function that is invoked when the user decides to execute the command; will receive opts table with values populated by the query ----@return editor.command command -function editor.command(opts) end - ----Create a directory if it does not exist, and all non-existent parent directories. ----Throws an error if the directory can't be created. ----@param resource_path string Resource path (starting with `/`) -function editor.create_directory(resource_path) end - ----Create resources (including non-existent parent directories). ----Throws an error if any of the provided resource paths already exist ----@param resources string[] ] Array of resource paths (strings starting with `/`) or resource definitions, lua tables with the following keys:`1 string`required, resource path (starting with `/`)`2 string`optional, created resource content -function editor.create_resources(resources) end - ----Delete a directory if it exists, and all existent child directories and files. ----Throws an error if the directory can't be deleted. ----@param resource_path string Resource path (starting with `/`) -function editor.delete_directory(resource_path) end - ----Execute a shell command. ----Any shell command arguments should be provided as separate argument strings to this function. If the exit code of the process is not zero, this function throws error. By default, the function returns `nil`, but it can be configured to capture the output of the shell command as string and return it — set `out` option to `"capture"` to do it.By default, after this shell command is executed, the editor will reload resources from disk. ----@param command string Shell command name to execute ----@param ...? string Optional shell command arguments ----@param options? { reload_resources?:boolean, out?:string, err?:string } Optional options table. Supported entries: - boolean `reload_resources`: make the editor reload the resources from disk after the command is executed, default `true` - string `out`: standard output mode, either: `"pipe"`: the output is piped to the editor console (this is the default behavior). - `"capture"`: capture and return the output to the editor script with trailing newlines trimmed. - `"discard"`: the output is discarded completely. - string `err`: standard error output mode, either: `"pipe"`: the error output is piped to the editor console (this is the default behavior). - `"stdout"`: the error output is redirected to the standard output of the process. - `"discard"`: the error output is discarded completely. ----@return nil|string result If `out` option is set to `"capture"`, returns the output as string with trimmed trailing newlines. Otherwise, returns `nil`. -function editor.execute(command, ..., options) end - ----Query information about file system path ----@param path string External file path, resolved against project root if relative ----@return { path:string, exists:boolean, is_file:boolean, is_directory:boolean } attributes A table with the following keys: `path string` resolved file path `exists boolean` whether there is a file system entry at the path `is_file boolean` whether the path corresponds to a file `is_directory boolean` whether the path corresponds to a directory -function editor.external_file_attributes(path) end - ----Get a value of a node property inside the editor. ----Some properties might be read-only, and some might be unavailable in different contexts, so you should use `editor.can_get()` before reading them and `editor.can_set()` before making the editor set them. ----@param node string|userdata Either resource path (e.g. `"/main/game.script"`), or internal node id passed to the script by the editor ----@param property string Either `"path"`, `"text"`, or a property from the Outline view (hover the label to see its editor script name) ----@return any value property value -function editor.get(node, property) end - ----Open a file in a registered application ----@param path string file path -function editor.open_external_file(path) end - ----Get preference value ----The schema for the preference value should be defined beforehand. ----@param key string dot-separated preference key path ----@return any value current pref value or default if a schema for the key path exists, nil otherwise -function editor.prefs.get(key) end - ----Check if preference value is explicitly set ----The schema for the preference value should be defined beforehand. ----@param key string dot-separated preference key path ----@return boolean value flag indicating if the value is explicitly set -function editor.prefs.is_set(key) end - ----array schema ----@param opts { item:editor.schema, default?:any[], scope?:string } Required opts: `item schema`array item schema Optional opts: `default item[]`default value`scope string`preference scope; either: - `editor.prefs.SCOPE.GLOBAL`: same preference value is used in every project on this computer- `editor.prefs.SCOPE.PROJECT`: a separate preference value per project ----@return editor.schema value Prefs schema -function editor.prefs.schema.array(opts) end - ----boolean schema ----@param opts? { default?:boolean, scope?:string } Optional opts: `default boolean`default value`scope string`preference scope; either: - `editor.prefs.SCOPE.GLOBAL`: same preference value is used in every project on this computer- `editor.prefs.SCOPE.PROJECT`: a separate preference value per project ----@return editor.schema value Prefs schema -function editor.prefs.schema.boolean(opts) end - ----enum value schema ----@param opts { values:(nil|boolean|number|string)[], default?:any, scope?:string } Required opts: `values any[]`allowed values, must be scalar (nil, boolean, number or string) Optional opts: `default any`default value`scope string`preference scope; either: - `editor.prefs.SCOPE.GLOBAL`: same preference value is used in every project on this computer- `editor.prefs.SCOPE.PROJECT`: a separate preference value per project ----@return editor.schema value Prefs schema -function editor.prefs.schema.enum(opts) end - ----integer schema ----@param opts? { default?:integer, scope?:string } Optional opts: `default integer`default value`scope string`preference scope; either: - `editor.prefs.SCOPE.GLOBAL`: same preference value is used in every project on this computer- `editor.prefs.SCOPE.PROJECT`: a separate preference value per project ----@return editor.schema value Prefs schema -function editor.prefs.schema.integer(opts) end - ----keyword schema ----A keyword is a short string that is interned within the editor runtime, useful e.g. for identifiers ----@param opts? { default?:string, scope?:string } Optional opts: `default string`default value`scope string`preference scope; either: - `editor.prefs.SCOPE.GLOBAL`: same preference value is used in every project on this computer- `editor.prefs.SCOPE.PROJECT`: a separate preference value per project ----@return editor.schema value Prefs schema -function editor.prefs.schema.keyword(opts) end - ----floating-point number schema ----@param opts? { default?:number, scope?:string } Optional opts: `default number`default value`scope string`preference scope; either: - `editor.prefs.SCOPE.GLOBAL`: same preference value is used in every project on this computer- `editor.prefs.SCOPE.PROJECT`: a separate preference value per project ----@return editor.schema value Prefs schema -function editor.prefs.schema.number(opts) end - ----heterogeneous object schema ----@param opts { properties:table, default?:table, scope?:string } Required opts: `properties table`a table from property key (string) to value schema Optional opts: `default table`default value`scope string`preference scope; either: - `editor.prefs.SCOPE.GLOBAL`: same preference value is used in every project on this computer- `editor.prefs.SCOPE.PROJECT`: a separate preference value per project ----@return editor.schema value Prefs schema -function editor.prefs.schema.object(opts) end - ----homogeneous object schema ----@param opts { key:editor.schema, val:editor.schema, default?:table, scope?:string } Required opts: `key schema`table key schema`val schema`table value schema Optional opts: `default table`default value`scope string`preference scope; either: - `editor.prefs.SCOPE.GLOBAL`: same preference value is used in every project on this computer- `editor.prefs.SCOPE.PROJECT`: a separate preference value per project ----@return editor.schema value Prefs schema -function editor.prefs.schema.object_of(opts) end - ----password schema ----A password is a string that is encrypted when stored in a preference file ----@param opts? table Optional opts: `default string`default value`scope string`preference scope; either: - `editor.prefs.SCOPE.GLOBAL`: same preference value is used in every project on this computer- `editor.prefs.SCOPE.PROJECT`: a separate preference value per project ----@return editor.schema value Prefs schema -function editor.prefs.schema.password(opts) end - ----set schema ----Set is represented as a lua table with `true` values ----@param opts { item:editor.schema, default?:table, scope?:string } Required opts: `item schema`set item schema Optional opts: `default table`default value`scope string`preference scope; either: - `editor.prefs.SCOPE.GLOBAL`: same preference value is used in every project on this computer- `editor.prefs.SCOPE.PROJECT`: a separate preference value per project ----@return editor.schema value Prefs schema -function editor.prefs.schema.set(opts) end - ----string schema ----@param opts? { default?:string, scope?:string } Optional opts: `default string`default value`scope string`preference scope; either: - `editor.prefs.SCOPE.GLOBAL`: same preference value is used in every project on this computer- `editor.prefs.SCOPE.PROJECT`: a separate preference value per project ----@return editor.schema value Prefs schema -function editor.prefs.schema.string(opts) end - ----tuple schema ----A tuple is a fixed-length array where each item has its own defined type ----@param opts { items:editor.schema[], default?:any[], scope?:string } Required opts: `items schema[]`schemas for the items Optional opts: `default any[]`default value`scope string`preference scope; either: - `editor.prefs.SCOPE.GLOBAL`: same preference value is used in every project on this computer- `editor.prefs.SCOPE.PROJECT`: a separate preference value per project ----@return editor.schema value Prefs schema -function editor.prefs.schema.tuple(opts) end - ----Set preference value ----The schema for the preference value should be defined beforehand. ----@param key string dot-separated preference key path ----@param value any new pref value to set -function editor.prefs.set(key, value) end - ----Query information about a project resource ----@param resource_path string Resource path (starting with `/`) ----@return { exists:boolean, is_file:boolean, is_directory:boolean } value A table with the following keys:`exists boolean`whether a resource identified by the path exists in the project`is_file boolean`whether the resource represents a file with some content`is_directory boolean`whether the resource represents a directory -function editor.resource_attributes(resource_path) end - ----Persist any unsaved changes to disk -function editor.save() end - ----Change the editor state in a single, undoable transaction ----@param txs editor.transaction_step[] ] An array of transaction steps created using `editor.tx.*` functions -function editor.transact(txs) end - ----Create a transaction step that will add a child item to a node's list property when transacted with `editor.transact()`. ----@param node string|userdata Either resource path (e.g. `"/main/game.script"`), or internal node id passed to the script by the editor ----@param property string Either `"path"`, `"text"`, or a property from the Outline view (hover the label to see its editor script name) ----@param value any Added item for the property, a table from property key to either a valid `editor.tx.set()`-able value, or an array of valid `editor.tx.add()`-able values -function editor.tx.add(node, property, value) end - ----Create a transaction step that will remove all items from node's list property when transacted with `editor.transact()`. ----@param node string|userdata Either resource path (e.g. `"/main/game.script"`), or internal node id passed to the script by the editor ----@param property string Either `"path"`, `"text"`, or a property from the Outline view (hover the label to see its editor script name) ----@return editor.transaction_step tx A transaction step -function editor.tx.clear(node, property) end - ----Create a transaction step that will remove a child node from the node's list property when transacted with `editor.transact()`. ----@param node string|userdata Either resource path (e.g. `"/main/game.script"`), or internal node id passed to the script by the editor ----@param property string Either `"path"`, `"text"`, or a property from the Outline view (hover the label to see its editor script name) ----@param child_node string|userdata Either resource path (e.g. `"/main/game.script"`), or internal node id passed to the script by the editor ----@return editor.transaction_step tx A transaction step -function editor.tx.remove(node, property, child_node) end - ----Create a transaction step that reorders child nodes in a node list defined by the property if supported (see `editor.can_reorder()`) ----@param node string|userdata Either resource path (e.g. `"/main/game.script"`), or internal node id passed to the script by the editor ----@param property string Either `"path"`, `"text"`, or a property from the Outline view (hover the label to see its editor script name) ----@param child_nodes table array of child nodes (the same as returned by `editor.get(node, property)`) in new order ----@return editor.transaction_step tx A transaction step -function editor.tx.reorder(node, property, child_nodes) end - ----Create a transaction step that will reset an overridden property to its default value when transacted with `editor.transact()`. ----@param node string|userdata Either resource path (e.g. `"/main/game.script"`), or internal node id passed to the script by the editor ----@param property string Either `"path"`, `"text"`, or a property from the Outline view (hover the label to see its editor script name) ----@return editor.transaction_step tx A transaction step -function editor.tx.reset(node, property) end - ----Create transaction step that will set the node's property to a supplied value when transacted with `editor.transact()`. ----@param node string|userdata Either resource path (e.g. `"/main/game.script"`), or internal node id passed to the script by the editor ----@param property string Either `"path"`, `"text"`, or a property from the Outline view (hover the label to see its editor script name) ----@param value any A new value for the property ----@return editor.transaction_step tx A transaction step -function editor.tx.set(node, property, value) end - ----Button with a label and/or an icon ----@param props table Optional props: `on_pressed function`button press callback, will be invoked without arguments when the user presses the button`text string`the text`text_alignment string`text alignment within paragraph bounds; either: - `editor.ui.TEXT_ALIGNMENT.LEFT`- `editor.ui.TEXT_ALIGNMENT.CENTER`- `editor.ui.TEXT_ALIGNMENT.RIGHT`- `editor.ui.TEXT_ALIGNMENT.JUSTIFY``icon string`predefined icon name; either: - `editor.ui.ICON.OPEN_RESOURCE`- `editor.ui.ICON.PLUS`- `editor.ui.ICON.MINUS`- `editor.ui.ICON.CLEAR``enabled boolean`determines if the input component can be interacted with`alignment string`alignment of the component content within its assigned bounds, defaults to `editor.ui.ALIGNMENT.TOP_LEFT`; either: - `editor.ui.ALIGNMENT.TOP_LEFT`- `editor.ui.ALIGNMENT.TOP`- `editor.ui.ALIGNMENT.TOP_RIGHT`- `editor.ui.ALIGNMENT.LEFT`- `editor.ui.ALIGNMENT.CENTER`- `editor.ui.ALIGNMENT.RIGHT`- `editor.ui.ALIGNMENT.BOTTOM_LEFT`- `editor.ui.ALIGNMENT.BOTTOM`- `editor.ui.ALIGNMENT.BOTTOM_RIGHT``grow boolean`determines if the component should grow to fill available space in a `horizontal` or `vertical` layout container`row_span integer`how many rows the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container.`column_span integer`how many columns the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container. ----@return editor.component value UI component -function editor.ui.button(props) end - ----Check box with a label ----@param props table Optional props: `value boolean`determines if the checkbox should appear checked`on_value_changed function`change callback, will receive the new value`text string`the text`text_alignment string`text alignment within paragraph bounds; either: - `editor.ui.TEXT_ALIGNMENT.LEFT`- `editor.ui.TEXT_ALIGNMENT.CENTER`- `editor.ui.TEXT_ALIGNMENT.RIGHT`- `editor.ui.TEXT_ALIGNMENT.JUSTIFY``issue table`issue related to the input; table with the following keys (all required):`severity string`either `editor.ui.ISSUE_SEVERITY.WARNING` or `editor.ui.ISSUE_SEVERITY.ERROR``message string`issue message, will be shown in a tooltip`tooltip string`tooltip message, shown on hover`enabled boolean`determines if the input component can be interacted with`alignment string`alignment of the component content within its assigned bounds, defaults to `editor.ui.ALIGNMENT.TOP_LEFT`; either: - `editor.ui.ALIGNMENT.TOP_LEFT`- `editor.ui.ALIGNMENT.TOP`- `editor.ui.ALIGNMENT.TOP_RIGHT`- `editor.ui.ALIGNMENT.LEFT`- `editor.ui.ALIGNMENT.CENTER`- `editor.ui.ALIGNMENT.RIGHT`- `editor.ui.ALIGNMENT.BOTTOM_LEFT`- `editor.ui.ALIGNMENT.BOTTOM`- `editor.ui.ALIGNMENT.BOTTOM_RIGHT``grow boolean`determines if the component should grow to fill available space in a `horizontal` or `vertical` layout container`row_span integer`how many rows the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container.`column_span integer`how many columns the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container. ----@return editor.component value UI component -function editor.ui.check_box(props) end - ----Convert a function to a UI component. ----The wrapped function may call any hooks functions (`editor.ui.use_*`), but on any function invocation, the hooks calls must be the same, and in the same order. This means that hooks should not be used inside loops and conditions or after a conditional return statement. ----The following props are supported automatically:`grow boolean`determines if the component should grow to fill available space in a `horizontal` or `vertical` layout container`row_span integer`how many rows the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container.`column_span integer`how many columns the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container. ----@param fn function function, will receive a single table of props when called ----@return function value decorated component function that may be invoked with a props table create component -function editor.ui.component(fn) end - ----Dialog component, a top-level window component that can't be used as a child of other components ----@param props table Required props: `title string`OS dialog window title Optional props: `header component`top part of the dialog, defaults to `editor.ui.heading({text = props.title})``content component`content of the dialog`buttons component[]`array of `editor.ui.dialog_button(...)` components, footer of the dialog. Defaults to a single Close button ----@return editor.component value UI component -function editor.ui.dialog(props) end - ----Dialog button shown in the footer of a dialog ----@param props table Required props: `text string`button text Optional props: `result any`value returned by `editor.ui.show_dialog(...)` if this button is pressed`default boolean`if set, pressing `Enter` in the dialog will trigger this button`cancel boolean`if set, pressing `Escape` in the dialog will trigger this button`enabled boolean`determines if the button can be interacted with ----@return editor.component value UI component -function editor.ui.dialog_button(props) end - ----Input component for selecting files from the file system ----@param props table Optional props: `value string`file or directory path; resolved against project root if relative`on_value_changed function`value change callback, will receive the absolute path of a selected file/folder or nil if the field was cleared; even though the selector dialog allows selecting only files, it's possible to receive directories and non-existent file system entries using text field input`title string`OS window title`filters table[]`File filters, an array of filter tables, where each filter has following keys:`description string`string explaining the filter, e.g. `"Text files (*.txt)"``extensions string[]`array of file extension patterns, e.g. `"*.txt"`, `"*.*"` or `"game.project"``issue table`issue related to the input; table with the following keys (all required):`severity string`either `editor.ui.ISSUE_SEVERITY.WARNING` or `editor.ui.ISSUE_SEVERITY.ERROR``message string`issue message, will be shown in a tooltip`tooltip string`tooltip message, shown on hover`enabled boolean`determines if the input component can be interacted with`alignment string`alignment of the component content within its assigned bounds, defaults to `editor.ui.ALIGNMENT.TOP_LEFT`; either: - `editor.ui.ALIGNMENT.TOP_LEFT`- `editor.ui.ALIGNMENT.TOP`- `editor.ui.ALIGNMENT.TOP_RIGHT`- `editor.ui.ALIGNMENT.LEFT`- `editor.ui.ALIGNMENT.CENTER`- `editor.ui.ALIGNMENT.RIGHT`- `editor.ui.ALIGNMENT.BOTTOM_LEFT`- `editor.ui.ALIGNMENT.BOTTOM`- `editor.ui.ALIGNMENT.BOTTOM_RIGHT``grow boolean`determines if the component should grow to fill available space in a `horizontal` or `vertical` layout container`row_span integer`how many rows the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container.`column_span integer`how many columns the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container. ----@return editor.component value UI component -function editor.ui.external_file_field(props) end - ----Layout container that places its children in a 2D grid ----@param props table Optional props: `children component[][]`array of arrays of child components`rows table[]`array of row option tables, separate configuration for each row:`grow boolean`determines if the row should grow to fill available space`columns table[]`array of column option tables, separate configuration for each column:`grow boolean`determines if the column should grow to fill available space`padding string, number`empty space from the edges of the container to its children; either: - `editor.ui.PADDING.NONE`- `editor.ui.PADDING.SMALL`- `editor.ui.PADDING.MEDIUM`- `editor.ui.PADDING.LARGE`- non-negative number, pixels`spacing string, number`empty space between child components, defaults to `editor.ui.SPACING.MEDIUM`; either: - `editor.ui.SPACING.NONE`- `editor.ui.SPACING.SMALL`- `editor.ui.SPACING.MEDIUM`- `editor.ui.SPACING.LARGE`- non-negative number, pixels`alignment string`alignment of the component content within its assigned bounds, defaults to `editor.ui.ALIGNMENT.TOP_LEFT`; either: - `editor.ui.ALIGNMENT.TOP_LEFT`- `editor.ui.ALIGNMENT.TOP`- `editor.ui.ALIGNMENT.TOP_RIGHT`- `editor.ui.ALIGNMENT.LEFT`- `editor.ui.ALIGNMENT.CENTER`- `editor.ui.ALIGNMENT.RIGHT`- `editor.ui.ALIGNMENT.BOTTOM_LEFT`- `editor.ui.ALIGNMENT.BOTTOM`- `editor.ui.ALIGNMENT.BOTTOM_RIGHT``grow boolean`determines if the component should grow to fill available space in a `horizontal` or `vertical` layout container`row_span integer`how many rows the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container.`column_span integer`how many columns the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container. ----@return editor.component value UI component -function editor.ui.grid(props) end - ----A text heading ----@param props table Optional props: `text string`the text`text_alignment string`text alignment within paragraph bounds; either: - `editor.ui.TEXT_ALIGNMENT.LEFT`- `editor.ui.TEXT_ALIGNMENT.CENTER`- `editor.ui.TEXT_ALIGNMENT.RIGHT`- `editor.ui.TEXT_ALIGNMENT.JUSTIFY``color string`semantic color, defaults to `editor.ui.COLOR.TEXT`; either: - `editor.ui.COLOR.TEXT`- `editor.ui.COLOR.HINT`- `editor.ui.COLOR.OVERRIDE`- `editor.ui.COLOR.WARNING`- `editor.ui.COLOR.ERROR``word_wrap boolean`determines if the lines of text are word-wrapped when they don't fit in the assigned bounds, defaults to true`style string`heading style, defaults to `editor.ui.HEADING_STYLE.H3`; either: - `editor.ui.HEADING_STYLE.H1`- `editor.ui.HEADING_STYLE.H2`- `editor.ui.HEADING_STYLE.H3`- `editor.ui.HEADING_STYLE.H4`- `editor.ui.HEADING_STYLE.H5`- `editor.ui.HEADING_STYLE.H6`- `editor.ui.HEADING_STYLE.DIALOG`- `editor.ui.HEADING_STYLE.FORM``alignment string`alignment of the component content within its assigned bounds, defaults to `editor.ui.ALIGNMENT.TOP_LEFT`; either: - `editor.ui.ALIGNMENT.TOP_LEFT`- `editor.ui.ALIGNMENT.TOP`- `editor.ui.ALIGNMENT.TOP_RIGHT`- `editor.ui.ALIGNMENT.LEFT`- `editor.ui.ALIGNMENT.CENTER`- `editor.ui.ALIGNMENT.RIGHT`- `editor.ui.ALIGNMENT.BOTTOM_LEFT`- `editor.ui.ALIGNMENT.BOTTOM`- `editor.ui.ALIGNMENT.BOTTOM_RIGHT``grow boolean`determines if the component should grow to fill available space in a `horizontal` or `vertical` layout container`row_span integer`how many rows the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container.`column_span integer`how many columns the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container. ----@return editor.component value UI component -function editor.ui.heading(props) end - ----Layout container that places its children in a horizontal row one after another ----@param props table Optional props: `children component[]`array of child components`padding string, number`empty space from the edges of the container to its children; either: - `editor.ui.PADDING.NONE`- `editor.ui.PADDING.SMALL`- `editor.ui.PADDING.MEDIUM`- `editor.ui.PADDING.LARGE`- non-negative number, pixels`spacing string, number`empty space between child components, defaults to `editor.ui.SPACING.MEDIUM`; either: - `editor.ui.SPACING.NONE`- `editor.ui.SPACING.SMALL`- `editor.ui.SPACING.MEDIUM`- `editor.ui.SPACING.LARGE`- non-negative number, pixels`alignment string`alignment of the component content within its assigned bounds, defaults to `editor.ui.ALIGNMENT.TOP_LEFT`; either: - `editor.ui.ALIGNMENT.TOP_LEFT`- `editor.ui.ALIGNMENT.TOP`- `editor.ui.ALIGNMENT.TOP_RIGHT`- `editor.ui.ALIGNMENT.LEFT`- `editor.ui.ALIGNMENT.CENTER`- `editor.ui.ALIGNMENT.RIGHT`- `editor.ui.ALIGNMENT.BOTTOM_LEFT`- `editor.ui.ALIGNMENT.BOTTOM`- `editor.ui.ALIGNMENT.BOTTOM_RIGHT``grow boolean`determines if the component should grow to fill available space in a `horizontal` or `vertical` layout container`row_span integer`how many rows the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container.`column_span integer`how many columns the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container. ----@return editor.component value UI component -function editor.ui.horizontal(props) end - ----An icon from a predefined set ----@param props table Required props: `icon string`predefined icon name; either: - `editor.ui.ICON.OPEN_RESOURCE`- `editor.ui.ICON.PLUS`- `editor.ui.ICON.MINUS`- `editor.ui.ICON.CLEAR` Optional props: `alignment string`alignment of the component content within its assigned bounds, defaults to `editor.ui.ALIGNMENT.TOP_LEFT`; either: - `editor.ui.ALIGNMENT.TOP_LEFT`- `editor.ui.ALIGNMENT.TOP`- `editor.ui.ALIGNMENT.TOP_RIGHT`- `editor.ui.ALIGNMENT.LEFT`- `editor.ui.ALIGNMENT.CENTER`- `editor.ui.ALIGNMENT.RIGHT`- `editor.ui.ALIGNMENT.BOTTOM_LEFT`- `editor.ui.ALIGNMENT.BOTTOM`- `editor.ui.ALIGNMENT.BOTTOM_RIGHT``grow boolean`determines if the component should grow to fill available space in a `horizontal` or `vertical` layout container`row_span integer`how many rows the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container.`column_span integer`how many columns the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container. ----@return editor.component value UI component -function editor.ui.icon(props) end - ----Integer input component based on a text field, reports changes on commit (`Enter` or focus loss) ----@param props table Optional props: `value any`value`on_value_changed function`value change callback, will receive the new value`issue table`issue related to the input; table with the following keys (all required):`severity string`either `editor.ui.ISSUE_SEVERITY.WARNING` or `editor.ui.ISSUE_SEVERITY.ERROR``message string`issue message, will be shown in a tooltip`tooltip string`tooltip message, shown on hover`enabled boolean`determines if the input component can be interacted with`alignment string`alignment of the component content within its assigned bounds, defaults to `editor.ui.ALIGNMENT.TOP_LEFT`; either: - `editor.ui.ALIGNMENT.TOP_LEFT`- `editor.ui.ALIGNMENT.TOP`- `editor.ui.ALIGNMENT.TOP_RIGHT`- `editor.ui.ALIGNMENT.LEFT`- `editor.ui.ALIGNMENT.CENTER`- `editor.ui.ALIGNMENT.RIGHT`- `editor.ui.ALIGNMENT.BOTTOM_LEFT`- `editor.ui.ALIGNMENT.BOTTOM`- `editor.ui.ALIGNMENT.BOTTOM_RIGHT``grow boolean`determines if the component should grow to fill available space in a `horizontal` or `vertical` layout container`row_span integer`how many rows the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container.`column_span integer`how many columns the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container. ----@return editor.component value UI component -function editor.ui.integer_field(props) end - ----Label intended for use with input components ----@param props table Optional props: `text string`the text`text_alignment string`text alignment within paragraph bounds; either: - `editor.ui.TEXT_ALIGNMENT.LEFT`- `editor.ui.TEXT_ALIGNMENT.CENTER`- `editor.ui.TEXT_ALIGNMENT.RIGHT`- `editor.ui.TEXT_ALIGNMENT.JUSTIFY``color string`semantic color, defaults to `editor.ui.COLOR.TEXT`; either: - `editor.ui.COLOR.TEXT`- `editor.ui.COLOR.HINT`- `editor.ui.COLOR.OVERRIDE`- `editor.ui.COLOR.WARNING`- `editor.ui.COLOR.ERROR``tooltip string`tooltip message, shown on hover`alignment string`alignment of the component content within its assigned bounds, defaults to `editor.ui.ALIGNMENT.TOP_LEFT`; either: - `editor.ui.ALIGNMENT.TOP_LEFT`- `editor.ui.ALIGNMENT.TOP`- `editor.ui.ALIGNMENT.TOP_RIGHT`- `editor.ui.ALIGNMENT.LEFT`- `editor.ui.ALIGNMENT.CENTER`- `editor.ui.ALIGNMENT.RIGHT`- `editor.ui.ALIGNMENT.BOTTOM_LEFT`- `editor.ui.ALIGNMENT.BOTTOM`- `editor.ui.ALIGNMENT.BOTTOM_RIGHT``grow boolean`determines if the component should grow to fill available space in a `horizontal` or `vertical` layout container`row_span integer`how many rows the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container.`column_span integer`how many columns the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container. ----@return editor.component value UI component -function editor.ui.label(props) end - ----Number input component based on a text field, reports changes on commit (`Enter` or focus loss) ----@param props table Optional props: `value any`value`on_value_changed function`value change callback, will receive the new value`issue table`issue related to the input; table with the following keys (all required):`severity string`either `editor.ui.ISSUE_SEVERITY.WARNING` or `editor.ui.ISSUE_SEVERITY.ERROR``message string`issue message, will be shown in a tooltip`tooltip string`tooltip message, shown on hover`enabled boolean`determines if the input component can be interacted with`alignment string`alignment of the component content within its assigned bounds, defaults to `editor.ui.ALIGNMENT.TOP_LEFT`; either: - `editor.ui.ALIGNMENT.TOP_LEFT`- `editor.ui.ALIGNMENT.TOP`- `editor.ui.ALIGNMENT.TOP_RIGHT`- `editor.ui.ALIGNMENT.LEFT`- `editor.ui.ALIGNMENT.CENTER`- `editor.ui.ALIGNMENT.RIGHT`- `editor.ui.ALIGNMENT.BOTTOM_LEFT`- `editor.ui.ALIGNMENT.BOTTOM`- `editor.ui.ALIGNMENT.BOTTOM_RIGHT``grow boolean`determines if the component should grow to fill available space in a `horizontal` or `vertical` layout container`row_span integer`how many rows the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container.`column_span integer`how many columns the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container. ----@return editor.component value UI component -function editor.ui.number_field(props) end - ----Open a resource, either in the editor or in a third-party app ----@param resource_path string Resource path (starting with `/`) -function editor.ui.open_resource(resource_path) end - ----A paragraph of text ----@param props table Optional props: `text string`the text`text_alignment string`text alignment within paragraph bounds; either: - `editor.ui.TEXT_ALIGNMENT.LEFT`- `editor.ui.TEXT_ALIGNMENT.CENTER`- `editor.ui.TEXT_ALIGNMENT.RIGHT`- `editor.ui.TEXT_ALIGNMENT.JUSTIFY``color string`semantic color, defaults to `editor.ui.COLOR.TEXT`; either: - `editor.ui.COLOR.TEXT`- `editor.ui.COLOR.HINT`- `editor.ui.COLOR.OVERRIDE`- `editor.ui.COLOR.WARNING`- `editor.ui.COLOR.ERROR``word_wrap boolean`determines if the lines of text are word-wrapped when they don't fit in the assigned bounds, defaults to true`alignment string`alignment of the component content within its assigned bounds, defaults to `editor.ui.ALIGNMENT.TOP_LEFT`; either: - `editor.ui.ALIGNMENT.TOP_LEFT`- `editor.ui.ALIGNMENT.TOP`- `editor.ui.ALIGNMENT.TOP_RIGHT`- `editor.ui.ALIGNMENT.LEFT`- `editor.ui.ALIGNMENT.CENTER`- `editor.ui.ALIGNMENT.RIGHT`- `editor.ui.ALIGNMENT.BOTTOM_LEFT`- `editor.ui.ALIGNMENT.BOTTOM`- `editor.ui.ALIGNMENT.BOTTOM_RIGHT``grow boolean`determines if the component should grow to fill available space in a `horizontal` or `vertical` layout container`row_span integer`how many rows the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container.`column_span integer`how many columns the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container. ----@return editor.component value UI component -function editor.ui.paragraph(props) end - ----Input component for selecting project resources ----@param props table Optional props: `value string`resource path (must start with `/`)`on_value_changed function`value change callback, will receive either resource path of a selected resource or nil when the field is cleared; even though the resource selector dialog allows filtering on resource extensions, it's possible to receive resources with other extensions and non-existent resources using text field input`title string`dialog title, defaults to `"Select Resource"``extensions string[]`if specified, restricts selectable resources in the dialog to specified file extensions; e.g. `{"collection", "go"}``issue table`issue related to the input; table with the following keys (all required):`severity string`either `editor.ui.ISSUE_SEVERITY.WARNING` or `editor.ui.ISSUE_SEVERITY.ERROR``message string`issue message, will be shown in a tooltip`tooltip string`tooltip message, shown on hover`enabled boolean`determines if the input component can be interacted with`alignment string`alignment of the component content within its assigned bounds, defaults to `editor.ui.ALIGNMENT.TOP_LEFT`; either: - `editor.ui.ALIGNMENT.TOP_LEFT`- `editor.ui.ALIGNMENT.TOP`- `editor.ui.ALIGNMENT.TOP_RIGHT`- `editor.ui.ALIGNMENT.LEFT`- `editor.ui.ALIGNMENT.CENTER`- `editor.ui.ALIGNMENT.RIGHT`- `editor.ui.ALIGNMENT.BOTTOM_LEFT`- `editor.ui.ALIGNMENT.BOTTOM`- `editor.ui.ALIGNMENT.BOTTOM_RIGHT``grow boolean`determines if the component should grow to fill available space in a `horizontal` or `vertical` layout container`row_span integer`how many rows the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container.`column_span integer`how many columns the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container. ----@return editor.component value UI component -function editor.ui.resource_field(props) end - ----Layout container that optionally shows scroll bars if child contents overflow the assigned bounds ----@param props table Required props: `content component`content component Optional props: `grow boolean`determines if the component should grow to fill available space in a `horizontal` or `vertical` layout container`row_span integer`how many rows the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container.`column_span integer`how many columns the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container. ----@return editor.component value UI component -function editor.ui.scroll(props) end - ----Dropdown select box with an array of options ----@param props table Optional props: `value any`selected value`on_value_changed function`change callback, will receive the selected value`options any[]`array of selectable options`to_string function`function that converts an item to a string, defaults to `tostring``issue table`issue related to the input; table with the following keys (all required):`severity string`either `editor.ui.ISSUE_SEVERITY.WARNING` or `editor.ui.ISSUE_SEVERITY.ERROR``message string`issue message, will be shown in a tooltip`tooltip string`tooltip message, shown on hover`enabled boolean`determines if the input component can be interacted with`alignment string`alignment of the component content within its assigned bounds, defaults to `editor.ui.ALIGNMENT.TOP_LEFT`; either: - `editor.ui.ALIGNMENT.TOP_LEFT`- `editor.ui.ALIGNMENT.TOP`- `editor.ui.ALIGNMENT.TOP_RIGHT`- `editor.ui.ALIGNMENT.LEFT`- `editor.ui.ALIGNMENT.CENTER`- `editor.ui.ALIGNMENT.RIGHT`- `editor.ui.ALIGNMENT.BOTTOM_LEFT`- `editor.ui.ALIGNMENT.BOTTOM`- `editor.ui.ALIGNMENT.BOTTOM_RIGHT``grow boolean`determines if the component should grow to fill available space in a `horizontal` or `vertical` layout container`row_span integer`how many rows the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container.`column_span integer`how many columns the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container. ----@return editor.component value UI component -function editor.ui.select_box(props) end - ----Thin line for visual content separation, by default horizontal and aligned to center ----@param props table Optional props: `orientation string`separator line orientation, `editor.ui.ORIENTATION.VERTICAL` or `editor.ui.ORIENTATION.HORIZONTAL`; either: - `editor.ui.ORIENTATION.VERTICAL`- `editor.ui.ORIENTATION.HORIZONTAL``alignment string`alignment of the component content within its assigned bounds, defaults to `editor.ui.ALIGNMENT.TOP_LEFT`; either: - `editor.ui.ALIGNMENT.TOP_LEFT`- `editor.ui.ALIGNMENT.TOP`- `editor.ui.ALIGNMENT.TOP_RIGHT`- `editor.ui.ALIGNMENT.LEFT`- `editor.ui.ALIGNMENT.CENTER`- `editor.ui.ALIGNMENT.RIGHT`- `editor.ui.ALIGNMENT.BOTTOM_LEFT`- `editor.ui.ALIGNMENT.BOTTOM`- `editor.ui.ALIGNMENT.BOTTOM_RIGHT``grow boolean`determines if the component should grow to fill available space in a `horizontal` or `vertical` layout container`row_span integer`how many rows the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container.`column_span integer`how many columns the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container. ----@return editor.component value UI component -function editor.ui.separator(props) end - ----Show a modal dialog and await a result ----@param dialog editor.component a component that resolves to `editor.ui.dialog(...)` ----@return any value dialog result, the value used as a `result` prop in a `editor.ui.dialog_button({...})` selected by the user, or `nil` if the dialog was closed and there was no `cancel = true` dialog button with `result` prop set -function editor.ui.show_dialog(dialog) end - ----Show a modal OS directory selection dialog and await a result ----@param opts? table `path string`initial file or directory path used by the dialog; resolved against project root if relative`title string`OS window title ----@return string|nil value either absolute directory path or nil if user canceled directory selection -function editor.ui.show_external_directory_dialog(opts) end - ----Show a modal OS file selection dialog and await a result ----@param opts? table `path string`initial file or directory path used by the dialog; resolved against project root if relative`title string`OS window title`filters table[]`File filters, an array of filter tables, where each filter has following keys:`description string`string explaining the filter, e.g. `"Text files (*.txt)"``extensions string[]`array of file extension patterns, e.g. `"*.txt"`, `"*.*"` or `"game.project"` ----@return string|nil value either absolute file path or nil if user canceled file selection -function editor.ui.show_external_file_dialog(opts) end - ----Show a modal resource selection dialog and await a result ----@param opts? table `extensions string[]`if specified, restricts selectable resources in the dialog to specified file extensions; e.g. `{"collection", "go"}``selection string`either `"single"` or `"multiple"`, defaults to `"single"``title string`dialog title, defaults to `"Select Resource"` ----@return string|string[]|nil value |nil] if user made no selection, returns `nil`. Otherwise, if selection mode is `"single"`, returns selected resource path; otherwise returns a non-empty array of selected resource paths. -function editor.ui.show_resource_dialog(opts) end - ----String input component based on a text field, reports changes on commit (`Enter` or focus loss) ----@param props table Optional props: `value any`value`on_value_changed function`value change callback, will receive the new value`issue table`issue related to the input; table with the following keys (all required):`severity string`either `editor.ui.ISSUE_SEVERITY.WARNING` or `editor.ui.ISSUE_SEVERITY.ERROR``message string`issue message, will be shown in a tooltip`tooltip string`tooltip message, shown on hover`enabled boolean`determines if the input component can be interacted with`alignment string`alignment of the component content within its assigned bounds, defaults to `editor.ui.ALIGNMENT.TOP_LEFT`; either: - `editor.ui.ALIGNMENT.TOP_LEFT`- `editor.ui.ALIGNMENT.TOP`- `editor.ui.ALIGNMENT.TOP_RIGHT`- `editor.ui.ALIGNMENT.LEFT`- `editor.ui.ALIGNMENT.CENTER`- `editor.ui.ALIGNMENT.RIGHT`- `editor.ui.ALIGNMENT.BOTTOM_LEFT`- `editor.ui.ALIGNMENT.BOTTOM`- `editor.ui.ALIGNMENT.BOTTOM_RIGHT``grow boolean`determines if the component should grow to fill available space in a `horizontal` or `vertical` layout container`row_span integer`how many rows the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container.`column_span integer`how many columns the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container. ----@return editor.component value UI component -function editor.ui.string_field(props) end - ----A hook that caches the result of a computation between re-renders. ----See `editor.ui.component` for hooks caveats and rules. If any of the arguments to `use_memo` change during a component refresh (checked with `==`), the value will be recomputed. ----@param compute function function that will be used to compute the cached value ----@param ...? any args to the computation function ----@return any ... values all returned values of the compute function -function editor.ui.use_memo(compute, ...) end - ----A hook that adds local state to the component. ----See `editor.ui.component` for hooks caveats and rules. If any of the arguments to `use_state` change during a component refresh (checked with `==`), the current state will be reset to the initial one. ----@param init any|function local state initializer, either initial data structure or function that produces the data structure ----@param ...? any used when `init` is a function, the args are passed to the initializer function ----@return any state current local state, starts with initial state, then may be changed using the returned `set_state` function ----@return function set_state function that changes the local state and causes the component to refresh. The function may be used in 2 ways: - to set the state to some other data structure: pass the data structure as a value to replace the state using updater function: pass a function to `set_state` — it will be invoked with the current state, as well as with the rest of the arguments passed to `set_state` after the updater function. The state will be set to the value returned from the updater function -function editor.ui.use_state(init, ...) end - ----Layout container that places its children in a vertical column one after another ----@param props table Optional props: `children component[]`array of child components`padding string, number`empty space from the edges of the container to its children; either: - `editor.ui.PADDING.NONE`- `editor.ui.PADDING.SMALL`- `editor.ui.PADDING.MEDIUM`- `editor.ui.PADDING.LARGE`- non-negative number, pixels`spacing string, number`empty space between child components, defaults to `editor.ui.SPACING.MEDIUM`; either: - `editor.ui.SPACING.NONE`- `editor.ui.SPACING.SMALL`- `editor.ui.SPACING.MEDIUM`- `editor.ui.SPACING.LARGE`- non-negative number, pixels`alignment string`alignment of the component content within its assigned bounds, defaults to `editor.ui.ALIGNMENT.TOP_LEFT`; either: - `editor.ui.ALIGNMENT.TOP_LEFT`- `editor.ui.ALIGNMENT.TOP`- `editor.ui.ALIGNMENT.TOP_RIGHT`- `editor.ui.ALIGNMENT.LEFT`- `editor.ui.ALIGNMENT.CENTER`- `editor.ui.ALIGNMENT.RIGHT`- `editor.ui.ALIGNMENT.BOTTOM_LEFT`- `editor.ui.ALIGNMENT.BOTTOM`- `editor.ui.ALIGNMENT.BOTTOM_RIGHT``grow boolean`determines if the component should grow to fill available space in a `horizontal` or `vertical` layout container`row_span integer`how many rows the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container.`column_span integer`how many columns the component spans inside a grid container, must be positive. This prop is only useful for components inside a `grid` container. ----@return editor.component value UI component -function editor.ui.vertical(props) end - ----Perform an HTTP request ----@param url string request URL ----@param opts? table Additional request options, a table with the following keys:`method string`request method, defaults to `"GET"``headers table`request headers, a table with string keys and values`body string`request body`as string`response body converter, either `"string"` or `"json"` ----@return table response HTTP response, a table with the following keys:`status integer`response code`headers table`response headers, a table where each key is a lower-cased string, and each value is either a string or an array of strings if the header was repeated`body string, any, nil`response body, present only when `as` option was provided, either a string or a parsed json value -function http.request(url, opts) end - ----Create HTTP response that will stream the content of a file defined by the path ----@param path string External file path, resolved against project root if relative ----@param status? integer HTTP status code, an integer, default 200 ----@param headers? table HTTP response headers, a table from lower-case header names to header values ----@return http.response response HTTP response value, userdata -function http.server.external_file_response(path, status, headers) end - ----Create HTTP response with a JSON value ----@param value any Any Lua value that may be represented as JSON ----@param status? integer HTTP status code, an integer, default 200 ----@param headers? table HTTP response headers, a table from lower-case header names to header values ----@return http.response response HTTP response value, userdata -function http.server.json_response(value, status, headers) end - ----Create HTTP response that will stream the content of a resource defined by the resource path ----@param resource_path string Resource path (starting with `/`) ----@param status? integer HTTP status code, an integer, default 200 ----@param headers? table HTTP response headers, a table from lower-case header names to header values ----@return http.response response HTTP response value, userdata -function http.server.resource_response(resource_path, status, headers) end - ----Create HTTP response ----@param status? integer HTTP status code, an integer, default 200 ----@param headers? table HTTP response headers, a table from lower-case header names to header values ----@param body? string HTTP response body ----@return http.response response HTTP response value, userdata -function http.server.response(status, headers, body) end - ----Create route definition for the editor's HTTP server ----@param path string HTTP URI path, starts with `/`; may include path patterns (`{name}` for a single segment and `{*name}` for the rest of the request path) that will be extracted from the path and provided to the handler as a part of the request ----@param method? string HTTP request method, default `"GET"` ----@param as? string Request body converter, either `"string"` or `"json"`; the body will be discarded if not specified ----@param handler function Request handler function, will receive request argument, a table with the following keys:`path string`full matched path, a string starting with `/``method string`HTTP request method, e.g. `"POST"``headers table`HTTP request headers, a table from lower-case header names to header values`query string`optional query string`body string, any`optional request body, depends on the `as` argument Handler function should return either a single response value, or 0 or more arguments to the `http.server.response()` function ----@return http.route route HTTP server route -function http.server.route(path, method, as, handler) end - ----Decode JSON string to Lua value ----@param json string json data ----@param options? { decode_null_as_userdata?:boolean } A table with the following keys:`all boolean`if `true`, decodes all json values in a string and returns an array -function json.decode(json, options) end - ----Encode Lua value to JSON string ----@param value any any Lua value that may be represented as JSON -function json.encode(value) end - ----Pretty-print a Lua value ----@param value any any Lua value to pretty-print -function pprint(value) end - ----Remove all tiles ----@param tiles editor.tiles unbounded 2d grid of tiles ----@return editor.tiles tiles unbounded 2d grid of tiles -function tilemap.tiles.clear(tiles) end - ----Get full information from a tile at a particular coordinate ----@param tiles editor.tiles unbounded 2d grid of tiles ----@param x integer x coordinate of a tile ----@param y integer y coordinate of a tile ----@return table info full tile information table with the following keys:`index integer`1-indexed tile index of a tilemap's tilesource`h_flip boolean`horizontal flip`v_flip boolean`vertical flip`rotate_90 boolean`whether the tile is rotated 90 degrees clockwise -function tilemap.tiles.get_info(tiles, x, y) end - ----Get a tile index at a particular coordinate ----@param tiles editor.tiles unbounded 2d grid of tiles ----@param x integer x coordinate of a tile ----@param y integer y coordinate of a tile ----@return integer tile_index 1-indexed tile index of a tilemap's tilesource -function tilemap.tiles.get_tile(tiles, x, y) end - ----Create an iterator over all tiles in a tiles data structure ----When iterating using for loop, each iteration returns x, y and tile index of a tile in a tile map ----@param tiles editor.tiles unbounded 2d grid of tiles ----@return function iter iterator -function tilemap.tiles.iterator(tiles) end - ----Create a new unbounded 2d grid data structure for storing tilemap layer tiles ----@return editor.tiles tiles unbounded 2d grid of tiles -function tilemap.tiles.new() end - ----Remove a tile at a particular coordinate ----@param tiles editor.tiles unbounded 2d grid of tiles ----@param x integer x coordinate of a tile ----@param y integer y coordinate of a tile ----@return editor.tiles tiles unbounded 2d grid of tiles -function tilemap.tiles.remove(tiles, x, y) end - ----Set a tile at a particular coordinate ----@param tiles editor.tiles unbounded 2d grid of tiles ----@param x integer x coordinate of a tile ----@param y integer y coordinate of a tile ----@param tile_or_info integer|table Either 1-indexed tile index of a tilemap's tilesource or full tile information table with the following keys:`index integer`1-indexed tile index of a tilemap's tilesource`h_flip boolean`horizontal flip`v_flip boolean`vertical flip`rotate_90 boolean`whether the tile is rotated 90 degrees clockwise ----@return editor.tiles tiles unbounded 2d grid of tiles -function tilemap.tiles.set(tiles, x, y, tile_or_info) end - ----Create a ZIP archive ----@param output_path string output zip file path, resolved against project root if relative ----@param opts? { method?:string, level?:integer } compression options, a table with the following keys:`method string`compression method, either `zip.METHOD.DEFLATED` (default) or `zip.METHOD.STORED``level integer`compression level, an integer between 0 and 9, only useful when the compression method is `zip.METHOD.DEFLATED`; defaults to 6 ----@param entries string|table entries to compress, either a string (relative path to file or folder to include) or a table with the following keys:`1 string`required; source file or folder path to include, resolved against project root if relative`2 string`optional; target file or folder path in the zip archive. May be omitted if source is a relative path that does not go above the project directory.`method string`compression method, either `zip.METHOD.DEFLATED` (default) or `zip.METHOD.STORED``level integer`compression level, an integer between 0 and 9, only useful when the compression method is `zip.METHOD.DEFLATED`; defaults to 6 -function zip.pack(output_path, opts, entries) end - ----Extract a ZIP archive ----@param archive_path string zip file path, resolved against project root if relative ----@param target_path? string target path for extraction, defaults to parent of `archive_path` if omitted ----@param opts? table extraction options, a table with the following keys:`on_conflict string`conflict resolution strategy, defaults to `zip.ON_CONFLICT.ERROR` ----@param paths? table entries to extract, relative string paths -function zip.unpack(archive_path, target_path, opts, paths) end - -return editor \ No newline at end of file diff --git a/resources/defold_api/factory.lua b/resources/defold_api/factory.lua deleted file mode 100644 index 94a3155..0000000 --- a/resources/defold_api/factory.lua +++ /dev/null @@ -1,80 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Factory API documentation - - Functions for controlling factory components which are used to - dynamically spawn game objects into the runtime. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.factory -factory = {} - ----The URL identifies which factory should create the game object. ----If the game object is created inside of the frame (e.g. from an update callback), the game object will be created instantly, but none of its component will be updated in the same frame. ----Properties defined in scripts in the created game object can be overridden through the properties-parameter below. ----See go.property for more information on script properties. ---- Calling factory.create on a factory that is marked as dynamic without having loaded resources ----using factory.load will synchronously load and create resources which may affect application performance. ----@param url string|hash|url the factory that should create a game object. ----@param position? vector3 the position of the new game object, the position of the game object calling `factory.create()` is used by default, or if the value is `nil`. ----@param rotation? quaternion the rotation of the new game object, the rotation of the game object calling `factory.create()` is used by default, or if the value is `nil`. ----@param properties? table the properties defined in a script attached to the new game object. ----@param scale? number|vector3 the scale of the new game object (must be greater than 0), the scale of the game object containing the factory is used by default, or if the value is `nil` ----@return hash id the global id of the spawned game object -function factory.create(url, position, rotation, properties, scale) end - ----This returns status of the factory. ----Calling this function when the factory is not marked as dynamic loading always returns ----factory.STATUS_LOADED. ----@param url? string|hash|url the factory component to get status from ----@return constant status status of the factory component ---- ----- `factory.STATUS_UNLOADED` ----- `factory.STATUS_LOADING` ----- `factory.STATUS_LOADED` ---- -function factory.get_status(url) end - ----Resources are referenced by the factory component until the existing (parent) collection is destroyed or factory.unload is called. ----Calling this function when the factory is not marked as dynamic loading does nothing. ----@param url? string|hash|url the factory component to load ----@param complete_function? fun(self, url, result) function to call when resources are loaded. ---- ----`self` ----object The current object. ----`url` ----url url of the factory component ----`result` ----boolean True if resources were loaded successfully ---- -function factory.load(url, complete_function) end - ----Changes the prototype for the factory. ----@param url? string|hash|url the factory component ----@param prototype? string|nil the path to the new prototype, or `nil` -function factory.set_prototype(url, prototype) end - ----This decreases the reference count for each resource loaded with factory.load. If reference is zero, the resource is destroyed. ----Calling this function when the factory is not marked as dynamic loading does nothing. ----@param url? string|hash|url the factory component to unload -function factory.unload(url) end - ----loaded -factory.STATUS_LOADED = nil - ----loading -factory.STATUS_LOADING = nil - ----unloaded -factory.STATUS_UNLOADED = nil - -return factory \ No newline at end of file diff --git a/resources/defold_api/font.lua b/resources/defold_api/font.lua deleted file mode 100644 index ae6a398..0000000 --- a/resources/defold_api/font.lua +++ /dev/null @@ -1,42 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Font API documentation - - Functions, messages and properties used to manipulate font resources. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.font -font = {} - ----Asynchronoously adds more glyphs to a .fontc resource ----@param path string|hash The path to the .fontc resource ----@param text string A string with unique unicode characters to be loaded ----@param callback? fun(self, request_id, result, errstring) (optional) A callback function that is called after the request is finished ---- ----`self` ----object The current object. ----`request_id` ----number The request id ----`result` ----boolean True if request was succesful ----`errstring` ----string `nil` if the request was successful ---- ----@return number request_id Returns the asynchronous request id -function font.add_glyphs(path, text, callback) end - ----Removes glyphs from the font ----@param path string|hash The path to the .fontc resource ----@param text string A string with unique unicode characters to be removed -function font.remove_glyphs(path, text) end - -return font \ No newline at end of file diff --git a/resources/defold_api/go.lua b/resources/defold_api/go.lua deleted file mode 100644 index cea4f5a..0000000 --- a/resources/defold_api/go.lua +++ /dev/null @@ -1,377 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Game object API documentation - - Functions, core hooks, messages and constants for manipulation of - game objects. The "go" namespace is accessible from game object script - files. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.go -go = {} - ----This is only supported for numerical properties. If the node property is already being ----animated, that animation will be canceled and replaced by the new one. ----If a `complete_function` (lua function) is specified, that function will be called when the animation has completed. ----By starting a new animation in that function, several animations can be sequenced together. See the examples for more information. ---- If you call `go.animate()` from a game object's `final()` function, ----any passed `complete_function` will be ignored and never called upon animation completion. ----See the properties guide for which properties can be animated and the animation guide for how ----them. ----@param url string|hash|url url of the game object or component having the property ----@param property string|hash id of the property to animate ----@param playback constant playback mode of the animation ---- ----- `go.PLAYBACK_ONCE_FORWARD` ----- `go.PLAYBACK_ONCE_BACKWARD` ----- `go.PLAYBACK_ONCE_PINGPONG` ----- `go.PLAYBACK_LOOP_FORWARD` ----- `go.PLAYBACK_LOOP_BACKWARD` ----- `go.PLAYBACK_LOOP_PINGPONG` ---- ----@param to number|vector3|vector4|quaternion target property value ----@param easing vector|constant easing to use during animation. Either specify a constant, see the animation guide for a complete list, or a vmath.vector with a curve ----@param duration number duration of the animation in seconds ----@param delay? number delay before the animation starts in seconds ----@param complete_function? fun(self, url, property) optional function to call when the animation has completed ---- ----`self` ---- ----object The current object. ---- ----`url` ---- ----url The game object or component instance for which the property is animated. ---- ----`property` ---- ----hash The id of the animated property. ---- ---- -function go.animate(url, property, playback, to, easing, duration, delay, complete_function) end - ----By calling this function, all or specified stored property animations of the game object or component will be canceled. ----See the properties guide for which properties can be animated and the animation guide for how to animate them. ----@param url string|hash|url url of the game object or component ----@param property? string|hash optional id of the property to cancel -function go.cancel_animations(url, property) end - ----Delete one or more game objects identified by id. Deletion is asynchronous meaning that ----the game object(s) are scheduled for deletion which will happen at the end of the current ----frame. Note that game objects scheduled for deletion will be counted against ----`max_instances` in "game.project" until they are actually removed. ---- Deleting a game object containing a particle FX component emitting particles will not immediately stop the particle FX from emitting particles. You need to manually stop the particle FX using `particlefx.stop()`. ---- Deleting a game object containing a sound component that is playing will not immediately stop the sound from playing. You need to manually stop the sound using `sound.stop()`. ----@param id? string|hash|url|table optional id or table of id's of the instance(s) to delete, the instance of the calling script is deleted by default ----@param recursive? boolean optional boolean, set to true to recursively delete child hiearchy in child to parent order -function go.delete(id, recursive) end - ----This function can check for game objects in any collection by specifying ----the collection name in the URL. ----@param url string|hash|url url of the game object to check ----@return boolean exists true if the game object exists -function go.exists(url) end - ----gets a named property of the specified game object or component ----@param url string|hash|url url of the game object or component having the property ----@param property string|hash id of the property to retrieve ----@param options? table optional options table ----- index number index into array property (1 based) ----- key hash name of internal property ----@return number|boolean|hash|url|vector3|vector4|quaternion|resource_data value the value of the specified property -function go.get(url, property, options) end - ----Returns or constructs an instance identifier. The instance id is a hash ----of the absolute path to the instance. ----- If `path` is specified, it can either be absolute or relative to the instance of the calling script. ----- If `path` is not specified, the id of the game object instance the script is attached to will be returned. ----@param path? string path of the instance for which to return the id ----@return hash id instance id -function go.get_id(path) end - ----Get the parent for a game object instance. ----@param id? string|hash|url optional id of the game object instance to get parent for, defaults to the instance containing the calling script ----@return hash|nil parent_id parent instance or `nil` -function go.get_parent(id) end - ----The position is relative the parent (if any). Use go.get_world_position to retrieve the global world position. ----@param id? string|hash|url optional id of the game object instance to get the position for, by default the instance of the calling script ----@return vector3 position instance position -function go.get_position(id) end - ----The rotation is relative to the parent (if any). Use go.get_world_rotation to retrieve the global world rotation. ----@param id? string|hash|url optional id of the game object instance to get the rotation for, by default the instance of the calling script ----@return quaternion rotation instance rotation -function go.get_rotation(id) end - ----The scale is relative the parent (if any). Use go.get_world_scale to retrieve the global world 3D scale factor. ----@param id? string|hash|url optional id of the game object instance to get the scale for, by default the instance of the calling script ----@return vector3 scale instance scale factor -function go.get_scale(id) end - ----The uniform scale is relative the parent (if any). If the underlying scale vector is non-uniform the min element of the vector is returned as the uniform scale factor. ----@param id? string|hash|url optional id of the game object instance to get the uniform scale for, by default the instance of the calling script ----@return number scale uniform instance scale factor -function go.get_scale_uniform(id) end - ----The function will return the world position calculated at the end of the previous frame. ----To recalculate it within the current frame, use go.update_world_transform on the instance before calling this. ----Use go.get_position to retrieve the position relative to the parent. ----@param id? string|hash|url optional id of the game object instance to get the world position for, by default the instance of the calling script ----@return vector3 position instance world position -function go.get_world_position(id) end - ----The function will return the world rotation calculated at the end of the previous frame. ----To recalculate it within the current frame, use go.update_world_transform on the instance before calling this. ----Use go.get_rotation to retrieve the rotation relative to the parent. ----@param id? string|hash|url optional id of the game object instance to get the world rotation for, by default the instance of the calling script ----@return quaternion rotation instance world rotation -function go.get_world_rotation(id) end - ----The function will return the world 3D scale factor calculated at the end of the previous frame. ----To recalculate it within the current frame, use go.update_world_transform on the instance before calling this. ----Use go.get_scale to retrieve the 3D scale factor relative to the parent. ----This vector is derived by decomposing the transformation matrix and should be used with care. ----For most cases it should be fine to use go.get_world_scale_uniform instead. ----@param id? string|hash|url optional id of the game object instance to get the world scale for, by default the instance of the calling script ----@return vector3 scale instance world 3D scale factor -function go.get_world_scale(id) end - ----The function will return the world scale factor calculated at the end of the previous frame. ----To recalculate it within the current frame, use go.update_world_transform on the instance before calling this. ----Use go.get_scale_uniform to retrieve the scale factor relative to the parent. ----@param id? string|hash|url optional id of the game object instance to get the world scale for, by default the instance of the calling script ----@return number scale instance world scale factor -function go.get_world_scale_uniform(id) end - ----The function will return the world transform matrix calculated at the end of the previous frame. ----To recalculate it within the current frame, use go.update_world_transform on the instance before calling this. ----@param id? string|hash|url optional id of the game object instance to get the world transform for, by default the instance of the calling script ----@return matrix4 transform instance world transform -function go.get_world_transform(id) end - ----This function defines a property which can then be used in the script through the self-reference. ----The properties defined this way are automatically exposed in the editor in game objects and collections which use the script. ----Note that you can only use this function outside any callback-functions like init and update. ----@param name string the id of the property ----@param value number|hash|url|vector3|vector4|quaternion|resource_data|boolean default value of the property. In the case of a url, only the empty constructor msg.url() is allowed. In the case of a resource one of the resource constructors (eg resource.atlas(), resource.font() etc) is expected. -function go.property(name, value) end - ----sets a named property of the specified game object or component, or a material constant ----@param url string|hash|url url of the game object or component having the property ----@param property string|hash id of the property to set ----@param value number|boolean|hash|url|vector3|vector4|quaternion|resource_data the value to set ----@param options? table optional options table ----- index integer index into array property (1 based) ----- key hash name of internal property -function go.set(url, property, value, options) end - ----Sets the parent for a game object instance. This means that the instance will exist in the geometrical space of its parent, ----like a basic transformation hierarchy or scene graph. If no parent is specified, the instance will be detached from any parent and exist in world ----space. ----This function will generate a `set_parent` message. It is not until the message has been processed that the change actually takes effect. This ----typically happens later in the same frame or the beginning of the next frame. Refer to the manual to learn how messages are processed by the ----engine. ----@param id? string|hash|url optional id of the game object instance to set parent for, defaults to the instance containing the calling script ----@param parent_id? string|hash|url optional id of the new parent game object, defaults to detaching game object from its parent ----@param keep_world_transform? boolean optional boolean, set to true to maintain the world transform when changing spaces. Defaults to false. -function go.set_parent(id, parent_id, keep_world_transform) end - ----The position is relative to the parent (if any). The global world position cannot be manually set. ----@param position vector3 position to set ----@param id? string|hash|url optional id of the game object instance to set the position for, by default the instance of the calling script -function go.set_position(position, id) end - ----The rotation is relative to the parent (if any). The global world rotation cannot be manually set. ----@param rotation quaternion rotation to set ----@param id? string|hash|url optional id of the game object instance to get the rotation for, by default the instance of the calling script -function go.set_rotation(rotation, id) end - ----The scale factor is relative to the parent (if any). The global world scale factor cannot be manually set. ---- See manual to know how physics affected when setting scale from this function. ----@param scale number|vector3 vector or uniform scale factor, must be greater than 0 ----@param id? string|hash|url optional id of the game object instance to get the scale for, by default the instance of the calling script -function go.set_scale(scale, id) end - ----The scale factor is relative to the parent (if any). The global world scale factor cannot be manually set. ---- See manual to know how physics affected when setting scale from this function. ----@param scale number|vector3 vector or uniform scale factor, must be greater than 0 ----@param id? string|hash|url optional id of the game object instance to get the scale for, by default the instance of the calling script -function go.set_scale_xy(scale, id) end - ----Recalculates and updates the cached world transform immediately for the target instance ----and its ancestors (parent chain up to the collection root). Descendants (children) are ----not updated by this function. ----If no id is provided, the instance of the calling script is used. ---- Use this after changing local transform mid-frame when you need the ----new world transform right away (e.g. before end-of-frame updates). Note that child ----instances will still have last-frame world transforms until the regular update. ----@param id? string|hash|url optional id of the game object instance to update -function go.update_world_transform(id) end - ---- The function uses world transformation calculated at the end of previous frame. ----@param position vector3 position which need to be converted ----@param url string|hash|url url of the game object which coordinate system convert to ----@return vector3 converted_postion converted position -function go.world_to_local_position(position, url) end - ---- The function uses world transformation calculated at the end of previous frame. ----@param transformation matrix4 transformation which need to be converted ----@param url string|hash|url url of the game object which coordinate system convert to ----@return matrix4 converted_transform converted transformation -function go.world_to_local_transform(transformation, url) end - ----in-back -go.EASING_INBACK = nil - ----in-bounce -go.EASING_INBOUNCE = nil - ----in-circlic -go.EASING_INCIRC = nil - ----in-cubic -go.EASING_INCUBIC = nil - ----in-elastic -go.EASING_INELASTIC = nil - ----in-exponential -go.EASING_INEXPO = nil - ----in-out-back -go.EASING_INOUTBACK = nil - ----in-out-bounce -go.EASING_INOUTBOUNCE = nil - ----in-out-circlic -go.EASING_INOUTCIRC = nil - ----in-out-cubic -go.EASING_INOUTCUBIC = nil - ----in-out-elastic -go.EASING_INOUTELASTIC = nil - ----in-out-exponential -go.EASING_INOUTEXPO = nil - ----in-out-quadratic -go.EASING_INOUTQUAD = nil - ----in-out-quartic -go.EASING_INOUTQUART = nil - ----in-out-quintic -go.EASING_INOUTQUINT = nil - ----in-out-sine -go.EASING_INOUTSINE = nil - ----in-quadratic -go.EASING_INQUAD = nil - ----in-quartic -go.EASING_INQUART = nil - ----in-quintic -go.EASING_INQUINT = nil - ----in-sine -go.EASING_INSINE = nil - ----linear interpolation -go.EASING_LINEAR = nil - ----out-back -go.EASING_OUTBACK = nil - ----out-bounce -go.EASING_OUTBOUNCE = nil - ----out-circlic -go.EASING_OUTCIRC = nil - ----out-cubic -go.EASING_OUTCUBIC = nil - ----out-elastic -go.EASING_OUTELASTIC = nil - ----out-exponential -go.EASING_OUTEXPO = nil - ----out-in-back -go.EASING_OUTINBACK = nil - ----out-in-bounce -go.EASING_OUTINBOUNCE = nil - ----out-in-circlic -go.EASING_OUTINCIRC = nil - ----out-in-cubic -go.EASING_OUTINCUBIC = nil - ----out-in-elastic -go.EASING_OUTINELASTIC = nil - ----out-in-exponential -go.EASING_OUTINEXPO = nil - ----out-in-quadratic -go.EASING_OUTINQUAD = nil - ----out-in-quartic -go.EASING_OUTINQUART = nil - ----out-in-quintic -go.EASING_OUTINQUINT = nil - ----out-in-sine -go.EASING_OUTINSINE = nil - ----out-quadratic -go.EASING_OUTQUAD = nil - ----out-quartic -go.EASING_OUTQUART = nil - ----out-quintic -go.EASING_OUTQUINT = nil - ----out-sine -go.EASING_OUTSINE = nil - ----loop backward -go.PLAYBACK_LOOP_BACKWARD = nil - ----loop forward -go.PLAYBACK_LOOP_FORWARD = nil - ----ping pong loop -go.PLAYBACK_LOOP_PINGPONG = nil - ----no playback -go.PLAYBACK_NONE = nil - ----once backward -go.PLAYBACK_ONCE_BACKWARD = nil - ----once forward -go.PLAYBACK_ONCE_FORWARD = nil - ----once ping pong -go.PLAYBACK_ONCE_PINGPONG = nil - -return go \ No newline at end of file diff --git a/resources/defold_api/graphics.lua b/resources/defold_api/graphics.lua deleted file mode 100644 index ca2028f..0000000 --- a/resources/defold_api/graphics.lua +++ /dev/null @@ -1,344 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Graphics API documentation - - Graphics functions and constants. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.graphics -graphics = {} - ---- -graphics.BLEND_FACTOR_CONSTANT_ALPHA = nil - ---- -graphics.BLEND_FACTOR_CONSTANT_COLOR = nil - ---- -graphics.BLEND_FACTOR_DST_ALPHA = nil - ---- -graphics.BLEND_FACTOR_DST_COLOR = nil - ---- -graphics.BLEND_FACTOR_ONE = nil - ---- -graphics.BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA = nil - ---- -graphics.BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR = nil - ---- -graphics.BLEND_FACTOR_ONE_MINUS_DST_ALPHA = nil - ---- -graphics.BLEND_FACTOR_ONE_MINUS_DST_COLOR = nil - ---- -graphics.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA = nil - ---- -graphics.BLEND_FACTOR_ONE_MINUS_SRC_COLOR = nil - ---- -graphics.BLEND_FACTOR_SRC_ALPHA = nil - ---- -graphics.BLEND_FACTOR_SRC_ALPHA_SATURATE = nil - ---- -graphics.BLEND_FACTOR_SRC_COLOR = nil - ---- -graphics.BLEND_FACTOR_ZERO = nil - ---- -graphics.BUFFER_TYPE_COLOR0_BIT = nil - ----May be nil if multitarget rendering isn't supported -graphics.BUFFER_TYPE_COLOR1_BIT = nil - ----May be nil if multitarget rendering isn't supported -graphics.BUFFER_TYPE_COLOR2_BIT = nil - ----May be nil if multitarget rendering isn't supported -graphics.BUFFER_TYPE_COLOR3_BIT = nil - ---- -graphics.BUFFER_TYPE_DEPTH_BIT = nil - ---- -graphics.BUFFER_TYPE_STENCIL_BIT = nil - ---- -graphics.COMPARE_FUNC_ALWAYS = nil - ---- -graphics.COMPARE_FUNC_EQUAL = nil - ---- -graphics.COMPARE_FUNC_GEQUAL = nil - ---- -graphics.COMPARE_FUNC_GREATER = nil - ---- -graphics.COMPARE_FUNC_LEQUAL = nil - ---- -graphics.COMPARE_FUNC_LESS = nil - ---- -graphics.COMPARE_FUNC_NEVER = nil - ---- -graphics.COMPARE_FUNC_NOTEQUAL = nil - ---- -graphics.COMPRESSION_TYPE_BASIS_ETC1S = nil - ---- -graphics.COMPRESSION_TYPE_BASIS_UASTC = nil - ---- -graphics.COMPRESSION_TYPE_DEFAULT = nil - ---- -graphics.COMPRESSION_TYPE_WEBP = nil - ---- -graphics.COMPRESSION_TYPE_WEBP_LOSSY = nil - ---- -graphics.FACE_TYPE_BACK = nil - ---- -graphics.FACE_TYPE_FRONT = nil - ---- -graphics.FACE_TYPE_FRONT_AND_BACK = nil - ---- -graphics.STATE_ALPHA_TEST = nil - ---- -graphics.STATE_ALPHA_TEST_SUPPORTED = nil - ---- -graphics.STATE_BLEND = nil - ---- -graphics.STATE_CULL_FACE = nil - ---- -graphics.STATE_DEPTH_TEST = nil - ---- -graphics.STATE_POLYGON_OFFSET_FILL = nil - ---- -graphics.STATE_SCISSOR_TEST = nil - ---- -graphics.STATE_STENCIL_TEST = nil - ---- -graphics.STENCIL_OP_DECR = nil - ---- -graphics.STENCIL_OP_DECR_WRAP = nil - ---- -graphics.STENCIL_OP_INCR = nil - ---- -graphics.STENCIL_OP_INCR_WRAP = nil - ---- -graphics.STENCIL_OP_INVERT = nil - ---- -graphics.STENCIL_OP_KEEP = nil - ---- -graphics.STENCIL_OP_REPLACE = nil - ---- -graphics.STENCIL_OP_ZERO = nil - ---- -graphics.TEXTURE_FILTER_DEFAULT = nil - ---- -graphics.TEXTURE_FILTER_LINEAR = nil - ---- -graphics.TEXTURE_FILTER_LINEAR_MIPMAP_LINEAR = nil - ---- -graphics.TEXTURE_FILTER_LINEAR_MIPMAP_NEAREST = nil - ---- -graphics.TEXTURE_FILTER_NEAREST = nil - ---- -graphics.TEXTURE_FILTER_NEAREST_MIPMAP_LINEAR = nil - ---- -graphics.TEXTURE_FILTER_NEAREST_MIPMAP_NEAREST = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_BGRA8U = nil - ---- -graphics.TEXTURE_FORMAT_DEPTH = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_LUMINANCE = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_LUMINANCE_ALPHA = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_R16F = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_R32F = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_R32UI = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RG16F = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RG32F = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RGB = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RGB16F = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RGB32F = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RGBA = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RGBA16F = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RGBA32F = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RGBA32UI = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RGBA_16BPP = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RGBA_ASTC_4X4 = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RGBA_BC3 = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RGBA_BC7 = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RGBA_ETC2 = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RGBA_PVRTC_2BPPV1 = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RGBA_PVRTC_4BPPV1 = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RGB_16BPP = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RGB_BC1 = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RGB_ETC1 = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RGB_PVRTC_2BPPV1 = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RGB_PVRTC_4BPPV1 = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RG_BC5 = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_RG_ETC2 = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_R_BC4 = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_FORMAT_R_ETC2 = nil - ---- -graphics.TEXTURE_FORMAT_STENCIL = nil - ---- -graphics.TEXTURE_TYPE_2D = nil - ---- -graphics.TEXTURE_TYPE_2D_ARRAY = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_TYPE_3D = nil - ---- -graphics.TEXTURE_TYPE_CUBE_MAP = nil - ---- -graphics.TEXTURE_TYPE_IMAGE_2D = nil - ----May be nil if the graphics driver doesn't support it -graphics.TEXTURE_TYPE_IMAGE_3D = nil - ---- -graphics.TEXTURE_USAGE_FLAG_COLOR = nil - ---- -graphics.TEXTURE_USAGE_FLAG_INPUT = nil - ---- -graphics.TEXTURE_USAGE_FLAG_MEMORYLESS = nil - ---- -graphics.TEXTURE_USAGE_FLAG_SAMPLE = nil - ---- -graphics.TEXTURE_USAGE_FLAG_STORAGE = nil - ---- -graphics.TEXTURE_WRAP_CLAMP_TO_BORDER = nil - ---- -graphics.TEXTURE_WRAP_CLAMP_TO_EDGE = nil - ---- -graphics.TEXTURE_WRAP_MIRRORED_REPEAT = nil - ---- -graphics.TEXTURE_WRAP_REPEAT = nil - -return graphics \ No newline at end of file diff --git a/resources/defold_api/gui.lua b/resources/defold_api/gui.lua deleted file mode 100644 index cbec9e5..0000000 --- a/resources/defold_api/gui.lua +++ /dev/null @@ -1,1310 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - GUI API documentation - - GUI core hooks, functions, messages, properties and constants for - creation and manipulation of GUI nodes. The "gui" namespace is - accessible only from gui scripts. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.gui -gui = {} - ----This starts an animation of a node property according to the specified parameters. ----If the node property is already being animated, that animation will be canceled and ----replaced by the new one. Note however that several different node properties ----can be animated simultaneously. Use `gui.cancel_animation` to stop the animation ----before it has completed. ----Composite properties of type vector3, vector4 or quaternion ----also expose their sub-components (x, y, z and w). ----You can address the components individually by suffixing the name with a dot '.' ----and the name of the component. ----For instance, `"position.x"` (the position x coordinate) or `"color.w"` ----(the color alpha value). ----If a `complete_function` (Lua function) is specified, that function will be called ----when the animation has completed. ----By starting a new animation in that function, several animations can be sequenced ----together. See the examples below for more information. ----@param node node node to animate ----@param property string|constant property to animate ---- ----- `"position"` ----- `"rotation"` ----- `"euler"` ----- `"scale"` ----- `"color"` ----- `"outline"` ----- `"shadow"` ----- `"size"` ----- `"fill_angle"` (pie) ----- `"inner_radius"` (pie) ----- `"leading"` (text) ----- `"tracking"` (text) ----- `"slice9"` (slice9) ---- ----The following property constants are defined equaling the corresponding property string names. ---- ----- `gui.PROP_POSITION` ----- `gui.PROP_ROTATION` ----- `gui.PROP_EULER` ----- `gui.PROP_SCALE` ----- `gui.PROP_COLOR` ----- `gui.PROP_OUTLINE` ----- `gui.PROP_SHADOW` ----- `gui.PROP_SIZE` ----- `gui.PROP_FILL_ANGLE` ----- `gui.PROP_INNER_RADIUS` ----- `gui.PROP_LEADING` ----- `gui.PROP_TRACKING` ----- `gui.PROP_SLICE9` ---- ----@param to number|vector3|vector4|quaternion target property value ----@param easing constant|vector easing to use during animation. ---- Either specify one of the `gui.EASING_*` constants or provide a ---- vector with a custom curve. See the animation guide for more information. ----@param duration number duration of the animation in seconds. ----@param delay? number delay before the animation starts in seconds. ----@param complete_function? fun(self, node) function to call when the ---- animation has completed ----@param playback? constant playback mode ---- ----- `gui.PLAYBACK_ONCE_FORWARD` ----- `gui.PLAYBACK_ONCE_BACKWARD` ----- `gui.PLAYBACK_ONCE_PINGPONG` ----- `gui.PLAYBACK_LOOP_FORWARD` ----- `gui.PLAYBACK_LOOP_BACKWARD` ----- `gui.PLAYBACK_LOOP_PINGPONG` ---- -function gui.animate(node, property, to, easing, duration, delay, complete_function, playback) end - ----If one or more animations of the specified node is currently running (started by `gui.animate`), they will immediately be canceled. ----@param node node node that should have its animation canceled ----@param property? nil|string|constant optional property for which the animation should be canceled ---- ----- `"position"` ----- `"rotation"` ----- `"euler"` ----- `"scale"` ----- `"color"` ----- `"outline"` ----- `"shadow"` ----- `"size"` ----- `"fill_angle"` (pie) ----- `"inner_radius"` (pie) ----- `"leading"` (text) ----- `"tracking"` (text) ----- `"slice9"` (slice9) ---- -function gui.cancel_animations(node, property) end - ----Cancels any running flipbook animation on the specified node. ----@param node node node cancel flipbook animation for -function gui.cancel_flipbook(node) end - ----Make a clone instance of a node. The cloned node will be identical to the ----original node, except the id which is generated as the string "node" plus ----a sequential unsigned integer value. ----This function does not clone the supplied node's children nodes. ----Use gui.clone_tree for that purpose. ----@param node node node to clone ----@return node clone the cloned node -function gui.clone(node) end - ----Make a clone instance of a node and all its children. ----Use gui.clone to clone a node excluding its children. ----@param node node root node to clone ----@return table clones a table mapping node ids to the corresponding cloned nodes -function gui.clone_tree(node) end - ----Deletes the specified node. Any child nodes of the specified node will be ----recursively deleted. ----@param node node node to delete -function gui.delete_node(node) end - ----Delete a dynamically created texture. ----@param texture string|hash texture id -function gui.delete_texture(texture) end - ----Instead of using specific getters such as gui.get_position or gui.get_scale, ----you can use gui.get instead and supply the property as a string or a hash. ----While this function is similar to go.get, there are a few more restrictions ----when operating in the gui namespace. Most notably, only these explicitly named properties are supported: ----- `"position"` ----- `"rotation"` ----- `"euler"` ----- `"scale"` ----- `"color"` ----- `"outline"` ----- `"shadow"` ----- `"size"` ----- `"fill_angle"` (pie) ----- `"inner_radius"` (pie) ----- `"leading"` (text) ----- `"tracking"` (text) ----- `"slice9"` (slice9) ----The value returned will either be a vmath.vector4 or a single number, i.e getting the "position" ----property will return a vec4 while getting the "position.x" property will return a single value. ----You can also use this function to get material constants. ----@param node node node to get the property for ----@param property string|hash|constant the property to retrieve ----@param options? table optional options table (only applicable for material constants) ----- `index` number index into array property (1 based) -function gui.get(node, property, options) end - ----Returns the adjust mode of a node. ----The adjust mode defines how the node will adjust itself to screen ----resolutions that differs from the one in the project settings. ----@param node node node from which to get the adjust mode (node) ----@return constant adjust_mode the current adjust mode ---- ----- `gui.ADJUST_FIT` ----- `gui.ADJUST_ZOOM` ----- `gui.ADJUST_STRETCH` ---- -function gui.get_adjust_mode(node) end - ----gets the node alpha ----@param node node node from which to get alpha ----@return number alpha alpha -function gui.get_alpha(node) end - ----Returns the blend mode of a node. ----Blend mode defines how the node will be blended with the background. ----@param node node node from which to get the blend mode ----@return constant blend_mode blend mode ---- ----- `gui.BLEND_ALPHA` ----- `gui.BLEND_ADD` ----- `gui.BLEND_ADD_ALPHA` ----- `gui.BLEND_MULT` ----- `gui.BLEND_SCREEN` ---- -function gui.get_blend_mode(node) end - ----If node is set as an inverted clipping node, it will clip anything inside as opposed to outside. ----@param node node node from which to get the clipping inverted state ----@return boolean inverted `true` or `false` -function gui.get_clipping_inverted(node) end - ----Clipping mode defines how the node will clip it's children nodes ----@param node node node from which to get the clipping mode ----@return constant clipping_mode clipping mode ---- ---- - `gui.CLIPPING_MODE_NONE` ---- - `gui.CLIPPING_MODE_STENCIL` ---- -function gui.get_clipping_mode(node) end - ----If node is set as visible clipping node, it will be shown as well as clipping. Otherwise, it will only clip but not show visually. ----@param node node node from which to get the clipping visibility state ----@return boolean visible `true` or `false` -function gui.get_clipping_visible(node) end - ----Returns the color of the supplied node. The components ----of the returned vector4 contains the color channel values: ----Component ----Color value ----x ----Red value ----y ----Green value ----z ----Blue value ----w ----Alpha value ----@param node node node to get the color from ----@return vector4 color node color -function gui.get_color(node) end - ----Returns the rotation of the supplied node. ----The rotation is expressed in degree Euler angles. ----@param node node node to get the rotation from ----@return vector3 rotation node rotation -function gui.get_euler(node) end - ----Returns the sector angle of a pie node. ----@param node node node from which to get the fill angle ----@return number angle sector angle -function gui.get_fill_angle(node) end - ----Get node flipbook animation. ----@param node node node to get flipbook animation from ----@return hash animation animation id -function gui.get_flipbook(node) end - ----This is only useful nodes with flipbook animations. Gets the normalized cursor of the flipbook animation on a node. ----@param node node node to get the cursor for (node) ----@return number cursor cursor value -function gui.get_flipbook_cursor(node) end - ----This is only useful nodes with flipbook animations. Gets the playback rate of the flipbook animation on a node. ----@param node node node to set the cursor for ----@return number rate playback rate -function gui.get_flipbook_playback_rate(node) end - ----This is only useful for text nodes. The font must be mapped to the gui scene in the gui editor. ----@param node node node from which to get the font ----@return hash font font id -function gui.get_font(node) end - ----This is only useful for text nodes. The font must be mapped to the gui scene in the gui editor. ----@param font_name hash|string font of which to get the path hash ----@return hash hash path hash to resource -function gui.get_font_resource(font_name) end - ----Returns the scene height. ----@return number height scene height -function gui.get_height() end - ----Retrieves the id of the specified node. ----@param node node the node to retrieve the id from ----@return hash id the id of the node -function gui.get_id(node) end - ----Retrieve the index of the specified node among its siblings. ----The index defines the order in which a node appear in a GUI scene. ----Higher index means the node is drawn on top of lower indexed nodes. ----@param node node the node to retrieve the id from ----@return number index the index of the node -function gui.get_index(node) end - ----gets the node inherit alpha state ----@param node node node from which to get the inherit alpha state ----@return boolean inherit_alpha `true` or `false` -function gui.get_inherit_alpha(node) end - ----Returns the inner radius of a pie node. ----The radius is defined along the x-axis. ----@param node node node from where to get the inner radius ----@return number radius inner radius -function gui.get_inner_radius(node) end - ----The layer must be mapped to the gui scene in the gui editor. ----@param node node node from which to get the layer ----@return hash layer layer id -function gui.get_layer(node) end - ----gets the scene current layout ----@return hash layout layout id -function gui.get_layout() end - ----Returns a table mapping each layout id hash to a vector3(width, height, 0). For the default layout, ----the current scene resolution is returned. If a layout name is not present in the Display Profiles (or when ----no display profiles are assigned), the width/height pair is 0. ----@return table layout_id_hash -> vmath.vector3(width, height, 0) -function gui.get_layouts() end - ----Returns the leading value for a text node. ----@param node node node from where to get the leading ----@return number leading leading scaling value (default=1) -function gui.get_leading(node) end - ----Returns whether a text node is in line-break mode or not. ----This is only useful for text nodes. ----@param node node node from which to get the line-break for ----@return boolean line_break `true` or `false` -function gui.get_line_break(node) end - ----Returns the material of a node. ----The material must be mapped to the gui scene in the gui editor. ----@param node node node to get the material for ----@return hash materal material id -function gui.get_material(node) end - ----Retrieves the node with the specified id. ----@param id string|hash id of the node to retrieve ----@return node instance a new node instance -function gui.get_node(id) end - ----Returns the outer bounds mode for a pie node. ----@param node node node from where to get the outer bounds mode ----@return constant bounds_mode the outer bounds mode of the pie node: ---- ----- `gui.PIEBOUNDS_RECTANGLE` ----- `gui.PIEBOUNDS_ELLIPSE` ---- -function gui.get_outer_bounds(node) end - ----Returns the outline color of the supplied node. ----See gui.get_color for info how vectors encode color values. ----@param node node node to get the outline color from ----@return vector4 color outline color -function gui.get_outline(node) end - ----Returns the parent node of the specified node. ----If the supplied node does not have a parent, `nil` is returned. ----@param node node the node from which to retrieve its parent ----@return node|nil parent parent instance or `nil` -function gui.get_parent(node) end - ----Get the paricle fx for a gui node ----@param node node node to get particle fx for ----@return hash particlefx particle fx id -function gui.get_particlefx(node) end - ----Returns the number of generated vertices around the perimeter ----of a pie node. ----@param node node pie node ----@return number vertices vertex count -function gui.get_perimeter_vertices(node) end - ----The pivot specifies how the node is drawn and rotated from its position. ----@param node node node to get pivot from ----@return constant pivot pivot constant ---- ---- - `gui.PIVOT_CENTER` ---- - `gui.PIVOT_N` ---- - `gui.PIVOT_NE` ---- - `gui.PIVOT_E` ---- - `gui.PIVOT_SE` ---- - `gui.PIVOT_S` ---- - `gui.PIVOT_SW` ---- - `gui.PIVOT_W` ---- - `gui.PIVOT_NW` ---- -function gui.get_pivot(node) end - ----Returns the position of the supplied node. ----@param node node node to get the position from ----@return vector3 position node position -function gui.get_position(node) end - ----Returns the rotation of the supplied node. ----The rotation is expressed as a quaternion ----@param node node node to get the rotation from ----@return quaternion rotation node rotation -function gui.get_rotation(node) end - ----Returns the scale of the supplied node. ----@param node node node to get the scale from ----@return vector3 scale node scale -function gui.get_scale(node) end - ----Returns the screen position of the supplied node. This function returns the ----calculated transformed position of the node, taking into account any parent node ----transforms. ----@param node node node to get the screen position from ----@return vector3 position node screen position -function gui.get_screen_position(node) end - ----Returns the shadow color of the supplied node. ----See gui.get_color for info how vectors encode color values. ----@param node node node to get the shadow color from ----@return vector4 color node shadow color -function gui.get_shadow(node) end - ----Returns the size of the supplied node. ----@param node node node to get the size from ----@return vector3 size node size -function gui.get_size(node) end - ----Returns the size of a node. ----The size mode defines how the node will adjust itself in size. Automatic ----size mode alters the node size based on the node's content. Automatic size ----mode works for Box nodes and Pie nodes which will both adjust their size ----to match the assigned image. Particle fx and Text nodes will ignore ----any size mode setting. ----@param node node node from which to get the size mode (node) ----@return constant size_mode the current size mode ---- ----- `gui.SIZE_MODE_MANUAL` ----- `gui.SIZE_MODE_AUTO` ---- -function gui.get_size_mode(node) end - ----Returns the slice9 configuration values for the node. ----@param node node node to manipulate ----@return vector4 values configuration values -function gui.get_slice9(node) end - ----Returns the text value of a text node. This is only useful for text nodes. ----@param node node node from which to get the text ----@return string text text value -function gui.get_text(node) end - ----Returns the texture of a node. ----This is currently only useful for box or pie nodes. ----The texture must be mapped to the gui scene in the gui editor. ----@param node node node to get texture from ----@return hash texture texture id -function gui.get_texture(node) end - ----Returns the tracking value of a text node. ----@param node node node from where to get the tracking ----@return number tracking tracking scaling number (default=0) -function gui.get_tracking(node) end - ----Get a node and all its children as a Lua table. ----@param node node root node to get node tree from ----@return table clones a table mapping node ids to the corresponding nodes -function gui.get_tree(node) end - ----gets the node type ----@param node node node from which to get the type ----@return constant type type ---- ----- `gui.TYPE_BOX` ----- `gui.TYPE_TEXT` ----- `gui.TYPE_PIE` ----- `gui.TYPE_PARTICLEFX` ----- `gui.TYPE_CUSTOM` ---- ----@return number|nil subtype id of the custom type -function gui.get_type(node) end - ----Returns `true` if a node is visible and `false` if it's not. ----Invisible nodes are not rendered. ----@param node node node to query ----@return boolean visible whether the node is visible or not -function gui.get_visible(node) end - ----Returns the scene width. ----@return number width scene width -function gui.get_width() end - ----The x-anchor specifies how the node is moved when the game is run in a different resolution. ----@param node node node to get x-anchor from ----@return constant anchor anchor constant ---- ----- `gui.ANCHOR_NONE` ----- `gui.ANCHOR_LEFT` ----- `gui.ANCHOR_RIGHT` ---- -function gui.get_xanchor(node) end - ----The y-anchor specifies how the node is moved when the game is run in a different resolution. ----@param node node node to get y-anchor from ----@return constant anchor anchor constant ---- ----- `gui.ANCHOR_NONE` ----- `gui.ANCHOR_TOP` ----- `gui.ANCHOR_BOTTOM` ---- -function gui.get_yanchor(node) end - ----Hides the on-display touch keyboard on the device. -function gui.hide_keyboard() end - ----Returns `true` if a node is enabled and `false` if it's not. ----Disabled nodes are not rendered and animations acting on them are not evaluated. ----@param node node node to query ----@param recursive? boolean check hierarchy recursively ----@return boolean enabled whether the node is enabled or not -function gui.is_enabled(node, recursive) end - ----Alters the ordering of the two supplied nodes by moving the first node ----above the second. ----If the second argument is `nil` the first node is moved to the top. ----@param node node to move ----@param reference node|nil reference node above which the first node should be moved -function gui.move_above(node, reference) end - ----Alters the ordering of the two supplied nodes by moving the first node ----below the second. ----If the second argument is `nil` the first node is moved to the bottom. ----@param node node to move ----@param reference node|nil reference node below which the first node should be moved -function gui.move_below(node, reference) end - ----Dynamically create a new box node. ----@param pos vector3|vector4 node position ----@param size vector3 node size ----@return node node new box node -function gui.new_box_node(pos, size) end - ----Dynamically create a particle fx node. ----@param pos vector3|vector4 node position ----@param particlefx hash|string particle fx resource name ----@return node node new particle fx node -function gui.new_particlefx_node(pos, particlefx) end - ----Dynamically create a new pie node. ----@param pos vector3|vector4 node position ----@param size vector3 node size ----@return node node new pie node -function gui.new_pie_node(pos, size) end - ----Dynamically create a new text node. ----@param pos vector3|vector4 node position ----@param text string node text ----@return node node new text node -function gui.new_text_node(pos, text) end - ----Dynamically create a new texture. ----@param texture_id string|hash texture id ----@param width number texture width ----@param height number texture height ----@param type string|constant texture type ---- ----- `"rgb"` - RGB ----- `"rgba"` - RGBA ----- `"l"` - LUMINANCE ---- ----@param buffer string texture data ----@param flip boolean flip texture vertically ----@return boolean success texture creation was successful ----@return number code one of the gui.RESULT_* codes if unsuccessful -function gui.new_texture(texture_id, width, height, type, buffer, flip) end - ----Tests whether a coordinate is within the bounding box of a ----node. ----@param node node node to be tested for picking ----@param x number x-coordinate (see on_input ) ----@param y number y-coordinate (see on_input ) ----@return boolean pickable pick result -function gui.pick_node(node, x, y) end - ----Play flipbook animation on a box or pie node. ----The current node texture must contain the animation. ----Use this function to set one-frame still images on the node. ----@param node node node to set animation for ----@param animation string|hash animation id ----@param complete_function? fun(self, node) optional function to call when the animation has completed ---- ----`self` ---- ----object The current object. ---- ----`node` ---- ----node The node that is animated. ---- ---- ----@param play_properties? { offset?:number, playback_rate?:number } optional table with properties ---- ----`offset` ----number The normalized initial value of the animation cursor when the animation starts playing ----`playback_rate` ----number The rate with which the animation will be played. Must be positive ---- -function gui.play_flipbook(node, animation, complete_function, play_properties) end - ----Plays the paricle fx for a gui node ----@param node node node to play particle fx for ----@param emitter_state_function? fun(self, node, emitter, state) optional callback function that will be called when an emitter attached to this particlefx changes state. ---- ----`self` ----object The current object ----`node` ----hash The particle fx node, or `nil` if the node was deleted ----`emitter` ----hash The id of the emitter ----`state` ----constant the new state of the emitter: ---- ---- ----- `particlefx.EMITTER_STATE_SLEEPING` ----- `particlefx.EMITTER_STATE_PRESPAWN` ----- `particlefx.EMITTER_STATE_SPAWNING` ----- `particlefx.EMITTER_STATE_POSTSPAWN` ---- -function gui.play_particlefx(node, emitter_state_function) end - ----Resets the input context of keyboard. This will clear marked text. -function gui.reset_keyboard() end - ----Resets the node material to the material assigned in the gui scene. ----@param node node node to reset the material for -function gui.reset_material(node) end - ----Resets all nodes in the current GUI scene to their initial state. ----The reset only applies to static node loaded from the scene. ----Nodes that are created dynamically from script are not affected. -function gui.reset_nodes() end - ----Convert the screen position to the local position of supplied node ----@param node node node used for getting local transformation matrix ----@param screen_position vector3 screen position ----@return vector3 local_position local position -function gui.screen_to_local(node, screen_position) end - ----Instead of using specific setteres such as gui.set_position or gui.set_scale, ----you can use gui.set instead and supply the property as a string or a hash. ----While this function is similar to go.get and go.set, there are a few more restrictions ----when operating in the gui namespace. Most notably, only these named properties identifiers are supported: ----- `"position"` ----- `"rotation"` ----- `"euler"` ----- `"scale"` ----- `"color"` ----- `"outline"` ----- `"shadow"` ----- `"size"` ----- `"fill_angle"` (pie) ----- `"inner_radius"` (pie) ----- `"leading"` (text) ----- `"tracking"` (text) ----- `"slice9"` (slice9) ----The value to set must either be a vmath.vector4, vmath.vector3, vmath.quat or a single number and depends on the property name you want to set. ----I.e when setting the "position" property, you need to use a vmath.vector4 and when setting a single component of the property, ----such as "position.x", you need to use a single value. ----Note: When setting the rotation using the "rotation" property, you need to pass in a vmath.quat. This behaviour is different than from the gui.set_rotation function, ----the intention is to move new functionality closer to go namespace so that migrating between gui and go is easier. To set the rotation using degrees instead, ----use the "euler" property instead. The rotation and euler properties are linked, changing one of them will change the backing data of the other. ----Similar to go.set, you can also use gui.set for setting material constant values on a node. E.g if a material has specified a constant called `tint` in ----the .material file, you can use gui.set to set the value of that constant by calling `gui.set(node, "tint", vmath.vec4(1,0,0,1))`, or `gui.set(node, "matrix", vmath.matrix4())` ----if the constant is a matrix. Arrays are also supported by gui.set - to set an array constant, you need to pass in an options table with the 'index' key set. ----If the material has a constant array called 'tint_array' specified in the material, you can use `gui.set(node, "tint_array", vmath.vec4(1,0,0,1), { index = 4})` to set the fourth array element to a different value. ----@param node node|url node to set the property for, or msg.url() to the gui itself ----@param property string|hash|constant the property to set ----@param value number|vector4|vector3|quaternion the property to set ----@param options? table optional options table (only applicable for material constants) ----- `index` number index into array property (1 based) ----- `key` hash name of internal property -function gui.set(node, property, value, options) end - ----Sets the adjust mode on a node. ----The adjust mode defines how the node will adjust itself to screen ----resolutions that differs from the one in the project settings. ----@param node node node to set adjust mode for ----@param adjust_mode constant adjust mode to set ---- ----- `gui.ADJUST_FIT` ----- `gui.ADJUST_ZOOM` ----- `gui.ADJUST_STRETCH` ---- -function gui.set_adjust_mode(node, adjust_mode) end - ----sets the node alpha ----@param node node node for which to set alpha ----@param alpha number 0..1 alpha color -function gui.set_alpha(node, alpha) end - ----Set the blend mode of a node. ----Blend mode defines how the node will be blended with the background. ----@param node node node to set blend mode for ----@param blend_mode constant blend mode to set ---- ----- `gui.BLEND_ALPHA` ----- `gui.BLEND_ADD` ----- `gui.BLEND_ADD_ALPHA` ----- `gui.BLEND_MULT` ----- `gui.BLEND_SCREEN` ---- -function gui.set_blend_mode(node, blend_mode) end - ----If node is set as an inverted clipping node, it will clip anything inside as opposed to outside. ----@param node node node to set clipping inverted state for ----@param inverted boolean `true` or `false` -function gui.set_clipping_inverted(node, inverted) end - ----Clipping mode defines how the node will clip it's children nodes ----@param node node node to set clipping mode for ----@param clipping_mode constant clipping mode to set ---- ---- - `gui.CLIPPING_MODE_NONE` ---- - `gui.CLIPPING_MODE_STENCIL` ---- -function gui.set_clipping_mode(node, clipping_mode) end - ----If node is set as an visible clipping node, it will be shown as well as clipping. Otherwise, it will only clip but not show visually. ----@param node node node to set clipping visibility for ----@param visible boolean `true` or `false` -function gui.set_clipping_visible(node, visible) end - ----Sets the color of the supplied node. The components ----of the supplied vector3 or vector4 should contain the color channel values: ----Component ----Color value ----x ----Red value ----y ----Green value ----z ----Blue value ----w vector4 ----Alpha value ----@param node node node to set the color for ----@param color vector3|vector4 new color -function gui.set_color(node, color) end - ----Sets a node to the disabled or enabled state. ----Disabled nodes are not rendered and animations acting on them are not evaluated. ----@param node node node to be enabled/disabled ----@param enabled boolean whether the node should be enabled or not -function gui.set_enabled(node, enabled) end - ----Sets the rotation of the supplied node. ----The rotation is expressed in degree Euler angles. ----@param node node node to set the rotation for ----@param rotation vector3|vector4 new rotation -function gui.set_euler(node, rotation) end - ----Set the sector angle of a pie node. ----@param node node node to set the fill angle for ----@param angle number sector angle -function gui.set_fill_angle(node, angle) end - ----This is only useful nodes with flipbook animations. The cursor is normalized. ----@param node node node to set the cursor for ----@param cursor number cursor value -function gui.set_flipbook_cursor(node, cursor) end - ----This is only useful nodes with flipbook animations. Sets the playback rate of the flipbook animation on a node. Must be positive. ----@param node node node to set the cursor for ----@param playback_rate number playback rate -function gui.set_flipbook_playback_rate(node, playback_rate) end - ----This is only useful for text nodes. ----The font must be mapped to the gui scene in the gui editor. ----@param node node node for which to set the font ----@param font string|hash font id -function gui.set_font(node, font) end - ----Set the id of the specicied node to a new value. ----Nodes created with the gui.new_*_node() functions get ----an empty id. This function allows you to give dynamically ----created nodes an id. ---- No checking is done on the uniqueness of supplied ids. ----It is up to you to make sure you use unique ids. ----@param node node node to set the id for ----@param id string|hash id to set -function gui.set_id(node, id) end - ----sets the node inherit alpha state ----@param node node node from which to set the inherit alpha state ----@param inherit_alpha boolean `true` or `false` -function gui.set_inherit_alpha(node, inherit_alpha) end - ----Sets the inner radius of a pie node. ----The radius is defined along the x-axis. ----@param node node node to set the inner radius for ----@param radius number inner radius -function gui.set_inner_radius(node, radius) end - ----The layer must be mapped to the gui scene in the gui editor. ----@param node node node for which to set the layer ----@param layer string|hash layer id -function gui.set_layer(node, layer) end - ----Applies a named layout on the GUI scene. This re-applies per-layout node descriptors ----and, if a matching Display Profile exists, updates the scene resolution. Emits ----the "layout_changed" message to the scene script when the layout actually changes. ----@param layout string|hash the layout id to apply ----@return boolean true if the layout exists in the scene and was applied, false otherwise -function gui.set_layout(layout) end - ----Sets the leading value for a text node. This value is used to ----scale the line spacing of text. ----@param node node node for which to set the leading ----@param leading number a scaling value for the line spacing (default=1) -function gui.set_leading(node, leading) end - ----Sets the line-break mode on a text node. ----This is only useful for text nodes. ----@param node node node to set line-break for ----@param line_break boolean `true` or `false` -function gui.set_line_break(node, line_break) end - ----Set the material on a node. The material must be mapped to the gui scene in the gui editor, ----and assigning a material is supported for all node types. To set the default material that ----is assigned to the gui scene node, use `gui.reset_material(node_id)` instead. ----@param node node node to set material for ----@param material string|hash material id -function gui.set_material(node, material) end - ----Sets the outer bounds mode for a pie node. ----@param node node node for which to set the outer bounds mode ----@param bounds_mode constant the outer bounds mode of the pie node: ---- ----- `gui.PIEBOUNDS_RECTANGLE` ----- `gui.PIEBOUNDS_ELLIPSE` ---- -function gui.set_outer_bounds(node, bounds_mode) end - ----Sets the outline color of the supplied node. ----See gui.set_color for info how vectors encode color values. ----@param node node node to set the outline color for ----@param color vector3|vector4 new outline color -function gui.set_outline(node, color) end - ----Sets the parent node of the specified node. ----@param node node node for which to set its parent ----@param parent? node parent node to set, pass `nil` to remove parent ----@param keep_scene_transform? boolean optional flag to make the scene position being perserved -function gui.set_parent(node, parent, keep_scene_transform) end - ----Set the paricle fx for a gui node ----@param node node node to set particle fx for ----@param particlefx hash|string particle fx id -function gui.set_particlefx(node, particlefx) end - ----Sets the number of generated vertices around the perimeter of a pie node. ----@param node node pie node ----@param vertices number vertex count -function gui.set_perimeter_vertices(node, vertices) end - ----The pivot specifies how the node is drawn and rotated from its position. ----@param node node node to set pivot for ----@param pivot constant pivot constant ---- ---- - `gui.PIVOT_CENTER` ---- - `gui.PIVOT_N` ---- - `gui.PIVOT_NE` ---- - `gui.PIVOT_E` ---- - `gui.PIVOT_SE` ---- - `gui.PIVOT_S` ---- - `gui.PIVOT_SW` ---- - `gui.PIVOT_W` ---- - `gui.PIVOT_NW` ---- -function gui.set_pivot(node, pivot) end - ----Sets the position of the supplied node. ----@param node node node to set the position for ----@param position vector3|vector4 new position -function gui.set_position(node, position) end - ----Set the order number for the current GUI scene. ----The number dictates the sorting of the "gui" render predicate, ----in other words in which order the scene will be rendered in relation ----to other currently rendered GUI scenes. ----The number must be in the range 0 to 15. ----@param order number rendering order (0-15) -function gui.set_render_order(order) end - ----Sets the rotation of the supplied node. ----The rotation is expressed as a quaternion ----@param node node node to set the rotation for ----@param rotation quaternion|vector4 new rotation -function gui.set_rotation(node, rotation) end - ----Sets the scaling of the supplied node. ----@param node node node to set the scale for ----@param scale vector3|vector4 new scale -function gui.set_scale(node, scale) end - ----Set the screen position to the supplied node ----@param node node node to set the screen position to ----@param screen_position vector3 screen position -function gui.set_screen_position(node, screen_position) end - ----Sets the shadow color of the supplied node. ----See gui.set_color for info how vectors encode color values. ----@param node node node to set the shadow color for ----@param color vector3|vector4 new shadow color -function gui.set_shadow(node, color) end - ----Sets the size of the supplied node. ---- You can only set size on nodes with size mode set to SIZE_MODE_MANUAL ----@param node node node to set the size for ----@param size vector3|vector4 new size -function gui.set_size(node, size) end - ----Sets the size mode of a node. ----The size mode defines how the node will adjust itself in size. Automatic ----size mode alters the node size based on the node's content. Automatic size ----mode works for Box nodes and Pie nodes which will both adjust their size ----to match the assigned image. Particle fx and Text nodes will ignore ----any size mode setting. ----@param node node node to set size mode for ----@param size_mode constant size mode to set ---- ----- `gui.SIZE_MODE_MANUAL` ----- `gui.SIZE_MODE_AUTO` ---- -function gui.set_size_mode(node, size_mode) end - ----Set the slice9 configuration values for the node. ----@param node node node to manipulate ----@param values vector4 new values -function gui.set_slice9(node, values) end - ----Set the text value of a text node. This is only useful for text nodes. ----@param node node node to set text for ----@param text string|number text to set -function gui.set_text(node, text) end - ----Set the texture on a box or pie node. The texture must be mapped to ----the gui scene in the gui editor. The function points out which texture ----the node should render from. If the texture is an atlas, further ----information is needed to select which image/animation in the atlas ----to render. In such cases, use `gui.play_flipbook()` in ----addition to this function. ----@param node node node to set texture for ----@param texture string|hash texture id -function gui.set_texture(node, texture) end - ----Set the texture buffer data for a dynamically created texture. ----@param texture string|hash texture id ----@param width number texture width ----@param height number texture height ----@param type string|constant texture type ---- ---- - `"rgb"` - RGB ---- - `"rgba"` - RGBA ---- - `"l"` - LUMINANCE ---- ----@param buffer string texture data ----@param flip boolean flip texture vertically ----@return boolean success setting the data was successful -function gui.set_texture_data(texture, width, height, type, buffer, flip) end - ----Sets the tracking value of a text node. This value is used to ----adjust the vertical spacing of characters in the text. ----@param node node node for which to set the tracking ----@param tracking number a scaling number for the letter spacing (default=0) -function gui.set_tracking(node, tracking) end - ----Set if a node should be visible or not. Only visible nodes are rendered. ----@param node node node to be visible or not ----@param visible boolean whether the node should be visible or not -function gui.set_visible(node, visible) end - ----The x-anchor specifies how the node is moved when the game is run in a different resolution. ----@param node node node to set x-anchor for ----@param anchor constant anchor constant ---- ----- `gui.ANCHOR_NONE` ----- `gui.ANCHOR_LEFT` ----- `gui.ANCHOR_RIGHT` ---- -function gui.set_xanchor(node, anchor) end - ----The y-anchor specifies how the node is moved when the game is run in a different resolution. ----@param node node node to set y-anchor for ----@param anchor constant anchor constant ---- ----- `gui.ANCHOR_NONE` ----- `gui.ANCHOR_TOP` ----- `gui.ANCHOR_BOTTOM` ---- -function gui.set_yanchor(node, anchor) end - ----Shows the on-display touch keyboard. ----The specified type of keyboard is displayed if it is available on ----the device. ----This function is only available on iOS and Android. . ----@param type constant keyboard type ---- ----- `gui.KEYBOARD_TYPE_DEFAULT` ----- `gui.KEYBOARD_TYPE_EMAIL` ----- `gui.KEYBOARD_TYPE_NUMBER_PAD` ----- `gui.KEYBOARD_TYPE_PASSWORD` ---- ----@param autoclose boolean if the keyboard should automatically close when clicking outside -function gui.show_keyboard(type, autoclose) end - ----Stops the particle fx for a gui node ----@param node node node to stop particle fx for ----@param options? { clear?:boolean } options when stopping the particle fx. Supported options: ---- ----- boolean `clear`: instantly clear spawned particles ---- -function gui.stop_particlefx(node, options) end - ----Adjust mode is used when the screen resolution differs from the project settings. ----The fit mode ensures that the entire node is visible in the adjusted gui scene. -gui.ADJUST_FIT = nil - ----Adjust mode is used when the screen resolution differs from the project settings. ----The stretch mode ensures that the node is displayed as is in the adjusted gui scene, which might scale it non-uniformally. -gui.ADJUST_STRETCH = nil - ----Adjust mode is used when the screen resolution differs from the project settings. ----The zoom mode ensures that the node fills its entire area and might make the node exceed it. -gui.ADJUST_ZOOM = nil - ----bottom y-anchor -gui.ANCHOR_BOTTOM = nil - ----left x-anchor -gui.ANCHOR_LEFT = nil - ----no anchor -gui.ANCHOR_NONE = nil - ----right x-anchor -gui.ANCHOR_RIGHT = nil - ----top y-anchor -gui.ANCHOR_TOP = nil - ----additive blending -gui.BLEND_ADD = nil - ----additive alpha blending -gui.BLEND_ADD_ALPHA = nil - ----alpha blending -gui.BLEND_ALPHA = nil - ----multiply blending -gui.BLEND_MULT = nil - ----screen blending -gui.BLEND_SCREEN = nil - ----clipping mode none -gui.CLIPPING_MODE_NONE = nil - ----clipping mode stencil -gui.CLIPPING_MODE_STENCIL = nil - ----in-back -gui.EASING_INBACK = nil - ----in-bounce -gui.EASING_INBOUNCE = nil - ----in-circlic -gui.EASING_INCIRC = nil - ----in-cubic -gui.EASING_INCUBIC = nil - ----in-elastic -gui.EASING_INELASTIC = nil - ----in-exponential -gui.EASING_INEXPO = nil - ----in-out-back -gui.EASING_INOUTBACK = nil - ----in-out-bounce -gui.EASING_INOUTBOUNCE = nil - ----in-out-circlic -gui.EASING_INOUTCIRC = nil - ----in-out-cubic -gui.EASING_INOUTCUBIC = nil - ----in-out-elastic -gui.EASING_INOUTELASTIC = nil - ----in-out-exponential -gui.EASING_INOUTEXPO = nil - ----in-out-quadratic -gui.EASING_INOUTQUAD = nil - ----in-out-quartic -gui.EASING_INOUTQUART = nil - ----in-out-quintic -gui.EASING_INOUTQUINT = nil - ----in-out-sine -gui.EASING_INOUTSINE = nil - ----in-quadratic -gui.EASING_INQUAD = nil - ----in-quartic -gui.EASING_INQUART = nil - ----in-quintic -gui.EASING_INQUINT = nil - ----in-sine -gui.EASING_INSINE = nil - ----linear interpolation -gui.EASING_LINEAR = nil - ----out-back -gui.EASING_OUTBACK = nil - ----out-bounce -gui.EASING_OUTBOUNCE = nil - ----out-circlic -gui.EASING_OUTCIRC = nil - ----out-cubic -gui.EASING_OUTCUBIC = nil - ----out-elastic -gui.EASING_OUTELASTIC = nil - ----out-exponential -gui.EASING_OUTEXPO = nil - ----out-in-back -gui.EASING_OUTINBACK = nil - ----out-in-bounce -gui.EASING_OUTINBOUNCE = nil - ----out-in-circlic -gui.EASING_OUTINCIRC = nil - ----out-in-cubic -gui.EASING_OUTINCUBIC = nil - ----out-in-elastic -gui.EASING_OUTINELASTIC = nil - ----out-in-exponential -gui.EASING_OUTINEXPO = nil - ----out-in-quadratic -gui.EASING_OUTINQUAD = nil - ----out-in-quartic -gui.EASING_OUTINQUART = nil - ----out-in-quintic -gui.EASING_OUTINQUINT = nil - ----out-in-sine -gui.EASING_OUTINSINE = nil - ----out-quadratic -gui.EASING_OUTQUAD = nil - ----out-quartic -gui.EASING_OUTQUART = nil - ----out-quintic -gui.EASING_OUTQUINT = nil - ----out-sine -gui.EASING_OUTSINE = nil - ----default keyboard -gui.KEYBOARD_TYPE_DEFAULT = nil - ----email keyboard -gui.KEYBOARD_TYPE_EMAIL = nil - ----number input keyboard -gui.KEYBOARD_TYPE_NUMBER_PAD = nil - ----password keyboard -gui.KEYBOARD_TYPE_PASSWORD = nil - ----elliptical pie node bounds -gui.PIEBOUNDS_ELLIPSE = nil - ----rectangular pie node bounds -gui.PIEBOUNDS_RECTANGLE = nil - ----center pivot -gui.PIVOT_CENTER = nil - ----east pivot -gui.PIVOT_E = nil - ----north pivot -gui.PIVOT_N = nil - ----north-east pivot -gui.PIVOT_NE = nil - ----north-west pivot -gui.PIVOT_NW = nil - ----south pivot -gui.PIVOT_S = nil - ----south-east pivot -gui.PIVOT_SE = nil - ----south-west pivot -gui.PIVOT_SW = nil - ----west pivot -gui.PIVOT_W = nil - ----loop backward -gui.PLAYBACK_LOOP_BACKWARD = nil - ----loop forward -gui.PLAYBACK_LOOP_FORWARD = nil - ----ping pong loop -gui.PLAYBACK_LOOP_PINGPONG = nil - ----once backward -gui.PLAYBACK_ONCE_BACKWARD = nil - ----once forward -gui.PLAYBACK_ONCE_FORWARD = nil - ----once forward and then backward -gui.PLAYBACK_ONCE_PINGPONG = nil - ----color property -gui.PROP_COLOR = nil - ----euler property -gui.PROP_EULER = nil - ----fill_angle property -gui.PROP_FILL_ANGLE = nil - ----inner_radius property -gui.PROP_INNER_RADIUS = nil - ----leading property -gui.PROP_LEADING = nil - ----outline color property -gui.PROP_OUTLINE = nil - ----position property -gui.PROP_POSITION = nil - ----rotation property -gui.PROP_ROTATION = nil - ----scale property -gui.PROP_SCALE = nil - ----shadow color property -gui.PROP_SHADOW = nil - ----size property -gui.PROP_SIZE = nil - ----slice9 property -gui.PROP_SLICE9 = nil - ----tracking property -gui.PROP_TRACKING = nil - ----The provided data is not in the expected format or is in some other way ----incorrect, for instance the image data provided to gui.new_texture(). -gui.RESULT_DATA_ERROR = nil - ----The system is out of resources, for instance when trying to create a new ----texture using gui.new_texture(). -gui.RESULT_OUT_OF_RESOURCES = nil - ----The texture id already exists when trying to use gui.new_texture(). -gui.RESULT_TEXTURE_ALREADY_EXISTS = nil - ----The size of the node is determined by the currently assigned texture. -gui.SIZE_MODE_AUTO = nil - ----The size of the node is determined by the size set in the editor, the constructor or by gui.set_size() -gui.SIZE_MODE_MANUAL = nil - ----box type -gui.TYPE_BOX = nil - ----custom type -gui.TYPE_CUSTOM = nil - ----particlefx type -gui.TYPE_PARTICLEFX = nil - ----pie type -gui.TYPE_PIE = nil - ----text type -gui.TYPE_TEXT = nil - -return gui \ No newline at end of file diff --git a/resources/defold_api/html5.lua b/resources/defold_api/html5.lua deleted file mode 100644 index a49e814..0000000 --- a/resources/defold_api/html5.lua +++ /dev/null @@ -1,39 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - HTML5 API documentation - - HTML5 platform specific functions. - [icon:html5] The following functions are only available on HTML5 builds, the `html5.*` Lua namespace will not be available on other platforms. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.html5 -html5 = {} - ----Executes the supplied string as JavaScript inside the browser. ----A call to this function is blocking, the result is returned as-is, as a string. ----(Internally this will execute the string using the `eval()` JavaScript function.) ----@param code string Javascript code to run ----@return string result result as string -function html5.run(code) end - ----Set a JavaScript interaction listener callaback from lua that will be ----invoked when a user interacts with the web page by clicking, touching or typing. ----The callback can then call DOM restricted actions like requesting a pointer lock, ----or start playing sounds the first time the callback is invoked. ----@param callback fun(self)|nil The interaction callback. Pass an empty function or `nil` if you no longer wish to receive callbacks. ---- ----`self` ----object The calling script ---- -function html5.set_interaction_listener(callback) end - -return html5 \ No newline at end of file diff --git a/resources/defold_api/http.lua b/resources/defold_api/http.lua deleted file mode 100644 index 154d913..0000000 --- a/resources/defold_api/http.lua +++ /dev/null @@ -1,57 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - HTTP API documentation - - Functions for performing HTTP and HTTPS requests. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.http -http = {} - ----Perform a HTTP/HTTPS request. ---- If no timeout value is passed, the configuration value "network.http_timeout" is used. If that is not set, the timeout value is `0` (which blocks indefinitely). ----@param url string target url ----@param method string HTTP/HTTPS method, e.g. "GET", "PUT", "POST" etc. ----@param callback fun(self, id, response) response callback function ---- ----`self` ----object The script instance ----`id` ----hash Internal message identifier. Do not use! ----`response` ----table The response data. Contains the fields: ---- ---- ----- number `status`: the status of the response ----- string `response`: the response data (if not saved on disc) ----- table `headers`: all the returned headers (if status is 200 or 206) ----- string `path`: the stored path (if saved to disc) ----- string `error`: if any unforeseen errors occurred (e.g. file I/O) ----- number `bytes_received`: the amount of bytes received/sent for a request, only if option `report_progress` is true ----- number `bytes_total`: the total amount of bytes for a request, only if option `report_progress` is true ----- number `range_start`: the start offset into the requested file ----- number `range_end`: the end offset into the requested file (inclusive) ----- number `document_size`: the full size of the requested file ---- ----@param headers? table optional table with custom headers ----@param post_data? string optional data to send ----@param options? table optional table with request parameters. Supported entries: ---- ----- number `timeout`: timeout in seconds ----- string `path`: path on disc where to download the file. Only overwrites the path if status is 200. Path should be absolute ----- boolean `ignore_cache`: don't return cached data if we get a 304. Not available in HTML5 build ----- boolean `chunked_transfer`: use chunked transfer encoding for https requests larger than 16kb. Defaults to true. Not available in HTML5 build ----- boolean `report_progress`: when it is true, the amount of bytes sent and/or received for a request will be passed into the callback function ---- -function http.request(url, method, callback, headers, post_data, options) end - -return http \ No newline at end of file diff --git a/resources/defold_api/image.lua b/resources/defold_api/image.lua deleted file mode 100644 index 03ae481..0000000 --- a/resources/defold_api/image.lua +++ /dev/null @@ -1,80 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Image API documentation - - Functions for creating image objects. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.image -image = {} - ----Load image (PNG or JPEG) from buffer. ----@param buffer string image data buffer ----@param options? table An optional table containing parameters for loading the image. Supported entries: ---- ----`premultiply_alpha` ----boolean True if alpha should be premultiplied into the color components. Defaults to `false`. ----`flip_vertically` ----boolean True if the image contents should be flipped vertically. Defaults to `false`. ---- ----@return { width:number, height:number, type:constant, buffer:string }|nil image object or `nil` if loading fails. The object is a table with the following fields: ---- ----- number `width`: image width ----- number `height`: image height ----- constant `type`: image type ----`image.TYPE_RGB` ----- `image.TYPE_RGBA` ----- `image.TYPE_LUMINANCE` ----- `image.TYPE_LUMINANCE_ALPHA` ---- ---- ----- string `buffer`: the raw image data ---- -function image.load(buffer, options) end - ----Load image (PNG or JPEG) from a string buffer. ----@param buffer string image data buffer ----@param options? table An optional table containing parameters for loading the image. Supported entries: ---- ----`premultiply_alpha` ----boolean True if alpha should be premultiplied into the color components. Defaults to `false`. ----`flip_vertically` ----boolean True if the image contents should be flipped vertically. Defaults to `false`. ---- ----@return { width:number, height:number, type:constant, buffer:buffer_data }|nil image object or `nil` if loading fails. The object is a table with the following fields: ---- ----- number `width`: image width ----- number `height`: image height ----- constant `type`: image type ----`image.TYPE_RGB` ----- `image.TYPE_RGBA` ----- `image.TYPE_LUMINANCE` ----- `image.TYPE_LUMINANCE_ALPHA` ---- ---- ----- buffer `buffer`: the script buffer that holds the decompressed image data. See buffer.create how to use the buffer. ---- -function image.load_buffer(buffer, options) end - ----luminance image type -image.TYPE_LUMINANCE = nil - ----luminance image type -image.TYPE_LUMINANCE_ALPHA = nil - ----RGB image type -image.TYPE_RGB = nil - ----RGBA image type -image.TYPE_RGBA = nil - -return image \ No newline at end of file diff --git a/resources/defold_api/json.lua b/resources/defold_api/json.lua deleted file mode 100644 index 567d307..0000000 --- a/resources/defold_api/json.lua +++ /dev/null @@ -1,43 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - JSON API documentation - - Manipulation of JSON data strings. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.json -json = {} - ----Represents the null primitive from a json file -json.null = nil - ----Decode a string of JSON data into a Lua table. ----A Lua error is raised for syntax errors. ----@param json string json data ----@param options? { decode_null_as_userdata?:boolean } table with decode options ---- ----- boolean `decode_null_as_userdata`: wether to decode a JSON null value as json.null or nil (default is nil) ---- ----@return table data decoded json -function json.decode(json, options) end - ----Encode a lua table to a JSON string. ----A Lua error is raised for syntax errors. ----@param tbl table lua table to encode ----@param options? { encode_empty_table_as_object:string } table with encode options ---- ----- string `encode_empty_table_as_object`: wether to encode an empty table as an JSON object or array (default is object) ---- ----@return string json encoded json -function json.encode(tbl, options) end - -return json \ No newline at end of file diff --git a/resources/defold_api/label.lua b/resources/defold_api/label.lua deleted file mode 100644 index 7926b4f..0000000 --- a/resources/defold_api/label.lua +++ /dev/null @@ -1,32 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Label API documentation - - Functions to manipulate a label component. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.label -label = {} - ----Gets the text from a label component ----@param url string|hash|url the label to get the text from ----@return string metrics the label text -function label.get_text(url) end - ----Sets the text of a label component ---- This method uses the message passing that means the value will be set after `dispatch messages` step. ----More information is available in the Application Lifecycle manual. ----@param url string|hash|url the label that should have a constant set ----@param text string|number the text -function label.set_text(url, text) end - -return label \ No newline at end of file diff --git a/resources/defold_api/liveupdate.lua b/resources/defold_api/liveupdate.lua deleted file mode 100644 index 161d75d..0000000 --- a/resources/defold_api/liveupdate.lua +++ /dev/null @@ -1,157 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - LiveUpdate API documentation - - Functions and constants to access resources. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.liveupdate -liveupdate = {} - ----Adds a resource mount to the resource system. ----The mounts are persisted between sessions. ----After the mount succeeded, the resources are available to load. (i.e. no reboot required) ----@param name string Unique name of the mount ----@param uri string The uri of the mount, including the scheme. Currently supported schemes are 'zip' and 'archive'. ----@param priority number Priority of mount. Larger priority takes prescedence ----@param callback function Callback after the asynchronous request completed ----@return number result The result of the request -function liveupdate.add_mount(name, uri, priority, callback) end - ----Return a reference to the Manifest that is currently loaded. ----@return number manifest_reference reference to the Manifest that is currently loaded -function liveupdate.get_current_manifest() end - ----Get an array of the current mounts ----This can be used to determine if a new mount is needed or not ----@return table mounts Array of mounts -function liveupdate.get_mounts() end - ----Is any liveupdate data mounted and currently in use? ----This can be used to determine if a new manifest or zip file should be downloaded. ----@return boolean bool true if a liveupdate archive (any format) has been loaded -function liveupdate.is_using_liveupdate_data() end - ----Remove a mount the resource system. ----The remaining mounts are persisted between sessions. ----Removing a mount does not affect any loaded resources. ----@param name string Unique name of the mount ----@return number result The result of the call -function liveupdate.remove_mount(name) end - ----Stores a zip file and uses it for live update content. The contents of the ----zip file will be verified against the manifest to ensure file integrity. ----It is possible to opt out of the resource verification using an option passed ----to this function. ----The path is stored in the (internal) live update location. ----@param path string the path to the original file on disc ----@param callback fun(self, status) the callback function ----executed after the storage has completed ---- ----`self` ----object The current object. ----`status` ----constant the status of the store operation (See liveupdate.store_manifest) ---- ----@param options? table optional table with extra parameters. Supported entries: ---- ----- boolean `verify`: if archive should be verified as well as stored (defaults to true) ---- -function liveupdate.store_archive(path, callback, options) end - ----Create a new manifest from a buffer. The created manifest is verified ----by ensuring that the manifest was signed using the bundled public/private ----key-pair during the bundle process and that the manifest supports the current ----running engine version. Once the manifest is verified it is stored on device. ----The next time the engine starts (or is rebooted) it will look for the stored ----manifest before loading resources. Storing a new manifest allows the ----developer to update the game, modify existing resources, or add new ----resources to the game through LiveUpdate. ----@param manifest_buffer string the binary data that represents the manifest ----@param callback fun(self, status) the callback function ----executed once the engine has attempted to store the manifest. ---- ----`self` ----object The current object. ----`status` ----constant the status of the store operation: ---- ---- ----- `liveupdate.LIVEUPDATE_OK` ----- `liveupdate.LIVEUPDATE_INVALID_RESOURCE` ----- `liveupdate.LIVEUPDATE_VERSION_MISMATCH` ----- `liveupdate.LIVEUPDATE_ENGINE_VERSION_MISMATCH` ----- `liveupdate.LIVEUPDATE_SIGNATURE_MISMATCH` ----- `liveupdate.LIVEUPDATE_BUNDLED_RESOURCE_MISMATCH` ----- `liveupdate.LIVEUPDATE_FORMAT_ERROR` ---- -function liveupdate.store_manifest(manifest_buffer, callback) end - ----add a resource to the data archive and runtime index. The resource will be verified ----internally before being added to the data archive. ----@param manifest_reference number The manifest to check against. ----@param data string The resource data that should be stored. ----@param hexdigest string The expected hash for the resource, ----retrieved through collectionproxy.missing_resources. ----@param callback fun(self, hexdigest, status) The callback ----function that is executed once the engine has been attempted to store ----the resource. ---- ----`self` ----object The current object. ----`hexdigest` ----string The hexdigest of the resource. ----`status` ----boolean Whether or not the resource was successfully stored. ---- -function liveupdate.store_resource(manifest_reference, data, hexdigest, callback) end - ----Mismatch between between expected bundled resources and actual bundled resources. The manifest expects a resource to be in the bundle, but it was not found in the bundle. This is typically the case when a non-excluded resource was modified between publishing the bundle and publishing the manifest. -liveupdate.LIVEUPDATE_BUNDLED_RESOURCE_MISMATCH = nil - ----Mismatch between running engine version and engine versions supported by manifest. -liveupdate.LIVEUPDATE_ENGINE_VERSION_MISMATCH = nil - ----Failed to parse manifest data buffer. The manifest was probably produced by a different engine version. -liveupdate.LIVEUPDATE_FORMAT_ERROR = nil - ----Argument was invalid -liveupdate.LIVEUPDATE_INVAL = nil - ----The handled resource is invalid. -liveupdate.LIVEUPDATE_INVALID_HEADER = nil - ----The header of the resource is invalid. -liveupdate.LIVEUPDATE_INVALID_RESOURCE = nil - ----I/O operation failed -liveupdate.LIVEUPDATE_IO_ERROR = nil - ----Memory wasn't allocated -liveupdate.LIVEUPDATE_MEM_ERROR = nil - ----LIVEUPDATE_OK -liveupdate.LIVEUPDATE_OK = nil - ----Mismatch between scheme used to load resources. Resources are loaded with a different scheme than from manifest, for example over HTTP or directly from file. This is typically the case when running the game directly from the editor instead of from a bundle. -liveupdate.LIVEUPDATE_SCHEME_MISMATCH = nil - ----Mismatch between manifest expected signature and actual signature. -liveupdate.LIVEUPDATE_SIGNATURE_MISMATCH = nil - ----Unspecified error -liveupdate.LIVEUPDATE_UNKNOWN = nil - ----Mismatch between manifest expected version and actual version. -liveupdate.LIVEUPDATE_VERSION_MISMATCH = nil - -return liveupdate \ No newline at end of file diff --git a/resources/defold_api/meta.lua b/resources/defold_api/meta.lua deleted file mode 100644 index 40efd67..0000000 --- a/resources/defold_api/meta.lua +++ /dev/null @@ -1,219 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Known types and aliases used in the Defold API ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class editor.bundle -editor.bundle = {} - ----@class editor.prefs -editor.prefs = {} - ----@class editor.prefs.SCOPE -editor.prefs.SCOPE = {} - ----@class editor.prefs.schema -editor.prefs.schema = {} - ----@class editor.tx -editor.tx = {} - ----@class editor.ui -editor.ui = {} - ----@class editor.ui.ALIGNMENT -editor.ui.ALIGNMENT = {} - ----@class editor.ui.COLOR -editor.ui.COLOR = {} - ----@class editor.ui.HEADING_STYLE -editor.ui.HEADING_STYLE = {} - ----@class editor.ui.ICON -editor.ui.ICON = {} - ----@class editor.ui.ISSUE_SEVERITY -editor.ui.ISSUE_SEVERITY = {} - ----@class editor.ui.ORIENTATION -editor.ui.ORIENTATION = {} - ----@class editor.ui.PADDING -editor.ui.PADDING = {} - ----@class editor.ui.SPACING -editor.ui.SPACING = {} - ----@class editor.ui.TEXT_ALIGNMENT -editor.ui.TEXT_ALIGNMENT = {} - ----@class http.server -http.server = {} - ----@class matrix4 ----@field c0 vector4 ----@field c1 vector4 ----@field c2 vector4 ----@field c3 vector4 ----@field m00 number ----@field m01 number ----@field m02 number ----@field m03 number ----@field m10 number ----@field m11 number ----@field m12 number ----@field m13 number ----@field m20 number ----@field m21 number ----@field m22 number ----@field m23 number ----@field m30 number ----@field m31 number ----@field m32 number ----@field m33 number - ----@class on_input.action ----@field dx? number The change in x value of a pointer device, if present. ----@field dy? number The change in y value of a pointer device, if present. ----@field gamepad? integer The change in screen space y value of a pointer device, if present. ----@field pressed? boolean If the input was pressed this frame. This is not present for mouse movement. ----@field released? boolean If the input was released this frame. This is not present for mouse movement. ----@field repeated? boolean If the input was repeated this frame. This is similar to how a key on a keyboard is repeated when you hold it down. This is not present for mouse movement. ----@field screen_dx? number The change in screen space x value of a pointer device, if present. ----@field screen_dy? number The index of the gamepad device that provided the input. ----@field screen_x? number The screen space x value of a pointer device, if present. ----@field screen_y? number The screen space y value of a pointer device, if present. ----@field text? string The text entered with the `text` action, if present ----@field touch? on_input.touch[] List of touch input, one element per finger, if present. ----@field value? number The amount of input given by the user. This is usually 1 for buttons and 0-1 for analogue inputs. This is not present for mouse movement. ----@field x? number The x value of a pointer device, if present. ----@field y? number The y value of a pointer device, if present. - ----@class on_input.touch ----@field acc_x? number Accelerometer x value (if present). ----@field acc_y? number Accelerometer y value (if present). ----@field acc_z? number Accelerometer z value (if present). ----@field dx number The change in x value. ----@field dy number The change in y value. ----@field id number A number identifying the touch input during its duration. ----@field pressed boolean True if the finger was pressed this frame. ----@field released boolean True if the finger was released this frame. ----@field screen_dx? number The change in screen space x value of a pointer device, if present. ----@field screen_dy? number The index of the gamepad device that provided the input. ----@field screen_x? number The screen space x value of a pointer device, if present. ----@field screen_y? number The screen space y value of a pointer device, if present. ----@field tap_count integer Number of taps, one for single, two for double-tap, etc ----@field x number The x touch location. ----@field y number The y touch location. - ----@class physics.raycast_response ----@field fraction number The fraction of the hit measured along the ray, where 0 is the start of the ray and 1 is the end ----@field group hash The collision group of the hit collision object as a hashed name ----@field id hash The instance id of the hit collision object ----@field normal vector3 The normal of the surface of the collision object where it was hit ----@field position vector3 The world position of the hit ----@field request_id number The id supplied when the ray cast was requested - ----@class resource.animation ----@field flip_horizontal? boolean Optional flip the animation horizontally, the default value is false ----@field flip_vertical? boolean Optional flip the animation vertically, the default value is false ----@field fps? integer Optional fps of the animation, the default value is 30 ----@field frame_end integer Index to the last geometry of the animation (non-inclusive). Indices are lua based and must be in the range of 1 .. in atlas. ----@field frame_start integer Index to the first geometry of the animation. Indices are lua based and must be in the range of 1 .. in atlas. ----@field height integer The height of the animation ----@field id string The id of the animation, used in e.g sprite.play_animation ----@field playback? constant Optional playback mode of the animation, the default value is go.PLAYBACK_ONCE_FORWARD ----@field width integer The width of the animation - ----@class resource.atlas ----@field animations resource.animation[] A list of the animations in the atlas ----@field geometries resource.geometry[] A list of the geometries that should map to the texture data ----@field texture string|hash The path to the texture resource, e.g "/main/my_texture.texturec" - ----@class resource.geometry ----@field height number The height of the image the sprite geometry represents ----@field id string The name of the geometry. Used when matching animations between multiple atlases ----@field indices number[] A list of the indices of the geometry in the form { i0, i1, i2, ..., in }. Each tripe in the list represents a triangle. ----@field pivot_x number The pivot x value of the image in unit coords. (0,0) is upper left corner, (1,1) is bottom right. Default is 0.5. ----@field pivot_y number The pivot y value of the image in unit coords. (0,0) is upper left corner, (1,1) is bottom right. Default is 0.5. ----@field rotated boolean Whether the image is rotated 90 degrees counter-clockwise in the atlas. This affects UV coordinate generation for proper rendering. Default is false. ----@field uvs number[] A list of the uv coordinates in texture space of the geometry in the form of { u0, v0, u1, v1, ..., un, vn } ----@field vertices number[] A list of the vertices in texture space of the geometry in the form { px0, py0, px1, py1, ..., pxn, pyn } ----@field width number The width of the image the sprite geometry represents - ----@class socket.dns -socket.dns = {} - ----@class tilemap.tiles -tilemap.tiles = {} - ----@class url ----@field fragment hash ----@field path hash ----@field socket hash - ----@class vector3 ----@field x number ----@field y number ----@field z number ----@operator add(vector3): vector3 ----@operator mul(number): vector3 ----@operator sub(vector3): vector3 ----@operator unm: vector3 - ----@class vector4 ----@field w number ----@field x number ----@field y number ----@field z number ----@operator add(vector4): vector4 ----@operator mul(number): vector4 ----@operator sub(vector4): vector4 ----@operator unm: vector4 - ----@class zip -zip = {} - ----@class zip.METHOD -zip.METHOD = {} - ----@class zip.ON_CONFLICT -zip.ON_CONFLICT = {} - ----@alias array table ----@alias b2Body userdata ----@alias b2BodyType number ----@alias b2World userdata ----@alias bool boolean ----@alias buffer_data userdata ----@alias buffer_stream userdata ----@alias constant number ----@alias constant_buffer userdata ----@alias editor.command userdata ----@alias editor.component userdata ----@alias editor.schema userdata ----@alias editor.tiles userdata ----@alias editor.transaction_step userdata ----@alias float number ----@alias hash userdata ----@alias http.response userdata ----@alias http.route userdata ----@alias node userdata ----@alias quaternion vector4 ----@alias render_predicate userdata ----@alias render_target string|userdata ----@alias resource_data userdata ----@alias socket_client userdata ----@alias socket_master userdata ----@alias socket_unconnected userdata ----@alias vector userdata \ No newline at end of file diff --git a/resources/defold_api/model.lua b/resources/defold_api/model.lua deleted file mode 100644 index 4cc7d1c..0000000 --- a/resources/defold_api/model.lua +++ /dev/null @@ -1,108 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Model API documentation - - Functions and messages for interacting with model components. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.model -model = {} - ----Cancels all animation on a model component. ----@param url string|hash|url the model for which to cancel the animation -function model.cancel(url) end - ----Get AABB of the whole model in local coordinate space. ----AABB information return as a table with `min` and `max` fields, where `min` and `max` has type `vmath.vector3`. ----@param url string|hash|url the model ----@return table aabb A table containing AABB of the model. If model has no meshes - return vmath.vector3(0,0,0) for min and max fields. -function model.get_aabb(url) end - ----Gets the id of the game object that corresponds to a model skeleton bone. ----The returned game object can be used for parenting and transform queries. ----This function has complexity `O(n)`, where `n` is the number of bones in the model skeleton. ----Game objects corresponding to a model skeleton bone can not be individually deleted. ----@param url string|hash|url the model to query ----@param bone_id string|hash id of the corresponding bone ----@return hash id id of the game object -function model.get_go(url, bone_id) end - ----Get AABB of all meshes. ----AABB information return as a table with `min` and `max` fields, where `min` and `max` has type `vmath.vector3`. ----@param url string|hash|url the model ----@return table aabb A table containing info about all AABB in the format -function model.get_mesh_aabb(url) end - ----Get the enabled state of a mesh ----@param url string|hash|url the model ----@param mesh_id string|hash|url the id of the mesh ----@return boolean enabled true if the mesh is visible, false otherwise -function model.get_mesh_enabled(url, mesh_id) end - ----Plays an animation on a model component with specified playback ----mode and parameters. ----An optional completion callback function can be provided that will be called when ----the animation has completed playing. If no function is provided, ----a model_animation_done message is sent to the script that started the animation. ---- The callback is not called (or message sent) if the animation is ----cancelled with model.cancel. The callback is called (or message sent) only for ----animations that play with the following playback modes: ----- `go.PLAYBACK_ONCE_FORWARD` ----- `go.PLAYBACK_ONCE_BACKWARD` ----- `go.PLAYBACK_ONCE_PINGPONG` ----@param url string|hash|url the model for which to play the animation ----@param anim_id string|hash id of the animation to play ----@param playback constant playback mode of the animation ---- ----- `go.PLAYBACK_ONCE_FORWARD` ----- `go.PLAYBACK_ONCE_BACKWARD` ----- `go.PLAYBACK_ONCE_PINGPONG` ----- `go.PLAYBACK_LOOP_FORWARD` ----- `go.PLAYBACK_LOOP_BACKWARD` ----- `go.PLAYBACK_LOOP_PINGPONG` ---- ----@param play_properties? { blend_duration?:number, offset?:number, playback_rate?:number} optional table with properties ----Play properties table: ---- ----`blend_duration` ----number Duration of a linear blend between the current and new animation. ----`offset` ----number The normalized initial value of the animation cursor when the animation starts playing. ----`playback_rate` ----number The rate with which the animation will be played. Must be positive. ---- ----@param complete_function? fun(self, message_id, message, sender) function to call when the animation has completed. ---- ----`self` ----object The current object. ----`message_id` ----hash The name of the completion message, `"model_animation_done"`. ----`message` ----table Information about the completion: ---- ---- ----- hash `animation_id` - the animation that was completed. ----- constant `playback` - the playback mode for the animation. ---- ---- ----`sender` ----url The invoker of the callback: the model component. ---- -function model.play_anim(url, anim_id, playback, play_properties, complete_function) end - ----Enable or disable visibility of a mesh ----@param url string|hash|url the model ----@param mesh_id string|hash|url the id of the mesh ----@param enabled boolean true if the mesh should be visible, false if it should be hideen -function model.set_mesh_enabled(url, mesh_id, enabled) end - -return model \ No newline at end of file diff --git a/resources/defold_api/msg.lua b/resources/defold_api/msg.lua deleted file mode 100644 index 6936719..0000000 --- a/resources/defold_api/msg.lua +++ /dev/null @@ -1,56 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Messaging API documentation - - Functions for passing messages and constructing URL objects. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.msg -msg = {} - ----Post a message to a receiving URL. The most common case is to send messages ----to a component. If the component part of the receiver is omitted, the message ----is broadcast to all components in the game object. ----The following receiver shorthands are available: ----- `"."` the current game object ----- `"#"` the current component ---- There is a 2 kilobyte limit to the message parameter table size. ----@param receiver string|url|hash The receiver must be a string in URL-format, a URL object or a hashed string. ----@param message_id string|hash The id must be a string or a hashed string. ----@param message? table|nil a lua table with message parameters to send. -function msg.post(receiver, message_id, message) end - ----creates a new URL from separate arguments ----@param socket? string|hash socket of the URL ----@param path? string|hash path of the URL ----@param fragment? string|hash fragment of the URL ----@return url url a new URL -function msg.url(socket, path, fragment) end - ----The format of the string must be `[socket:][path][#fragment]`, which is similar to a HTTP URL. ----When addressing instances: ----- `socket` is the name of a valid world (a collection) ----- `path` is the id of the instance, which can either be relative the instance of the calling script or global ----- `fragment` would be the id of the desired component ----In addition, the following shorthands are available: ----- `"."` the current game object ----- `"#"` the current component ----@param urlstring string string to create the url from ----@return url url a new URL -function msg.url(urlstring) end - ----This is equivalent to `msg.url(nil)` or `msg.url("#")`, which creates an url to the current ----script component. ----@return url url a new URL -function msg.url() end - -return msg \ No newline at end of file diff --git a/resources/defold_api/particlefx.lua b/resources/defold_api/particlefx.lua deleted file mode 100644 index 314d2e1..0000000 --- a/resources/defold_api/particlefx.lua +++ /dev/null @@ -1,87 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Particle effects API documentation - - Functions for controlling particle effect component playback and - shader constants. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.particlefx -particlefx = {} - ----Starts playing a particle FX component. ----Particle FX started this way need to be manually stopped through `particlefx.stop()`. ----Which particle FX to play is identified by the URL. ---- A particle FX will continue to emit particles even if the game object the particle FX component belonged to is deleted. You can call `particlefx.stop()` to stop it from emitting more particles. ----@param url string|hash|url the particle fx that should start playing. ----@param emitter_state_function? fun(self, id, emitter, state) optional callback function that will be called when an emitter attached to this particlefx changes state. ---- ----`self` ----object The current object ----`id` ----hash The id of the particle fx component ----`emitter` ----hash The id of the emitter ----`state` ----constant the new state of the emitter: ---- ---- ----- `particlefx.EMITTER_STATE_SLEEPING` ----- `particlefx.EMITTER_STATE_PRESPAWN` ----- `particlefx.EMITTER_STATE_SPAWNING` ----- `particlefx.EMITTER_STATE_POSTSPAWN` ---- -function particlefx.play(url, emitter_state_function) end - ----Resets a shader constant for a particle FX component emitter. ----The constant must be defined in the material assigned to the emitter. ----Resetting a constant through this function implies that the value defined in the material will be used. ----Which particle FX to reset a constant for is identified by the URL. ----@param url string|hash|url the particle FX that should have a constant reset ----@param emitter string|hash the id of the emitter ----@param constant string|hash the name of the constant -function particlefx.reset_constant(url, emitter, constant) end - ----Sets a shader constant for a particle FX component emitter. ----The constant must be defined in the material assigned to the emitter. ----Setting a constant through this function will override the value set for that constant in the material. ----The value will be overridden until particlefx.reset_constant is called. ----Which particle FX to set a constant for is identified by the URL. ----@param url string|hash|url the particle FX that should have a constant set ----@param emitter string|hash the id of the emitter ----@param constant string|hash the name of the constant ----@param value vector4 the value of the constant -function particlefx.set_constant(url, emitter, constant, value) end - ----Stops a particle FX component from playing. ----Stopping a particle FX does not remove already spawned particles. ----Which particle FX to stop is identified by the URL. ----@param url string|hash|url the particle fx that should stop playing ----@param options? { clear?:boolean } Options when stopping the particle fx. Supported options: ---- ----- boolean `clear`: instantly clear spawned particles ---- -function particlefx.stop(url, options) end - ----The emitter is not spawning any particles, but has particles that are still alive. -particlefx.EMITTER_STATE_POSTSPAWN = nil - ----The emitter will be in this state when it has been started but before spawning any particles. Normally the emitter is in this state for a short time, depending on if a start delay has been set for this emitter or not. -particlefx.EMITTER_STATE_PRESPAWN = nil - ----The emitter does not have any living particles and will not spawn any particles in this state. -particlefx.EMITTER_STATE_SLEEPING = nil - ----The emitter is spawning particles. -particlefx.EMITTER_STATE_SPAWNING = nil - -return particlefx \ No newline at end of file diff --git a/resources/defold_api/physics.lua b/resources/defold_api/physics.lua deleted file mode 100644 index f899b40..0000000 --- a/resources/defold_api/physics.lua +++ /dev/null @@ -1,315 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Collision object physics API documentation - - Functions and messages for collision object physics interaction - with other objects (collisions and ray-casting) and control of - physical behaviors. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.physics -physics = {} - ----Create a physics joint between two collision object components. ----Note: Currently only supported in 2D physics. ----@param joint_type number the joint type ----@param collisionobject_a string|hash|url first collision object ----@param joint_id string|hash id of the joint ----@param position_a vector3 local position where to attach the joint on the first collision object ----@param collisionobject_b string|hash|url second collision object ----@param position_b vector3 local position where to attach the joint on the second collision object ----@param properties? table optional joint specific properties table ----See each joint type for possible properties field. The one field that is accepted for all joint types is: ----- boolean `collide_connected`: Set this flag to true if the attached bodies should collide. -function physics.create_joint(joint_type, collisionobject_a, joint_id, position_a, collisionobject_b, position_b, properties) end - ----Destroy an already physics joint. The joint has to be created before a ----destroy can be issued. ----Note: Currently only supported in 2D physics. ----@param collisionobject string|hash|url collision object where the joint exist ----@param joint_id string|hash id of the joint -function physics.destroy_joint(collisionobject, joint_id) end - ----Get the gravity in runtime. The gravity returned is not global, it will return ----the gravity for the collection that the function is called from. ----Note: For 2D physics the z component will always be zero. ----@return vector3 gravity gravity vector of collection -function physics.get_gravity() end - ----Returns the group name of a collision object as a hash. ----@param url string|hash|url the collision object to return the group of. ----@return hash group hash value of the group. ----`local function check_is_enemy() ---- local group = physics.get_group("#collisionobject") ---- return group == hash("enemy") ----end ----` -function physics.get_group(url) end - ----Get a table for properties for a connected joint. The joint has to be created before ----properties can be retrieved. ----Note: Currently only supported in 2D physics. ----@param collisionobject string|hash|url collision object where the joint exist ----@param joint_id string|hash id of the joint ----@return { collide_connected?:boolean } properties properties table. See the joint types for what fields are available, the only field available for all types is: ---- ----- boolean `collide_connected`: Set this flag to true if the attached bodies should collide. ---- -function physics.get_joint_properties(collisionobject, joint_id) end - ----Get the reaction force for a joint. The joint has to be created before ----the reaction force can be calculated. ----Note: Currently only supported in 2D physics. ----@param collisionobject string|hash|url collision object where the joint exist ----@param joint_id string|hash id of the joint ----@return vector3 force reaction force for the joint -function physics.get_joint_reaction_force(collisionobject, joint_id) end - ----Get the reaction torque for a joint. The joint has to be created before ----the reaction torque can be calculated. ----Note: Currently only supported in 2D physics. ----@param collisionobject string|hash|url collision object where the joint exist ----@param joint_id string|hash id of the joint ----@return number torque the reaction torque on bodyB in N*m. -function physics.get_joint_reaction_torque(collisionobject, joint_id) end - ----Returns true if the specified group is set in the mask of a collision ----object, false otherwise. ----@param url string|hash|url the collision object to check the mask of. ----@param group string the name of the group to check for. ----@return boolean maskbit boolean value of the maskbit. 'true' if present, 'false' otherwise. ----`local function is_invincible() ---- -- check if the collisionobject would collide with the "bullet" group ---- local invincible = physics.get_maskbit("#collisionobject", "bullet") ---- return invincible ----end ----` -function physics.get_maskbit(url, group) end - ----Gets collision shape data from a collision object ----@param url string|hash|url the collision object. ----@param shape string|hash the name of the shape to get data for. ----@return { type?:number, diameter?:number, dimensions?:vector3, height?:number } table A table containing meta data about the physics shape ---- ----`type` ----number The shape type. Supported values: ---- ---- ----- `physics.SHAPE_TYPE_SPHERE` ----- `physics.SHAPE_TYPE_BOX` ----- `physics.SHAPE_TYPE_CAPSULE` *Only supported for 3D physics* ----- `physics.SHAPE_TYPE_HULL` ---- ----The returned table contains different fields depending on which type the shape is. ----If the shape is a sphere: ---- ----`diameter` ----number the diameter of the sphere shape ---- ----If the shape is a box: ---- ----`dimensions` ----vector3 a `vmath.vector3` of the box dimensions ---- ----If the shape is a capsule: ---- ----`diameter` ----number the diameter of the capsule poles ----`height` ----number the height of the capsule ---- ----`local function get_shape_meta() ---- local sphere = physics.get_shape("#collisionobject", "my_sphere_shape") ---- -- returns a table with sphere.diameter ---- return sphere ----end ----` -function physics.get_shape(url, shape) end - ----Ray casts are used to test for intersections against collision objects in the physics world. ----Collision objects of types kinematic, dynamic and static are tested against. Trigger objects ----do not intersect with ray casts. ----Which collision objects to hit is filtered by their collision groups and can be configured ----through `groups`. ----NOTE: Ray casts will ignore collision objects that contain the starting point of the ray. This is a limitation in Box2D. ----@param from vector3 the world position of the start of the ray ----@param to vector3 the world position of the end of the ray ----@param groups table a lua table containing the hashed groups for which to test collisions against ----@param options? { all?:boolean } a lua table containing options for the raycast. ---- ----`all` ----boolean Set to `true` to return all ray cast hits. If `false`, it will only return the closest hit. ---- ----@return physics.raycast_response[]|physics.raycast_response|nil result It returns a list. If missed it returns `nil`. See ray_cast_response for details on the returned values. -function physics.raycast(from, to, groups, options) end - ----Ray casts are used to test for intersections against collision objects in the physics world. ----Collision objects of types kinematic, dynamic and static are tested against. Trigger objects ----do not intersect with ray casts. ----Which collision objects to hit is filtered by their collision groups and can be configured ----through `groups`. ----The actual ray cast will be performed during the physics-update. ----- If an object is hit, the result will be reported via a ray_cast_response message. ----- If there is no object hit, the result will be reported via a ray_cast_missed message. ----NOTE: Ray casts will ignore collision objects that contain the starting point of the ray. This is a limitation in Box2D. ----@param from vector3 the world position of the start of the ray ----@param to vector3 the world position of the end of the ray ----@param groups table a lua table containing the hashed groups for which to test collisions against ----@param request_id? number a number in range [0,255]. It will be sent back in the response for identification, 0 by default -function physics.raycast_async(from, to, groups, request_id) end - ----sets a physics world event listener. If a function is set, physics messages will no longer be sent to on_message. ----@param callback fun(self, events)|nil A callback that receives an information about all the physics interactions in this physics world. ---- ----`self` ----object The calling script ----`event` ----constant The type of event. Can be one of these messages: ---- ---- ----- contact_point_event ----- collision_event ----- trigger_event ----- ray_cast_response ----- ray_cast_missed ---- ---- ----`data` ----table The callback value data is a table that contains event-related data. See the documentation for details on the messages. ---- -function physics.set_event_listener(callback) end - ----Set the gravity in runtime. The gravity change is not global, it will only affect ----the collection that the function is called from. ----Note: For 2D physics the z component of the gravity vector will be ignored. ----@param gravity vector3 the new gravity vector -function physics.set_gravity(gravity) end - ----Updates the group property of a collision object to the specified ----string value. The group name should exist i.e. have been used in ----a collision object in the editor. ----@param url string|hash|url the collision object affected. ----@param group string the new group name to be assigned. ----`local function change_collision_group() ---- physics.set_group("#collisionobject", "enemy") ----end ----` -function physics.set_group(url, group) end - ----Flips the collision shapes horizontally for a collision object ----@param url string|hash|url the collision object that should flip its shapes ----@param flip boolean `true` if the collision object should flip its shapes, `false` if not -function physics.set_hflip(url, flip) end - ----Updates the properties for an already connected joint. The joint has to be created before ----properties can be changed. ----Note: Currently only supported in 2D physics. ----@param collisionobject string|hash|url collision object where the joint exist ----@param joint_id string|hash id of the joint ----@param properties table joint specific properties table ----Note: The `collide_connected` field cannot be updated/changed after a connection has been made. -function physics.set_joint_properties(collisionobject, joint_id, properties) end - ----Sets or clears the masking of a group (maskbit) in a collision object. ----@param url string|hash|url the collision object to change the mask of. ----@param group string the name of the group (maskbit) to modify in the mask. ----@param maskbit boolean boolean value of the new maskbit. 'true' to enable, 'false' to disable. ----`local function make_invincible() ---- -- no longer collide with the "bullet" group ---- physics.set_maskbit("#collisionobject", "bullet", false) ----end ----` -function physics.set_maskbit(url, group, maskbit) end - ----Sets collision shape data for a collision object. Please note that updating data in 3D ----can be quite costly for box and capsules. Because of the physics engine, the cost ----comes from having to recreate the shape objects when certain shapes needs to be updated. ----@param url string|hash|url the collision object. ----@param shape string|hash the name of the shape to get data for. ----@param table { diameter?:number, dimensions?:vector3, height?:number } the shape data to update the shape with. ----See physics.get_shape for a detailed description of each field in the data table. ----`local function set_shape_data() ---- -- set capsule shape data ---- local data = {} ---- data.type = physics.SHAPE_TYPE_CAPSULE ---- data.diameter = 10 ---- data.height = 20 ---- physics.set_shape("#collisionobject", "my_capsule_shape", data) ---- ---- -- set sphere shape data ---- data = {} ---- data.type = physics.SHAPE_TYPE_SPHERE ---- data.diameter = 10 ---- physics.set_shape("#collisionobject", "my_sphere_shape", data) ---- ---- -- set box shape data ---- data = {} ---- data.type = physics.SHAPE_TYPE_BOX ---- data.dimensions = vmath.vector3(10, 10, 5) ---- physics.set_shape("#collisionobject", "my_box_shape", data) ----end ----` -function physics.set_shape(url, shape, table) end - ----Flips the collision shapes vertically for a collision object ----@param url string|hash|url the collision object that should flip its shapes ----@param flip boolean `true` if the collision object should flip its shapes, `false` if not -function physics.set_vflip(url, flip) end - ----The function recalculates the density of each shape based on the total area of all shapes and the specified mass, then updates the mass of the body accordingly. ----Note: Currently only supported in 2D physics. ----@param collisionobject string|hash|url the collision object whose mass needs to be updated. ----@param mass number the new mass value to set for the collision object. -function physics.update_mass(collisionobject, mass) end - ----Collision objects tend to fall asleep when inactive for a small period of time for ----efficiency reasons. This function wakes them up. ----@param url string|hash|url the collision object to wake. ----`function on_input(self, action_id, action) ---- if action_id == hash("test") and action.pressed then ---- physics.wakeup("#collisionobject") ---- end ----end ----` -function physics.wakeup(url) end - ----The following properties are available when connecting a joint of `JOINT_TYPE_FIXED` type: -physics.JOINT_TYPE_FIXED = nil - ----The following properties are available when connecting a joint of `JOINT_TYPE_HINGE` type: -physics.JOINT_TYPE_HINGE = nil - ----The following properties are available when connecting a joint of `JOINT_TYPE_SLIDER` type: -physics.JOINT_TYPE_SLIDER = nil - ----The following properties are available when connecting a joint of `JOINT_TYPE_SPRING` type: -physics.JOINT_TYPE_SPRING = nil - ----The following properties are available when connecting a joint of `JOINT_TYPE_WELD` type: -physics.JOINT_TYPE_WELD = nil - ----The following properties are available when connecting a joint of `JOINT_TYPE_WHEEL` type: -physics.JOINT_TYPE_WHEEL = nil - ---- -physics.SHAPE_TYPE_BOX = nil - ---- -physics.SHAPE_TYPE_CAPSULE = nil - ---- -physics.SHAPE_TYPE_HULL = nil - ---- -physics.SHAPE_TYPE_SPHERE = nil - -return physics \ No newline at end of file diff --git a/resources/defold_api/profiler.lua b/resources/defold_api/profiler.lua deleted file mode 100644 index cb421e8..0000000 --- a/resources/defold_api/profiler.lua +++ /dev/null @@ -1,136 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Profiler API documentation - - Functions for getting profiling data in runtime. - More detailed [profiling](https://www.defold.com/manuals/profiling/) and [debugging](http://www.defold.com/manuals/debugging/) information available in the manuals. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.profiler -profiler = {} - ----logs the current frame to the console -function profiler.dump_frame() end - ----The profiler is a real-time tool that shows the numbers of milliseconds spent ----in each scope per frame as well as counters. The profiler is very useful for ----tracking down performance and resource problems. ----@param enabled boolean true to enable, false to disable -function profiler.enable(enabled) end - ----Creates and shows or hides and destroys the on-sceen profiler ui ----The profiler is a real-time tool that shows the numbers of milliseconds spent ----in each scope per frame as well as counters. The profiler is very useful for ----tracking down performance and resource problems. ----@param enabled boolean true to enable, false to disable -function profiler.enable_ui(enabled) end - ----Get the percent of CPU usage by the application, as reported by the OS. ---- This function is not available on HTML5. ----For some platforms ( Android, Linux and Windows), this information is only available ----by default in the debug version of the engine. It can be enabled in release version as well ----by checking `track_cpu` under `profiler` in the `game.project` file. ----(This means that the engine will sample the CPU usage in intervalls during execution even in release mode.) ----@return number percent of CPU used by the application -function profiler.get_cpu_usage() end - ----Get the amount of memory used (resident/working set) by the application in bytes, as reported by the OS. ---- This function is not available on HTML5. ----The values are gathered from internal OS functions which correspond to the following; ----OS ----Value ---- iOS MacOSAndroid Linux ----Resident memory ---- Windows ----Working set ---- HTML5 ---- Not available ----@return number bytes used by the application -function profiler.get_memory_usage() end - ----Send a text to the connected profiler ----@param text string the string to send to the connected profiler -function profiler.log_text(text) end - ----Get the number of recorded frames in the on-screen profiler ui recording buffer ----@return number frame_count the number of recorded frames, zero if on-screen profiler is disabled -function profiler.recorded_frame_count() end - ----Starts a profile scope. ----@param name string The name of the scope -function profiler.scope_begin(name) end - ----End the current profile scope. -function profiler.scope_end() end - ----Set the on-screen profile mode - run, pause, record or show peak frame ----@param mode constant the mode to set the ui profiler in ---- ----- `profiler.MODE_RUN` This is default mode that continously shows the last frame ----- `profiler.MODE_PAUSE` Pauses on the currently displayed frame ----- `profiler.MODE_SHOW_PEAK_FRAME` Pauses on the currently displayed frame but shows a new frame if that frame is slower ----- `profiler.MODE_RECORD` Records all incoming frames to the recording buffer ---- ----To stop recording, switch to a different mode such as `MODE_PAUSE` or `MODE_RUN`. ----You can also use the `view_recorded_frame` function to display a recorded frame. Doing so stops the recording as well. ----Every time you switch to recording mode the recording buffer is cleared. -function profiler.set_ui_mode(mode) end - ----Set the on-screen profile view mode - minimized or expanded ----@param mode constant the view mode to set the ui profiler in ---- ----- `profiler.VIEW_MODE_FULL` The default mode which displays all the ui profiler details ----- `profiler.VIEW_MODE_MINIMIZED` Minimized mode which only shows the top header (fps counters and ui profiler mode) ---- -function profiler.set_ui_view_mode(mode) end - ----Shows or hides the time the engine waits for vsync in the on-screen profiler ----Each frame the engine waits for vsync and depending on your vsync settings and how much time ----your game logic takes this time can dwarf the time in the game logic making it hard to ----see details in the on-screen profiler graph and lists. ----Also, by hiding this the FPS times in the header show the time spent each time excuding the ----time spent waiting for vsync. This shows you how long time your game is spending actively ----working each frame. ----This setting also effects the display of recorded frames but does not affect the actual ----recorded frames so it is possible to toggle this on and off when viewing recorded frames. ----By default the vsync wait times is displayed in the profiler. ----@param visible boolean true to include it in the display, false to hide it. -function profiler.set_ui_vsync_wait_visible(visible) end - ----Pauses and displays a frame from the recording buffer in the on-screen profiler ui ----The frame to show can either be an absolute frame or a relative frame to the current frame. ----@param frame_index table a table where you specify one of the following parameters: ---- ----- `distance` The offset from the currently displayed frame (this is truncated between zero and the number of recorded frames) ----- `frame` The frame index in the recording buffer (1 is first recorded frame) ---- -function profiler.view_recorded_frame(frame_index) end - ----pause on current frame -profiler.MODE_PAUSE = nil - ----start recording -profiler.MODE_RECORD = nil - ----continously show latest frame -profiler.MODE_RUN = nil - ----pause at peak frame -profiler.MODE_SHOW_PEAK_FRAME = nil - ----show full profiler ui -profiler.VIEW_MODE_FULL = nil - ----show mimimal profiler ui -profiler.VIEW_MODE_MINIMIZED = nil - -return profiler \ No newline at end of file diff --git a/resources/defold_api/render.lua b/resources/defold_api/render.lua deleted file mode 100644 index 4764ca3..0000000 --- a/resources/defold_api/render.lua +++ /dev/null @@ -1,543 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Rendering API documentation - - Rendering functions, messages and constants. The "render" namespace is - accessible only from render scripts. - The rendering API was originally built on top of OpenGL ES 2.0, and it uses a subset of the - OpenGL computer graphics rendering API for rendering 2D and 3D computer - graphics. Our current target is OpenGLES 3.0 with fallbacks to 2.0 on some platforms. - [icon:attention] It is possible to create materials and write shaders that - require features not in OpenGL ES 2.0, but those will not work cross platform. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.render -render = {} - ----Clear buffers in the currently enabled render target with specified value. If the render target has been created with multiple ----color attachments, all buffers will be cleared with the same value. ----@param buffers table table with keys specifying which buffers to clear and values set to clear values. Available keys are: ---- ----- `graphics.BUFFER_TYPE_COLOR0_BIT` ----- `graphics.BUFFER_TYPE_DEPTH_BIT` ----- `graphics.BUFFER_TYPE_STENCIL_BIT` ---- -function render.clear(buffers) end - ----Constant buffers are used to set shader program variables and are optionally passed to the `render.draw()` function. ----The buffer's constant elements can be indexed like an ordinary Lua table, but you can't iterate over them with pairs() or ipairs(). ----@return constant_buffer buffer new constant buffer -function render.constant_buffer() end - ----Deletes a render target created by a render script. ----You cannot delete a render target resource. ----@param render_target render_target render target to delete -function render.delete_render_target(render_target) end - ----If a material is currently enabled, disable it. ----The name of the material must be specified in the ".render" resource set ----in the "game.project" setting. -function render.disable_material() end - ----Disables a render state. ----@param state constant state to disable ---- ----- `graphics.STATE_DEPTH_TEST` ----- `graphics.STATE_STENCIL_TEST` ----- `graphics.STATE_BLEND` ----- `graphics.STATE_ALPHA_TEST` ( not available on iOS and Android) ----- `graphics.STATE_CULL_FACE` ----- `graphics.STATE_POLYGON_OFFSET_FILL` ---- -function render.disable_state(state) end - ----Disables a texture that has previourly been enabled. ----@param binding number|string|hash texture binding, either by texture unit, string or hash that should be disabled -function render.disable_texture(binding) end - ----Dispatches the currently enabled compute program. The dispatch call takes three arguments x,y,z which constitutes ----the 'global working group' of the compute dispatch. Together with the 'local working group' specified in the compute shader ----as a layout qualifier, these two sets of parameters forms the number of invocations the compute shader will execute. ----An optional constant buffer can be provided to override the default constants. If no constants buffer is provided, a default ----system constants buffer is used containing constants as defined in the compute program. ----@param x number global work group size X ----@param y number global work group size Y ----@param z number global work group size Z ----@param options? table optional table with properties: ---- ----`constants` ----constant_buffer optional constants to use while rendering ---- -function render.dispatch_compute(x, y, z, options) end - ----Draws all objects that match a specified predicate. An optional constant buffer can be ----provided to override the default constants. If no constants buffer is provided, a default ----system constants buffer is used containing constants as defined in materials and set through ----go.set (or particlefx.set_constant) on visual components. ----@param predicate number predicate to draw for ----@param options? { frustum?:matrix4, frustum_planes?:number, constants?:constant_buffer, sort_order?:integer } optional table with properties: ---- ----`frustum` ----matrix4 A frustum matrix used to cull renderable items. (E.g. `local frustum = proj * view`). default=nil ----`frustum_planes` ----int Determines which sides of the frustum will be used. Default is render.FRUSTUM_PLANES_SIDES. ---- ---- ----- render.FRUSTUM_PLANES_SIDES : The left, right, top and bottom sides of the frustum. ----- render.FRUSTUM_PLANES_ALL : All 6 sides of the frustum. ---- ---- ----`constants` ----constant_buffer optional constants to use while rendering ----`sort_order` ----int How to sort draw order for world-ordered entries. Default uses the renderer's preferred world sorting (back-to-front). ---- -function render.draw(predicate, options) end - ----Draws all 3d debug graphics such as lines drawn with "draw_line" messages and physics visualization. ----@param options? { frustum?:matrix4, frustum_planes?:number } optional table with properties: ---- ----`frustum` ----matrix4 A frustum matrix used to cull renderable items. (E.g. `local frustum = proj * view`). May be nil. ----`frustum_planes` ----int Determines which sides of the frustum will be used. Default is render.FRUSTUM_PLANES_SIDES. ---- ---- ----- render.FRUSTUM_PLANES_SIDES : The left, right, top and bottom sides of the frustum. ----- render.FRUSTUM_PLANES_ALL : All sides of the frustum. ---- -function render.draw_debug3d(options) end - ----If another material was already enabled, it will be automatically disabled ----and the specified material is used instead. ----The name of the material must be specified in the ".render" resource set ----in the "game.project" setting. ----@param material_id string|hash material id to enable -function render.enable_material(material_id) end - ----Enables a particular render state. The state will be enabled until disabled. ----@param state constant state to enable ---- ----- `graphics.STATE_DEPTH_TEST` ----- `graphics.STATE_STENCIL_TEST` ----- `graphics.STATE_BLEND` ----- `graphics.STATE_ALPHA_TEST` ( not available on iOS and Android) ----- `graphics.STATE_CULL_FACE` ----- `graphics.STATE_POLYGON_OFFSET_FILL` ---- -function render.enable_state(state) end - ----Sets the specified texture handle for a render target attachment or a regular texture ----that should be used for rendering. The texture can be bound to either a texture unit ----or to a sampler name by a hash or a string. ----A texture can be bound to multiple units and sampler names at the same time, ----the actual binding will be applied to the shaders when a shader program is bound. ----When mixing binding using both units and sampler names, you might end up in situations ----where two different textures will be applied to the same bind location in the shader. ----In this case, the texture set to the named sampler will take precedence over the unit. ----Note that you can bind multiple sampler names to the same texture, in case you want to reuse ----the same texture for differnt use-cases. It is however recommended that you use the same name ----everywhere for the textures that should be shared across different materials. ----@param binding number|string|hash texture binding, either by texture unit, string or hash for the sampler name that the texture should be bound to ----@param handle_or_name number|string|hash render target or texture handle that should be bound, or a named resource in the "Render Resource" table in the currently assigned .render file ----@param buffer_type? constant optional buffer type from which to enable the texture. Note that this argument only applies to render targets. Defaults to `graphics.BUFFER_TYPE_COLOR0_BIT`. These values are supported: ---- ----- `graphics.BUFFER_TYPE_COLOR0_BIT` ---- ----If The render target has been created as depth and/or stencil textures, these buffer types can be used: ---- ----- `graphics.BUFFER_TYPE_DEPTH_BIT` ----- `graphics.BUFFER_TYPE_STENCIL_BIT` ---- ----If the render target has been created with multiple color attachments, these buffer types can be used ----to enable those textures as well. Currently 4 color attachments are supported: ---- ----- `graphics.BUFFER_TYPE_COLOR0_BIT` ----- `graphics.BUFFER_TYPE_COLOR1_BIT` ----- `graphics.BUFFER_TYPE_COLOR2_BIT` ----- `graphics.BUFFER_TYPE_COLOR3_BIT` ---- -function render.enable_texture(binding, handle_or_name, buffer_type) end - ----Returns the logical window height that is set in the "game.project" settings. ----Note that the actual window pixel size can change, either by device constraints ----or user input. ----@return number height specified window height -function render.get_height() end - ----Returns the specified buffer height from a render target. ----@param render_target render_target render target from which to retrieve the buffer height ----@param buffer_type constant which type of buffer to retrieve the height from ---- ----- `graphics.BUFFER_TYPE_COLOR0_BIT` ----- `graphics.BUFFER_TYPE_DEPTH_BIT` ----- `graphics.BUFFER_TYPE_STENCIL_BIT` ---- ----@return number height the height of the render target buffer texture -function render.get_render_target_height(render_target, buffer_type) end - ----Returns the specified buffer width from a render target. ----@param render_target render_target render target from which to retrieve the buffer width ----@param buffer_type constant which type of buffer to retrieve the width from ---- ----- `graphics.BUFFER_TYPE_COLOR0_BIT` ----- `graphics.BUFFER_TYPE_COLOR[x]_BIT` (x: [0..3], if supported!) ----- `graphics.BUFFER_TYPE_DEPTH_BIT` ----- `graphics.BUFFER_TYPE_STENCIL_BIT` ---- ----@return number width the width of the render target buffer texture -function render.get_render_target_width(render_target, buffer_type) end - ----Returns the logical window width that is set in the "game.project" settings. ----Note that the actual window pixel size can change, either by device constraints ----or user input. ----@return number width specified window width (number) -function render.get_width() end - ----Returns the actual physical window height. ----Note that this value might differ from the logical height that is set in the ----"game.project" settings. ----@return number height actual window height -function render.get_window_height() end - ----Returns the actual physical window width. ----Note that this value might differ from the logical width that is set in the ----"game.project" settings. ----@return number width actual window width -function render.get_window_width() end - ----This function returns a new render predicate for objects with materials matching ----the provided material tags. The provided tags are combined into a bit mask ----for the predicate. If multiple tags are provided, the predicate matches materials ----with all tags ANDed together. ----The current limit to the number of tags that can be defined is `64`. ----@param tags (string|hash)[] table of tags that the predicate should match. The tags can be of either hash or string type ----@return number predicate new predicate -function render.predicate(tags) end - ----Creates a new render target according to the supplied ----specification table. ----The table should contain keys specifying which buffers should be created ----with what parameters. Each buffer key should have a table value consisting ----of parameters. The following parameter keys are available: ----Key ----Values ----`format` ----`graphics.TEXTURE_FORMAT_LUMINANCE``graphics.TEXTURE_FORMAT_RGB``graphics.TEXTURE_FORMAT_RGBA``graphics.TEXTURE_FORMAT_DEPTH``graphics.TEXTURE_FORMAT_STENCIL``graphics.TEXTURE_FORMAT_RGBA32F``graphics.TEXTURE_FORMAT_RGBA16F` ----`width` ----number ----`height` ----number ----`min_filter` (optional) ----`graphics.TEXTURE_FILTER_LINEAR``graphics.TEXTURE_FILTER_NEAREST` ----`mag_filter` (optional) ----`graphics.TEXTURE_FILTER_LINEAR``graphics.TEXTURE_FILTER_NEAREST` ----`u_wrap` (optional) ----`graphics.TEXTURE_WRAP_CLAMP_TO_BORDER``graphics.TEXTURE_WRAP_CLAMP_TO_EDGE``graphics.TEXTURE_WRAP_MIRRORED_REPEAT``graphics.TEXTURE_WRAP_REPEAT` ----`v_wrap` (optional) ----`graphics.TEXTURE_WRAP_CLAMP_TO_BORDER``graphics.TEXTURE_WRAP_CLAMP_TO_EDGE``graphics.TEXTURE_WRAP_MIRRORED_REPEAT``graphics.TEXTURE_WRAP_REPEAT` ----`flags` (optional) ----`render.TEXTURE_BIT` (only applicable to depth and stencil buffers) ----The render target can be created to support multiple color attachments. Each attachment can have different format settings and texture filters, ----but attachments must be added in sequence, meaning you cannot create a render target at slot 0 and 3. ----Instead it has to be created with all four buffer types ranging from [0..3] (as denoted by graphics.BUFFER_TYPE_COLORX_BIT where 'X' is the attachment you want to create). ----It is not guaranteed that the device running the script can support creating render targets with multiple color attachments. To check if the device can support multiple attachments, ----you can check if the `render` table contains any of the `BUFFER_TYPE_COLOR1_BIT`, `BUFFER_TYPE_COLOR2_BIT` or `BUFFER_TYPE_COLOR3_BIT` constants: ----`function init(self) ---- if graphics.BUFFER_TYPE_COLOR1_BIT == nil then ---- -- this devices does not support multiple color attachments ---- end ----end ----` ----@param name string render target name ----@param parameters table table of buffer parameters, see the description for available keys and values ----@return render_target render_target new render target -function render.render_target(name, parameters) end - ----Specifies the arithmetic used when computing pixel values that are written to the frame ----buffer. In RGBA mode, pixels can be drawn using a function that blends the source RGBA ----pixel values with the destination pixel values already in the frame buffer. ----Blending is initially disabled. ----`source_factor` specifies which method is used to scale the source color components. ----`destination_factor` specifies which method is used to scale the destination color ----components. ----Source color components are referred to as (Rs,Gs,Bs,As). ----Destination color components are referred to as (Rd,Gd,Bd,Ad). ----The color specified by setting the blendcolor is referred to as (Rc,Gc,Bc,Ac). ----The source scale factor is referred to as (sR,sG,sB,sA). ----The destination scale factor is referred to as (dR,dG,dB,dA). ----The color values have integer values between 0 and (kR,kG,kB,kA), where kc = 2mc - 1 and mc is the number of bitplanes for that color. I.e for 8 bit color depth, color values are between `0` and `255`. ----Available factor constants and corresponding scale factors: ----Factor constant ----Scale factor (fR,fG,fB,fA) ----`graphics.BLEND_FACTOR_ZERO` ----(0,0,0,0) ----`graphics.BLEND_FACTOR_ONE` ----(1,1,1,1) ----`graphics.BLEND_FACTOR_SRC_COLOR` ----(Rs/kR,Gs/kG,Bs/kB,As/kA) ----`graphics.BLEND_FACTOR_ONE_MINUS_SRC_COLOR` ----(1,1,1,1) - (Rs/kR,Gs/kG,Bs/kB,As/kA) ----`graphics.BLEND_FACTOR_DST_COLOR` ----(Rd/kR,Gd/kG,Bd/kB,Ad/kA) ----`graphics.BLEND_FACTOR_ONE_MINUS_DST_COLOR` ----(1,1,1,1) - (Rd/kR,Gd/kG,Bd/kB,Ad/kA) ----`graphics.BLEND_FACTOR_SRC_ALPHA` ----(As/kA,As/kA,As/kA,As/kA) ----`graphics.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA` ----(1,1,1,1) - (As/kA,As/kA,As/kA,As/kA) ----`graphics.BLEND_FACTOR_DST_ALPHA` ----(Ad/kA,Ad/kA,Ad/kA,Ad/kA) ----`graphics.BLEND_FACTOR_ONE_MINUS_DST_ALPHA` ----(1,1,1,1) - (Ad/kA,Ad/kA,Ad/kA,Ad/kA) ----`graphics.BLEND_FACTOR_CONSTANT_COLOR` ----(Rc,Gc,Bc,Ac) ----`graphics.BLEND_FACTOR_ONE_MINUS_CONSTANT_COLOR` ----(1,1,1,1) - (Rc,Gc,Bc,Ac) ----`graphics.BLEND_FACTOR_CONSTANT_ALPHA` ----(Ac,Ac,Ac,Ac) ----`graphics.BLEND_FACTOR_ONE_MINUS_CONSTANT_ALPHA` ----(1,1,1,1) - (Ac,Ac,Ac,Ac) ----`graphics.BLEND_FACTOR_SRC_ALPHA_SATURATE` ----(i,i,i,1) where i = min(As, kA - Ad) /kA ----The blended RGBA values of a pixel comes from the following equations: ----- Rd = min(kR, Rs * sR + Rd * dR) ----- Gd = min(kG, Gs * sG + Gd * dG) ----- Bd = min(kB, Bs * sB + Bd * dB) ----- Ad = min(kA, As * sA + Ad * dA) ----Blend function `(graphics.BLEND_FACTOR_SRC_ALPHA, graphics.BLEND_FACTOR_ONE_MINUS_SRC_ALPHA)` is useful for ----drawing with transparency when the drawn objects are sorted from farthest to nearest. ----It is also useful for drawing antialiased points and lines in arbitrary order. ----@param source_factor number source factor ----@param destination_factor number destination factor -function render.set_blend_func(source_factor, destination_factor) end - ----Sets the current render camera to be used for rendering. If a render camera ----has been set by the render script, the renderer will be using its projection and view matrix ----during rendering. If a projection and/or view matrix has been set by the render script, ----they will not be used until the current render camera has been reset by calling `render.set_camera()`. ----If the 'use_frustum' flag in the options table has been set to true, the renderer will automatically use the ----camera frustum for frustum culling regardless of what frustum is being passed into the render.draw() function. ----Note that the frustum plane option in render.draw can still be used together with the camera. ----@param camera url|number|nil camera id to use, or nil to reset ----@param options? { use_frustum?:boolean } optional table with properties: ---- ----`use_frustum` ----boolean If true, the renderer will use the cameras view-projection matrix for frustum culling (default: false) ---- -function render.set_camera(camera, options) end - ----Specifies whether the individual color components in the frame buffer is enabled for writing (`true`) or disabled (`false`). For example, if `blue` is `false`, nothing is written to the blue component of any pixel in any of the color buffers, regardless of the drawing operation attempted. Note that writing are either enabled or disabled for entire color components, not the individual bits of a component. ----The component masks are all initially `true`. ----@param red boolean red mask ----@param green boolean green mask ----@param blue boolean blue mask ----@param alpha boolean alpha mask -function render.set_color_mask(red, green, blue, alpha) end - ----The name of the compute program must be specified in the ".render" resource set ----in the "game.project" setting. If nil (or no arguments) are passed to this function, ----the current compute program will instead be disabled. ----@param compute string|hash|nil compute id to use, or nil to disable -function render.set_compute(compute) end - ----Specifies whether front- or back-facing polygons can be culled ----when polygon culling is enabled. Polygon culling is initially disabled. ----If mode is `graphics.FACE_TYPE_FRONT_AND_BACK`, no polygons are drawn, but other ----primitives such as points and lines are drawn. The initial value for ----`face_type` is `graphics.FACE_TYPE_BACK`. ----@param face_type number face type ---- ----- `graphics.FACE_TYPE_FRONT` ----- `graphics.FACE_TYPE_BACK` ----- `graphics.FACE_TYPE_FRONT_AND_BACK` ---- -function render.set_cull_face(face_type) end - ----Specifies the function that should be used to compare each incoming pixel ----depth value with the value present in the depth buffer. ----The comparison is performed only if depth testing is enabled and specifies ----the conditions under which a pixel will be drawn. ----Function constants: ----- `graphics.COMPARE_FUNC_NEVER` (never passes) ----- `graphics.COMPARE_FUNC_LESS` (passes if the incoming depth value is less than the stored value) ----- `graphics.COMPARE_FUNC_LEQUAL` (passes if the incoming depth value is less than or equal to the stored value) ----- `graphics.COMPARE_FUNC_GREATER` (passes if the incoming depth value is greater than the stored value) ----- `graphics.COMPARE_FUNC_GEQUAL` (passes if the incoming depth value is greater than or equal to the stored value) ----- `graphics.COMPARE_FUNC_EQUAL` (passes if the incoming depth value is equal to the stored value) ----- `graphics.COMPARE_FUNC_NOTEQUAL` (passes if the incoming depth value is not equal to the stored value) ----- `graphics.COMPARE_FUNC_ALWAYS` (always passes) ----The depth function is initially set to `graphics.COMPARE_FUNC_LESS`. ----@param func number depth test function, see the description for available values -function render.set_depth_func(func) end - ----Specifies whether the depth buffer is enabled for writing. The supplied mask governs ----if depth buffer writing is enabled (`true`) or disabled (`false`). ----The mask is initially `true`. ----@param depth boolean depth mask -function render.set_depth_mask(depth) end - ----Set or remove listener. Currenly only only two type of events can arrived: ----`render.CONTEXT_EVENT_CONTEXT_LOST` - when rendering context lost. Rending paused and all graphics resources become invalid. ----`render.CONTEXT_EVENT_CONTEXT_RESTORED` - when rendering context was restored. Rendering still paused and graphics resources still ----invalid but can be reloaded. ----@param callback fun(self, event_type)|nil A callback that receives all render related events. ----Pass `nil` if want to remove listener. ---- ----`self` ----object The render script ----`event_type` ----string Rendering event. Possible values: `render.CONTEXT_EVENT_CONTEXT_LOST`, `render.CONTEXT_EVENT_CONTEXT_RESTORED` ---- -function render.set_listener(callback) end - ----Sets the scale and units used to calculate depth values. ----If `graphics.STATE_POLYGON_OFFSET_FILL` is enabled, each fragment's depth value ----is offset from its interpolated value (depending on the depth value of the ----appropriate vertices). Polygon offset can be used when drawing decals, rendering ----hidden-line images etc. ----`factor` specifies a scale factor that is used to create a variable depth ----offset for each polygon. The initial value is `0`. ----`units` is multiplied by an implementation-specific value to create a ----constant depth offset. The initial value is `0`. ----The value of the offset is computed as `factor` × `DZ` + `r` × `units` ----`DZ` is a measurement of the depth slope of the polygon which is the change in z (depth) ----values divided by the change in either x or y coordinates, as you traverse a polygon. ----The depth values are in window coordinates, clamped to the range [0, 1]. ----`r` is the smallest value that is guaranteed to produce a resolvable difference. ----It's value is an implementation-specific constant. ----The offset is added before the depth test is performed and before the ----value is written into the depth buffer. ----@param factor number polygon offset factor ----@param units number polygon offset units -function render.set_polygon_offset(factor, units) end - ----Sets the projection matrix to use when rendering. ----@param matrix matrix4 projection matrix -function render.set_projection(matrix) end - ----Sets a render target. Subsequent draw operations will be to the ----render target until it is replaced by a subsequent call to set_render_target. ----This function supports render targets created by a render script, or a render target resource. ----@param render_target render_target render target to set. render.RENDER_TARGET_DEFAULT to set the default render target ----@param options? { transient?:number[] } optional table with behaviour parameters ---- ----`transient` ----table Transient frame buffer types are only valid while the render target is active, i.e becomes undefined when a new target is set by a subsequent call to set_render_target. ---- Default is all non-transient. Be aware that some hardware uses a combined depth stencil buffer and when this is the case both are considered non-transient if exclusively selected! ---- A buffer type defined that doesn't exist in the render target is silently ignored. ---- ---- ----- `graphics.BUFFER_TYPE_COLOR0_BIT` ----- `graphics.BUFFER_TYPE_DEPTH_BIT` ----- `graphics.BUFFER_TYPE_STENCIL_BIT` ---- -function render.set_render_target(render_target, options) end - ----Sets the render target size for a render target created from ----either a render script, or from a render target resource. ----@param render_target render_target render target to set size for ----@param width number new render target width ----@param height number new render target height -function render.set_render_target_size(render_target, width, height) end - ----Stenciling is similar to depth-buffering as it enables and disables drawing on a ----per-pixel basis. First, GL drawing primitives are drawn into the stencil planes. ----Second, geometry and images are rendered but using the stencil planes to mask out ----where to draw. ----The stencil test discards a pixel based on the outcome of a comparison between the ----reference value `ref` and the corresponding value in the stencil buffer. ----`func` specifies the comparison function. See the table below for values. ----The initial value is `graphics.COMPARE_FUNC_ALWAYS`. ----`ref` specifies the reference value for the stencil test. The value is clamped to ----the range [0, 2n-1], where n is the number of bitplanes in the stencil buffer. ----The initial value is `0`. ----`mask` is ANDed with both the reference value and the stored stencil value when the test ----is done. The initial value is all `1`'s. ----Function constant: ----- `graphics.COMPARE_FUNC_NEVER` (never passes) ----- `graphics.COMPARE_FUNC_LESS` (passes if (ref & mask) < (stencil & mask)) ----- `graphics.COMPARE_FUNC_LEQUAL` (passes if (ref & mask) <= (stencil & mask)) ----- `graphics.COMPARE_FUNC_GREATER` (passes if (ref & mask) > (stencil & mask)) ----- `graphics.COMPARE_FUNC_GEQUAL` (passes if (ref & mask) >= (stencil & mask)) ----- `graphics.COMPARE_FUNC_EQUAL` (passes if (ref & mask) = (stencil & mask)) ----- `graphics.COMPARE_FUNC_NOTEQUAL` (passes if (ref & mask) != (stencil & mask)) ----- `graphics.COMPARE_FUNC_ALWAYS` (always passes) ----@param func number stencil test function, see the description for available values ----@param ref number reference value for the stencil test ----@param mask number mask that is ANDed with both the reference value and the stored stencil value when the test is done -function render.set_stencil_func(func, ref, mask) end - ----The stencil mask controls the writing of individual bits in the stencil buffer. ----The least significant `n` bits of the parameter `mask`, where `n` is the number of ----bits in the stencil buffer, specify the mask. ----Where a `1` bit appears in the mask, the corresponding ----bit in the stencil buffer can be written. Where a `0` bit appears in the mask, ----the corresponding bit in the stencil buffer is never written. ----The mask is initially all `1`'s. ----@param mask number stencil mask -function render.set_stencil_mask(mask) end - ----The stencil test discards a pixel based on the outcome of a comparison between the ----reference value `ref` and the corresponding value in the stencil buffer. ----To control the test, call render.set_stencil_func. ----This function takes three arguments that control what happens to the stored stencil ----value while stenciling is enabled. If the stencil test fails, no change is made to the ----pixel's color or depth buffers, and `sfail` specifies what happens to the stencil buffer ----contents. ----Operator constants: ----- `graphics.STENCIL_OP_KEEP` (keeps the current value) ----- `graphics.STENCIL_OP_ZERO` (sets the stencil buffer value to 0) ----- `graphics.STENCIL_OP_REPLACE` (sets the stencil buffer value to `ref`, as specified by render.set_stencil_func) ----- `graphics.STENCIL_OP_INCR` (increments the stencil buffer value and clamp to the maximum representable unsigned value) ----- `graphics.STENCIL_OP_INCR_WRAP` (increments the stencil buffer value and wrap to zero when incrementing the maximum representable unsigned value) ----- `graphics.STENCIL_OP_DECR` (decrements the current stencil buffer value and clamp to 0) ----- `graphics.STENCIL_OP_DECR_WRAP` (decrements the current stencil buffer value and wrap to the maximum representable unsigned value when decrementing zero) ----- `graphics.STENCIL_OP_INVERT` (bitwise inverts the current stencil buffer value) ----`dppass` and `dpfail` specify the stencil buffer actions depending on whether subsequent ----depth buffer tests succeed (dppass) or fail (dpfail). ----The initial value for all operators is `graphics.STENCIL_OP_KEEP`. ----@param sfail number action to take when the stencil test fails ----@param dpfail number the stencil action when the stencil test passes ----@param dppass number the stencil action when both the stencil test and the depth test pass, or when the stencil test passes and either there is no depth buffer or depth testing is not enabled -function render.set_stencil_op(sfail, dpfail, dppass) end - ----Sets the view matrix to use when rendering. ----@param matrix matrix4 view matrix to set -function render.set_view(matrix) end - ----Set the render viewport to the specified rectangle. ----@param x number left corner ----@param y number bottom corner ----@param width number viewport width ----@param height number viewport height -function render.set_viewport(x, y, width, height) end - ---- -render.FRUSTUM_PLANES_ALL = nil - ---- -render.FRUSTUM_PLANES_SIDES = nil - ---- -render.RENDER_TARGET_DEFAULT = nil - ----Depth sort far-to-near (default; good for transparent passes). -render.SORT_BACK_TO_FRONT = nil - ----Depth sort near-to-far (good for opaque passes to reduce overdraw). -render.SORT_FRONT_TO_BACK = nil - ----No per-call sorting; draw entries in insertion order. -render.SORT_NONE = nil - -return render \ No newline at end of file diff --git a/resources/defold_api/resource.lua b/resources/defold_api/resource.lua deleted file mode 100644 index 9fe33f8..0000000 --- a/resources/defold_api/resource.lua +++ /dev/null @@ -1,795 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Resource API documentation - - Functions and constants to access resources. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.resource -resource = {} - ----Constructor-like function with two purposes: ----- Load the specified resource as part of loading the script ----- Return a hash to the run-time version of the resource ---- This function can only be called within go.property function calls. ----@param path? string optional resource path string to the resource ----@return hash path a path hash to the binary version of the resource -function resource.atlas(path) end - ----Constructor-like function with two purposes: ----- Load the specified resource as part of loading the script ----- Return a hash to the run-time version of the resource ---- This function can only be called within go.property function calls. ----@param path? string optional resource path string to the resource ----@return hash path a path hash to the binary version of the resource -function resource.buffer(path) end - ----This function creates a new atlas resource that can be used in the same way as any atlas created during build time. ----The path used for creating the atlas must be unique, trying to create a resource at a path that is already ----registered will trigger an error. If the intention is to instead modify an existing atlas, use the resource.set_atlas ----function. Also note that the path to the new atlas resource must have a '.texturesetc' extension, ----meaning "/path/my_atlas" is not a valid path but "/path/my_atlas.texturesetc" is. ----When creating the atlas, at least one geometry and one animation is required, and an error will be ----raised if these requirements are not met. A reference to the resource will be held by the collection ----that created the resource and will automatically be released when that collection is destroyed. ----Note that releasing a resource essentially means decreasing the reference count of that resource, ----and not necessarily that it will be deleted. ----@param path string The path to the resource. ----@param table resource.atlas A table containing info about how to create the atlas. Supported entries: ---- ----- ---- ----`texture` ----string | hash the path to the texture resource, e.g "/main/my_texture.texturec" ---- ---- ----- ---- ----`animations` ----table a list of the animations in the atlas. Supports the following fields: ---- ---- ----- ---- ----`id` ----string the id of the animation, used in e.g sprite.play_animation ---- ---- ----- ---- ----`width` ----number the width of the animation ---- ---- ----- ---- ----`height` ----number the height of the animation ---- ---- ----- ---- ----`frame_start` ----number index to the first geometry of the animation. Indices are lua based and must be in the range of 1 .. in atlas. ---- ---- ----- ---- ----`frame_end` ----number index to the last geometry of the animation (non-inclusive). Indices are lua based and must be in the range of 1 .. in atlas. ---- ---- ----- ---- ----`playback` ----constant optional playback mode of the animation, the default value is go.PLAYBACK_ONCE_FORWARD ---- ---- ----- ---- ----`fps` ----number optional fps of the animation, the default value is 30 ---- ---- ----- ---- ----`flip_vertical` ----boolean optional flip the animation vertically, the default value is false ---- ---- ----- ---- ----`flip_horizontal` ----boolean optional flip the animation horizontally, the default value is false ---- ---- ----- ---- ----`geometries` ----table A list of the geometries that should map to the texture data. Supports the following fields: ---- ---- ----- ---- ----`id` ----string The name of the geometry. Used when matching animations between multiple atlases ---- ---- ----- ---- ----`width` ----number The width of the image the sprite geometry represents ---- ---- ----- ---- ----`height` ----number The height of the image the sprite geometry represents ---- ---- ----- ---- ----`pivot_x` ----number The pivot x value of the image in unit coords. (0,0) is upper left corner, (1,1) is bottom right. Default is 0.5. ---- ---- ----- ---- ----`pivot_y` ----number The pivot y value of the image in unit coords. (0,0) is upper left corner, (1,1) is bottom right. Default is 0.5. ---- ---- ----- ---- ----`rotated` ----boolean Whether the image is rotated 90 degrees counter-clockwise in the atlas. This affects UV coordinate generation for proper rendering. Default is false. ---- ---- ----- ---- ----`vertices` ----table a list of the vertices in image space of the geometry in the form {px0, py0, px1, py1, ..., pxn, pyn} ---- ---- ----- ---- ----`uvs` ----table a list of the uv coordinates in image space of the geometry in the form of {u0, v0, u1, v1, ..., un, vn}. ---- ---- ----- ---- ----`indices` ----table a list of the indices of the geometry in the form {i0, i1, i2, ..., in}. Each tripe in the list represents a triangle. ---- ---- ---- ----@return hash path Returns the atlas resource path -function resource.create_atlas(path, table) end - ----This function creates a new buffer resource that can be used in the same way as any buffer created during build time. ----The function requires a valid buffer created from either buffer.create or another pre-existing buffer resource. ----By default, the new resource will take ownership of the buffer lua reference, meaning the buffer will not automatically be removed ----when the lua reference to the buffer is garbage collected. This behaviour can be overruled by specifying 'transfer_ownership = false' ----in the argument table. If the new buffer resource is created from a buffer object that is created by another resource, ----the buffer object will be copied and the new resource will effectively own a copy of the buffer instead. ----Note that the path to the new resource must have the '.bufferc' extension, "/path/my_buffer" is not a valid path but "/path/my_buffer.bufferc" is. ----The path must also be unique, attempting to create a buffer with the same name as an existing resource will raise an error. ----@param path string The path to the resource. ----@param table? { buffer:buffer_data, transfer_ownership?:boolean } A table containing info about how to create the buffer. Supported entries: ---- ----- ---- ----`buffer` ----buffer the buffer to bind to this resource ---- ---- ----- ---- ----`transfer_ownership` ----boolean optional flag to determine wether or not the resource should take over ownership of the buffer object (default true) ---- ---- ---- ----@return hash path Returns the buffer resource path -function resource.create_buffer(path, table) end - ----Creates a sound data resource ----Supported formats are .oggc, .opusc and .wavc ----@param path string the path to the resource. Must not already exist. ----@param options? { data?:string, filesize?:number, partial?:boolean } A table containing parameters for the text. Supported entries: ---- ----`data` ----string The raw data of the file. May be partial, but must include the header of the file ----`filesize` ----number If the file is partial, it must also specify the full size of the complete file. ----`partial` ----boolean Is the data not representing the full file, but just the initial chunk? ---- ----@return hash path_hash the resulting path hash to the resource -function resource.create_sound_data(path, options) end - ----Creates a new texture resource that can be used in the same way as any texture created during build time. ----The path used for creating the texture must be unique, trying to create a resource at a path that is already ----registered will trigger an error. If the intention is to instead modify an existing texture, use the resource.set_texture ----function. Also note that the path to the new texture resource must have a '.texturec' extension, ----meaning "/path/my_texture" is not a valid path but "/path/my_texture.texturec" is. ----If the texture is created without a buffer, the pixel data will be blank. ----@param path string The path to the resource. ----@param table { type:number, width:number, height:number, depth:number, format:number, flags?:number, max_mipmaps?:number, compression_type?:number} A table containing info about how to create the texture. Supported entries: ---- ----`type` ----number The texture type. Supported values: ---- ---- ----- `graphics.TEXTURE_TYPE_2D` ----- `graphics.TEXTURE_TYPE_IMAGE_2D` ----- `graphics.TEXTURE_TYPE_3D` ----- `graphics.TEXTURE_TYPE_IMAGE_3D` ----- `graphics.TEXTURE_TYPE_CUBE_MAP` ---- ---- ----`width` ----number The width of the texture (in pixels). Must be larger than 0. ----`height` ----number The width of the texture (in pixels). Must be larger than 0. ----`depth` ----number The depth of the texture (in pixels). Must be larger than 0. Only used when `type` is `graphics.TEXTURE_TYPE_3D` or `graphics.TEXTURE_TYPE_IMAGE_3D`. ----`format` ----number The texture format, note that some of these formats might not be supported by the running device. Supported values: ---- ---- ----- `graphics.TEXTURE_FORMAT_LUMINANCE` ----- `graphics.TEXTURE_FORMAT_RGB` ----- `graphics.TEXTURE_FORMAT_RGBA` ---- ----These constants might not be available on the device: ---- ----- `graphics.TEXTURE_FORMAT_RGB_PVRTC_2BPPV1` ----- `graphics.TEXTURE_FORMAT_RGB_PVRTC_4BPPV1` ----- `graphics.TEXTURE_FORMAT_RGBA_PVRTC_2BPPV1` ----- `graphics.TEXTURE_FORMAT_RGBA_PVRTC_4BPPV1` ----- `graphics.TEXTURE_FORMAT_RGB_ETC1` ----- `graphics.TEXTURE_FORMAT_RGBA_ETC2` ----- `graphics.TEXTURE_FORMAT_RGBA_ASTC_4X4` ----- `graphics.TEXTURE_FORMAT_RGB_BC1` ----- `graphics.TEXTURE_FORMAT_RGBA_BC3` ----- `graphics.TEXTURE_FORMAT_R_BC4` ----- `graphics.TEXTURE_FORMAT_RG_BC5` ----- `graphics.TEXTURE_FORMAT_RGBA_BC7` ----- `graphics.TEXTURE_FORMAT_RGB16F` ----- `graphics.TEXTURE_FORMAT_RGB32F` ----- `graphics.TEXTURE_FORMAT_RGBA16F` ----- `graphics.TEXTURE_FORMAT_RGBA32F` ----- `graphics.TEXTURE_FORMAT_R16F` ----- `graphics.TEXTURE_FORMAT_RG16F` ----- `graphics.TEXTURE_FORMAT_R32F` ----- `graphics.TEXTURE_FORMAT_RG32F` ---- ----You can test if the device supports these values by checking if a specific enum is nil or not: ----`if graphics.TEXTURE_FORMAT_RGBA16F ~= nil then ---- -- it is safe to use this format ----end ----` ---- ---- ----`flags` ----number Texture creation flags that can be used to dictate how the texture is created. The default value is graphics.TEXTURE_USAGE_FLAG_SAMPLE, which means that the texture can be sampled from a shader. ----These flags may or may not be supported on the running device and/or the underlying graphics API and is simply used internally as a 'hint' when creating the texture. There is no guarantee that any of these will have any effect. Supported values: ---- ---- ----- `graphics.TEXTURE_USAGE_FLAG_SAMPLE` - The texture can be sampled from a shader (default) ----- `graphics.TEXTURE_USAGE_FLAG_MEMORYLESS` - The texture can be used as a memoryless texture, i.e only transient memory for the texture is used during rendering ----- `graphics.TEXTURE_USAGE_FLAG_STORAGE` - The texture can be used as a storage texture, which is required for a shader to write to the texture ---- ---- ----`max_mipmaps` ----number optional max number of mipmaps. Defaults to zero, i.e no mipmap support ----`compression_type` ----number optional specify the compression type for the data in the buffer object that holds the texture data. Will only be used when a compressed buffer has been passed into the function. ----Creating an empty texture with no buffer data is not supported as a core feature. Defaults to graphics.COMPRESSION_TYPE_DEFAULT, i.e no compression. Supported values: ---- ---- ----- `COMPRESSION_TYPE_DEFAULT` ----- `COMPRESSION_TYPE_BASIS_UASTC` ---- ----@param buffer buffer_data optional buffer of precreated pixel data ----@return hash path The path to the resource. ---- 3D Textures are currently only supported on OpenGL and Vulkan adapters. To check if your device supports 3D textures, use: ----```lua ----if graphics.TEXTURE_TYPE_3D ~= nil then ---- -- Device and graphics adapter support 3D textures ----end -function resource.create_texture(path, table, buffer) end - ----Creates a new texture resource that can be used in the same way as any texture created during build time. ----The path used for creating the texture must be unique, trying to create a resource at a path that is already ----registered will trigger an error. If the intention is to instead modify an existing texture, use the resource.set_texture ----function. Also note that the path to the new texture resource must have a '.texturec' extension, ----meaning "/path/my_texture" is not a valid path but "/path/my_texture.texturec" is. ----If the texture is created without a buffer, the pixel data will be blank. ----The difference between the async version and resource.create_texture is that the texture data will be uploaded ----in a graphics worker thread. The function will return a resource immediately that contains a 1x1 blank texture which can be used ----immediately after the function call. When the new texture has been uploaded, the initial blank texture will be deleted and replaced with the ----new texture. Be careful when using the initial texture handle handle as it will not be valid after the upload has finished. ----@param path string|hash The path to the resource. ----@param table { type:number, width:number, height:number, depth:number, format:number, flags?:number, max_mipmaps?:number, compression_type?:number} A table containing info about how to create the texture. Supported entries: ----`type` ----number The texture type. Supported values: ---- ---- ----- `graphics.TEXTURE_TYPE_2D` ----- `graphics.TEXTURE_TYPE_IMAGE_2D` ----- `graphics.TEXTURE_TYPE_3D` ----- `graphics.TEXTURE_TYPE_IMAGE_3D` ----- `graphics.TEXTURE_TYPE_CUBE_MAP` ---- ---- ----`width` ----number The width of the texture (in pixels). Must be larger than 0. ----`height` ----number The width of the texture (in pixels). Must be larger than 0. ----`depth` ----number The depth of the texture (in pixels). Must be larger than 0. Only used when `type` is `graphics.TEXTURE_TYPE_3D` or `graphics.TEXTURE_TYPE_IMAGE_3D`. ----`format` ----number The texture format, note that some of these formats might not be supported by the running device. Supported values: ---- ---- ----- `graphics.TEXTURE_FORMAT_LUMINANCE` ----- `graphics.TEXTURE_FORMAT_RGB` ----- `graphics.TEXTURE_FORMAT_RGBA` ---- ----These constants might not be available on the device: ---- ----- `graphics.TEXTURE_FORMAT_RGB_PVRTC_2BPPV1` ----- `graphics.TEXTURE_FORMAT_RGB_PVRTC_4BPPV1` ----- `graphics.TEXTURE_FORMAT_RGBA_PVRTC_2BPPV1` ----- `graphics.TEXTURE_FORMAT_RGBA_PVRTC_4BPPV1` ----- `graphics.TEXTURE_FORMAT_RGB_ETC1` ----- `graphics.TEXTURE_FORMAT_RGBA_ETC2` ----- `graphics.TEXTURE_FORMAT_RGBA_ASTC_4X4` ----- `graphics.TEXTURE_FORMAT_RGB_BC1` ----- `graphics.TEXTURE_FORMAT_RGBA_BC3` ----- `graphics.TEXTURE_FORMAT_R_BC4` ----- `graphics.TEXTURE_FORMAT_RG_BC5` ----- `graphics.TEXTURE_FORMAT_RGBA_BC7` ----- `graphics.TEXTURE_FORMAT_RGB16F` ----- `graphics.TEXTURE_FORMAT_RGB32F` ----- `graphics.TEXTURE_FORMAT_RGBA16F` ----- `graphics.TEXTURE_FORMAT_RGBA32F` ----- `graphics.TEXTURE_FORMAT_R16F` ----- `graphics.TEXTURE_FORMAT_RG16F` ----- `graphics.TEXTURE_FORMAT_R32F` ----- `graphics.TEXTURE_FORMAT_RG32F` ---- ----You can test if the device supports these values by checking if a specific enum is nil or not: ----`if graphics.TEXTURE_FORMAT_RGBA16F ~= nil then ---- -- it is safe to use this format ----end ----` ---- ---- ----`flags` ----number Texture creation flags that can be used to dictate how the texture is created. Supported values: ---- ---- ----- `graphics.TEXTURE_USAGE_FLAG_SAMPLE` - The texture can be sampled from a shader (default) ----- `graphics.TEXTURE_USAGE_FLAG_MEMORYLESS` - The texture can be used as a memoryless texture, i.e only transient memory for the texture is used during rendering ----- `graphics.TEXTURE_USAGE_FLAG_STORAGE` - The texture can be used as a storage texture, which is required for a shader to write to the texture ---- ---- ----`max_mipmaps` ----number optional max number of mipmaps. Defaults to zero, i.e no mipmap support ----`compression_type` ----number optional specify the compression type for the data in the buffer object that holds the texture data. Will only be used when a compressed buffer has been passed into the function. ----Creating an empty texture with no buffer data is not supported as a core feature. Defaults to graphics.COMPRESSION_TYPE_DEFAULT, i.e no compression. Supported values: ---- ---- ----- `COMPRESSION_TYPE_DEFAULT` ----- `COMPRESSION_TYPE_BASIS_UASTC` ---- ----@param buffer buffer_data optional buffer of precreated pixel data ----@param callback fun(self, request_id, resource) callback function when texture is created (self, request_id, resource) ----@return hash path The path to the texture resource. ----@return number request_id The request id for the async request. ---- 3D Textures are currently only supported on OpenGL and Vulkan adapters. To check if your device supports 3D textures, use: ----```lua ----if graphics.TEXTURE_TYPE_3D ~= nil then ---- -- Device and graphics adapter support 3D textures ----end -function resource.create_texture_async(path, table, buffer, callback) end - ----Constructor-like function with two purposes: ----- Load the specified resource as part of loading the script ----- Return a hash to the run-time version of the resource ---- This function can only be called within go.property function calls. ----@param path? string optional resource path string to the resource ----@return hash path a path hash to the binary version of the resource -function resource.font(path) end - ----Returns the atlas data for an atlas ----@param path hash|string The path to the atlas resource ----@return resource.atlas data A table with the following entries: ---- ----- texture ----- geometries ----- animations ---- ----See resource.set_atlas for a detailed description of each field -function resource.get_atlas(path) end - ----gets the buffer from a resource ----@param path hash|string The path to the resource ----@return buffer_data buffer The resource buffer -function resource.get_buffer(path) end - ----Gets render target info from a render target resource path or a render target handle ----@param path hash|string|number The path to the resource or a render target handle ----@return { handle:number, attachments:{ handle:number, width:number, height:number, depth:number, mipmaps:number, type:number, buffer_type:number, texture:hash }[] } table A table containing info about the render target: ---- ----`handle` ----number the opaque handle to the texture resource ----'attachments' ----table a table of attachments, where each attachment contains the following entries: ----`width` ----number width of the texture ----`height` ----number height of the texture ----`depth` ----number depth of the texture (i.e 1 for a 2D texture and 6 for a cube map) ----`mipmaps` ----number number of mipmaps of the texture ----`type` ----number The texture type. Supported values: ---- ---- ----- `graphics.TEXTURE_TYPE_2D` ----- `graphics.TEXTURE_TYPE_CUBE_MAP` ----- `graphics.TEXTURE_TYPE_2D_ARRAY` ---- ---- ----`buffer_type` ----number The attachment buffer type. Supported values: ---- ---- ----- `resource.BUFFER_TYPE_COLOR0` ----- `resource.BUFFER_TYPE_COLOR1` ----- `resource.BUFFER_TYPE_COLOR2` ----- `resource.BUFFER_TYPE_COLOR3` ----- `resource.BUFFER_TYPE_DEPTH` ----- ----`resource.BUFFER_TYPE_STENCIL` ---- ----- ---- ----`texture` ----hash The hashed path to the attachment texture resource. This field is only available if the render target passed in is a resource. ---- ---- ---- -function resource.get_render_target_info(path) end - ----Gets the text metrics from a font ----@param url hash the font to get the (unscaled) metrics from ----@param text string text to measure ----@param options? { width?:number, leading?:number, tracking?:number, line_break?:boolean} A table containing parameters for the text. Supported entries: ---- ----`width` ----number The width of the text field. Not used if `line_break` is false. ----`leading` ----number The leading (default 1.0) ----`tracking` ----number The tracking (default 0.0) ----`line_break` ----boolean If the calculation should consider line breaks (default false) ---- ----@return { width:number, height:number, max_ascent:number, max_descent:number } metrics a table with the following fields: ---- ----- width ----- height ----- max_ascent ----- max_descent ---- -function resource.get_text_metrics(url, text, options) end - ----Gets texture info from a texture resource path or a texture handle ----@param path hash|string|number The path to the resource or a texture handle ----@return { handle:number, width:number, height:number, depth:number, mipmaps:number, flags:number, type:number } table A table containing info about the texture: ---- ----`handle` ----number the opaque handle to the texture resource ----`width` ----number width of the texture ----`height` ----number height of the texture ----`depth` ----number depth of the texture (i.e 1 for a 2D texture, 6 for a cube map, the actual depth of a 3D texture) ----`page_count` ----number number of pages of the texture array. For 2D texture value is 1. For cube map - 6 ----`mipmaps` ----number number of mipmaps of the texture ----`flags` ----number usage hints of the texture. ----`type` ----number The texture type. Supported values: ---- ---- ----- `graphics.TEXTURE_TYPE_2D` ----- `graphics.TEXTURE_TYPE_2D_ARRAY` ----- `graphics.TEXTURE_TYPE_IMAGE_2D` ----- `graphics.TEXTURE_TYPE_3D` ----- `graphics.TEXTURE_TYPE_IMAGE_3D` ----- `graphics.TEXTURE_TYPE_CUBE_MAP` ---- -function resource.get_texture_info(path) end - ----Loads the resource data for a specific resource. ----@param path string The path to the resource ----@return buffer_data buffer Returns the buffer stored on disc -function resource.load(path) end - ----Constructor-like function with two purposes: ----- Load the specified resource as part of loading the script ----- Return a hash to the run-time version of the resource ---- This function can only be called within go.property function calls. ----@param path? string optional resource path string to the resource ----@return hash path a path hash to the binary version of the resource -function resource.material(path) end - ----Release a resource. ---- This is a potentially dangerous operation, releasing resources currently being used can cause unexpected behaviour. ----@param path hash|string The path to the resource. -function resource.release(path) end - ----Constructor-like function with two purposes: ----- Load the specified resource as part of loading the script ----- Return a hash to the run-time version of the resource ---- This function can only be called within go.property function calls. ----@param path? string optional resource path string to the resource ----@return hash path a path hash to the binary version of the resource -function resource.render_target(path) end - ----Sets the resource data for a specific resource ----@param path string|hash The path to the resource ----@param buffer buffer_data The buffer of precreated data, suitable for the intended resource type -function resource.set(path, buffer) end - ----Sets the data for a specific atlas resource. Setting new atlas data is specified by passing in ----a texture path for the backing texture of the atlas, a list of geometries and a list of animations ----that map to the entries in the geometry list. The geometry entries are represented by three lists: ----vertices, uvs and indices that together represent triangles that are used in other parts of the ----engine to produce render objects from. ----Vertex and uv coordinates for the geometries are expected to be ----in pixel coordinates where 0,0 is the top left corner of the texture. ----There is no automatic padding or margin support when setting custom data, ----which could potentially cause filtering artifacts if used with a material sampler that has linear filtering. ----If that is an issue, you need to calculate padding and margins manually before passing in the geometry data to ----this function. ----@param path hash|string The path to the atlas resource ----@param table resource.atlas A table containing info about the atlas. Supported entries: ---- ----- ---- ----`texture` ----string | hash the path to the texture resource, e.g "/main/my_texture.texturec" ---- ---- ----- ---- ----`animations` ----table a list of the animations in the atlas. Supports the following fields: ---- ---- ----- ---- ----`id` ----string the id of the animation, used in e.g sprite.play_animation ---- ---- ----- ---- ----`width` ----number the width of the animation ---- ---- ----- ---- ----`height` ----number the height of the animation ---- ---- ----- ---- ----`frame_start` ----number index to the first geometry of the animation. Indices are lua based and must be in the range of 1 .. in atlas. ---- ---- ----- ---- ----`frame_end` ----number index to the last geometry of the animation (non-inclusive). Indices are lua based and must be in the range of 1 .. in atlas. ---- ---- ----- ---- ----`playback` ----constant optional playback mode of the animation, the default value is go.PLAYBACK_ONCE_FORWARD ---- ---- ----- ---- ----`fps` ----number optional fps of the animation, the default value is 30 ---- ---- ----- ---- ----`flip_vertical` ----boolean optional flip the animation vertically, the default value is false ---- ---- ----- ---- ----`flip_horizontal` ----boolean optional flip the animation horizontally, the default value is false ---- ---- ----- ---- ----`geometries` ----table A list of the geometries that should map to the texture data. Supports the following fields: ---- ---- ----- ---- ----`vertices` ----table a list of the vertices in texture space of the geometry in the form {px0, py0, px1, py1, ..., pxn, pyn} ---- ---- ----- ---- ----`uvs` ----table a list of the uv coordinates in texture space of the geometry in the form of {u0, v0, u1, v1, ..., un, vn} ---- ---- ----- ---- ----`indices` ----table a list of the indices of the geometry in the form {i0, i1, i2, ..., in}. Each tripe in the list represents a triangle. ---- ---- ---- -function resource.set_atlas(path, table) end - ----Sets the buffer of a resource. By default, setting the resource buffer will either copy the data from the incoming buffer object ----to the buffer stored in the destination resource, or make a new buffer object if the sizes between the source buffer and the destination buffer ----stored in the resource differs. In some cases, e.g performance reasons, it might be beneficial to just set the buffer object on the resource without copying or cloning. ----To achieve this, set the `transfer_ownership` flag to true in the argument table. Transferring ownership from a lua buffer to a resource with this function ----works exactly the same as resource.create_buffer: the destination resource will take ownership of the buffer held by the lua reference, i.e the buffer will not automatically be removed ----when the lua reference to the buffer is garbage collected. ----Note: When setting a buffer with `transfer_ownership = true`, the currently bound buffer in the resource will be destroyed. ----@param path hash|string The path to the resource ----@param buffer buffer_data The resource buffer ----@param table? { transfer_ownership?: boolean } A table containing info about how to set the buffer. Supported entries: ---- ----- ---- ----`transfer_ownership` ----boolean optional flag to determine wether or not the resource should take over ownership of the buffer object (default false) ---- ---- ---- -function resource.set_buffer(path, buffer, table) end - ----Update internal sound resource (wavc/oggc/opusc) with new data ----@param path hash|string The path to the resource ----@param buffer string A lua string containing the binary sound data -function resource.set_sound(path, buffer) end - ----Sets the pixel data for a specific texture. ----@param path hash|string The path to the resource ----@param table { type:number, width:number, height:number, format:number, x?:number, y?:number, z?:number, mipmap?:number, compression_type?:number} A table containing info about the texture. Supported entries: ---- ----`type` ----number The texture type. Supported values: ---- ---- ----- `graphics.TEXTURE_TYPE_2D` ----- `graphics.TEXTURE_TYPE_IMAGE_2D` ----- `graphics.TEXTURE_TYPE_3D` ----- `graphics.TEXTURE_TYPE_IMAGE_3D` ----- `graphics.TEXTURE_TYPE_CUBE_MAP` ---- ---- ----`width` ----number The width of the texture (in pixels) ----`height` ----number The width of the texture (in pixels) ----`format` ----number The texture format, note that some of these formats are platform specific. Supported values: ---- ---- ----- `graphics.TEXTURE_FORMAT_LUMINANCE` ----- `graphics.TEXTURE_FORMAT_RGB` ----- `graphics.TEXTURE_FORMAT_RGBA` ---- ----These constants might not be available on the device: ----- `graphics.TEXTURE_FORMAT_RGB_PVRTC_2BPPV1` ----- `graphics.TEXTURE_FORMAT_RGB_PVRTC_4BPPV1` ----- `graphics.TEXTURE_FORMAT_RGBA_PVRTC_2BPPV1` ----- `graphics.TEXTURE_FORMAT_RGBA_PVRTC_4BPPV1` ----- `graphics.TEXTURE_FORMAT_RGB_ETC1` ----- `graphics.TEXTURE_FORMAT_RGBA_ETC2` ----- `graphics.TEXTURE_FORMAT_RGBA_ASTC_4X4` ----- `graphics.TEXTURE_FORMAT_RGB_BC1` ----- `graphics.TEXTURE_FORMAT_RGBA_BC3` ----- `graphics.TEXTURE_FORMAT_R_BC4` ----- `graphics.TEXTURE_FORMAT_RG_BC5` ----- `graphics.TEXTURE_FORMAT_RGBA_BC7` ----- `graphics.TEXTURE_FORMAT_RGB16F` ----- `graphics.TEXTURE_FORMAT_RGB32F` ----- `graphics.TEXTURE_FORMAT_RGBA16F` ----- `graphics.TEXTURE_FORMAT_RGBA32F` ----- `graphics.TEXTURE_FORMAT_R16F` ----- `graphics.TEXTURE_FORMAT_RG16F` ----- `graphics.TEXTURE_FORMAT_R32F` ----- `graphics.TEXTURE_FORMAT_RG32F` ----You can test if the device supports these values by checking if a specific enum is nil or not: ----`if graphics.TEXTURE_FORMAT_RGBA16F ~= nil then ---- -- it is safe to use this format ----end ----` ---- ---- ----`x` ----number optional x offset of the texture (in pixels) ----`y` ----number optional y offset of the texture (in pixels) ----`z` ----number optional z offset of the texture (in pixels). Only applies to 3D textures ----`page` ----number optional slice of the array texture. Only applies to 2D texture arrays. Zero-based ----`mipmap` ----number optional mipmap to upload the data to ----`compression_type` ----number optional specify the compression type for the data in the buffer object that holds the texture data. Defaults to graphics.COMPRESSION_TYPE_DEFAULT, i.e no compression. Supported values: ---- ---- ----- `COMPRESSION_TYPE_DEFAULT` ----- `COMPRESSION_TYPE_BASIS_UASTC` ---- ----@param buffer buffer_data The buffer of precreated pixel data ---- To update a cube map texture you need to pass in six times the amount of data via the buffer, since a cube map has six sides! ---- 3D Textures are currently only supported on OpenGL and Vulkan adapters. To check if your device supports 3D textures, use: ----```lua ----if graphics.TEXTURE_TYPE_3D ~= nil then ---- -- Device and graphics adapter support 3D textures ----end -function resource.set_texture(path, table, buffer) end - ----Constructor-like function with two purposes: ----- Load the specified resource as part of loading the script ----- Return a hash to the run-time version of the resource ---- This function can only be called within go.property function calls. ----@param path? string optional resource path string to the resource ----@return hash path a path hash to the binary version of the resource -function resource.texture(path) end - ----Constructor-like function with two purposes: ----- Load the specified resource as part of loading the script ----- Return a hash to the run-time version of the resource ---- This function can only be called within go.property function calls. ----@param path? string optional resource path string to the resource ----@return hash path a path hash to the binary version of the resource -function resource.tile_source(path) end - -return resource \ No newline at end of file diff --git a/resources/defold_api/socket.lua b/resources/defold_api/socket.lua deleted file mode 100644 index 4fd0cd2..0000000 --- a/resources/defold_api/socket.lua +++ /dev/null @@ -1,177 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - LuaSocket API documentation - - [LuaSocket](https://github.com/diegonehab/luasocket) is a Lua extension library that provides - support for the TCP and UDP transport layers. Defold provides the "socket" namespace in - runtime, which contain the core C functionality. Additional LuaSocket support modules for - SMTP, HTTP, FTP etc are not part of the core included, but can be easily added to a project - and used. - Note the included helper module "socket.lua" in "builtins/scripts/socket.lua". Require this - module to add some additional functions and shortcuts to the namespace: - ```lua - require "builtins.scripts.socket" - ``` - LuaSocket is Copyright © 2004-2007 Diego Nehab. All rights reserved. - LuaSocket is free software, released under the MIT license (same license as the Lua core). ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.socket -socket = {} - ----This function is a shortcut that creates and returns a TCP client object connected to a remote ----address at a given port. Optionally, the user can also specify the local address and port to ----bind (`locaddr` and `locport`), or restrict the socket family to `"inet"` or `"inet6"`. ----Without specifying family to connect, whether a tcp or tcp6 connection is created depends on ----your system configuration. ----@param address string the address to connect to. ----@param port number the port to connect to. ----@param locaddr? string optional local address to bind to. ----@param locport? number optional local port to bind to. ----@param family? string optional socket family to use, `"inet"` or `"inet6"`. ----@return socket_client|nil tcp_client a new IPv6 TCP client object, or `nil` in case of error. ----@return string|nil error the error message, or `nil` if no error occurred. -function socket.connect(address, port, locaddr, locport, family) end - ----This function converts a host name to IPv4 or IPv6 address. ----The supplied address can be an IPv4 or IPv6 address or host name. ----The function returns a table with all information returned by the resolver: ----`{ ---- [1] = { ---- family = family-name-1, ---- addr = address-1 ---- }, ---- ... ---- [n] = { ---- family = family-name-n, ---- addr = address-n ---- } ----} ----` ----Here, family contains the string `"inet"` for IPv4 addresses, and `"inet6"` for IPv6 addresses. ----In case of error, the function returns nil followed by an error message. ----@param address string a hostname or an IPv4 or IPv6 address. ----@return table|nil resolved a table with all information returned by the resolver, or if an error occurs, `nil`. ----@return string|nil error the error message, or `nil` if no error occurred. -function socket.dns.getaddrinfo(address) end - ----Returns the standard host name for the machine as a string. ----@return string hostname the host name for the machine. -function socket.dns.gethostname() end - ----This function converts an address to host name. ----The supplied address can be an IPv4 or IPv6 address or host name. ----The function returns a table with all information returned by the resolver: ----`{ ---- [1] = host-name-1, ---- ... ---- [n] = host-name-n, ----} ----` ----@param address string a hostname or an IPv4 or IPv6 address. ----@return table|nil resolved a table with all information returned by the resolver, or if an error occurs, `nil`. ----@return string|nil error the error message, or `nil` if no error occurred. -function socket.dns.getnameinfo(address) end - ----This function converts from an IPv4 address to host name. ----The address can be an IPv4 address or a host name. ----@param address string an IPv4 address or host name. ----@return string|nil hostname the canonic host name of the given address, or `nil` in case of an error. ----@return table|string resolved a table with all information returned by the resolver, or if an error occurs, the error message string. -function socket.dns.tohostname(address) end - ----This function converts a host name to IPv4 address. ----The address can be an IP address or a host name. ----@param address string a hostname or an IP address. ----@return string|nil ip_address the first IP address found for the hostname, or `nil` in case of an error. ----@return table|string resolved a table with all information returned by the resolver, or if an error occurs, the error message string. -function socket.dns.toip(address) end - ----Returns the time in seconds, relative to the system epoch (Unix epoch time since January 1, 1970 (UTC) or Windows file time since January 1, 1601 (UTC)). ----You should use the values returned by this function for relative measurements only. ----@return number seconds the number of seconds elapsed. -function socket.gettime() end - ----This function creates and returns a clean try function that allows for cleanup before the exception is raised. ----The `finalizer` function will be called in protected mode (see protect). ----@param finalizer function a function that will be called before the try throws the exception. ----@return function try the customized try function. -function socket.newtry(finalizer) end - ----Converts a function that throws exceptions into a safe function. This function only catches exceptions thrown by try functions. It does not catch normal Lua errors. ---- Beware that if your function performs some illegal operation that raises an error, the protected function will catch the error and return it as a string. This is because try functions uses errors as the mechanism to throw exceptions. ----@param func function a function that calls a try function (or assert, or error) to throw exceptions. ----@return fun(function) safe_func an equivalent function that instead of throwing exceptions, returns `nil` followed by an error message. -function socket.protect(func) end - ----The function returns a list with the sockets ready for reading, a list with the sockets ready for writing and an error message. The error message is "timeout" if a timeout condition was met and nil otherwise. The returned tables are doubly keyed both by integers and also by the sockets themselves, to simplify the test if a specific socket has changed status. ----`Recvt` and `sendt` parameters can be empty tables or `nil`. Non-socket values (or values with non-numeric indices) in these arrays will be silently ignored. ----The returned tables are doubly keyed both by integers and also by the sockets themselves, to simplify the test if a specific socket has changed status. ---- This function can monitor a limited number of sockets, as defined by the constant socket._SETSIZE. This number may be as high as 1024 or as low as 64 by default, depending on the system. It is usually possible to change this at compile time. Invoking select with a larger number of sockets will raise an error. ---- A known bug in WinSock causes select to fail on non-blocking TCP sockets. The function may return a socket as writable even though the socket is not ready for sending. ---- Calling select with a server socket in the receive parameter before a call to accept does not guarantee accept will return immediately. Use the settimeout method or accept might block forever. ---- If you close a socket and pass it to select, it will be ignored. ----(Using select with non-socket objects: Any object that implements `getfd` and `dirty` can be used with select, allowing objects from other libraries to be used within a socket.select driven loop.) ----@param recvt table array with the sockets to test for characters available for reading. ----@param sendt table array with sockets that are watched to see if it is OK to immediately write on them. ----@param timeout? number the maximum amount of time (in seconds) to wait for a change in status. Nil, negative or omitted timeout value allows the function to block indefinitely. ----@return table sockets_r a list with the sockets ready for reading. ----@return table sockets_w a list with the sockets ready for writing. ----@return string|nil error an error message. "timeout" if a timeout condition was met, otherwise `nil`. -function socket.select(recvt, sendt, timeout) end - ----This function drops a number of arguments and returns the remaining. ----It is useful to avoid creation of dummy variables: ----`D` is the number of arguments to drop. `Ret1` to `retN` are the arguments. ----The function returns `retD+1` to `retN`. ----@param d number the number of arguments to drop. ----@param ret1? any argument 1. ----@param ret2? any argument 2. ----@param retN? any argument N. ----@return any|nil retD+1 argument D+1. ----@return any|nil retD+2 argument D+2. ----@return any|nil retN argument N. -function socket.skip(d, ret1, ret2, retN) end - ----Freezes the program execution during a given amount of time. ----@param time number the number of seconds to sleep for. -function socket.sleep(time) end - ----Creates and returns an IPv4 TCP master object. A master object can be transformed into a server object with the method `listen` (after a call to `bind`) or into a client object with the method `connect`. The only other method supported by a master object is the `close` method. ----@return socket_master|nil tcp_master a new IPv4 TCP master object, or `nil` in case of error. ----@return string|nil error the error message, or `nil` if no error occurred. -function socket.tcp() end - ----Creates and returns an IPv6 TCP master object. A master object can be transformed into a server object with the method `listen` (after a call to `bind`) or into a client object with the method connect. The only other method supported by a master object is the close method. ----Note: The TCP object returned will have the option "ipv6-v6only" set to true. ----@return socket_master|nil tcp_master a new IPv6 TCP master object, or `nil` in case of error. ----@return string|nil error the error message, or `nil` if no error occurred. -function socket.tcp6() end - ----Creates and returns an unconnected IPv4 UDP object. Unconnected objects support the `sendto`, `receive`, `receivefrom`, `getoption`, `getsockname`, `setoption`, `settimeout`, `setpeername`, `setsockname`, and `close` methods. The `setpeername` method is used to connect the object. ----@return socket_unconnected|nil udp_unconnected a new unconnected IPv4 UDP object, or `nil` in case of error. ----@return string|nil error the error message, or `nil` if no error occurred. -function socket.udp() end - ----Creates and returns an unconnected IPv6 UDP object. Unconnected objects support the `sendto`, `receive`, `receivefrom`, `getoption`, `getsockname`, `setoption`, `settimeout`, `setpeername`, `setsockname`, and `close` methods. The `setpeername` method is used to connect the object. ----Note: The UDP object returned will have the option "ipv6-v6only" set to true. ----@return socket_unconnected|nil udp_unconnected a new unconnected IPv6 UDP object, or `nil` in case of error. ----@return string|nil error the error message, or `nil` if no error occurred. -function socket.udp6() end - ----This constant contains the maximum number of sockets that the select function can handle. -socket._SETSIZE = nil - ----This constant has a string describing the current LuaSocket version. -socket._VERSION = nil - -return socket \ No newline at end of file diff --git a/resources/defold_api/sound.lua b/resources/defold_api/sound.lua deleted file mode 100644 index 83ba0fa..0000000 --- a/resources/defold_api/sound.lua +++ /dev/null @@ -1,151 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Sound API documentation - - Functions and messages for controlling sound components and - mixer groups. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.sound -sound = {} - ----Get mixer group gain ----@param group string|hash group name ----@return number gain gain in [0 1] range ([-60dB.. 0dB]) -function sound.get_group_gain(group) end - ----Get a mixer group name as a string. ---- This function is to be used for debugging and ----development tooling only. The function does a reverse hash lookup, which does not ----return a proper string value when the game is built in release mode. ----@param group string|hash group name ----@return string name group name -function sound.get_group_name(group) end - ----Get a table of all mixer group names (hashes). ----@return hash[] groups table of mixer group names -function sound.get_groups() end - ----Get peak value from mixer group. ---- Note that gain is in linear scale, between 0 and 1. ----To get the dB value from the gain, use the formula `20 * log(gain)`. ----Inversely, to find the linear value from a dB value, use the formula ----`10db/20`. ----Also note that the returned value might be an approximation and in particular ----the effective window might be larger than specified. ----@param group string|hash group name ----@param window number window length in seconds ----@return number peak_l peak value for left channel ----@return number peak_r peak value for right channel -function sound.get_peak(group, window) end - ----Get RMS (Root Mean Square) value from mixer group. This value is the ----square root of the mean (average) value of the squared function of ----the instantaneous values. ----For instance: for a sinewave signal with a peak gain of -1.94 dB (0.8 linear), ----the RMS is `0.8 × 1/sqrt(2)` which is about 0.566. ---- Note the returned value might be an approximation and in particular ----the effective window might be larger than specified. ----@param group string|hash group name ----@param window number window length in seconds ----@return number rms_l RMS value for left channel ----@return number rms_r RMS value for right channel -function sound.get_rms(group, window) end - ----Checks if background music is playing, e.g. from iTunes. ---- On non mobile platforms, ----this function always return `false`. ---- On Android you can only get a correct reading ----of this state if your game is not playing any sounds itself. This is a limitation ----in the Android SDK. If your game is playing any sounds, *even with a gain of zero*, this ----function will return `false`. ----The best time to call this function is: ----- In the `init` function of your main collection script before any sounds are triggered ----- In a window listener callback when the window.WINDOW_EVENT_FOCUS_GAINED event is received ----Both those times will give you a correct reading of the state even when your application is ----swapped out and in while playing sounds and it works equally well on Android and iOS. ----@return boolean playing `true` if music is playing, otherwise `false`. -function sound.is_music_playing() end - ----Checks if a phone call is active. If there is an active phone call all ----other sounds will be muted until the phone call is finished. ---- On non mobile platforms, ----this function always return `false`. ----@return boolean call_active `true` if there is an active phone call, `false` otherwise. -function sound.is_phone_call_active() end - ----Pause all active voices ----@param url string|hash|url the sound that should pause ----@param pause boolean true if the sound should pause -function sound.pause(url, pause) end - ----Make the sound component play its sound. Multiple voices are supported. The limit is set to 32 voices per sound component. ---- A sound will continue to play even if the game object the sound component belonged to is deleted. You can call `sound.stop()` to stop the sound. ----@param url string|hash|url the sound that should play ----@param play_properties? { delay?:number, gain?:number, pan?:number, speed?:number, start_time?:number, start_frame?:number } optional table with properties: ----`delay` ----number delay in seconds before the sound starts playing, default is 0. ----`gain` ----number sound gain between 0 and 1, default is 1. The final gain of the sound will be a combination of this gain, the group gain and the master gain. ----`pan` ----number sound pan between -1 and 1, default is 0. The final pan of the sound will be an addition of this pan and the sound pan. ----`speed` ----number sound speed where 1.0 is normal speed, 0.5 is half speed and 2.0 is double speed. The final speed of the sound will be a multiplication of this speed and the sound speed. ----`start_time` ----number start playback offset (seconds). Optional, mutually exclusive with `start_frame`. ----`start_frame` ----number start playback offset (frames/samples). Optional, mutually exclusive with `start_time`. If both are provided, `start_frame` is used. ---- ----@param complete_function? fun(self, message_id, message, sender) function to call when the sound has finished playing or stopped manually via sound.stop. ---- ----`self` ----object The current object. ----`message_id` ----hash The name of the completion message, which can be either `"sound_done"` if the sound has finished playing, or `"sound_stopped"` if it was stopped manually. ----`message` ----table Information about the completion: ---- ---- ----- number `play_id` - the sequential play identifier that was given by the sound.play function. ---- ---- ----`sender` ----url The invoker of the callback: the sound component. ---- ----@return number play_id The identifier for the sound voice -function sound.play(url, play_properties, complete_function) end - ----Set gain on all active playing voices of a sound. ----@param url string|hash|url the sound to set the gain of ----@param gain? number sound gain between 0 and 1 [-60dB .. 0dB]. The final gain of the sound will be a combination of this gain, the group gain and the master gain. -function sound.set_gain(url, gain) end - ----Set mixer group gain ----@param group string|hash group name ----@param gain number gain in range [0..1] mapped to [0 .. -60dB] -function sound.set_group_gain(group, gain) end - ----Set panning on all active playing voices of a sound. ----The valid range is from -1.0 to 1.0, representing -45 degrees left, to +45 degrees right. ----@param url string|hash|url the sound to set the panning value to ----@param pan? number sound panning between -1.0 and 1.0 -function sound.set_pan(url, pan) end - ----Stop playing all active voices or just one voice if `play_id` provided ----@param url string|hash|url the sound component that should stop ----@param stop_properties? { play_id:number } optional table with properties: ----`play_id` ----number the sequential play identifier that should be stopped (was given by the sound.play() function) ---- -function sound.stop(url, stop_properties) end - -return sound \ No newline at end of file diff --git a/resources/defold_api/sprite.lua b/resources/defold_api/sprite.lua deleted file mode 100644 index f8028e6..0000000 --- a/resources/defold_api/sprite.lua +++ /dev/null @@ -1,66 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Sprite API documentation - - Functions, messages and properties used to manipulate sprite components. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.sprite -sprite = {} - ----Play an animation on a sprite component from its tile set ----An optional completion callback function can be provided that will be called when ----the animation has completed playing. If no function is provided, ----a animation_done message is sent to the script that started the animation. ----@param url string|hash|url the sprite that should play the animation ----@param id string|hash hashed id of the animation to play ----@param complete_function? fun(self, message_id, message, sender) function to call when the animation has completed. ---- ----`self` ----object The current object. ----`message_id` ----hash The name of the completion message, `"animation_done"`. ----`message` ----table Information about the completion: ---- ---- ----- number `current_tile` - the current tile of the sprite. ----- hash `id` - id of the animation that was completed. ---- ---- ----`sender` ----url The invoker of the callback: the sprite component. ---- ----@param play_properties? table optional table with properties: ---- ----`offset` ----number the normalized initial value of the animation cursor when the animation starts playing. ----`playback_rate` ----number the rate with which the animation will be played. Must be positive. ---- -function sprite.play_flipbook(url, id, complete_function, play_properties) end - ----Sets horizontal flipping of the provided sprite's animations. ----The sprite is identified by its URL. ----If the currently playing animation is flipped by default, flipping it again will make it appear like the original texture. ----@param url string|hash|url the sprite that should flip its animations ----@param flip boolean `true` if the sprite should flip its animations, `false` if not -function sprite.set_hflip(url, flip) end - ----Sets vertical flipping of the provided sprite's animations. ----The sprite is identified by its URL. ----If the currently playing animation is flipped by default, flipping it again will make it appear like the original texture. ----@param url string|hash|url the sprite that should flip its animations ----@param flip boolean `true` if the sprite should flip its animations, `false` if not -function sprite.set_vflip(url, flip) end - -return sprite \ No newline at end of file diff --git a/resources/defold_api/sys.lua b/resources/defold_api/sys.lua deleted file mode 100644 index f7f7b4f..0000000 --- a/resources/defold_api/sys.lua +++ /dev/null @@ -1,316 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - System API documentation - - Functions and messages for using system resources, controlling the engine, - error handling and debugging. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.sys -sys = {} - ----deserializes buffer into a lua table ----@param buffer string buffer to deserialize from ----@return table table lua table with deserialized data -function sys.deserialize(buffer) end - ----Check if a path exists ----Good for checking if a file exists before loading a large file ----@param path string path to check ----@return boolean result `true` if the path exists, `false` otherwise -function sys.exists(path) end - ----Terminates the game application and reports the specified `code` to the OS. ----@param code number exit code to report to the OS, 0 means clean exit -function sys.exit(code) end - ----Returns a table with application information for the requested app. ---- On iOS, the `app_string` is an url scheme for the app that is queried. Your ----game needs to list the schemes that are queried in an `LSApplicationQueriesSchemes` array ----in a custom "Info.plist". ---- On Android, the `app_string` is the package identifier for the app. ----@param app_string string platform specific string with application package or query, see above for details. ----@return { installed:boolean } app_info table with application information in the following fields: ---- ----`installed` ----boolean `true` if the application is installed, `false` otherwise. ---- -function sys.get_application_info(app_string) end - ----The path from which the application is run. ----@return string path path to application executable -function sys.get_application_path() end - ----Get integer config value from the game.project configuration file with optional default value ----@param key string key to get value for. The syntax is SECTION.KEY ----@param default_value? number (optional) default value to return if the value does not exist ----@return number value config value as an integer. default_value if the config key does not exist. 0 if no default value was supplied. -function sys.get_config_int(key, default_value) end - ----Get number config value from the game.project configuration file with optional default value ----@param key string key to get value for. The syntax is SECTION.KEY ----@param default_value? number (optional) default value to return if the value does not exist ----@return number value config value as an number. default_value if the config key does not exist. 0 if no default value was supplied. -function sys.get_config_number(key, default_value) end - ----Get string config value from the game.project configuration file with optional default value ----@param key string key to get value for. The syntax is SECTION.KEY ----@param default_value? string (optional) default value to return if the value does not exist ----@return string value config value as a string. default_value if the config key does not exist. nil if no default value was supplied. -function sys.get_config_string(key, default_value) end - ---- Returns the current network connectivity status ----on mobile platforms. ----On desktop, this function always return `sys.NETWORK_CONNECTED`. ----@return constant status network connectivity status: ---- ----- `sys.NETWORK_DISCONNECTED` (no network connection is found) ----- `sys.NETWORK_CONNECTED_CELLULAR` (connected through mobile cellular) ----- `sys.NETWORK_CONNECTED` (otherwise, Wifi) ---- -function sys.get_connectivity() end - ----Returns a table with engine information. ----@return { version:string, version_sha1:string, is_debug:boolean } engine_info table with engine information in the following fields: ---- ----`version` ----string The current Defold engine version, i.e. "1.2.96" ----`version_sha1` ----string The SHA1 for the current engine build, i.e. "0060183cce2e29dbd09c85ece83cbb72068ee050" ----`is_debug` ----boolean If the engine is a debug or release version ---- -function sys.get_engine_info() end - ----Create a path to the host device for unit testing ----Useful for saving logs etc during development ----@param filename string file to read from ----@return string host_path the path prefixed with the proper host mount -function sys.get_host_path(filename) end - ----Returns an array of tables with information on network interfaces. ----@return { name:string, address?:string, mac?:string, up:boolean, running:boolean } ifaddrs an array of tables. Each table entry contain the following fields: ---- ----`name` ----string Interface name ----`address` ----string IP address. might be `nil` if not available. ----`mac` ----string Hardware MAC address. might be nil if not available. ----`up` ----boolean `true` if the interface is up (available to transmit and receive data), `false` otherwise. ----`running` ----boolean `true` if the interface is running, `false` otherwise. ---- -function sys.get_ifaddrs() end - ----The save-file path is operating system specific and is typically located under the user's home directory. ----@param application_id string user defined id of the application, which helps define the location of the save-file ----@param file_name string file-name to get path for ----@return string path path to save-file -function sys.get_save_file(application_id, file_name) end - ----Returns a table with system information. ----@param options? { ignore_secure?:boolean } optional options table ----- ignore_secure boolean this flag ignores values might be secured by OS e.g. `device_ident` ----@return { device_model?:string, manufacturer?:string, system_name:string, system_version:string, api_version:string, language:string, device_language:string, territory:string, gmt_offset:number, device_ident?:string, user_agent?:string } sys_info table with system information in the following fields: ---- ----`device_model` ----string Only available on iOS and Android. ----`manufacturer` ----string Only available on iOS and Android. ----`system_name` ----string The system name: "Darwin", "Linux", "Windows", "HTML5", "Android" or "iPhone OS" ----`system_version` ----string The system OS version. ----`api_version` ----string The API version on the system. ----`language` ----string Two character ISO-639 format, i.e. "en". ----`device_language` ----string Two character ISO-639 format (i.e. "sr") and, if applicable, followed by a dash (-) and an ISO 15924 script code (i.e. "sr-Cyrl" or "sr-Latn"). Reflects the device preferred language. ----`territory` ----string Two character ISO-3166 format, i.e. "US". ----`gmt_offset` ----number The current offset from GMT (Greenwich Mean Time), in minutes. ----`device_ident` ----string This value secured by OS. "identifierForVendor" on iOS. "android_id" on Android. On Android, you need to add `READ_PHONE_STATE` permission to be able to get this data. We don't use this permission in Defold. ----`user_agent` ----string The HTTP user agent, i.e. "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_3) AppleWebKit/602.4.8 (KHTML, like Gecko) Version/10.0.3 Safari/602.4.8" ---- -function sys.get_sys_info(options) end - ----If the file exists, it must have been created by `sys.save` to be loaded. ----@param filename string file to read from ----@return table loaded lua table, which is empty if the file could not be found -function sys.load(filename) end - ----The sys.load_buffer function will first try to load the resource ----from any of the mounted resource locations and return the data if ----any matching entries found. If not, the path will be tried ----as is from the primary disk on the device. ----In order for the engine to include custom resources in the build process, you need ----to specify them in the "custom_resources" key in your "game.project" settings file. ----You can specify single resource files or directories. If a directory is included ----in the resource list, all files and directories in that directory is recursively ----included: ----For example "main/data/,assets/level_data.json". ----@param path string the path to load the buffer from ----@return buffer_data buffer the buffer with data -function sys.load_buffer(path) end - ----The sys.load_buffer function will first try to load the resource ----from any of the mounted resource locations and return the data if ----any matching entries found. If not, the path will be tried ----as is from the primary disk on the device. ----In order for the engine to include custom resources in the build process, you need ----to specify them in the "custom_resources" key in your "game.project" settings file. ----You can specify single resource files or directories. If a directory is included ----in the resource list, all files and directories in that directory is recursively ----included: ----For example "main/data/,assets/level_data.json". ----Note that issuing multiple requests of the same resource will yield ----individual buffers per request. There is no implic caching of the buffers ----based on request path. ----@param path string the path to load the buffer from ----@param status_callback fun(self, request_id, result) A status callback that will be invoked when a request has been handled, or an error occured. The result is a table containing: ---- ----`status` ----number The status of the request, supported values are: ---- ---- ----- `resource.REQUEST_STATUS_FINISHED` ----- `resource.REQUEST_STATUS_ERROR_IO_ERROR` ----- `resource.REQUEST_STATUS_ERROR_NOT_FOUND` ---- ---- ----`buffer` ----buffer If the request was successfull, this will contain the request payload in a buffer object, and nil otherwise. Make sure to check the status before doing anything with the buffer value! ---- ----@return number handle a handle to the request -function sys.load_buffer_async(path, status_callback) end - ----Loads a custom resource. Specify the full filename of the resource that you want ----to load. When loaded, the file data is returned as a string. ----If loading fails, the function returns `nil` plus the error message. ----In order for the engine to include custom resources in the build process, you need ----to specify them in the "custom_resources" key in your "game.project" settings file. ----You can specify single resource files or directories. If a directory is included ----in the resource list, all files and directories in that directory is recursively ----included: ----For example "main/data/,assets/level_data.json". ----@param filename string resource to load, full path ----@return string|nil data loaded data, or `nil` if the resource could not be loaded ----@return string|nil error the error message, or `nil` if no error occurred -function sys.load_resource(filename) end - ----Open URL in default application, typically a browser ----@param url string url to open ----@param attributes? { target?:string, name?:string } table with attributes ----`target` ----- string : Optional. Specifies the target attribute or the name of the window. The following values are supported: ----- `_self` - (default value) URL replaces the current page. ----- `_blank` - URL is loaded into a new window, or tab. ----- `_parent` - URL is loaded into the parent frame. ----- `_top` - URL replaces any framesets that may be loaded. ----- `name` - The name of the window (Note: the name does not specify the title of the new window). ----@return boolean success a boolean indicating if the url could be opened or not -function sys.open_url(url, attributes) end - ----Reboots the game engine with a specified set of arguments. ----Arguments will be translated into command line arguments. Calling reboot ----function is equivalent to starting the engine with the same arguments. ----On startup the engine reads configuration from "game.project" in the ----project root. ----@param arg1? string argument 1 ----@param arg2? string argument 2 ----@param arg3? string argument 3 ----@param arg4? string argument 4 ----@param arg5? string argument 5 ----@param arg6? string argument 6 -function sys.reboot(arg1, arg2, arg3, arg4, arg5, arg6) end - ----The table can later be loaded by `sys.load`. ----Use `sys.get_save_file` to obtain a valid location for the file. ----Internally, this function uses a workspace buffer sized output file sized 512kb. ----This size reflects the output file size which must not exceed this limit. ----Additionally, the total number of rows that any one table may contain is limited to 65536 ----(i.e. a 16 bit range). When tables are used to represent arrays, the values of ----keys are permitted to fall within a 32 bit range, supporting sparse arrays, however ----the limit on the total number of rows remains in effect. ----@param filename string file to write to ----@param table table lua table to save ----@return boolean success a boolean indicating if the table could be saved or not -function sys.save(filename, table) end - ----The buffer can later deserialized by `sys.deserialize`. ----This method has all the same limitations as `sys.save`. ----@param table table lua table to serialize ----@return string buffer serialized data buffer -function sys.serialize(table) end - ----Sets the host that is used to check for network connectivity against. ----@param host string hostname to check against -function sys.set_connectivity_host(host) end - ----Set the Lua error handler function. ----The error handler is a function which is called whenever a lua runtime error occurs. ----@param error_handler fun(source, message, traceback) the function to be called on error ---- ----`source` ----string The runtime context of the error. Currently, this is always `"lua"`. ----`message` ----string The source file, line number and error message. ----`traceback` ----string The stack traceback. ---- -function sys.set_error_handler(error_handler) end - ----Set game update-frequency (frame cap). This option is equivalent to `display.update_frequency` in ----the "game.project" settings but set in run-time. If `Vsync` checked in "game.project", the rate will ----be clamped to a swap interval that matches any detected main monitor refresh rate. If `Vsync` is ----unchecked the engine will try to respect the rate in software using timers. There is no ----guarantee that the frame cap will be achieved depending on platform specifics and hardware settings. ----@param frequency number target frequency. 60 for 60 fps -function sys.set_update_frequency(frequency) end - ----Set the vsync swap interval. The interval with which to swap the front and back buffers ----in sync with vertical blanks (v-blank), the hardware event where the screen image is updated ----with data from the front buffer. A value of 1 swaps the buffers at every v-blank, a value of ----2 swaps the buffers every other v-blank and so on. A value of 0 disables waiting for v-blank ----before swapping the buffers. Default value is 1. ----When setting the swap interval to 0 and having `vsync` disabled in ----"game.project", the engine will try to respect the set frame cap value from ----"game.project" in software instead. ----This setting may be overridden by driver settings. ----@param swap_interval number target swap interval. -function sys.set_vsync_swap_interval(swap_interval) end - ----network connected through other, non cellular, connection -sys.NETWORK_CONNECTED = nil - ----network connected through mobile cellular -sys.NETWORK_CONNECTED_CELLULAR = nil - ----no network connection found -sys.NETWORK_DISCONNECTED = nil - ----an asyncronous request is unable to read the resource -sys.REQUEST_STATUS_ERROR_IO_ERROR = nil - ----an asyncronous request is unable to locate the resource -sys.REQUEST_STATUS_ERROR_NOT_FOUND = nil - ----an asyncronous request has finished successfully -sys.REQUEST_STATUS_FINISHED = nil - -return sys \ No newline at end of file diff --git a/resources/defold_api/tilemap.lua b/resources/defold_api/tilemap.lua deleted file mode 100644 index 0729eee..0000000 --- a/resources/defold_api/tilemap.lua +++ /dev/null @@ -1,113 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Tilemap API documentation - - Functions and messages used to manipulate tile map components. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.tilemap -tilemap = {} - ----Get the bounds for a tile map. This function returns multiple values: ----The lower left corner index x and y coordinates (1-indexed), ----the tile map width and the tile map height. ----The resulting values take all tile map layers into account, meaning that ----the bounds are calculated as if all layers were collapsed into one. ----@param url string|hash|url the tile map ----@return number x x coordinate of the bottom left corner ----@return number y y coordinate of the bottom left corner ----@return number w number of columns (width) in the tile map ----@return number h number of rows (height) in the tile map -function tilemap.get_bounds(url) end - ----Get the tile set at the specified position in the tilemap. ----The position is identified by the tile index starting at origin ----with index 1, 1. (see tilemap.set_tile()) ----Which tile map and layer to query is identified by the URL and the ----layer name parameters. ----@param url string|hash|url the tile map ----@param layer string|hash name of the layer for the tile ----@param x number x-coordinate of the tile ----@param y number y-coordinate of the tile ----@return number tile index of the tile -function tilemap.get_tile(url, layer, x, y) end - ----Get the tile information at the specified position in the tilemap. ----The position is identified by the tile index starting at origin ----with index 1, 1. (see tilemap.set_tile()) ----Which tile map and layer to query is identified by the URL and the ----layer name parameters. ----@param url string|hash|url the tile map ----@param layer string|hash name of the layer for the tile ----@param x number x-coordinate of the tile ----@param y number y-coordinate of the tile ----@return table tile_info index of the tile -function tilemap.get_tile_info(url, layer, x, y) end - ----Retrieves all the tiles for the specified layer in the tilemap. ----It returns a table of rows where the keys are the ----tile positions (see tilemap.get_bounds()). ----You can iterate it using `tiles[row_index][column_index]`. ----@param url string|hash|url the tilemap ----@param layer string|hash the name of the layer for the tiles ----@return table tiles a table of rows representing the layer -function tilemap.get_tiles(url, layer) end - ----Replace a tile in a tile map with a new tile. ----The coordinates of the tiles are indexed so that the "first" tile just ----above and to the right of origin has coordinates 1,1. ----Tiles to the left of and below origin are indexed 0, -1, -2 and so forth. ----+-------+-------+------+------+ ----| 0,3 | 1,3 | 2,3 | 3,3 | ----+-------+-------+------+------+ ----| 0,2 | 1,2 | 2,2 | 3,2 | ----+-------+-------+------+------+ ----| 0,1 | 1,1 | 2,1 | 3,1 | ----+-------O-------+------+------+ ----| 0,0 | 1,0 | 2,0 | 3,0 | ----+-------+-------+------+------+ ----The coordinates must be within the bounds of the tile map as it were created. ----That is, it is not possible to extend the size of a tile map by setting tiles outside the edges. ----To clear a tile, set the tile to number 0. Which tile map and layer to manipulate is identified by the URL and the layer name parameters. ----Transform bitmask is arithmetic sum of one or both FLIP constants (`tilemap.H_FLIP`, `tilemap.V_FLIP`) and/or one of ROTATION constants ----(`tilemap.ROTATE_90`, `tilemap.ROTATE_180`, `tilemap.ROTATE_270`). ----Flip always applies before rotation (clockwise). ----@param url string|hash|url the tile map ----@param layer string|hash name of the layer for the tile ----@param x number x-coordinate of the tile ----@param y number y-coordinate of the tile ----@param tile number index of new tile to set. 0 resets the cell ----@param transform_bitmask? number optional flip and/or rotation should be applied to the tile -function tilemap.set_tile(url, layer, x, y, tile, transform_bitmask) end - ----Sets the visibility of the tilemap layer ----@param url string|hash|url the tile map ----@param layer string|hash name of the layer for the tile ----@param visible boolean should the layer be visible -function tilemap.set_visible(url, layer, visible) end - ----flip tile horizontally -tilemap.H_FLIP = nil - ----rotate tile 180 degrees clockwise -tilemap.ROTATE_180 = nil - ----rotate tile 270 degrees clockwise -tilemap.ROTATE_270 = nil - ----rotate tile 90 degrees clockwise -tilemap.ROTATE_90 = nil - ----flip tile vertically -tilemap.V_FLIP = nil - -return tilemap \ No newline at end of file diff --git a/resources/defold_api/timer.lua b/resources/defold_api/timer.lua deleted file mode 100644 index 25d670c..0000000 --- a/resources/defold_api/timer.lua +++ /dev/null @@ -1,70 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Timer API documentation - - Timers allow you to set a delay and a callback to be called when the timer completes. - The timers created with this API are updated with the collection timer where they - are created. If you pause or speed up the collection (using `set_time_step`) it will - also affect the new timer. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.timer -timer = {} - ----You may cancel a timer from inside a timer callback. ----Cancelling a timer that is already executed or cancelled is safe. ----@param handle number the timer handle returned by timer.delay() ----@return boolean true if the timer was active, false if the timer is already cancelled / complete -function timer.cancel(handle) end - ----Adds a timer and returns a unique handle. ----You may create more timers from inside a timer callback. ----Using a delay of 0 will result in a timer that triggers at the next frame just before ----script update functions. ----If you want a timer that triggers on each frame, set delay to 0.0f and repeat to true. ----Timers created within a script will automatically die when the script is deleted. ----@param delay number time interval in seconds ----@param repeating boolean true = repeat timer until cancel, false = one-shot timer ----@param callback fun(self, handle, time_elapsed) timer callback function ---- ----`self` ----object The current object ----`handle` ----number The handle of the timer ----`time_elapsed` ----number The elapsed time - on first trigger it is time since timer.delay call, otherwise time since last trigger ---- ----@return number handle identifier for the create timer, returns timer.INVALID_TIMER_HANDLE if the timer can not be created -function timer.delay(delay, repeating, callback) end - ----Get information about timer. ----@param handle number the timer handle returned by timer.delay() ----@return { time_remaining:number, delay:number, repeating:boolean }|nil data table or `nil` if timer is cancelled/completed. table with data in the following fields: ---- ----`time_remaining` ----number Time remaining until the next time a timer.delay() fires. ----`delay` ----number Time interval. ----`repeating` ----boolean true = repeat timer until cancel, false = one-shot timer. ---- -function timer.get_info(handle) end - ----Manual triggering a callback for a timer. ----@param handle number the timer handle returned by timer.delay() ----@return boolean true if the timer was active, false if the timer is already cancelled / complete -function timer.trigger(handle) end - ----Indicates an invalid timer handle -timer.INVALID_TIMER_HANDLE = nil - -return timer \ No newline at end of file diff --git a/resources/defold_api/types.lua b/resources/defold_api/types.lua deleted file mode 100644 index a2a52e1..0000000 --- a/resources/defold_api/types.lua +++ /dev/null @@ -1,55 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Types API documentation - - Functions for checking Defold userdata types. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.types -types = {} - ----Check if passed type is hash. ----@param var any Variable to check type ----@return boolean result True if passed type is hash -function types.is_hash(var) end - ----Check if passed type is matrix4. ----@param var any Variable to check type ----@return boolean result True if passed type is matrix4 -function types.is_matrix4(var) end - ----Check if passed type is quaternion. ----@param var any Variable to check type ----@return boolean result True if passed type is quaternion -function types.is_quat(var) end - ----Check if passed type is URL. ----@param var any Variable to check type ----@return boolean result True if passed type is URL -function types.is_url(var) end - ----Check if passed type is vector. ----@param var any Variable to check type ----@return boolean result True if passed type is vector -function types.is_vector(var) end - ----Check if passed type is vector3. ----@param var any Variable to check type ----@return boolean result True if passed type is vector3 -function types.is_vector3(var) end - ----Check if passed type is vector4. ----@param var any Variable to check type ----@return boolean result True if passed type is vector4 -function types.is_vector4(var) end - -return types \ No newline at end of file diff --git a/resources/defold_api/vmath.lua b/resources/defold_api/vmath.lua deleted file mode 100644 index 3b4256e..0000000 --- a/resources/defold_api/vmath.lua +++ /dev/null @@ -1,471 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Vector math API documentation - - Functions for mathematical operations on vectors, matrices and quaternions. - - The vector types (`vmath.vector3` and `vmath.vector4`) supports addition and subtraction - with vectors of the same type. Vectors can be negated and multiplied (scaled) or divided by numbers. - - The quaternion type (`vmath.quat`) supports multiplication with other quaternions. - - The matrix type (`vmath.matrix4`) can be multiplied with numbers, other matrices - and `vmath.vector4` values. - - All types performs equality comparison by each component value. - The following components are available for the various types: - vector3 - : `x`, `y` and `z`. Example: `v.y` - vector4 - : `x`, `y`, `z`, and `w`. Example: `v.w` - quaternion - : `x`, `y`, `z`, and `w`. Example: `q.w` - matrix4 - : `m00` to `m33` where the first number is the row (starting from 0) and the second - number is the column. Columns can be accessed with `c0` to `c3`, returning a `vector4`. - Example: `m.m21` which is equal to `m.c1.z` - vector - : indexed by number 1 to the vector length. Example: `v[3]` ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.vmath -vmath = {} - ----Clamp input value to be in range of [min, max]. In case if input value has vector3|vector4 type ----return new vector3|vector4 with clamped value at every vector's element. ----Min/max arguments can be vector3|vector4. In that case clamp excuted per every vector's element ----@generic T: number|vector3|vector4 ----@param value T Input value or vector of values ----@param min T Min value(s) border ----@param max T Max value(s) border ----@return T clamped_value Clamped value or vector -function vmath.clamp(value, min, max) end - ----Calculates the conjugate of a quaternion. The result is a ----quaternion with the same magnitudes but with the sign of ----the imaginary (vector) parts changed: ----`q* = [w, -v]` ----@param q1 quaternion quaternion of which to calculate the conjugate ----@return quaternion q the conjugate -function vmath.conj(q1) end - ----Given two linearly independent vectors P and Q, the cross product, ----P × Q, is a vector that is perpendicular to both P and Q and ----therefore normal to the plane containing them. ----If the two vectors have the same direction (or have the exact ----opposite direction from one another, i.e. are not linearly independent) ----or if either one has zero length, then their cross product is zero. ----@param v1 vector3 first vector ----@param v2 vector3 second vector ----@return vector3 v a new vector representing the cross product -function vmath.cross(v1, v2) end - ----The returned value is a scalar defined as: ----`P ⋅ Q = |P| |Q| cos θ` ----where θ is the angle between the vectors P and Q. ----- If the dot product is positive then the angle between the vectors is below 90 degrees. ----- If the dot product is zero the vectors are perpendicular (at right-angles to each other). ----- If the dot product is negative then the angle between the vectors is more than 90 degrees. ----@generic T: vector3|vector4 ----@param v1 T first vector ----@param v2 T second vector ----@return number n dot product -function vmath.dot(v1, v2) end - ----Converts euler angles (x, y, z) in degrees into a quaternion ----The error is guaranteed to be less than 0.001. ----If the first argument is vector3, its values are used as x, y, z angles. ----@param x number|vector3 rotation around x-axis in degrees or vector3 with euler angles in degrees ----@param y number? rotation around y-axis in degrees ----@param z number? rotation around z-axis in degrees ----@return quaternion q quaternion describing an equivalent rotation (231 (YZX) rotation sequence) -function vmath.euler_to_quat(x, y, z) end - ----The resulting matrix is the inverse of the supplied matrix. ---- For ortho-normal matrices, e.g. regular object transformation, ----use `vmath.ortho_inv()` instead. ----The specialized inverse for ortho-normalized matrices is much faster ----than the general inverse. ----@param m1 matrix4 matrix to invert ----@return matrix4 m inverse of the supplied matrix -function vmath.inv(m1) end - ----Returns the length of the supplied vector or quaternion. ----If you are comparing the lengths of vectors or quaternions, you should compare ----the length squared instead as it is slightly more efficient to calculate ----(it eliminates a square root calculation). ----@param v vector3|vector4|quaternion value of which to calculate the length ----@return number n length -function vmath.length(v) end - ----Returns the squared length of the supplied vector or quaternion. ----@param v vector3|vector4|quaternion value of which to calculate the squared length ----@return number n squared length -function vmath.length_sqr(v) end - ----Linearly interpolate between two quaternions. Linear ----interpolation of rotations are only useful for small ----rotations. For interpolations of arbitrary rotations, ----vmath.slerp yields much better results. ---- The function does not clamp t between 0 and 1. ----@param t number interpolation parameter, 0-1 ----@param q1 quaternion quaternion to lerp from ----@param q2 quaternion quaternion to lerp to ----@return quaternion q the lerped quaternion -function vmath.lerp(t, q1, q2) end - ----Linearly interpolate between two vectors. The function ----treats the vectors as positions and interpolates between ----the positions in a straight line. Lerp is useful to describe ----transitions from one place to another over time. ---- The function does not clamp t between 0 and 1. ----@generic T: vector3|vector4 ----@param t number interpolation parameter, 0-1 ----@param v1 T vector to lerp from ----@param v2 T vector to lerp to ----@return T v the lerped vector -function vmath.lerp(t, v1, v2) end - ----Linearly interpolate between two values. Lerp is useful ----to describe transitions from one value to another over time. ---- The function does not clamp t between 0 and 1. ----@param t number interpolation parameter, 0-1 ----@param n1 number number to lerp from ----@param n2 number number to lerp to ----@return number n the lerped number -function vmath.lerp(t, n1, n2) end - ----Creates a new matrix with all components set to the ----corresponding values from the supplied matrix. I.e. ----the function creates a copy of the given matrix. ----@param m1 matrix4 existing matrix ----@return matrix4 m matrix which is a copy of the specified matrix -function vmath.matrix4(m1) end - ----The resulting identity matrix describes a transform with ----no translation or rotation. ----@return matrix4 m identity matrix -function vmath.matrix4() end - ----The resulting matrix describes a rotation around the axis by the specified angle. ----@param v vector3 axis ----@param angle number angle in radians ----@return matrix4 m matrix represented by axis and angle -function vmath.matrix4_axis_angle(v, angle) end - ----Creates a new matrix constructed from separate ----translation vector, roation quaternion and scale vector ----@param translation vector3|vector4 translation ----@param rotation quaternion rotation ----@param scale vector3 scale ----@return matrix4 matrix new matrix4 -function vmath.matrix4_compose(translation, rotation, scale) end - ----Constructs a frustum matrix from the given values. The left, right, ----top and bottom coordinates of the view cone are expressed as distances ----from the center of the near clipping plane. The near and far coordinates ----are expressed as distances from the tip of the view frustum cone. ----@param left number coordinate for left clipping plane ----@param right number coordinate for right clipping plane ----@param bottom number coordinate for bottom clipping plane ----@param top number coordinate for top clipping plane ----@param near number coordinate for near clipping plane ----@param far number coordinate for far clipping plane ----@return matrix4 m matrix representing the frustum -function vmath.matrix4_frustum(left, right, bottom, top, near, far) end - ----The resulting matrix is created from the supplied look-at parameters. ----This is useful for constructing a view matrix for a camera or ----rendering in general. ----@param eye vector3 eye position ----@param look_at vector3 look-at position ----@param up vector3 up vector ----@return matrix4 m look-at matrix -function vmath.matrix4_look_at(eye, look_at, up) end - ----Creates an orthographic projection matrix. ----This is useful to construct a projection matrix for a camera or rendering in general. ----@param left number coordinate for left clipping plane ----@param right number coordinate for right clipping plane ----@param bottom number coordinate for bottom clipping plane ----@param top number coordinate for top clipping plane ----@param near number coordinate for near clipping plane ----@param far number coordinate for far clipping plane ----@return matrix4 m orthographic projection matrix -function vmath.matrix4_orthographic(left, right, bottom, top, near, far) end - ----Creates a perspective projection matrix. ----This is useful to construct a projection matrix for a camera or rendering in general. ----@param fov number angle of the full vertical field of view in radians ----@param aspect number aspect ratio ----@param near number coordinate for near clipping plane ----@param far number coordinate for far clipping plane ----@return matrix4 m perspective projection matrix -function vmath.matrix4_perspective(fov, aspect, near, far) end - ----The resulting matrix describes the same rotation as the quaternion, but does not have any translation (also like the quaternion). ----@param q quaternion quaternion to create matrix from ----@return matrix4 m matrix represented by quaternion -function vmath.matrix4_quat(q) end - ----The resulting matrix describes a rotation around the x-axis ----by the specified angle. ----@param angle number angle in radians around x-axis ----@return matrix4 m matrix from rotation around x-axis -function vmath.matrix4_rotation_x(angle) end - ----The resulting matrix describes a rotation around the y-axis ----by the specified angle. ----@param angle number angle in radians around y-axis ----@return matrix4 m matrix from rotation around y-axis -function vmath.matrix4_rotation_y(angle) end - ----The resulting matrix describes a rotation around the z-axis ----by the specified angle. ----@param angle number angle in radians around z-axis ----@return matrix4 m matrix from rotation around z-axis -function vmath.matrix4_rotation_z(angle) end - ----Creates a new matrix constructed from scale vector ----@param scale vector3 scale ----@return matrix4 matrix new matrix4 -function vmath.matrix4_scale(scale) end - ----creates a new matrix4 from uniform scale ----@param scale number scale ----@return matrix4 matrix new matrix4 -function vmath.matrix4_scale(scale) end - ----Creates a new matrix4 from three scale components ----@param scale_x number scale along X axis ----@param scale_y number sclae along Y axis ----@param scale_z number scale along Z asis ----@return matrix4 matrix new matrix4 -function vmath.matrix4_scale(scale_x, scale_y, scale_z) end - ----The resulting matrix describes a translation of a point ----in euclidean space. ----@param position vector3|vector4 position vector to create matrix from ----@return matrix4 m matrix from the supplied position vector -function vmath.matrix4_translation(position) end - ----Performs an element wise multiplication between two vectors of the same type ----The returned value is a vector defined as (e.g. for a vector3): ----`v = vmath.mul_per_elem(a, b) = vmath.vector3(a.x * b.x, a.y * b.y, a.z * b.z)` ----@generic T: vector3|vector4 ----@param v1 T first vector ----@param v2 T second vector ----@return T v multiplied vector -function vmath.mul_per_elem(v1, v2) end - ----Normalizes a vector, i.e. returns a new vector with the same ----direction as the input vector, but with length 1. ---- The length of the vector must be above 0, otherwise a ----division-by-zero will occur. ----@generic T: vector3|vector4|quaternion ----@param v1 T vector to normalize ----@return T v new normalized vector -function vmath.normalize(v1) end - ----The resulting matrix is the inverse of the supplied matrix. ----The supplied matrix has to be an ortho-normal matrix, e.g. ----describe a regular object transformation. ---- For matrices that are not ortho-normal ----use the general inverse `vmath.inv()` instead. ----@param m1 matrix4 ortho-normalized matrix to invert ----@return matrix4 m inverse of the supplied matrix -function vmath.ortho_inv(m1) end - ----Calculates the extent the projection of the first vector onto the second. ----The returned value is a scalar p defined as: ----`p = |P| cos θ / |Q|` ----where θ is the angle between the vectors P and Q. ----@param v1 vector3 vector to be projected on the second ----@param v2 vector3 vector onto which the first will be projected, must not have zero length ----@return number n the projected extent of the first vector onto the second -function vmath.project(v1, v2) end - ----Creates a new identity quaternion. The identity ----quaternion is equal to: ----`vmath.quat(0, 0, 0, 1)` ----@return quaternion q new identity quaternion -function vmath.quat() end - ----Creates a new quaternion with all components set to the ----corresponding values from the supplied quaternion. I.e. ----This function creates a copy of the given quaternion. ----@param q1 quaternion existing quaternion ----@return quaternion q new quaternion -function vmath.quat(q1) end - ----Creates a new quaternion with the components set ----according to the supplied parameter values. ----@param x number x coordinate ----@param y number y coordinate ----@param z number z coordinate ----@param w number w coordinate ----@return quaternion q new quaternion -function vmath.quat(x, y, z, w) end - ----The resulting quaternion describes a rotation of `angle` ----radians around the axis described by the unit vector `v`. ----@param v vector3 axis ----@param angle number angle ----@return quaternion q quaternion representing the axis-angle rotation -function vmath.quat_axis_angle(v, angle) end - ----The resulting quaternion describes the rotation from the ----identity quaternion (no rotation) to the coordinate system ----as described by the given x, y and z base unit vectors. ----@param x vector3 x base vector ----@param y vector3 y base vector ----@param z vector3 z base vector ----@return quaternion q quaternion representing the rotation of the specified base vectors -function vmath.quat_basis(x, y, z) end - ----The resulting quaternion describes the rotation that, ----if applied to the first vector, would rotate the first ----vector to the second. The two vectors must be unit ----vectors (of length 1). ---- The result is undefined if the two vectors point in opposite directions ----@param v1 vector3 first unit vector, before rotation ----@param v2 vector3 second unit vector, after rotation ----@return quaternion q quaternion representing the rotation from first to second vector -function vmath.quat_from_to(v1, v2) end - ----Creates a new quaternion with the components set ----according to the supplied parameter values. ----@param matrix matrix4 source matrix4 ----@return quaternion q new quaternion -function vmath.quat_matrix4(matrix) end - ----The resulting quaternion describes a rotation of `angle` ----radians around the x-axis. ----@param angle number angle in radians around x-axis ----@return quaternion q quaternion representing the rotation around the x-axis -function vmath.quat_rotation_x(angle) end - ----The resulting quaternion describes a rotation of `angle` ----radians around the y-axis. ----@param angle number angle in radians around y-axis ----@return quaternion q quaternion representing the rotation around the y-axis -function vmath.quat_rotation_y(angle) end - ----The resulting quaternion describes a rotation of `angle` ----radians around the z-axis. ----@param angle number angle in radians around z-axis ----@return quaternion q quaternion representing the rotation around the z-axis -function vmath.quat_rotation_z(angle) end - ----Converts a quaternion into euler angles (r0, r1, r2), based on YZX rotation order. ----To handle gimbal lock (singularity at r1 ~ +/- 90 degrees), the cut off is at r0 = +/- 88.85 degrees, which snaps to +/- 90. ----The provided quaternion is expected to be normalized. ----The error is guaranteed to be less than +/- 0.02 degrees ----@param q quaternion source quaternion ----@return number x euler angle x in degrees ----@return number y euler angle y in degrees ----@return number z euler angle z in degrees -function vmath.quat_to_euler(q) end - ----Returns a new vector from the supplied vector that is ----rotated by the rotation described by the supplied ----quaternion. ----@param q quaternion quaternion ----@param v1 vector3 vector to rotate ----@return vector3 v the rotated vector -function vmath.rotate(q, v1) end - ----Slerp travels the torque-minimal path maintaining constant ----velocity, which means it travels along the straightest path along ----the rounded surface of a sphere. Slerp is useful for interpolation ----of rotations. ----Slerp travels the torque-minimal path, which means it travels ----along the straightest path the rounded surface of a sphere. ---- The function does not clamp t between 0 and 1. ----@param t number interpolation parameter, 0-1 ----@param q1 quaternion quaternion to slerp from ----@param q2 quaternion quaternion to slerp to ----@return quaternion q the slerped quaternion -function vmath.slerp(t, q1, q2) end - ----Spherically interpolates between two vectors. The difference to ----lerp is that slerp treats the vectors as directions instead of ----positions in space. ----The direction of the returned vector is interpolated by the angle ----and the magnitude is interpolated between the magnitudes of the ----from and to vectors. ---- Slerp is computationally more expensive than lerp. ----The function does not clamp t between 0 and 1. ----@generic T: vector3|vector4 ----@param t number interpolation parameter, 0-1 ----@param v1 T vector to slerp from ----@param v2 T vector to slerp to ----@return T v the slerped vector -function vmath.slerp(t, v1, v2) end - ----Creates a vector of arbitrary size. The vector is initialized ----with numeric values from a table. ---- The table values are converted to floating point ----values. If a value cannot be converted, a 0 is stored in that ----value position in the vector. ----@param t number[] table of numbers ----@return vector v new vector -function vmath.vector(t) end - ----Creates a new vector with the components set to the ----supplied values. ----@param x number x coordinate ----@param y number y coordinate ----@param z number z coordinate ----@return vector3 v new vector -function vmath.vector3(x, y, z) end - ----Creates a new vector with all components set to the ----corresponding values from the supplied vector. I.e. ----This function creates a copy of the given vector. ----@param v1 vector3 existing vector ----@return vector3 v new vector -function vmath.vector3(v1) end - ----Creates a new zero vector with all components set to 0. ----@return vector3 v new zero vector -function vmath.vector3() end - ----Creates a new vector with all components set to the ----supplied scalar value. ----@param n number scalar value to splat ----@return vector3 v new vector -function vmath.vector3(n) end - ----Creates a new vector with the components set to the ----supplied values. ----@param x number x coordinate ----@param y number y coordinate ----@param z number z coordinate ----@param w number w coordinate ----@return vector4 v new vector -function vmath.vector4(x, y, z, w) end - ----Creates a new vector with all components set to the ----corresponding values from the supplied vector. I.e. ----This function creates a copy of the given vector. ----@param v1 vector4 existing vector ----@return vector4 v new vector -function vmath.vector4(v1) end - ----Creates a new zero vector with all components set to 0. ----@return vector4 v new zero vector -function vmath.vector4() end - ----Creates a new vector with all components set to the ----supplied scalar value. ----@param n number scalar value to splat ----@return vector4 v new vector -function vmath.vector4(n) end - -return vmath \ No newline at end of file diff --git a/resources/defold_api/window.lua b/resources/defold_api/window.lua deleted file mode 100644 index 300da4b..0000000 --- a/resources/defold_api/window.lua +++ /dev/null @@ -1,130 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Window API documentation - - Functions and constants to access the window, window event listeners - and screen dimming. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.window -window = {} - ---- Returns the current dimming mode set on a mobile device. ----The dimming mode specifies whether or not a mobile device should dim the screen after a period without user interaction. ----On platforms that does not support dimming, `window.DIMMING_UNKNOWN` is always returned. ----@return constant mode The mode for screen dimming ---- ----- `window.DIMMING_UNKNOWN` ----- `window.DIMMING_ON` ----- `window.DIMMING_OFF` ---- -function window.get_dim_mode() end - ----This returns the content scale of the current display. ----@return number scale The display scale -function window.get_display_scale() end - ----This returns the current lock state of the mouse cursor ----@return boolean state The lock state -function window.get_mouse_lock() end - ----This returns the current window size (width and height). ----@return number width The window width ----@return number height The window height -function window.get_size() end - ---- Sets the dimming mode on a mobile device. ----The dimming mode specifies whether or not a mobile device should dim the screen after a period without user interaction. The dimming mode will only affect the mobile device while the game is in focus on the device, but not when the game is running in the background. ----This function has no effect on platforms that does not support dimming. ----@param mode constant The mode for screen dimming ---- ----- `window.DIMMING_ON` ----- `window.DIMMING_OFF` ---- -function window.set_dim_mode(mode) end - ----Sets a window event listener. ----@param callback fun(self, event, data)|nil A callback which receives info about window events. Pass an empty function or `nil` if you no longer wish to receive callbacks. ---- ----`self` ----object The calling script ----`event` ----constant The type of event. Can be one of these: ---- ---- ----- `window.WINDOW_EVENT_FOCUS_LOST` ----- `window.WINDOW_EVENT_FOCUS_GAINED` ----- `window.WINDOW_EVENT_RESIZED` ----- `window.WINDOW_EVENT_ICONIFIED` ----- `window.WINDOW_EVENT_DEICONIFIED` ---- ---- ----`data` ----table The callback value `data` is a table which currently holds these values ---- ---- ----- number `width`: The width of a resize event. nil otherwise. ----- number `height`: The height of a resize event. nil otherwise. ---- -function window.set_listener(callback) end - ----Set the locking state for current mouse cursor on a PC platform. ----This function locks or unlocks the mouse cursor to the center point of the window. While the cursor is locked, ----mouse position updates will still be sent to the scripts as usual. ----@param flag boolean The lock state for the mouse cursor -function window.set_mouse_lock(flag) end - ----Sets the window position. ----@param x number Horizontal position of window ----@param y number Vertical position of window -function window.set_position(x, y) end - ----Sets the window size. Works on desktop platforms only. ----@param width number Width of window ----@param height number Height of window -function window.set_size(width, height) end - ----Sets the window title. Works on desktop platforms. ----@param title string The title, encoded as UTF-8 -function window.set_title(title) end - ----Dimming mode is used to control whether or not a mobile device should dim the screen after a period without user interaction. -window.DIMMING_OFF = nil - ----Dimming mode is used to control whether or not a mobile device should dim the screen after a period without user interaction. -window.DIMMING_ON = nil - ----Dimming mode is used to control whether or not a mobile device should dim the screen after a period without user interaction. ----This mode indicates that the dim mode can't be determined, or that the platform doesn't support dimming. -window.DIMMING_UNKNOWN = nil - ---- This event is sent to a window event listener when the game window or app screen is ----restored after being iconified. -window.WINDOW_EVENT_DEICONIFIED = nil - ----This event is sent to a window event listener when the game window or app screen has ----gained focus. ----This event is also sent at game startup and the engine gives focus to the game. -window.WINDOW_EVENT_FOCUS_GAINED = nil - ----This event is sent to a window event listener when the game window or app screen has lost focus. -window.WINDOW_EVENT_FOCUS_LOST = nil - ---- This event is sent to a window event listener when the game window or app screen is ----iconified (reduced to an application icon in a toolbar, application tray or similar). -window.WINDOW_EVENT_ICONFIED = nil - ----This event is sent to a window event listener when the game window or app screen is resized. ----The new size is passed along in the data field to the event listener. -window.WINDOW_EVENT_RESIZED = nil - -return window \ No newline at end of file diff --git a/resources/defold_api/zlib.lua b/resources/defold_api/zlib.lua deleted file mode 100644 index 42224e2..0000000 --- a/resources/defold_api/zlib.lua +++ /dev/null @@ -1,30 +0,0 @@ ---[[ - Generated with github.com/astrochili/defold-annotations - Defold 1.11.2 - - Zlib compression API documentation - - Functions for compression and decompression of string buffers. ---]] - ----@meta ----@diagnostic disable: lowercase-global ----@diagnostic disable: missing-return ----@diagnostic disable: duplicate-doc-param ----@diagnostic disable: duplicate-set-field ----@diagnostic disable: args-after-dots - ----@class defold_api.zlib -zlib = {} - ----A lua error is raised is on error ----@param buf string buffer to deflate ----@return string buf deflated buffer -function zlib.deflate(buf) end - ----A lua error is raised is on error ----@param buf string buffer to inflate ----@return string buf inflated buffer -function zlib.inflate(buf) end - -return zlib \ No newline at end of file From 7e468b7a085aa436d0839c03ca5e488ae8f2bda7 Mon Sep 17 00:00:00 2001 From: Christopher Kaster Date: Sun, 21 Dec 2025 23:49:45 +0100 Subject: [PATCH 70/70] remove redundant code --- crates/bridge/src/launcher.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/crates/bridge/src/launcher.rs b/crates/bridge/src/launcher.rs index 02d14d6..09213dc 100644 --- a/crates/bridge/src/launcher.rs +++ b/crates/bridge/src/launcher.rs @@ -409,12 +409,6 @@ pub fn run( .context("could not convert path to string")?, )?, ) - } else if cfg!(target_os = "macos") { - // TODO: macos - launcher - } else if cfg!(target_os = "windows") { - // TODO: windows - launcher } else { launcher };