Release 1.1.6

This commit is contained in:
Omelette 蛋卷 2024-03-09 07:21:40 -05:00
parent 0679ed9141
commit a3bb990500
10 changed files with 447 additions and 74 deletions

3
.gitignore vendored
View File

@ -3,4 +3,5 @@ dist
node_modules
.vscode-test/
*.vsix
vsc-extension-quickstart.md
vsc-extension-quickstart.md
.vscode

View File

@ -1,8 +0,0 @@
{
// See http://go.microsoft.com/fwlink/?LinkId=827846
// for the documentation about the extensions.json format
"recommendations": [
"dbaeumer.vscode-eslint",
"ms-vscode.extension-test-runner"
]
}

21
.vscode/launch.json vendored
View File

@ -1,21 +0,0 @@
// A launch configuration that compiles the extension and then opens it inside a new window
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Extension",
"type": "extensionHost",
"request": "launch",
"args": [
"--extensionDevelopmentPath=${workspaceFolder}"
],
"outFiles": [
"${workspaceFolder}/out/**/*.js"
],
"preLaunchTask": "${defaultBuildTask}"
}
]
}

18
.vscode/settings.json vendored
View File

@ -1,18 +0,0 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.exclude": {
"out": false // set this to true to hide the "out" folder with the compiled JS files
},
"search.exclude": {
"out": true // set this to false to include "out" folder in search results
},
// Turn off tsc task auto detection since we have the necessary tasks as npm scripts
"typescript.tsc.autoDetect": "off",
"cSpell.words": [
"reimplementation"
],
"Lua.diagnostics.globals": [
"net",
"log"
]
}

20
.vscode/tasks.json vendored
View File

@ -1,20 +0,0 @@
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
{
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "watch",
"problemMatcher": "$tsc-watch",
"isBackground": true,
"presentation": {
"reveal": "never"
},
"group": {
"kind": "build",
"isDefault": true
}
}
]
}

View File

@ -80,3 +80,13 @@ Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how
### Fixed
- Fix JSON to Lua table regex with numerical keys.
## [1.1.6] - 2024-03-09
### Changed
- Revert to providing an earlier version of dcs-fiddle-server.lua script that comes with a custom JSON Serialization Module that is capable of handling non-standard tables used in DCS. The JSON-to-Lua conversion in this extension lavages this custom module to properly reconstruct the in-game Lua table.
*YOU MUST UPDATE THE [dcs-fiddle-server.lua](https://github.com/omltcat/dcs-lua-runner/blob/master/src/hooks/dcs-fiddle-server.lua) FILE IN YOUR INSTALLATION TO TAKE ADVANTAGE OF THIS FEATURE.*
- Display format now defaults to Lua table after it has been thoroughly tested.
### Fixed
- Fix JSON to Lua table regex with escaped quotes.

View File

@ -1,8 +1,10 @@
# DCS Lua Runner
A VS Code extension to run lua code in DCS World (on local or remote server, in mission or GUI environment). A reimplementation of the [DCS Fiddle](https://github.com/JonathanTurnock/dcsfiddle) web lua console.
A VS Code extension to run lua code in DCS World (on local or remote server, in mission or GUI environment). A reimplementation of the [DCS Fiddle](https://github.com/JonathanTurnock/dcsfiddle) web lua console. Allows for quick development and debugging of running scripted missions directly from the comfort of VS Code.
Allows for quick development and debugging of running scripted missions directly from the comfort of VS Code.
**IMPORTANT (new):**
v1.1.6 reverts to providing an earlier version of dcs-fiddle-server.lua that comes with a custom JSON Serialization Module. The JSON-to-Lua conversion in this extension lavages this module to properly reconstruct the in-game Lua table.
*YOU MUST UPDATE THE [dcs-fiddle-server.lua](https://github.com/omltcat/dcs-lua-runner/blob/master/src/hooks/dcs-fiddle-server.lua) FILE IN YOUR INSTALLATION TO TAKE ADVANTAGE OF THIS FEATURE.*
![Demo1](docs/img/demo1-new.png)

View File

@ -68,7 +68,7 @@
"dcsLuaRunner.returnDisplayFormat": {
"type": "string",
"enum": ["Lua Table", "JSON"],
"default": "JSON",
"default": "Lua Table",
"description": "Note: Experimental feature! Original returned data is in JSON. Conversion to Lua table is done after receiving data and may not be 100% accurate. Please report any issues."
},
"dcsLuaRunner.quickButtons": {

View File

@ -60,7 +60,7 @@ async function runLua(lua: string, outputChannel: vscode.OutputChannel, filename
let luaString = jsonString;
// Replace JSON syntax with Lua syntax
luaString = luaString.replace(/null/g, 'nil'); // Replace null with nil
luaString = luaString.replace(/"([^"]+)":/g, '["$1"] ='); // Replace "key": with ["key"] =
luaString = luaString.replace(/"((?:[^"\\]|\\.)+)":/g, '["$1"] ='); // Replace "key": with ["key"] =
luaString = luaString.replace(/\["_([0-9]+)"\]\s*=/g, '[$1] ='); // Replace ["_n"] = with [n] =
luaString = luaString.replace(/\[\]/g, '{}'); // Replace [] with {}
luaString = luaString.replace(/\[\n/g, '{\n'); // Replace [ followed by a line break with { followed by a line break

View File

@ -1,3 +1,6 @@
-- Note: This is a modified version of the DCS Fiddle Server to add authentication for public servers.
-- All credit goes to the original authors of DCS Fiddle: JonathanTurnock and john681611
FIDDLE = {}
-- Configs:
@ -7,6 +10,430 @@ FIDDLE.USERNAME = 'username'
FIDDLE.PASSWORD = 'password'
FIDDLE.BIND_IP = '0.0.0.0' -- for remote access
FIDDLE.BYPASS_LOCAL = true -- allow requests to 127.0.0.1:12080 without auth, so DCS Fiddle website can still work. (Not a very secure implementation. Use at your own risk if your 12080 port is public)
--[[
Custom JSON Serialization Module that handles DCS' usage of integers as table keys such as {[1]=1, ["name"]="Enfield11", [2]=1, [3]=1} which is not valid json
This is handled by encoding tables with mixed key types as objects so the above example would be
{
"_1": 1,
"_2": 1,
"name": "Enfield11",
"_3": 1
}
Anything that is clearly a table is still converted to a table
]]--
fiddlejson = { _version = "0.2.0" }
-------------------------------------------------------------------------------
-- Encode
-------------------------------------------------------------------------------
local encode
local escape_char_map = {
[ "\\" ] = "\\",
[ "\"" ] = "\"",
[ "\b" ] = "b",
[ "\f" ] = "f",
[ "\n" ] = "n",
[ "\r" ] = "r",
[ "\t" ] = "t",
}
local escape_char_map_inv = { [ "/" ] = "/" }
for k, v in pairs(escape_char_map) do
escape_char_map_inv[v] = k
end
local function escape_char(c)
return "\\" .. (escape_char_map[c] or string.format("u%04x", c:byte()))
end
local function encode_nil(val)
return "null"
end
--- Checks if a given table is an array.
-- The function verifies that all keys are numeric and sequential starting from 1.
-- @param val Table to be checked.
-- @return boolean Returns true if the table is an array, otherwise false.
local function is_table_array(val)
-- Check if the first element of the table is not nil or the table is empty.
-- These are two signs that the table might be an array.
if rawget(val, 1) ~= nil or next(val) == nil then
-- Extract all numeric keys and sort them.
local keys = {}
for k in pairs(val) do
if type(k) == "number" then
table.insert(keys, k)
else
-- If any key is not a number, the table is not an array.
return false
end
end
table.sort(keys)
-- Check the sorted keys to see if they form a continuous sequence starting from 1.
for i, k in ipairs(keys) do
if i ~= k then
return false
end
end
-- If all keys were numeric and in sequence, then the table is an array.
return true
else
-- If the first element of the table is nil and the table isn't empty,
-- then it's not an array.
return false
end
end
local function encode_table(val, stack)
local res = {}
stack = stack or {}
-- Circular reference?
if stack[val] then error("circular reference") end
stack[val] = true
if is_table_array(val) then
-- Treat as array -- check keys are valid and it is not sparse
-- Encode
for i, v in ipairs(val) do
table.insert(res, encode(v, stack))
end
stack[val] = nil
return "[" .. table.concat(res, ",") .. "]"
else
-- Treat as an object
for k, v in pairs(val) do
if type(k) ~= "string" then
table.insert(res, encode("_"..k, stack) .. ":" .. encode(v, stack))
else
table.insert(res, encode(k, stack) .. ":" .. encode(v, stack))
end
end
stack[val] = nil
return "{" .. table.concat(res, ",") .. "}"
end
end
local function encode_string(val)
return '"' .. val:gsub('[%z\1-\31\\"]', escape_char) .. '"'
end
local function encode_number(val)
-- Check for NaN, -inf and inf
if val ~= val or val <= -math.huge or val >= math.huge then
error("unexpected number value '" .. tostring(val) .. "'")
end
return string.format("%.14g", val)
end
local type_func_map = {
[ "nil" ] = encode_nil,
[ "table" ] = encode_table,
[ "string" ] = encode_string,
[ "number" ] = encode_number,
[ "boolean" ] = tostring,
}
encode = function(val, stack)
local t = type(val)
local f = type_func_map[t]
if f then
return f(val, stack)
end
error("unexpected type '" .. t .. "'")
end
function fiddlejson.encode(val)
return ( encode(val) )
end
-------------------------------------------------------------------------------
-- Decode
-------------------------------------------------------------------------------
local parse
local function create_set(...)
local res = {}
for i = 1, select("#", ...) do
res[ select(i, ...) ] = true
end
return res
end
local space_chars = create_set(" ", "\t", "\r", "\n")
local delim_chars = create_set(" ", "\t", "\r", "\n", "]", "}", ",")
local escape_chars = create_set("\\", "/", '"', "b", "f", "n", "r", "t", "u")
local literals = create_set("true", "false", "null")
local literal_map = {
[ "true" ] = true,
[ "false" ] = false,
[ "null" ] = nil,
}
local function next_char(str, idx, set, negate)
for i = idx, #str do
if set[str:sub(i, i)] ~= negate then
return i
end
end
return #str + 1
end
local function decode_error(str, idx, msg)
local line_count = 1
local col_count = 1
for i = 1, idx - 1 do
col_count = col_count + 1
if str:sub(i, i) == "\n" then
line_count = line_count + 1
col_count = 1
end
end
error( string.format("%s at line %d col %d", msg, line_count, col_count) )
end
local function codepoint_to_utf8(n)
-- http://scripts.sil.org/cms/scripts/page.php?site_id=nrsi&id=iws-appendixa
local f = math.floor
if n <= 0x7f then
return string.char(n)
elseif n <= 0x7ff then
return string.char(f(n / 64) + 192, n % 64 + 128)
elseif n <= 0xffff then
return string.char(f(n / 4096) + 224, f(n % 4096 / 64) + 128, n % 64 + 128)
elseif n <= 0x10ffff then
return string.char(f(n / 262144) + 240, f(n % 262144 / 4096) + 128,
f(n % 4096 / 64) + 128, n % 64 + 128)
end
error( string.format("invalid unicode codepoint '%x'", n) )
end
local function parse_unicode_escape(s)
local n1 = tonumber( s:sub(1, 4), 16 )
local n2 = tonumber( s:sub(7, 10), 16 )
-- Surrogate pair?
if n2 then
return codepoint_to_utf8((n1 - 0xd800) * 0x400 + (n2 - 0xdc00) + 0x10000)
else
return codepoint_to_utf8(n1)
end
end
local function parse_string(str, i)
local res = ""
local j = i + 1
local k = j
while j <= #str do
local x = str:byte(j)
if x < 32 then
decode_error(str, j, "control character in string")
elseif x == 92 then -- `\`: Escape
res = res .. str:sub(k, j - 1)
j = j + 1
local c = str:sub(j, j)
if c == "u" then
local hex = str:match("^[dD][89aAbB]%x%x\\u%x%x%x%x", j + 1)
or str:match("^%x%x%x%x", j + 1)
or decode_error(str, j - 1, "invalid unicode escape in string")
res = res .. parse_unicode_escape(hex)
j = j + #hex
else
if not escape_chars[c] then
decode_error(str, j - 1, "invalid escape char '" .. c .. "' in string")
end
res = res .. escape_char_map_inv[c]
end
k = j + 1
elseif x == 34 then -- `"`: End of string
res = res .. str:sub(k, j - 1)
return res, j + 1
end
j = j + 1
end
decode_error(str, i, "expected closing quote for string")
end
local function parse_number(str, i)
local x = next_char(str, i, delim_chars)
local s = str:sub(i, x - 1)
local n = tonumber(s)
if not n then
decode_error(str, i, "invalid number '" .. s .. "'")
end
return n, x
end
local function parse_literal(str, i)
local x = next_char(str, i, delim_chars)
local word = str:sub(i, x - 1)
if not literals[word] then
decode_error(str, i, "invalid literal '" .. word .. "'")
end
return literal_map[word], x
end
local function parse_array(str, i)
local res = {}
local n = 1
i = i + 1
while 1 do
local x
i = next_char(str, i, space_chars, true)
-- Empty / end of array?
if str:sub(i, i) == "]" then
i = i + 1
break
end
-- Read token
x, i = parse(str, i)
res[n] = x
n = n + 1
-- Next token
i = next_char(str, i, space_chars, true)
local chr = str:sub(i, i)
i = i + 1
if chr == "]" then break end
if chr ~= "," then decode_error(str, i, "expected ']' or ','") end
end
return res, i
end
function set_table(res, key, val)
-- Check if the key starts with an underscore
if string.sub(key, 1, 1) == "_" then
-- Remove the underscore and try to convert the rest to a number
local numKey = tonumber(string.sub(key, 2))
-- If the result is not nil, meaning the conversion was successful
if numKey then
-- Update the table with the number key instead of the string key
res[numKey] = val
end
else
-- If the key does not start with an underscore, just use it as is
res[key] = val
end
end
local function parse_object(str, i)
local res = {}
i = i + 1
while 1 do
local key, val
i = next_char(str, i, space_chars, true)
-- Empty / end of object?
if str:sub(i, i) == "}" then
i = i + 1
break
end
-- Read key
if str:sub(i, i) ~= '"' then
decode_error(str, i, "expected string for key")
end
key, i = parse(str, i)
-- Read ':' delimiter
i = next_char(str, i, space_chars, true)
if str:sub(i, i) ~= ":" then
decode_error(str, i, "expected ':' after key")
end
i = next_char(str, i + 1, space_chars, true)
-- Read value
val, i = parse(str, i)
-- Set
--res[key] = val
set_table(res, key, val)
-- Next token
i = next_char(str, i, space_chars, true)
local chr = str:sub(i, i)
i = i + 1
if chr == "}" then break end
if chr ~= "," then decode_error(str, i, "expected '}' or ','") end
end
return res, i
end
local char_func_map = {
[ '"' ] = parse_string,
[ "0" ] = parse_number,
[ "1" ] = parse_number,
[ "2" ] = parse_number,
[ "3" ] = parse_number,
[ "4" ] = parse_number,
[ "5" ] = parse_number,
[ "6" ] = parse_number,
[ "7" ] = parse_number,
[ "8" ] = parse_number,
[ "9" ] = parse_number,
[ "-" ] = parse_number,
[ "t" ] = parse_literal,
[ "f" ] = parse_literal,
[ "n" ] = parse_literal,
[ "[" ] = parse_array,
[ "{" ] = parse_object,
}
parse = function(str, idx)
local chr = str:sub(idx, idx)
local f = char_func_map[chr]
if f then
return f(str, idx)
end
decode_error(str, idx, "unexpected character '" .. chr .. "'")
end
function fiddlejson.decode(str)
if type(str) ~= "string" then
error("expected argument of type string, got " .. type(str))
end
local res, idx = parse(str, next_char(str, 1, space_chars, true))
idx = next_char(str, idx, space_chars, true)
if idx <= #str then
decode_error(str, idx, "trailing garbage")
end
return res
end
--[[
base64 -- v1.5.3 public domain Lua base64 encoder/decoder
no warranty implied; use at your own risk
@ -540,11 +967,11 @@ local function handle_client_connection(client)
local success, res = pcall(handle_request, res, env)
if (not success) then
__error("Failed to handle request due to \n" .. res)
response.body = net.lua2json({error=tostring(res)})
response.body = fiddlejson.encode({error=tostring(res)})
response.status = INTERNAL_SERVER_ERROR
else
__info("Handled request")
response.body = net.lua2json({result=res})
response.body = fiddlejson.encode({result=res})
response.status = OK
end
end