forked from redo/BlockLua
Compare commits
16 Commits
ed5c254480
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| b328f0b21a | |||
| 71b73c816b | |||
| c5dc8b15f9 | |||
|
|
8399b11322 | ||
| d494f02fe3 | |||
| a7db0d8e81 | |||
| 33f5ec9bbe | |||
| f6bf18efaa | |||
| 4f42801da6 | |||
| 15f67e0eef | |||
| 5885dcbed3 | |||
| d9a416f5d5 | |||
|
|
2191e004ad | ||
|
|
ae34bb8b7a | ||
|
|
b71bfdb73e | ||
|
|
7232ede09d |
BIN
BlockLua.dll
BIN
BlockLua.dll
Binary file not shown.
@@ -5,13 +5,13 @@ project(BlockLua CXX)
|
|||||||
# Export compile_commands.json for VSCode IntelliSense
|
# Export compile_commands.json for VSCode IntelliSense
|
||||||
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
|
||||||
|
|
||||||
# Output directories to mirror compile.bat's build folder
|
# Output directories to mirror build.bat's build folder
|
||||||
set(OUTPUT_DIR ${CMAKE_SOURCE_DIR}/build)
|
set(OUTPUT_DIR ${CMAKE_SOURCE_DIR}/build)
|
||||||
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR})
|
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${OUTPUT_DIR})
|
||||||
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIR})
|
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${OUTPUT_DIR})
|
||||||
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_DIR})
|
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${OUTPUT_DIR})
|
||||||
|
|
||||||
# Global compile options to mirror compile.bat
|
# Global build options to mirror build.bat
|
||||||
add_compile_options(
|
add_compile_options(
|
||||||
-Wall
|
-Wall
|
||||||
-Werror
|
-Werror
|
||||||
@@ -27,14 +27,14 @@ include_directories(
|
|||||||
${CMAKE_SOURCE_DIR}/inc/lua
|
${CMAKE_SOURCE_DIR}/inc/lua
|
||||||
)
|
)
|
||||||
|
|
||||||
# Link directories (for -L.) and libraries from compile.bat
|
# Link directories (for -L.) and libraries from build.bat
|
||||||
link_directories(
|
link_directories(
|
||||||
${CMAKE_SOURCE_DIR}
|
${CMAKE_SOURCE_DIR}
|
||||||
)
|
)
|
||||||
|
|
||||||
# Safe DLL
|
# Safe DLL
|
||||||
add_library(BlockLua SHARED src/bllua4.cpp)
|
add_library(BlockLua SHARED src/bllua4.cpp)
|
||||||
# Ensure output name matches compile.bat
|
# Ensure output name matches build.bat
|
||||||
set_target_properties(BlockLua PROPERTIES OUTPUT_NAME "BlockLua")
|
set_target_properties(BlockLua PROPERTIES OUTPUT_NAME "BlockLua")
|
||||||
# Linker flags and libraries
|
# Linker flags and libraries
|
||||||
if(MSVC)
|
if(MSVC)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
@echo off
|
@echo off
|
||||||
cd /d %~dp0
|
cd /d %~dp0
|
||||||
|
|
||||||
REM Ensure MinGW32 toolchain is first in PATH (matches compile.bat)
|
REM Ensure MinGW32 toolchain is first in PATH
|
||||||
set "PATH=C:\msys64\mingw32\bin;%PATH%"
|
set "PATH=C:\msys64\mingw32\bin;%PATH%"
|
||||||
|
|
||||||
REM Configure CMake (generate into build/)
|
REM Configure CMake (generate into build/)
|
||||||
13
compile.bat
13
compile.bat
@@ -1,13 +0,0 @@
|
|||||||
@echo off
|
|
||||||
cd /d %~dp0
|
|
||||||
|
|
||||||
set "PATH=C:\msys64\mingw32\bin;%PATH%"
|
|
||||||
|
|
||||||
if not exist build mkdir build
|
|
||||||
|
|
||||||
set buildargs=-Wall -Werror -m32 -shared -Isrc -Iinc/tsfuncs -Iinc/lua -lpsapi -L. -llua5.1 -static-libgcc -static-libstdc++
|
|
||||||
|
|
||||||
echo on
|
|
||||||
g++ src/bllua4.cpp %buildargs% -o build\BlockLua.dll
|
|
||||||
g++ -DBLLUA_UNSAFE src/bllua4.cpp %buildargs% -o build\BlockLua-Unsafe.dll
|
|
||||||
@echo off
|
|
||||||
@@ -27,7 +27,7 @@ What these packages are for:
|
|||||||
- Run the script:
|
- Run the script:
|
||||||
|
|
||||||
```powershell
|
```powershell
|
||||||
compile.bat
|
build.bat
|
||||||
```
|
```
|
||||||
|
|
||||||
What the script does:
|
What the script does:
|
||||||
@@ -48,5 +48,5 @@ You should see `architecture: i386` in the output.
|
|||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
|
|
||||||
- Ensure you installed the i686 (32-bit) variants of the packages; x86_64 packages won’t work for a 32-bit build.
|
- Ensure you installed the i686 (32-bit) variants of the packages; x86_64 packages won't work for a 32-bit build.
|
||||||
- If the linker cannot find `-llua5.1`, confirm `mingw-w64-i686-lua51` is installed and you are using the `mingw32` toolchain (not `x86_64`).
|
- If the linker cannot find `-llua5.1`, confirm `mingw-w64-i686-lua51` is installed and you are using the `mingw32` toolchain (not `x86_64`).
|
||||||
|
|||||||
91
readme.md
91
readme.md
@@ -1,25 +1,26 @@
|
|||||||
|
|
||||||
# BlockLua
|
# BlockLua
|
||||||
|
|
||||||
Lua scripting for Blockland
|
Lua scripting for Blockland
|
||||||
|
|
||||||
## How to Install
|
## How to Install
|
||||||
|
|
||||||
- Install RedBlocklandLoader
|
- Install RedBlocklandLoader
|
||||||
- Copy `lua5.1.dll` into your Blockland install folder, next to `Blockland.exe`
|
- Copy `lua5.1.dll` into your Blockland install folder, next to `Blockland.exe`
|
||||||
- Copy `BlockLua.dll` into the `modules` folder within the Blockland folder
|
- Copy `BlockLua.dll` into the `modules` folder within the Blockland folder
|
||||||
|
|
||||||
|
|
||||||
## Quick Reference
|
## Quick Reference
|
||||||
|
|
||||||
### From TorqueScript
|
### From TorqueScript
|
||||||
|
|
||||||
`'print('hello world')` - Execute Lua in the console by prepending a `'` (single quote)
|
`'print('hello world')` - Execute Lua in the console by prepending a `'` (single quote)
|
||||||
`luaeval("code");` - Execute Lua code
|
`luaeval("code");` - Execute Lua code
|
||||||
`luacall("funcName", %args...);` - Call a Lua global function
|
`luacall("funcName", %args...);` - Call a Lua function (supports indexing tables and object methods)
|
||||||
`luaexec("fileName");` - Execute a Lua file. Path rules are the same as executing .cs files.
|
`luaexec("fileName");` - Execute a Lua file. Path rules are the same as when executing .cs files, relative paths are allowed.
|
||||||
`luaget("varName");` - Read a Lua global variable
|
`luaget("varName");` - Read a Lua global variable (supports indexing tables)
|
||||||
`luaset("varName", %value);` - Write a Lua global variable
|
`luaset("varName", %value);` - Write a Lua global variable (supports indexing tables)
|
||||||
|
|
||||||
### From Lua
|
### From Lua
|
||||||
|
|
||||||
`bl.eval('code')` - Eval TorqueScript code
|
`bl.eval('code')` - Eval TorqueScript code
|
||||||
`bl.funcName(args)` - Call a TorqueScript function
|
`bl.funcName(args)` - Call a TorqueScript function
|
||||||
`bl.varName` - Read a TorqueScript global variable
|
`bl.varName` - Read a TorqueScript global variable
|
||||||
@@ -28,6 +29,7 @@ Lua scripting for Blockland
|
|||||||
`bl['namespaceName::funcName'](args)` - Call a namespaced TorqueScript function
|
`bl['namespaceName::funcName'](args)` - Call a namespaced TorqueScript function
|
||||||
|
|
||||||
### Accessing Torque Objects from Lua
|
### Accessing Torque Objects from Lua
|
||||||
|
|
||||||
`bl.objectName` - Access a Torque object by name
|
`bl.objectName` - Access a Torque object by name
|
||||||
`bl[objectID]` - Access a Torque object by ID (or name)
|
`bl[objectID]` - Access a Torque object by ID (or name)
|
||||||
`object.fieldOrKey` - Read a field or Lua key from a Torque object
|
`object.fieldOrKey` - Read a field or Lua key from a Torque object
|
||||||
@@ -35,28 +37,43 @@ Lua scripting for Blockland
|
|||||||
`object.key = value` - Associate Lua data with a Torque object
|
`object.key = value` - Associate Lua data with a Torque object
|
||||||
`object:method(args)` - Call a Torque object method
|
`object:method(args)` - Call a Torque object method
|
||||||
`object[index]` - Access a member of a Torque set or group
|
`object[index]` - Access a member of a Torque set or group
|
||||||
`for childIndex, child in object:members() do` - Iterate objects within of a Torque set or group. Indices start at 0 like in Torque.
|
`for child in object:members() do` - Iterate objects within of a Torque set or group. Indices start at 0 like in Torque.
|
||||||
`bl.isObject(object, objectID, or 'objectName')` - Check if an object exists
|
`bl.isObject(object, objectID, or 'objectName')` - Check if an object exists
|
||||||
`object:exists()` - Check if an object exists
|
`object:exists()` - Check if an object exists
|
||||||
|
|
||||||
### Timing/Schedules
|
### Timing/Schedules
|
||||||
|
|
||||||
`sched = bl.schedule(timeMs, function, args...)` - Schedule a Lua function to be called later, similar to schedule in Torque
|
`sched = bl.schedule(timeMs, function, args...)` - Schedule a Lua function to be called later, similar to schedule in Torque
|
||||||
`sched:cancel()` - Cancel a previously scheduled timer
|
`sched:cancel()` - Cancel a previously scheduled timer
|
||||||
|
|
||||||
### Raycasts and Searches
|
### Raycasts and Searches
|
||||||
|
|
||||||
`hitObject, hitPos, hitNormal = bl.raycast(vector{startPosX,y,z}, vector{endPosX,y,z}, 'objtype'/{'objtypes',...}, ignoreObjects...?)` - Cast a ray in the world over objects of the specified type(s) (possibly excluding some objects), and return the object hit, the position of the hit, and the normal vector to the surface hit. See the Types section for a list of valid object types.
|
`hitObject, hitPos, hitNormal = bl.raycast(vector{startPosX,y,z}, vector{endPosX,y,z}, 'objtype'/{'objtypes',...}, ignoreObjects...?)` - Cast a ray in the world over objects of the specified type(s) (possibly excluding some objects), and return the object hit, the position of the hit, and the normal vector to the surface hit. See the Types section for a list of valid object types.
|
||||||
`for object in bl.boxSearch(vector{centerX,y,z}, vector{sizeX,y,z}, 'objtype'/{'objtypes',...}) do` - Find all objects in the world of the specified type(s) whose bounding box overlaps with the specified box. See the Types section for a list of valid object types.
|
`for object in bl.boxSearch(vector{centerX,y,z}, vector{sizeX,y,z}, 'objtype'/{'objtypes',...}) do` - Find all objects in the world of the specified type(s) whose bounding box overlaps with the specified box. See the Types section for a list of valid object types.
|
||||||
`for object in bl.radiusSearch(vector{centerX,y,z}, radius, 'objtype'/{'objtypes',...}) do` - Find all objects of the specified type(s) whose bounding box overlaps with the specified sphere. See the Types section for a list of valid object types.
|
`for object in bl.radiusSearch(vector{centerX,y,z}, radius, 'objtype'/{'objtypes',...}) do` - Find all objects of the specified type(s) whose bounding box overlaps with the specified sphere. See the Types section for a list of valid object types.
|
||||||
|
|
||||||
|
### List of Object Classes (for raycasts and searches)
|
||||||
|
|
||||||
|
`'all'` - Any object
|
||||||
|
`'player'` - Players or bots
|
||||||
|
`'item'` - Items
|
||||||
|
`'vehicle'` - Vehicles
|
||||||
|
`'projectile'` - Projectiles
|
||||||
|
`'brick'` - Bricks with raycasting enabled
|
||||||
|
`'brickalways'` - All bricks including those with raycasting disabled
|
||||||
|
Other types: `'static'`, `'environment'`, `'terrain'`, `'water'`, `'trigger'`, `'marker'`, `'gamebase'`, `'shapebase'`, `'camera'`, `'staticshape'`, `'vehicleblocker'`, `'explosion'`, `'corpse'`, `'debris'`, `'physicalzone'`, `'staticts'`, `'staticrendered'`, `'damagableitem'`
|
||||||
|
|
||||||
### Server-Client Communication
|
### Server-Client Communication
|
||||||
`bl.addServerCmd('commandName', function(client, args...) yourCode end)` - Register a /command on the server
|
|
||||||
`bl.addClientCmd('commandName', function(args...) yourCode end)` - Register a client command on the client
|
`bl.addServerCmd('commandName', function(client, args...) ... end)` - Register a /command on the server
|
||||||
`bl.commandToServer('commandName', args...)` - Execute a server command as a client
|
`bl.addClientCmd('commandName', function(args...) ... end)` - Register a client command on the client
|
||||||
`bl.commandToClient('commandName', args...)` - As the server, execute a client command on a specific client
|
`bl.commandToServer('commandName', args...)` - As a client, execute a server command
|
||||||
|
`bl.commandToClient(client, 'commandName', args...)` - As the server, execute a client command on a specific client
|
||||||
`bl.commandToAll('commandName', args...)` - As the server, execute a client command on all clients
|
`bl.commandToAll('commandName', args...)` - As the server, execute a client command on all clients
|
||||||
|
|
||||||
### Packages/Hooks
|
### Packages/Hooks
|
||||||
`bl.hook('packageName', 'functionName', 'before'/'after', function(args) yourCode end)` - Hook a Torque function with a Lua function.
|
|
||||||
|
`bl.hook('packageName', 'functionName', 'before'/'after', function(args) ... end)` - Hook a Torque function with a Lua function.
|
||||||
`args` is an array containing the arguments provided to the function. If the hook is `before`, these can be modified before being passed to the parent function.
|
`args` is an array containing the arguments provided to the function. If the hook is `before`, these can be modified before being passed to the parent function.
|
||||||
If `args._return` is set to anything other than nil by a `before` hook, the parent function will not be called, and the function will simply return that value. Also in this case, any `after` hook will not be executed.
|
If `args._return` is set to anything other than nil by a `before` hook, the parent function will not be called, and the function will simply return that value. Also in this case, any `after` hook will not be executed.
|
||||||
In an `after` hook, `args._return` is set to the value returned by the parent function, and can be modified.
|
In an `after` hook, `args._return` is set to the value returned by the parent function, and can be modified.
|
||||||
@@ -65,10 +82,12 @@ In an `after` hook, `args._return` is set to the value returned by the parent fu
|
|||||||
`bl.unhook('packageName')` - Remove any previously defined hooks within the package
|
`bl.unhook('packageName')` - Remove any previously defined hooks within the package
|
||||||
|
|
||||||
### Modules and Dependencies
|
### Modules and Dependencies
|
||||||
|
|
||||||
`dofile('Add-Ons/Path/file.lua')` - Execute a Lua file. Relative paths (`./file.lua`) are allowed. `..` is not allowed.
|
`dofile('Add-Ons/Path/file.lua')` - Execute a Lua file. Relative paths (`./file.lua`) are allowed. `..` is not allowed.
|
||||||
|
|
||||||
`require('modulePath.moduleName')` - Load a Lua file or external library.
|
`require('modulePath.moduleName')` - Load a Lua file or external library.
|
||||||
`require` replaces `.` with `/` in the path, and then searches for files in the following order:
|
`require` replaces `.` with `/` in the path, and then searches for files in the following order:
|
||||||
|
|
||||||
- `./modulePath/moduleName.lua`
|
- `./modulePath/moduleName.lua`
|
||||||
- `./modulePath/moduleName/init.lua`
|
- `./modulePath/moduleName/init.lua`
|
||||||
- `modulePath/moduleName.lua` (Relative to game directory)
|
- `modulePath/moduleName.lua` (Relative to game directory)
|
||||||
@@ -80,6 +99,7 @@ In an `after` hook, `args._return` is set to the value returned by the parent fu
|
|||||||
Like in standard Lua, modules loaded using `require` are only executed the first time `require` is called with that path (from anywhere). Subsequent calls simply return the result from the initial execution. To allow hot reloading, use `dofile`.
|
Like in standard Lua, modules loaded using `require` are only executed the first time `require` is called with that path (from anywhere). Subsequent calls simply return the result from the initial execution. To allow hot reloading, use `dofile`.
|
||||||
|
|
||||||
### File I/O
|
### File I/O
|
||||||
|
|
||||||
Lua's builtin file I/O is emulated, and is confined to the same directories as TorqueScript file I/O.
|
Lua's builtin file I/O is emulated, and is confined to the same directories as TorqueScript file I/O.
|
||||||
Relative paths (`./`) are allowed. `..` is not allowed.
|
Relative paths (`./`) are allowed. `..` is not allowed.
|
||||||
`file = io.open('./file.txt', 'r'/'w'/'a'/'rb'/'wb'/'ab')` - Open a file
|
`file = io.open('./file.txt', 'r'/'w'/'a'/'rb'/'wb'/'ab')` - Open a file
|
||||||
@@ -90,6 +110,7 @@ Reading files from ZIPs is supported, with caveats. Null characters are not allo
|
|||||||
When reading from outside ZIPs, binary files are fully supported.
|
When reading from outside ZIPs, binary files are fully supported.
|
||||||
|
|
||||||
### Object Creation
|
### Object Creation
|
||||||
|
|
||||||
`bl.new('className')` - Create a new Torque object
|
`bl.new('className')` - Create a new Torque object
|
||||||
`bl.new('className', {fieldName = value, ...})` - Create a new Torque object with the given fields
|
`bl.new('className', {fieldName = value, ...})` - Create a new Torque object with the given fields
|
||||||
`bl.new('className objectName', fields?)` - Create a new named Torque object
|
`bl.new('className objectName', fields?)` - Create a new named Torque object
|
||||||
@@ -98,6 +119,7 @@ When reading from outside ZIPs, binary files are fully supported.
|
|||||||
`bl.datablock('datablockClassName datablockName:parentDatablockName', fields?)` - Create a new datablock with inheritance
|
`bl.datablock('datablockClassName datablockName:parentDatablockName', fields?)` - Create a new datablock with inheritance
|
||||||
|
|
||||||
### Classes and Types
|
### Classes and Types
|
||||||
|
|
||||||
`bl.type('varName', 'type')` - Register the type of a Torque global variable, for conversion when accessing from Lua. Valid types are 'boolean', 'object', 'string' (prevents automatic conversion), and nil (default, applies automatic conversion).
|
`bl.type('varName', 'type')` - Register the type of a Torque global variable, for conversion when accessing from Lua. Valid types are 'boolean', 'object', 'string' (prevents automatic conversion), and nil (default, applies automatic conversion).
|
||||||
`bl.type('funcName', 'type')` - Register the return type of a Torque function, for conversion when calling from Lua. Valid types are 'bool', 'object', and nil - all other conversion is automatic. Already done for all default functions.
|
`bl.type('funcName', 'type')` - Register the return type of a Torque function, for conversion when calling from Lua. Valid types are 'bool', 'object', and nil - all other conversion is automatic. Already done for all default functions.
|
||||||
`bl.type('className::funcName', 'type')` - Register the return type of a Torque object method.
|
`bl.type('className::funcName', 'type')` - Register the return type of a Torque object method.
|
||||||
@@ -108,6 +130,7 @@ When reading from outside ZIPs, binary files are fully supported.
|
|||||||
`bl.string(arg)` - Manually convert any automatically-converted Torque value back into a string. This is not as reliable as using `bl.type` to specify the type as a string beforehand.
|
`bl.string(arg)` - Manually convert any automatically-converted Torque value back into a string. This is not as reliable as using `bl.type` to specify the type as a string beforehand.
|
||||||
|
|
||||||
### Vector
|
### Vector
|
||||||
|
|
||||||
`vec = vector{x,y,z}` - Create a vector. Can have any number of elements
|
`vec = vector{x,y,z}` - Create a vector. Can have any number of elements
|
||||||
`vec1 + vec2` - Add
|
`vec1 + vec2` - Add
|
||||||
`vec1 - vec2` - Subtract
|
`vec1 - vec2` - Subtract
|
||||||
@@ -133,17 +156,21 @@ When reading from outside ZIPs, binary files are fully supported.
|
|||||||
`vec2 = vec:copy()` - Clone a vector so its elements can be modified without affecting the original. Usually not needed - the builtin vector functions never modify vectors in-place.
|
`vec2 = vec:copy()` - Clone a vector so its elements can be modified without affecting the original. Usually not needed - the builtin vector functions never modify vectors in-place.
|
||||||
|
|
||||||
### Matrix
|
### Matrix
|
||||||
|
|
||||||
WIP
|
WIP
|
||||||
|
|
||||||
### Extended Standard Lua Library
|
### Extended Standard Lua Library
|
||||||
`string[index]`
|
|
||||||
`string[{start,stop}]`
|
`str[index]`
|
||||||
|
`str[{start,stop}]`
|
||||||
`string.split(str, separator='' (splits into chars), noregex=false)`
|
`string.split(str, separator='' (splits into chars), noregex=false)`
|
||||||
`string.bytes(str)`
|
`string.bytes(str)`
|
||||||
`string.trim(str, charsToTrim=' \t\r\n')`
|
`string.trim(str, charsToTrim=' \t\r\n')`
|
||||||
`table.empty`
|
`table.empty`
|
||||||
`table.map(func, ...)`
|
`table.map(func, ...)`
|
||||||
|
`table.mapk(func, ...)`
|
||||||
`table.map_list(func, ...)`
|
`table.map_list(func, ...)`
|
||||||
|
`table.mapi_list(func, ...)`
|
||||||
`table.swap(tbl)`
|
`table.swap(tbl)`
|
||||||
`table.reverse(list)`
|
`table.reverse(list)`
|
||||||
`table.islist(list)`
|
`table.islist(list)`
|
||||||
@@ -167,49 +194,47 @@ WIP
|
|||||||
`math.clamp(num, min, max)`
|
`math.clamp(num, min, max)`
|
||||||
|
|
||||||
## Type Conversion
|
## Type Conversion
|
||||||
|
|
||||||
When a TorqueScript function is called from Lua or vice-versa, the arguments and return value must be converted between the two languages' type systems.
|
When a TorqueScript function is called from Lua or vice-versa, the arguments and return value must be converted between the two languages' type systems.
|
||||||
TorqueScript stores no type information; all values in TorqueScript are strings. So it's necessary to make some inferences when converting values between the two languages.
|
TorqueScript stores no type information; all values in TorqueScript are strings. So it's necessary to make some inferences when converting values between the two languages.
|
||||||
|
|
||||||
### From Lua to TorqueScript
|
### From Lua to TorqueScript
|
||||||
|
|
||||||
- `nil` becomes the empty string ""
|
- `nil` becomes the empty string ""
|
||||||
- `true` and `false` become "1" and "0" respectively
|
- `true` and `false` become "1" and "0" respectively
|
||||||
- Torque containers become their object ID
|
- A Torque object container becomes its object ID
|
||||||
- A `vector` becomes a string containing three numbers separated by spaces
|
- A `vector` becomes a string containing three numbers separated by spaces
|
||||||
- A table of two vectors becomes a string containing six numbers separated by spaces
|
- A table of two `vector`s becomes a string containing six numbers separated by spaces
|
||||||
|
- (WIP) A `matrix` is converted into an axis-angle (a "transform"), a string containing seven numbers separated by spaces
|
||||||
- Any `string` is passed directly as a string
|
- Any `string` is passed directly as a string
|
||||||
- Tables cannot be passed and will throw an error
|
- Tables cannot be passed and will throw an error
|
||||||
|
|
||||||
### From TorqueScript to Lua
|
### From TorqueScript to Lua
|
||||||
- Any numeric value becomes a Lua `number`, except as specified with `bl.type`, which may convert a value into a `boolean` or a Torque object container.
|
|
||||||
- The empty string "" becomes `nil`
|
- The empty string "" becomes `nil`
|
||||||
|
- Any numeric value becomes a Lua `number`, except as specified with `bl.type`, which may convert a value into a `boolean` or a Torque object container.
|
||||||
- A string containing two or three numbers separated by single spaces becomes a `vector`
|
- A string containing two or three numbers separated by single spaces becomes a `vector`
|
||||||
- A string containing six numbers separated by single spaces becomes a table of two vectors, usually defining the corners a bounding box
|
- A string containing six numbers separated by single spaces becomes a table of two vectors, usually defining the corners a bounding box
|
||||||
- (WIP) A string containing seven numbers separated by single spaces is treated as an axis-angle (a "transform" in TorqueScript parlance), and is converted into a `matrix` representing the translation and rotation.
|
- (WIP) A string containing seven numbers separated by single spaces is treated as an axis-angle (a "transform"), and is converted into a `matrix` representing the translation and rotation
|
||||||
- Any other string is passed directly as a `string`
|
- Any other string is passed directly as a `string`
|
||||||
|
|
||||||
For scenarios where the automatic TorqueScript->Lua conversion rules are insufficient or incorrect, use `bl.type`.
|
For scenarios where the automatic TorqueScript->Lua conversion rules are insufficient or incorrect, use `bl.type`.
|
||||||
To convert objects by hand, use `bl.object`, `bl.boolean`, or `bl.string`.
|
To convert things by hand, use `bl.object`, `bl.boolean`, or `bl.string`.
|
||||||
|
|
||||||
## I/O and Safety
|
## I/O and Safety
|
||||||
|
|
||||||
All Lua code is sandboxed, and file access is confined to the default directories in the same way TorqueScript is.
|
All Lua code is sandboxed, and file access is confined to the default directories in the same way TorqueScript is.
|
||||||
BlockLua also has access to any C libraries installed in the `modules/lualib` folder, so be careful throwing things in there.
|
BlockLua also has access to any C libraries installed in the `modules/lualib` folder, so be careful throwing things in there.
|
||||||
### Unsafe Mode
|
|
||||||
BlockLua can be built in Unsafe Mode by specifying the `-DBLLUA_UNSAFE` compiler flag. This removes the sandboxing of Lua code, allowing it to access any file and use any library, including ffi.
|
|
||||||
Please do not publish add-ons that require unsafe mode.
|
|
||||||
|
|
||||||
### List of Object Types
|
### Unsafe Mode
|
||||||
`'all'` - Any object
|
|
||||||
`'player'` - Players or bots
|
BlockLua can be built in Unsafe Mode by specifying the `-DBLLUA_UNSAFE` compiler flag. This removes the sandboxing of Lua code, allowing it to access any file and use any library, including ffi.
|
||||||
`'item'` - Items
|
A more limited option is `-DBLLUA_ALLOWFFI`, which allows the use of the `ffi` library. This can still be exploited to grant all the same access as full unsafe mode.
|
||||||
`'vehicle'` - Vehicles
|
Please do not publish add-ons that require either of these.
|
||||||
`'projectile'` - Projectiles
|
|
||||||
`'brick'` - Bricks with raycasting enabled
|
|
||||||
`'brickalways'` - All bricks including those with raycasting disabled
|
|
||||||
Other types: `'static'`, `'environment'`, `'terrain'`, `'water'`, `'trigger'`, `'marker'`, `'gamebase'`, `'shapebase'`, `'camera'`, `'staticshape'`, `'vehicleblocker'`, `'explosion'`, `'corpse'`, `'debris'`, `'physicalzone'`, `'staticts'`, `'staticrendered'`, `'damagableitem'`
|
|
||||||
|
|
||||||
## Compiling
|
## Compiling
|
||||||
|
|
||||||
With any *32-bit* variant of GCC installed (such as MinGW or MSYS2), run the following command in the repo directory:
|
With any _32-bit_ variant of GCC installed (such as MinGW or MSYS2), run the following command in the repo directory:
|
||||||
`g++ src/bllua4.cpp -o BlockLua.dll -m32 -shared -static-libgcc -Isrc -Iinc/tsfuncs -Iinc/lua -lpsapi -L. -llua5.1 src/bllua`
|
`g++ src/bllua4.cpp -o BlockLua.dll -m32 -shared -static-libgcc -Isrc -Iinc/tsfuncs -Iinc/lua -lpsapi -L. -llua5.1`
|
||||||
|
|
||||||
LuaJIT (lua5.1.dll) can be obtained from https://luajit.org/
|
LuaJIT (lua5.1.dll) can be obtained from https://luajit.org/
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// BlockLua (bllua4): Simple Lua interface for TorqueScript
|
// BlockLua (bllua4): Advanced Lua interface for TorqueScript
|
||||||
|
|
||||||
// Includes
|
// Includes
|
||||||
|
|
||||||
@@ -60,23 +60,27 @@ bool init() {
|
|||||||
|
|
||||||
// Set up Lua environment
|
// Set up Lua environment
|
||||||
BLL_LOAD_LUA(gL, bll_fileLuaEnv);
|
BLL_LOAD_LUA(gL, bll_fileLuaEnv);
|
||||||
|
#ifdef BLLUA_ALLOWFFI
|
||||||
|
lua_pushboolean(gL, true);
|
||||||
|
lua_setglobal(gL, "_bllua_allowffi");
|
||||||
|
#endif
|
||||||
#ifndef BLLUA_UNSAFE
|
#ifndef BLLUA_UNSAFE
|
||||||
BLL_LOAD_LUA(gL, bll_fileLuaEnvSafe);
|
BLL_LOAD_LUA(gL, bll_fileLuaEnvSafe);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Load utilities in Lua
|
||||||
|
BLL_LOAD_LUA(gL, bll_fileLuaStd);
|
||||||
|
BLL_LOAD_LUA(gL, bll_fileLuaVector);
|
||||||
|
BLL_LOAD_LUA(gL, bll_fileLuaMatrix);
|
||||||
|
BLL_LOAD_LUA(gL, bll_fileLuaLibts);
|
||||||
|
BLL_LOAD_LUA(gL, bll_fileLuaLibbl);
|
||||||
|
BLL_LOAD_LUA(gL, bll_fileLuaLibblTypes);
|
||||||
|
|
||||||
// Expose Lua API to TS
|
// Expose Lua API to TS
|
||||||
BlAddFunction(
|
BlAddFunction(
|
||||||
NULL, NULL, "_bllua_luacall", bll_ts_luacall, "LuaCall(name, ...) - Call Lua function and return result", 2, 20);
|
NULL, NULL, "_bllua_luacall", bll_ts_luacall, "LuaCall(name, ...) - Call Lua function and return result", 2, 20);
|
||||||
BlEval(bll_fileTsEnv);
|
BlEval(bll_fileTsEnv);
|
||||||
|
|
||||||
// Load utilities
|
|
||||||
BLL_LOAD_LUA(gL, bll_fileLuaStd);
|
|
||||||
BLL_LOAD_LUA(gL, bll_fileLuaVector);
|
|
||||||
BLL_LOAD_LUA(gL, bll_fileLuaMatrix);
|
|
||||||
BLL_LOAD_LUA(gL, bll_fileLuaLibts);
|
|
||||||
BlEval(bll_fileTsLibts);
|
BlEval(bll_fileTsLibts);
|
||||||
BLL_LOAD_LUA(gL, bll_fileLuaLibbl);
|
|
||||||
BLL_LOAD_LUA(gL, bll_fileLuaLibblTypes);
|
|
||||||
BlEval(bll_fileTsLibblSupport);
|
BlEval(bll_fileTsLibblSupport);
|
||||||
BlEval(bll_fileLoadaddons);
|
BlEval(bll_fileLoadaddons);
|
||||||
|
|
||||||
@@ -89,8 +93,7 @@ bool init() {
|
|||||||
bool deinit() {
|
bool deinit() {
|
||||||
BlPrintf("BlockLua: Unloading");
|
BlPrintf("BlockLua: Unloading");
|
||||||
|
|
||||||
BlEval("deactivatePackage(_bllua_main);");
|
BlEval("$_bllua_active=0;deactivatePackage(_bllua_main);");
|
||||||
BlEval("$_bllua_active = 0;");
|
|
||||||
bll_LuaEval(gL, "for _,f in pairs(_bllua_on_unload) do f() end");
|
bll_LuaEval(gL, "for _,f in pairs(_bllua_on_unload) do f() end");
|
||||||
|
|
||||||
lua_close(gL);
|
lua_close(gL);
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ local old_require = require
|
|||||||
local old_os = os
|
local old_os = os
|
||||||
local old_debug = debug
|
local old_debug = debug
|
||||||
local old_package = package
|
local old_package = package
|
||||||
|
local old_allowffi = _bllua_allowffi
|
||||||
|
|
||||||
-- Remove all global variables except a whitelist
|
-- Remove all global variables except a whitelist
|
||||||
local ok_names = tmap {
|
local ok_names = tmap {
|
||||||
@@ -39,13 +40,10 @@ end
|
|||||||
|
|
||||||
-- Sanitize file paths to point only to allowed files within the game directory
|
-- Sanitize file paths to point only to allowed files within the game directory
|
||||||
-- List of allowed directories for reading/writing
|
-- List of allowed directories for reading/writing
|
||||||
|
-- modules/lualib is also allowed as read-only
|
||||||
local allowed_dirs = tmap {
|
local allowed_dirs = tmap {
|
||||||
'add-ons', 'base', 'config', 'saves', 'screenshots', 'shaders'
|
'add-ons', 'base', 'config', 'saves', 'screenshots', 'shaders'
|
||||||
}
|
}
|
||||||
-- List of allowed directories for reading only
|
|
||||||
local allowed_dirs_readonly = tmap {
|
|
||||||
'lualib'
|
|
||||||
}
|
|
||||||
-- List of disallowed file extensions - basically executable file extensions
|
-- List of disallowed file extensions - basically executable file extensions
|
||||||
-- Note that even without this protection, exploiting would still require somehow
|
-- Note that even without this protection, exploiting would still require somehow
|
||||||
-- getting a file within the allowed directories to autorun,
|
-- getting a file within the allowed directories to autorun,
|
||||||
@@ -64,6 +62,9 @@ local disallowed_exts = tmap {
|
|||||||
-- Return: clean file path if allowed (or nil if disallowed),
|
-- Return: clean file path if allowed (or nil if disallowed),
|
||||||
-- error string (or nil if allowed)
|
-- error string (or nil if allowed)
|
||||||
local function safe_path(fn, readonly)
|
local function safe_path(fn, readonly)
|
||||||
|
if type(fn) ~= 'string' then
|
||||||
|
return nil, 'Filename must be a string'
|
||||||
|
end
|
||||||
fn = fn:gsub('\\', '/')
|
fn = fn:gsub('\\', '/')
|
||||||
fn = fn:gsub('^ +', '')
|
fn = fn:gsub('^ +', '')
|
||||||
fn = fn:gsub(' +$', '')
|
fn = fn:gsub(' +$', '')
|
||||||
@@ -81,14 +82,15 @@ local function safe_path(fn, readonly)
|
|||||||
end
|
end
|
||||||
-- allow only whitelisted dirs
|
-- allow only whitelisted dirs
|
||||||
local dir = fn:match('^([^/]+)/')
|
local dir = fn:match('^([^/]+)/')
|
||||||
if (not dir) or (
|
if not (dir and (
|
||||||
(not allowed_dirs[dir:lower()]) and
|
allowed_dirs[dir:lower()] or
|
||||||
((not readonly) or (not allowed_dirs_readonly[dir:lower()]))) then
|
(readonly and fn:find('^modules/lualib/'))))
|
||||||
return nil, 'filename is in disallowed directory ' .. (dir or 'nil')
|
then
|
||||||
|
return nil, 'File is in disallowed directory ' .. (dir or 'nil')
|
||||||
end
|
end
|
||||||
-- disallow blacklisted extensions or no extension
|
-- disallow blacklisted extensions
|
||||||
local ext = fn:match('%.([^/%.]+)$')
|
local ext = fn:match('%.([^/%.]+)$')
|
||||||
if (not ext) or (disallowed_exts[ext:lower()]) then
|
if ext and disallowed_exts[ext:lower()] then
|
||||||
return nil, 'Filename \'' .. fn .. '\' has disallowed extension \'' ..
|
return nil, 'Filename \'' .. fn .. '\' has disallowed extension \'' ..
|
||||||
(ext or '') .. '\''
|
(ext or '') .. '\''
|
||||||
end
|
end
|
||||||
@@ -120,6 +122,7 @@ local disallowed_packages = tmap {
|
|||||||
'ffi', 'debug', 'package', 'io', 'os',
|
'ffi', 'debug', 'package', 'io', 'os',
|
||||||
'_bllua_ts',
|
'_bllua_ts',
|
||||||
}
|
}
|
||||||
|
if old_allowffi then disallowed_packages['ffi'] = nil end
|
||||||
function _bllua_requiresecure(name)
|
function _bllua_requiresecure(name)
|
||||||
if name:find('[^a-zA-Z0-9_%-%.]') or name:find('%.%.') or
|
if name:find('[^a-zA-Z0-9_%-%.]') or name:find('%.%.') or
|
||||||
name:find('^%.') or name:find('%.$') then
|
name:find('^%.') or name:find('%.$') then
|
||||||
|
|||||||
@@ -18,6 +18,8 @@ end
|
|||||||
|
|
||||||
-- Called when pcall fails on a ts->lua call, used to print detailed error info
|
-- Called when pcall fails on a ts->lua call, used to print detailed error info
|
||||||
function _bllua_on_error(err)
|
function _bllua_on_error(err)
|
||||||
|
-- Convert error to string if it's not already
|
||||||
|
err = tostring(err)
|
||||||
err = err:match(': (.+)$') or err
|
err = err:match(': (.+)$') or err
|
||||||
local tracelines = { err }
|
local tracelines = { err }
|
||||||
local level = 2
|
local level = 2
|
||||||
@@ -25,7 +27,7 @@ function _bllua_on_error(err)
|
|||||||
local info = debug.getinfo(level)
|
local info = debug.getinfo(level)
|
||||||
if not info then break end
|
if not info then break end
|
||||||
local filename = debug.getfilename(level) or info.short_src
|
local filename = debug.getfilename(level) or info.short_src
|
||||||
local funcname = info.name
|
local funcname = info.name or '<unknown>'
|
||||||
if funcname == 'dofile' then break end
|
if funcname == 'dofile' then break end
|
||||||
table.insert(tracelines, string.format('%s:%s in function \'%s\'',
|
table.insert(tracelines, string.format('%s:%s in function \'%s\'',
|
||||||
filename,
|
filename,
|
||||||
@@ -37,5 +39,9 @@ function _bllua_on_error(err)
|
|||||||
return table.concat(tracelines, '\n')
|
return table.concat(tracelines, '\n')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
-- overridden in lua-env-safe.lua (executed if not BLLUA_UNSAFE)
|
||||||
|
_bllua_io_open = io.open
|
||||||
|
_bllua_requiresecure = require
|
||||||
|
|
||||||
print = _bllua_ts.echo
|
print = _bllua_ts.echo
|
||||||
print(' Executed bllua-env.lua')
|
print(' Executed bllua-env.lua')
|
||||||
|
|||||||
1784
src/util/libbl.lua
1784
src/util/libbl.lua
File diff suppressed because it is too large
Load Diff
@@ -93,11 +93,15 @@ local allowed_zip_dirs = tflip {
|
|||||||
local function io_open_absolute(fn, mode)
|
local function io_open_absolute(fn, mode)
|
||||||
-- if file exists, use original mode
|
-- if file exists, use original mode
|
||||||
local res, err = _bllua_io_open(fn, mode)
|
local res, err = _bllua_io_open(fn, mode)
|
||||||
if res then return res end
|
if res then
|
||||||
|
return res
|
||||||
|
elseif err and not err:find('No such file or directory$') then
|
||||||
|
return nil, err
|
||||||
|
end
|
||||||
|
|
||||||
-- otherwise, if TS sees file but Lua doesn't, it must be in a zip, so use TS reader
|
-- otherwise, if TS sees file but Lua doesn't, it must be in a zip, so use TS reader
|
||||||
local dir = fn:match('^[^/]+')
|
local dir = fn:match('^[^/]+')
|
||||||
if not allowed_zip_dirs[dir:lower()] then return nil, 'File is not in one of the allowed directories' end
|
if not allowed_zip_dirs[dir:lower()] then return nil, 'Zip is not in one of the allowed directories' end
|
||||||
local exist = _bllua_ts.call('isFile', fn) == '1'
|
local exist = _bllua_ts.call('isFile', fn) == '1'
|
||||||
if not exist then return nil, err end
|
if not exist then return nil, err end
|
||||||
|
|
||||||
@@ -142,9 +146,9 @@ end
|
|||||||
|
|
||||||
---@diagnostic disable-next-line: duplicate-set-field
|
---@diagnostic disable-next-line: duplicate-set-field
|
||||||
function io.type(f)
|
function io.type(f)
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
if type(f) == 'table' and f._is_file then
|
if type(f) == 'table' and f._is_file then
|
||||||
---@diagnostic disable-next-line: undefined-field
|
---@diagnostic disable-next-line: undefined-field
|
||||||
return f._is_open and 'file' or 'closed file'
|
return f._is_open and 'file' or 'closed file'
|
||||||
else
|
else
|
||||||
return _bllua_io_type(f)
|
return _bllua_io_type(f)
|
||||||
@@ -183,14 +187,14 @@ function require(mod)
|
|||||||
if require_memo[mod] then return unpack(require_memo[mod]) end
|
if require_memo[mod] then return unpack(require_memo[mod]) end
|
||||||
local fp = mod:gsub('%.', '/')
|
local fp = mod:gsub('%.', '/')
|
||||||
local fns = {
|
local fns = {
|
||||||
'./' .. fp .. '.lua', -- local file
|
'./' .. fp .. '.lua', -- local file
|
||||||
'./' .. fp .. '/init.lua', -- local library
|
'./' .. fp .. '/init.lua', -- local library
|
||||||
fp .. '.lua', -- global file
|
fp .. '.lua', -- global file
|
||||||
fp .. '/init.lua', -- global library
|
fp .. '/init.lua', -- global library
|
||||||
}
|
}
|
||||||
if fp:lower():find('^add-ons/') then
|
if fp:lower():find('^add-ons/') then
|
||||||
local addonpath = fp:lower():match('^add-ons/[^/]+') .. '/'
|
local addonpath = fp:lower():match('^add-ons/[^/]+') .. '/'
|
||||||
table.insert(fns, addonpath .. fp .. '.lua') -- add-on file
|
table.insert(fns, addonpath .. fp .. '.lua') -- add-on file
|
||||||
table.insert(fns, addonpath .. fp .. '/init.lua') -- add-on library
|
table.insert(fns, addonpath .. fp .. '/init.lua') -- add-on library
|
||||||
end
|
end
|
||||||
for _, fn in ipairs(fns) do
|
for _, fn in ipairs(fns) do
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
-- Table / List
|
-- Table / List
|
||||||
-- Whether a table contains no keys
|
-- Whether a table contains no keys
|
||||||
function table.empty(t)
|
function table.empty(t)
|
||||||
return next(t) ~= nil
|
return next(t) == nil
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Apply a function to each key in a table
|
-- Apply a function to each key in a table
|
||||||
@@ -13,8 +13,19 @@ function table.map(f, ...)
|
|||||||
local u = {}
|
local u = {}
|
||||||
for k, _ in pairs(ts[1]) do
|
for k, _ in pairs(ts[1]) do
|
||||||
local args = {}
|
local args = {}
|
||||||
for j = 1, #ts do args[j] = ts[j][i] end
|
for j = 1, #ts do args[j] = ts[j][k] end
|
||||||
u[i] = f(unpack(args))
|
u[k] = f(unpack(args))
|
||||||
|
end
|
||||||
|
return u
|
||||||
|
end
|
||||||
|
|
||||||
|
function table.mapk(f, ...)
|
||||||
|
local ts = { ... }
|
||||||
|
local u = {}
|
||||||
|
for k, _ in pairs(ts[1]) do
|
||||||
|
local args = {}
|
||||||
|
for j = 1, #ts do args[j] = ts[j][k] end
|
||||||
|
u[k] = f(k, unpack(args))
|
||||||
end
|
end
|
||||||
return u
|
return u
|
||||||
end
|
end
|
||||||
@@ -30,6 +41,17 @@ function table.map_list(f, ...)
|
|||||||
return u
|
return u
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function table.mapi_list(f, ...)
|
||||||
|
local ts = { ... }
|
||||||
|
local u = {}
|
||||||
|
for i = 1, #ts[1] do
|
||||||
|
local args = {}
|
||||||
|
for j = 1, #ts do args[j] = ts[j][i] end
|
||||||
|
u[i] = f(i, unpack(args))
|
||||||
|
end
|
||||||
|
return u
|
||||||
|
end
|
||||||
|
|
||||||
-- Swap keys/values
|
-- Swap keys/values
|
||||||
function table.swap(t)
|
function table.swap(t)
|
||||||
local u = {}
|
local u = {}
|
||||||
@@ -193,7 +215,7 @@ valueToString = function(v, tabLevel, seen)
|
|||||||
return tostring(v)
|
return tostring(v)
|
||||||
else
|
else
|
||||||
--error('table.tostring: table contains a '..t..' value, cannot serialize')
|
--error('table.tostring: table contains a '..t..' value, cannot serialize')
|
||||||
return 'nil --[[ cannot serialize ' .. tostring(v) .. ' ]]'
|
return 'nil --[[ ' .. tostring(v) .. ' ]]'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
function table.tostring(t)
|
function table.tostring(t)
|
||||||
|
|||||||
Reference in New Issue
Block a user