Merge branch 'main' into dev
This commit is contained in:
commit
289e0f597c
117
README.md
117
README.md
@ -5,6 +5,8 @@
|
||||
- [Download](https://github.com/MihailRis/VoxelCore/releases/latest) | [Скачать](https://github.com/MihailRis/VoxelCore/releases/latest)
|
||||
- [Documentation](https://github.com/MihailRis/VoxelCore/blob/release-0.28/doc/en/main-page.md) | [Документация](https://github.com/MihailRis/VoxelCore/blob/release-0.28/doc/ru/main-page.md)
|
||||
|
||||
---
|
||||
|
||||
## Build project in Linux
|
||||
|
||||
### Install libraries
|
||||
@ -13,13 +15,14 @@
|
||||
|
||||
```sh
|
||||
git clone https://github.com/skypjack/entt.git
|
||||
cd entt/build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DENTT_INSTALL=on ..
|
||||
cd entt
|
||||
mkdir build && cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release -DENTT_INSTALL=ON ..
|
||||
sudo make install
|
||||
```
|
||||
|
||||
> [!WARNING]
|
||||
> If you are using ALT Linux, you should not use this EnTT installation method
|
||||
> If you are using ALT Linux, do **not** use this EnTT installation method.
|
||||
|
||||
#### ALT Linux based distros
|
||||
|
||||
@ -31,11 +34,11 @@ apt-get install entt-devel libglfw3-devel libGLEW-devel libglm-devel libpng-deve
|
||||
#### Debian based distros
|
||||
|
||||
```sh
|
||||
sudo apt install libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libcurl4-openssl-dev
|
||||
sudo apt install libglfw3 libglfw3-dev libglew-dev libglm-dev libpng-dev libopenal-dev libluajit-5.1-dev libvorbis-dev libcurl4-openssl-dev
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> CMake missing LUA_INCLUDE_DIR and LUA_LIBRARIES fix:
|
||||
> CMake missing `LUA_INCLUDE_DIR` and `LUA_LIBRARIES` fix:
|
||||
>
|
||||
> ```sh
|
||||
> sudo ln -s /usr/lib/x86_64-linux-gnu/libluajit-5.1.a /usr/lib/x86_64-linux-gnu/liblua5.1.a
|
||||
@ -45,24 +48,24 @@ sudo apt install libglfw3-dev libglfw3 libglew-dev libglm-dev libpng-dev libopen
|
||||
#### RHEL based distros
|
||||
|
||||
```sh
|
||||
sudo dnf install glfw-devel glfw glew-devel glm-devel libpng-devel libvorbis-devel openal-devel luajit-devel libcurl-devel
|
||||
sudo dnf install glfw-devel glew-devel glm-devel libpng-devel libvorbis-devel openal-soft-devel luajit-devel libcurl-devel
|
||||
```
|
||||
|
||||
#### Arch based distros
|
||||
|
||||
If you use X11
|
||||
If you use X11:
|
||||
|
||||
```sh
|
||||
sudo pacman -S glfw-x11 glew glm libpng libvorbis openal luajit libcurl
|
||||
```
|
||||
|
||||
If you use Wayland
|
||||
If you use Wayland:
|
||||
|
||||
```sh
|
||||
sudo pacman -S glfw-wayland glew glm libpng libvorbis openal luajit libcurl
|
||||
```
|
||||
|
||||
And you need entt. In yay you can use
|
||||
And install EnTT:
|
||||
|
||||
```sh
|
||||
yay -S entt
|
||||
@ -76,9 +79,14 @@ cd VoxelCore
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||
cmake --build .
|
||||
cmake --build . --parallel
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> Use `--parallel` to utilize all CPU cores during build.
|
||||
|
||||
---
|
||||
|
||||
## Building project in macOS
|
||||
|
||||
### Install libraries
|
||||
@ -88,9 +96,7 @@ brew install glfw3 glew glm libpng libvorbis lua luajit libcurl openal-soft skyp
|
||||
```
|
||||
|
||||
> [!TIP]
|
||||
> If homebrew for some reason could not install the necessary packages:
|
||||
> ```lua luajit openal-soft```, then download, install and compile them manually
|
||||
> (Lua, LuaJIT and OpenAL).
|
||||
> If Homebrew fails to install `lua`, `luajit`, or `openal-soft`, download, compile, and install them manually.
|
||||
|
||||
### Building engine with CMake
|
||||
|
||||
@ -100,90 +106,109 @@ cd VoxelCore
|
||||
mkdir build
|
||||
cd build
|
||||
cmake -DCMAKE_BUILD_TYPE=Release ..
|
||||
cmake --build .
|
||||
cmake --build . --parallel
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Building in Windows
|
||||
|
||||
>[!NOTE]
|
||||
> Requirement:
|
||||
>
|
||||
> vcpkg, CMake, Git
|
||||
> [!NOTE]
|
||||
> Requirements: **vcpkg**, **CMake**, **Git**, and **Visual Studio** (with C++ tools).
|
||||
|
||||
There are two options to use vcpkg:
|
||||
1. If you have Visual Studio installed, most likely the **VCPKG_ROOT** environment variable will already exist in **Developer Command Prompt for VS**
|
||||
2. If you want use **vcpkg**, install **vcpkg** from git to you system:
|
||||
```PowerShell
|
||||
cd C:/
|
||||
|
||||
1. If you have Visual Studio installed, the **VCPKG_ROOT** environment variable is often already set in the **Developer Command Prompt for VS**.
|
||||
2. Otherwise, install **vcpkg** manually:
|
||||
|
||||
```powershell
|
||||
cd C:\
|
||||
git clone https://github.com/microsoft/vcpkg.git
|
||||
cd vcpkg
|
||||
.\bootstrap-vcpkg.bat
|
||||
```
|
||||
After installing **vcpkg**, setup env variable **VCPKG_ROOT** and add it to **PATH**:
|
||||
```PowerShell
|
||||
$env:VCPKG_ROOT = "C:\path\to\vcpkg"
|
||||
|
||||
Then set the `VCPKG_ROOT` environment variable and add it to `PATH`:
|
||||
|
||||
```powershell
|
||||
$env:VCPKG_ROOT = "C:\vcpkg"
|
||||
$env:PATH = "$env:VCPKG_ROOT;$env:PATH"
|
||||
```
|
||||
>[!TIP]
|
||||
>For troubleshooting you can read full [documentation](https://learn.microsoft.com/ru-ru/vcpkg/get_started/get-started?pivots=shell-powershell) for **vcpkg**
|
||||
|
||||
After installing **vcpkg** you can build project:
|
||||
```PowerShell
|
||||
> [!TIP]
|
||||
> For troubleshooting, refer to the official [vcpkg documentation](https://learn.microsoft.com/ru-ru/vcpkg/get_started/get-started?pivots=shell-powershell).
|
||||
|
||||
After installing **vcpkg**, build the project:
|
||||
|
||||
```powershell
|
||||
git clone --recursive https://github.com/MihailRis/VoxelCore.git
|
||||
cd VoxelCore
|
||||
cmake --preset default-vs-msvc-windows
|
||||
cmake --build --preset default-vs-msvc-windows
|
||||
```
|
||||
|
||||
> [!NOTE]
|
||||
> Make sure your `CMakeUserPresets.json` (if used) contains the correct `VCPKG_ROOT` path.
|
||||
|
||||
---
|
||||
|
||||
## Build using Docker
|
||||
|
||||
### Step 0. Install docker on your system
|
||||
> [!NOTE]
|
||||
> First, install Docker Engine: [https://docs.docker.com/engine/install](https://docs.docker.com/engine/install)
|
||||
|
||||
See <https://docs.docker.com/engine/install>
|
||||
### On Linux
|
||||
|
||||
### Do you have Linux
|
||||
|
||||
### Step 1. Build docker container
|
||||
#### Step 1. Build Docker image
|
||||
|
||||
```sh
|
||||
docker build -t voxel-engine .
|
||||
```
|
||||
|
||||
### Step 2. Build project using the docker container
|
||||
#### Step 2. Build project inside container
|
||||
|
||||
```sh
|
||||
docker run --rm -it -v$(pwd):/project voxel-engine bash -c "cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && cmake --build build"
|
||||
docker run --rm -it -v "$(pwd):/project" voxel-engine bash -c "cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && cmake --build build --parallel"
|
||||
```
|
||||
|
||||
### Step 3. Run project using the docker container
|
||||
#### Step 3. Run the application (requires X11 forwarding)
|
||||
|
||||
```sh
|
||||
docker run --rm -it -v$(pwd):/project -v/tmp/.X11-unix:/tmp/.X11-unix -v${XAUTHORITY}:/home/user/.Xauthority:ro -eDISPLAY --network=host voxel-engine ./build/VoxelEngine
|
||||
docker run --rm -it \
|
||||
-v "$(pwd):/project" \
|
||||
-v /tmp/.X11-unix:/tmp/.X11-unix \
|
||||
-v "$XAUTHORITY:/home/user/.Xauthority:ro" \
|
||||
-e DISPLAY="$DISPLAY" \
|
||||
--network=host \
|
||||
voxel-engine ./build/VoxelEngine
|
||||
```
|
||||
|
||||
### Do you have Windows
|
||||
### On Windows
|
||||
|
||||
### Step 1. You need to install VcXsrv
|
||||
> [!NOTE]
|
||||
> You need an X server like **VcXsrv** to display the GUI.
|
||||
|
||||
### Step 2. Run VcXsrv with the command
|
||||
#### Step 1. Install and run VcXsrv
|
||||
|
||||
Launch with:
|
||||
```powershell
|
||||
.\vcxsrv.exe :0 -multiwindow -ac
|
||||
```
|
||||
|
||||
### Step 3. Build docker container
|
||||
#### Step 2. Build Docker image
|
||||
|
||||
```powershell
|
||||
docker build -t voxel-engine .
|
||||
```
|
||||
|
||||
### Step 4. Build project using the docker container
|
||||
#### Step 3. Build project
|
||||
|
||||
```powershell
|
||||
docker run --rm -it -v "${PWD}:/project" voxel-engine bash -c "cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && cmake --build build"
|
||||
docker run --rm -it -v "${PWD}:/project" voxel-engine bash -c "cmake -DCMAKE_BUILD_TYPE=Release -Bbuild && cmake --build build --parallel"
|
||||
```
|
||||
|
||||
### Step 5. Run project using the docker container
|
||||
#### Step 4. Run the application
|
||||
|
||||
```powershell
|
||||
docker run --rm -it -v "${PWD}:/project" -e DISPLAY=host.docker.internal:0.0 --network host voxel-engine ./build/VoxelEngine
|
||||
docker run --rm -it -v "${PWD}:/project" -e DISPLAY=host.docker.internal:0.0 --network=host voxel-engine ./build/VoxelEngine
|
||||
```
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
for i=1,3 do
|
||||
print(string.format("iteration %s", i + 1))
|
||||
print(string.format("iteration %s", i))
|
||||
local text = ""
|
||||
local complete = false
|
||||
|
||||
|
||||
35
dev/tests/network_udp.lua
Normal file
35
dev/tests/network_udp.lua
Normal file
@ -0,0 +1,35 @@
|
||||
math.randomseed(43172)
|
||||
for i = 1, 15 do
|
||||
debug.log(string.format("iteration %s", i))
|
||||
local complete = false
|
||||
|
||||
local server = network.udp_open(8645 + i, function (address, port, data, srv)
|
||||
debug.log(string.format("server received %s byte(s) from %s:%s", #data, address, port))
|
||||
srv:send(address, port, "pong")
|
||||
end)
|
||||
|
||||
app.tick()
|
||||
network.udp_connect("localhost", 8645 + i, function (data)
|
||||
debug.log(string.format("client received %s byte(s) from server", #data))
|
||||
complete = true
|
||||
end, function (socket)
|
||||
debug.log("udp socket opened")
|
||||
start_coroutine(function()
|
||||
debug.log("udp data-sender started")
|
||||
for k = 1, 15 do
|
||||
local payload = ""
|
||||
for j = 1, 16 do
|
||||
payload = payload .. math.random(0, 9)
|
||||
end
|
||||
socket:send(payload)
|
||||
debug.log(string.format("sent packet %s (%s bytes)", k, #payload))
|
||||
coroutine.yield()
|
||||
end
|
||||
app.sleep_until(function () return complete end, nil, 5)
|
||||
socket:close()
|
||||
end, "udp-data-sender")
|
||||
end)
|
||||
|
||||
app.sleep_until(function () return complete end, nil, 5)
|
||||
server:close()
|
||||
end
|
||||
@ -86,9 +86,9 @@ body:get_size() -> vec3
|
||||
body:set_size(size: vec3)
|
||||
|
||||
-- Returns the gravity multiplier
|
||||
body:get_gravity_scale() -> vec3
|
||||
body:get_gravity_scale() -> number
|
||||
-- Sets the gravity multiplier
|
||||
body:set_gravity_scale(scale: vec3)
|
||||
body:set_gravity_scale(scale: number)
|
||||
|
||||
-- Returns the linear velocity attenuation multiplier (used to simulate air resistance and friction)
|
||||
body:get_linear_damping() -> number
|
||||
|
||||
@ -45,6 +45,7 @@
|
||||
- [Модуль core:bit_converter](scripting/modules/core_bit_converter.md)
|
||||
- [Модуль core:data_buffer](scripting/modules/core_data_buffer.md)
|
||||
- [Модули core:vector2, core:vector3](scripting/modules/core_vector2_vector3.md)
|
||||
- [Встроенные компоненты сущностей](scripting/core_components.md)
|
||||
|
||||
## Аннотации типов данных
|
||||
|
||||
|
||||
89
doc/ru/scripting/core_components.md
Normal file
89
doc/ru/scripting/core_components.md
Normal file
@ -0,0 +1,89 @@
|
||||
# Встроенные компоненты
|
||||
|
||||
## *core:pathfinding*
|
||||
|
||||
Компонент для построение путей движения мобов.
|
||||
|
||||
```lua
|
||||
local pathfinding = entity:require_component("core:pathfinding")
|
||||
|
||||
-- Устанавливает цель движения, не сбрасывая текущий маршрут
|
||||
pathfinding.set_target(target: vec3)
|
||||
|
||||
-- Возвращает текущую цель движения
|
||||
pathfinding.get_target() --> vec3
|
||||
|
||||
-- Устанавливает высоту преодолимого прыжком препятствия
|
||||
pathfinding.set_jump_height(height: number)
|
||||
|
||||
-- Возвращает текущий построенный маршрут или nil
|
||||
pathfinding.get_route()
|
||||
|
||||
-- Сбрасывает текущий построенный маршрут
|
||||
pathfinding.reset_route()
|
||||
|
||||
-- Возвращает следующую точку маршрута, по текущим координатам.
|
||||
-- (следует использовать компонент core:mob - функция mob.follow_waypoints)
|
||||
pathfinding.next_waypoint() --> vec3 или nil
|
||||
|
||||
-- Устанавливает интервал перестройки маршрута в тактах обновления.
|
||||
pathfinding.set_refresh_interval(interval: number)
|
||||
```
|
||||
|
||||
## *core:mob*
|
||||
|
||||
Компонент для управления движением (включая полёт) и вращением мобов.
|
||||
|
||||
```lua
|
||||
local mob = entity:require_component("core:mob")
|
||||
|
||||
-- Выполняет прыжок с силой jump_force * multiplier
|
||||
mob.jump([опционально] multiplier: number = 1.0)
|
||||
|
||||
|
||||
-- Вертикальное движение (работает в полёте или в плавании (в будущем))
|
||||
mob.move_vertical(
|
||||
-- Скорость вертикального движения
|
||||
speed: number,
|
||||
-- Текущая скорость сущности (для минимизации вызовов rigidbody:get_vel())
|
||||
[опционально] current_velocity
|
||||
)
|
||||
|
||||
-- Горизонтальное движение
|
||||
mob.go(
|
||||
-- 2D вектор направления движения
|
||||
dir: vec2,
|
||||
-- Множитель скорости
|
||||
speed_multiplier: number,
|
||||
-- Бег
|
||||
sprint: bool,
|
||||
-- Присядь
|
||||
crouch: bool,
|
||||
-- Текущая скорость сущности (для минимизации вызовов rigidbody:get_vel())
|
||||
[опционально] current_velocity
|
||||
)
|
||||
|
||||
-- Меняет направление взгляда сущности, направляя на указанную точку
|
||||
mob.look_at(
|
||||
-- Целевая точка
|
||||
point: vec3,
|
||||
-- Менять ли направление всей сущности
|
||||
change_dir: bool = false
|
||||
)
|
||||
|
||||
-- Движение по построенному маршруту.
|
||||
-- Если не указан pathfinding, требуется наличие у сущности компонента core:pathfinding
|
||||
mob.follow_waypoints(
|
||||
-- Возможная замена компонента pathfinding
|
||||
[опционально] pathfinding
|
||||
)
|
||||
|
||||
-- Устанавливает направление всей сущности
|
||||
mob.set_dir(dir: vec3)
|
||||
|
||||
-- Проверяет, включён ли режим полёта
|
||||
mob.is_flight() --> bool
|
||||
|
||||
-- Включает/выключает режим полёта
|
||||
mob.set_flight(flag: bool)
|
||||
```
|
||||
@ -86,9 +86,9 @@ body:get_size() -> vec3
|
||||
body:set_size(size: vec3)
|
||||
|
||||
-- Возвращает множитель гравитации
|
||||
body:get_gravity_scale() -> vec3
|
||||
body:get_gravity_scale() -> number
|
||||
-- Устанавливает множитель гравитации
|
||||
body:set_gravity_scale(scale: vec3)
|
||||
body:set_gravity_scale(scale: number)
|
||||
|
||||
-- Возвращает множитель затухания линейной скорости (используется для имитации сопротивления воздуха и трения)
|
||||
body:get_linear_damping() -> number
|
||||
|
||||
@ -22,6 +22,11 @@
|
||||
|
||||
На данный момент существует два вида примитивов: box и rect, а также, part (описывает часть примитива, такую как, например, сторона куба).
|
||||
|
||||
> [!NOTE]
|
||||
> По-умолчанию текстурные координаты определяются размерами примитивов.
|
||||
> При размерах более 1.0 результат не определён и может измениться в последующих обновлениях.
|
||||
> Используйте `region` или `region-scale` для ручной настройки.
|
||||
|
||||
### Свойства `rect`
|
||||
|
||||
- `from` - точка начала примитива. Пример: `from (0,0.5,0.125)`
|
||||
|
||||
@ -31,11 +31,11 @@
|
||||
size='32' margin='0,0,425,64' gravity='bottom-right'
|
||||
color='#FFFFFF50' hover-color='#FFFFFF10'/>
|
||||
|
||||
<panel id='packs_add' pos='485,34' size='440,507' color='0' max-length='455' scrollable='true'>
|
||||
<panel id='packs_add' pos='485,34' size='440,455' color='0' max-length='455' scrollable='true'>
|
||||
<!-- content is generated in script -->
|
||||
</panel>
|
||||
|
||||
<panel id='packs_cur' pos='15,34' size='440,507' color='0' max-length='455' scrollable='true'>
|
||||
<panel id='packs_cur' pos='15,34' size='440,455' color='0' max-length='455' scrollable='true'>
|
||||
<!-- content is generated in script -->
|
||||
</panel>
|
||||
</container>
|
||||
@ -255,30 +255,33 @@ function check_dependencies(packinfo)
|
||||
if packinfo.dependencies == nil then
|
||||
return
|
||||
end
|
||||
for i,dep in ipairs(packinfo.dependencies) do
|
||||
for i, dep in ipairs(packinfo.dependencies) do
|
||||
local depid, depver = unpack(string.split(dep:sub(2,-1), "@"))
|
||||
|
||||
if dep:sub(1,1) == '!' then
|
||||
if not table.has(packs_all, depid) then
|
||||
return string.format(
|
||||
"%s (%s)", gui.str("error.dependency-not-found"), depid
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
local dep_pack = pack.get_info(depid);
|
||||
|
||||
if not compare_version(depver, dep_pack.version) then
|
||||
local op, ver = Version.parse(depver);
|
||||
|
||||
print(string.format("%s: %s !%s %s (%s)", gui.str("error.dependency-version-not-met"), dep_pack.version, op, ver, depid));
|
||||
return string.format("%s: %s != %s (%s)", gui.str("error.dependency-version-not-met"), dep_pack.version, ver, depid);
|
||||
end
|
||||
|
||||
if table.has(packs_installed, packinfo.id) then
|
||||
table.insert(required, depid)
|
||||
end
|
||||
if dep:sub(1,1) ~= '!' then
|
||||
goto continue
|
||||
end
|
||||
if not table.has(packs_all, depid) then
|
||||
return string.format(
|
||||
"%s (%s)", gui.str("error.dependency-not-found"), depid
|
||||
)
|
||||
end
|
||||
|
||||
local dep_pack = pack.get_info(depid);
|
||||
|
||||
if not compare_version(depver, dep_pack.version) then
|
||||
local op, ver = Version.parse(depver)
|
||||
return string.format(
|
||||
"%s: %s != %s (%s)",
|
||||
gui.str("error.dependency-version-not-met"),
|
||||
dep_pack.version, ver, depid
|
||||
);
|
||||
end
|
||||
|
||||
if table.has(packs_installed, packinfo.id) then
|
||||
table.insert(required, depid)
|
||||
end
|
||||
::continue::
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
@ -1,35 +0,0 @@
|
||||
local Random = {}
|
||||
|
||||
local M = 2 ^ 31
|
||||
local A = 1103515245
|
||||
local C = 12345
|
||||
|
||||
function Random.randint(self)
|
||||
self._seed = (A * self._seed + C) % M
|
||||
return self._seed
|
||||
end
|
||||
|
||||
function Random.random(self, a, b)
|
||||
local num = self:randint() % M / M
|
||||
if b then
|
||||
return math.floor(num * (b - a + 1) + a)
|
||||
elseif a then
|
||||
return math.floor(num * a + 1)
|
||||
else
|
||||
return num
|
||||
end
|
||||
end
|
||||
|
||||
function Random.seed(self, number)
|
||||
if type(number) ~= "number" then
|
||||
error("number expected")
|
||||
end
|
||||
self._seed = number
|
||||
end
|
||||
|
||||
return function(seed)
|
||||
if seed and type(seed) ~= "number" then
|
||||
error("number expected")
|
||||
end
|
||||
return setmetatable({_seed = seed or random.random(M)}, {__index = Random})
|
||||
end
|
||||
@ -144,7 +144,11 @@ network.udp_connect = function (address, port, datagramHandler, openCallback)
|
||||
socket.id = network.__connect_udp(address, port)
|
||||
|
||||
_udp_client_datagram_callbacks[socket.id] = datagramHandler
|
||||
_udp_client_open_callbacks[socket.id] = openCallback
|
||||
if openCallback then
|
||||
_udp_client_open_callbacks[socket.id] = function()
|
||||
openCallback(socket)
|
||||
end
|
||||
end
|
||||
|
||||
return socket
|
||||
end
|
||||
@ -225,8 +229,6 @@ block.__process_register_events = function()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
print(type, id, x, y, z)
|
||||
end
|
||||
end
|
||||
|
||||
@ -256,9 +258,15 @@ network.__process_events = function()
|
||||
end
|
||||
elseif etype == DATAGRAM then
|
||||
if side == ON_CLIENT then
|
||||
_udp_client_datagram_callbacks[cid](data)
|
||||
local callback = _udp_client_datagram_callbacks[cid]
|
||||
if callback then
|
||||
callback(data)
|
||||
end
|
||||
elseif side == ON_SERVER then
|
||||
_udp_server_callbacks[sid](addr, port, data)
|
||||
local callback = _udp_server_callbacks[sid]
|
||||
if callback then
|
||||
callback(addr, port, data)
|
||||
end
|
||||
end
|
||||
elseif etype == RESPONSE then
|
||||
if event[2] / 100 == 2 then
|
||||
|
||||
@ -51,6 +51,7 @@ end
|
||||
|
||||
function move_vertical(speed, vel)
|
||||
vel = vel or body:get_vel()
|
||||
speed = speed or 1.0
|
||||
vel[2] = vel[2] * 0.2 + props.movement_speed * speed * 0.8
|
||||
body:set_vel(vel)
|
||||
end
|
||||
@ -145,6 +146,10 @@ function set_dir(new_dir)
|
||||
dir = new_dir
|
||||
end
|
||||
|
||||
function get_dir()
|
||||
return dir
|
||||
end
|
||||
|
||||
function is_flight() return flight end
|
||||
|
||||
function set_flight(flag) flight = flag end
|
||||
@ -154,7 +159,7 @@ local prev_angle = (vec2.angle({dir[3], dir[1]})) % 360
|
||||
function on_physics_update(delta)
|
||||
local grounded = body:is_grounded()
|
||||
body:set_vdamping(flight)
|
||||
body:set_gravity_scale({0, flight and 0.0 or props.gravity_scale, 0})
|
||||
body:set_gravity_scale(flight and 0.0 or props.gravity_scale)
|
||||
body:set_linear_damping(
|
||||
(flight or not grounded) and props.air_damping or props.ground_damping
|
||||
)
|
||||
|
||||
@ -25,6 +25,10 @@ function get_route()
|
||||
return route
|
||||
end
|
||||
|
||||
function reset_route()
|
||||
route = nil
|
||||
end
|
||||
|
||||
function next_waypoint()
|
||||
if not route or #route == 0 then
|
||||
return
|
||||
|
||||
@ -4,13 +4,22 @@ local mob = entity:require_component("core:mob")
|
||||
|
||||
local cheat_speed_mul = 10.0
|
||||
|
||||
local function process_player_inputs(pid, delta)
|
||||
local function get_player_rotation(pid)
|
||||
local rx, ry, rz = player.get_rot(pid)
|
||||
local matrix = mat4.rotate({0, 1, 0}, rx)
|
||||
mat4.rotate(matrix, {1, 0, 0}, ry, matrix)
|
||||
mat4.rotate(matrix, {0, 0, 1}, rz, matrix)
|
||||
return matrix
|
||||
end
|
||||
|
||||
local function process_player_inputs(pid, rot, delta)
|
||||
if not hud or hud.is_inventory_open() or menu.page ~= "" then
|
||||
return
|
||||
end
|
||||
local cam = cameras.get("core:first-person")
|
||||
local front = cam:get_front()
|
||||
local right = cam:get_right()
|
||||
|
||||
local front = mat4.mul(rot, {0, 0, -1})
|
||||
local right = mat4.mul(rot, {1, 0, 0})
|
||||
|
||||
front[2] = 0.0
|
||||
vec3.normalize(front, front)
|
||||
|
||||
@ -22,8 +31,6 @@ local function process_player_inputs(pid, delta)
|
||||
local isback = input.is_active('movement.back')
|
||||
local isleft = input.is_active('movement.left')
|
||||
local isright = input.is_active('movement.right')
|
||||
mob.set_flight(player.is_flight(pid))
|
||||
body:set_body_type(player.is_noclip(pid) and "kinematic" or "dynamic")
|
||||
body:set_crouching(iscrouch)
|
||||
|
||||
local vel = body:get_vel()
|
||||
@ -53,10 +60,19 @@ end
|
||||
|
||||
function on_physics_update(delta)
|
||||
local pid = entity:get_player()
|
||||
if pid ~= -1 then
|
||||
if pid == -1 then
|
||||
return
|
||||
end
|
||||
|
||||
mob.set_flight(player.is_flight(pid))
|
||||
body:set_body_type(player.is_noclip(pid) and "kinematic" or "dynamic")
|
||||
|
||||
if hud and pid == hud.get_player() then
|
||||
local pos = tsf:get_pos()
|
||||
local cam = cameras.get("core:first-person")
|
||||
process_player_inputs(pid, delta)
|
||||
mob.look_at(vec3.add(pos, cam:get_front()))
|
||||
local rot = get_player_rotation(pid)
|
||||
local front = mat4.mul(rot, {0, 0, -1})
|
||||
|
||||
process_player_inputs(pid, rot, delta)
|
||||
mob.look_at(vec3.add(pos, front))
|
||||
end
|
||||
end
|
||||
|
||||
@ -669,4 +669,41 @@ end
|
||||
bit.compile = require "core:bitwise/compiler"
|
||||
bit.execute = require "core:bitwise/executor"
|
||||
|
||||
random.Random = require "core:internal/random_generator"
|
||||
function __vc_create_random_methods(random_methods)
|
||||
local index = 1
|
||||
local buffer = nil
|
||||
local buffer_size = 64
|
||||
|
||||
local seed_func = random_methods.seed
|
||||
local random_func = random_methods.random
|
||||
|
||||
function random_methods:bytes(n)
|
||||
local bytes = Bytearray(n)
|
||||
for i=1,n do
|
||||
bytes[i] = self:random(255)
|
||||
end
|
||||
return bytes
|
||||
end
|
||||
|
||||
function random_methods:seed(x)
|
||||
seed_func(self, x)
|
||||
buffer = nil
|
||||
end
|
||||
|
||||
function random_methods:random(a, b)
|
||||
if not buffer or index > #buffer then
|
||||
buffer = random_func(self, buffer_size)
|
||||
index = 1
|
||||
end
|
||||
local value = buffer[index]
|
||||
if b then
|
||||
value = math.floor(value * (b - a + 1) + a)
|
||||
elseif a then
|
||||
value = math.floor(value * a + 1)
|
||||
end
|
||||
|
||||
index = index + 4
|
||||
return value
|
||||
end
|
||||
return random_methods
|
||||
end
|
||||
|
||||
@ -43,7 +43,7 @@ float calc_shadow(
|
||||
// TODO: add array textures support
|
||||
float calc_shadow(vec4 modelPos, vec3 realnormal, float distance) {
|
||||
#ifdef ENABLE_SHADOWS
|
||||
float s = pow(abs(cos(u_dayTime * PI2)), 0.25) * u_shadowsOpacity;
|
||||
float s = u_shadowsOpacity;
|
||||
vec3 normalOffset = realnormal * (distance > 64.0 ? 0.2 : 0.04);
|
||||
|
||||
// as slow as mix(...)
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
#include <constants>
|
||||
|
||||
vec3 pick_sky_color(samplerCube cubemap) {
|
||||
vec3 skyLightColor = texture(cubemap, vec3(0.8f, 0.01f, 0.4f)).rgb;
|
||||
vec3 skyLightColor = texture(cubemap, vec3(0.4f, 0.05f, 0.4f)).rgb;
|
||||
skyLightColor *= SKY_LIGHT_TINT;
|
||||
skyLightColor = min(vec3(1.0f), skyLightColor * SKY_LIGHT_MUL);
|
||||
skyLightColor = max(MIN_SKY_LIGHT, skyLightColor);
|
||||
|
||||
@ -268,7 +268,11 @@ void main() {
|
||||
camera_vector, // the camera vector (ray direction of this pixel)
|
||||
1e12f, // max dist, essentially the scene depth
|
||||
vec3(0.0f), // scene color, the color of the current pixel being rendered
|
||||
vec3(u_lightDir.x, pow(u_lightDir.y, 3.0), u_lightDir.z), // light direction
|
||||
vec3(
|
||||
u_lightDir.x,
|
||||
u_lightDir.y,
|
||||
u_lightDir.z
|
||||
), // light direction
|
||||
vec3(40.0*fog), // light intensity, 40 looks nice
|
||||
PLANET_POS, // position of the planet
|
||||
PLANET_RADIUS, // radius of the planet in meters
|
||||
|
||||
@ -32,7 +32,7 @@ protected:
|
||||
void goBack(size_t count = 1);
|
||||
void reset();
|
||||
|
||||
int64_t parseSimpleInt(int base);
|
||||
int64_t parseSimpleInt(int base, size_t maxLength = 0xFFFFFFFF);
|
||||
dv::value parseNumber(int sign);
|
||||
dv::value parseNumber();
|
||||
StringT parseString(CharT chr, bool closeRequired = true);
|
||||
|
||||
@ -349,7 +349,10 @@ std::basic_string<CharT> BasicParser<CharT>::parseXmlName() {
|
||||
}
|
||||
|
||||
template <typename CharT>
|
||||
int64_t BasicParser<CharT>::parseSimpleInt(int base) {
|
||||
int64_t BasicParser<CharT>::parseSimpleInt(int base, size_t maxLength) {
|
||||
if (maxLength == 0) return 0;
|
||||
|
||||
size_t start = pos;
|
||||
CharT c = peek();
|
||||
int index = hexchar2int(c);
|
||||
if (index == -1 || index >= base) {
|
||||
@ -357,7 +360,7 @@ int64_t BasicParser<CharT>::parseSimpleInt(int base) {
|
||||
}
|
||||
int64_t value = index;
|
||||
pos++;
|
||||
while (hasNext()) {
|
||||
while (hasNext() && pos - start < maxLength) {
|
||||
c = source[pos];
|
||||
while (c == '_') {
|
||||
c = source[++pos];
|
||||
@ -476,7 +479,7 @@ std::basic_string<CharT> BasicParser<CharT>::parseString(
|
||||
continue;
|
||||
}
|
||||
if (c == 'u' || c == 'x') {
|
||||
int codepoint = parseSimpleInt(16);
|
||||
int codepoint = parseSimpleInt(16, c == 'u' ? 4 : 2);
|
||||
ubyte bytes[4];
|
||||
int size = util::encode_utf8(codepoint, bytes);
|
||||
CharT chars[4];
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
#define VC_ENABLE_REFLECTION
|
||||
#include "ContentPack.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
@ -146,7 +147,7 @@ ContentPack ContentPack::read(const io::path& folder) {
|
||||
std::uint8_t op_size = 0;
|
||||
|
||||
// Two symbol operators
|
||||
if (op == ">=" || op == "=>" || op == "<=" || op == "=<") {
|
||||
if (op == ">=" || op == "<=") {
|
||||
op_size = 2;
|
||||
depVerOperator = op;
|
||||
}
|
||||
@ -169,7 +170,16 @@ ContentPack ContentPack::read(const io::path& folder) {
|
||||
}
|
||||
}
|
||||
|
||||
pack.dependencies.push_back({level, depName, depVer, depVerOperator});
|
||||
VersionOperator versionOperator;
|
||||
if (VersionOperatorMeta.getItem(depVerOperator, versionOperator)) {
|
||||
pack.dependencies.push_back(
|
||||
{level, depName, depVer, versionOperator}
|
||||
);
|
||||
} else {
|
||||
throw contentpack_error(
|
||||
pack.id, folder, "invalid version operator"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,14 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "typedefs.hpp"
|
||||
#include "content_fwd.hpp"
|
||||
#include "io/io.hpp"
|
||||
#include "util/EnumMetadata.hpp"
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
|
||||
#include "typedefs.hpp"
|
||||
#include "content_fwd.hpp"
|
||||
#include "io/io.hpp"
|
||||
|
||||
class EnginePaths;
|
||||
|
||||
class contentpack_error : public std::runtime_error {
|
||||
@ -25,11 +26,19 @@ public:
|
||||
io::path getFolder() const;
|
||||
};
|
||||
|
||||
enum class DependencyVersionOperator {
|
||||
enum class VersionOperator {
|
||||
EQUAL, GREATHER, LESS,
|
||||
GREATHER_OR_EQUAL, LESS_OR_EQUAL
|
||||
};
|
||||
|
||||
VC_ENUM_METADATA(VersionOperator)
|
||||
{"=", VersionOperator::EQUAL},
|
||||
{">", VersionOperator::GREATHER},
|
||||
{"<", VersionOperator::LESS},
|
||||
{">=", VersionOperator::GREATHER_OR_EQUAL},
|
||||
{"<=", VersionOperator::LESS_OR_EQUAL},
|
||||
VC_ENUM_END
|
||||
|
||||
enum class DependencyLevel {
|
||||
REQUIRED, // dependency must be installed
|
||||
OPTIONAL, // dependency will be installed if found
|
||||
@ -41,7 +50,7 @@ struct DependencyPack {
|
||||
DependencyLevel level;
|
||||
std::string id;
|
||||
std::string version;
|
||||
std::string op;
|
||||
VersionOperator op;
|
||||
};
|
||||
|
||||
struct ContentPackStats {
|
||||
|
||||
@ -26,26 +26,11 @@ Version::Version(const std::string& version) {
|
||||
if (parts.size() > 2) patch = parts[2];
|
||||
}
|
||||
|
||||
DependencyVersionOperator Version::string_to_operator(const std::string& op) {
|
||||
if (op == "=")
|
||||
return DependencyVersionOperator::EQUAL;
|
||||
else if (op == ">")
|
||||
return DependencyVersionOperator::GREATHER;
|
||||
else if (op == "<")
|
||||
return DependencyVersionOperator::LESS;
|
||||
else if (op == ">=" || op == "=>")
|
||||
return DependencyVersionOperator::GREATHER_OR_EQUAL;
|
||||
else if (op == "<=" || op == "=<")
|
||||
return DependencyVersionOperator::LESS_OR_EQUAL;
|
||||
else
|
||||
return DependencyVersionOperator::EQUAL;
|
||||
}
|
||||
|
||||
bool isNumber(const std::string& s) {
|
||||
return !s.empty() && std::all_of(s.begin(), s.end(), ::is_digit);
|
||||
}
|
||||
|
||||
bool Version::matches_pattern(const std::string& version) {
|
||||
bool Version::matchesPattern(const std::string& version) {
|
||||
for (char c : version) {
|
||||
if (!isdigit(c) && c != '.') {
|
||||
return false;
|
||||
|
||||
@ -33,25 +33,22 @@ public:
|
||||
return !(*this > other);
|
||||
}
|
||||
|
||||
bool process_operator(const std::string& op, const Version& other) const {
|
||||
auto dep_op = Version::string_to_operator(op);
|
||||
|
||||
switch (dep_op) {
|
||||
case DependencyVersionOperator::EQUAL:
|
||||
bool processOperator(VersionOperator op, const Version& other) const {
|
||||
switch (op) {
|
||||
case VersionOperator::EQUAL:
|
||||
return *this == other;
|
||||
case DependencyVersionOperator::GREATHER:
|
||||
case VersionOperator::GREATHER:
|
||||
return *this > other;
|
||||
case DependencyVersionOperator::LESS:
|
||||
case VersionOperator::LESS:
|
||||
return *this < other;
|
||||
case DependencyVersionOperator::LESS_OR_EQUAL:
|
||||
case VersionOperator::LESS_OR_EQUAL:
|
||||
return *this <= other;
|
||||
case DependencyVersionOperator::GREATHER_OR_EQUAL:
|
||||
case VersionOperator::GREATHER_OR_EQUAL:
|
||||
return *this >= other;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static DependencyVersionOperator string_to_operator(const std::string& op);
|
||||
static bool matches_pattern(const std::string& version);
|
||||
static bool matchesPattern(const std::string& version);
|
||||
};
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
#define VC_ENABLE_REFLECTION
|
||||
#include "PacksManager.hpp"
|
||||
|
||||
#include <queue>
|
||||
@ -109,9 +110,9 @@ static bool resolve_dependencies(
|
||||
|
||||
auto dep_pack = found -> second;
|
||||
|
||||
if (Version::matches_pattern(dep.version) && Version::matches_pattern(dep_pack.version)
|
||||
if (Version::matchesPattern(dep.version) && Version::matchesPattern(dep_pack.version)
|
||||
&& Version(dep_pack.version)
|
||||
.process_operator(dep.op, Version(dep.version))
|
||||
.processOperator(dep.op, Version(dep.version))
|
||||
) {
|
||||
// dependency pack version meets the required one
|
||||
continue;
|
||||
@ -120,7 +121,11 @@ static bool resolve_dependencies(
|
||||
continue;
|
||||
} else {
|
||||
throw contentpack_error(
|
||||
dep.id, io::path(), "does not meet required version '" + dep.op + dep.version +"' of '" + pack->id + "'"
|
||||
dep.id,
|
||||
io::path(),
|
||||
"does not meet required version '" +
|
||||
VersionOperatorMeta.getNameString(dep.op) + dep.version +
|
||||
"' of '" + pack->id + "'"
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@ -96,12 +96,16 @@ void Shadows::setup(Shader& shader, const Weather& weather) {
|
||||
if (shadows) {
|
||||
const auto& worldInfo = level.getWorld()->getInfo();
|
||||
float cloudsIntensity = glm::max(worldInfo.fog, weather.clouds());
|
||||
float shadowsOpacity = 1.0f - cloudsIntensity;
|
||||
shadowsOpacity *= glm::sqrt(glm::abs(
|
||||
glm::mod((worldInfo.daytime + 0.5f) * 2.0f, 1.0f) * 2.0f - 1.0f
|
||||
));
|
||||
shader.uniform1i("u_screen", 0);
|
||||
shader.uniformMatrix("u_shadowsMatrix[0]", shadowCamera.getProjView());
|
||||
shader.uniformMatrix("u_shadowsMatrix[1]", wideShadowCamera.getProjView());
|
||||
shader.uniform3f("u_sunDir", shadowCamera.front);
|
||||
shader.uniform1i("u_shadowsRes", shadowMap->getResolution());
|
||||
shader.uniform1f("u_shadowsOpacity", 1.0f - cloudsIntensity); // TODO: make it configurable
|
||||
shader.uniform1f("u_shadowsOpacity", shadowsOpacity); // TODO: make it configurable
|
||||
shader.uniform1f("u_shadowsSoftness", 1.0f + cloudsIntensity * 4); // TODO: make it configurable
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + TARGET_SHADOWS0);
|
||||
|
||||
@ -22,7 +22,7 @@ namespace markdown {
|
||||
Result<wchar_t> process(std::wstring_view source, bool eraseMarkdown);
|
||||
|
||||
template <typename CharT>
|
||||
inline std::basic_string<CharT> escape(std::string_view source) {
|
||||
inline std::basic_string<CharT> escape(std::basic_string_view<CharT> source) {
|
||||
std::basic_stringstream<CharT> ss;
|
||||
int pos = 0;
|
||||
while (pos < source.size()) {
|
||||
|
||||
@ -54,7 +54,12 @@ static int l_get_gravity_scale(lua::State* L) {
|
||||
|
||||
static int l_set_gravity_scale(lua::State* L) {
|
||||
if (auto entity = get_entity(L, 1)) {
|
||||
entity->getRigidbody().hitbox.gravityScale = lua::tovec3(L, 2).y;
|
||||
auto& hitbox = entity->getRigidbody().hitbox;
|
||||
if (lua::istable(L, 2)) {
|
||||
hitbox.gravityScale = lua::tovec3(L, 2).y;
|
||||
} else {
|
||||
hitbox.gravityScale = lua::tonumber(L, 2);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -1,8 +1,4 @@
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <set>
|
||||
#define VC_ENABLE_REFLECTION
|
||||
|
||||
#include "assets/AssetsLoader.hpp"
|
||||
#include "content/Content.hpp"
|
||||
@ -19,6 +15,12 @@
|
||||
#include "world/World.hpp"
|
||||
#include "api_lua.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <filesystem>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <set>
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
static int l_pack_get_folder(lua::State* L) {
|
||||
@ -114,8 +116,16 @@ static int l_pack_get_info(
|
||||
default:
|
||||
throw std::runtime_error("");
|
||||
}
|
||||
auto opString = VersionOperatorMeta.getNameString(dpack.op);
|
||||
|
||||
lua::pushfstring(L, "%s%s@%s%s", prefix.c_str(), dpack.id.c_str(), dpack.op.c_str(), dpack.version.c_str());
|
||||
lua::pushfstring(
|
||||
L,
|
||||
"%s%s@%s%s",
|
||||
prefix.c_str(),
|
||||
dpack.id.c_str(),
|
||||
(dpack.op == VersionOperator::EQUAL ? "" : opString).c_str(),
|
||||
dpack.version.c_str()
|
||||
);
|
||||
lua::rawseti(L, i + 1);
|
||||
}
|
||||
lua::setfield(L, "dependencies");
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <random>
|
||||
|
||||
#include "lua_commons.hpp"
|
||||
|
||||
@ -114,4 +115,20 @@ namespace lua {
|
||||
std::shared_ptr<ImageData> mData;
|
||||
};
|
||||
static_assert(!std::is_abstract<LuaCanvas>());
|
||||
|
||||
class LuaRandom : public Userdata {
|
||||
public:
|
||||
std::mt19937 rng;
|
||||
|
||||
explicit LuaRandom(uint64_t seed) : rng(seed) {}
|
||||
virtual ~LuaRandom() override = default;
|
||||
|
||||
const std::string& getTypeName() const override {
|
||||
return TYPENAME;
|
||||
}
|
||||
|
||||
static int createMetatable(lua::State*);
|
||||
inline static std::string TYPENAME = "__vc_Random";
|
||||
};
|
||||
static_assert(!std::is_abstract<LuaRandom>());
|
||||
}
|
||||
|
||||
@ -169,5 +169,13 @@ State* lua::create_state(const EnginePaths& paths, StateType stateType) {
|
||||
auto file = "res:scripts/stdmin.lua";
|
||||
auto src = io::read_string(file);
|
||||
lua::pop(L, lua::execute(L, 0, src, "core:scripts/stdmin.lua"));
|
||||
|
||||
newusertype<LuaRandom>(L);
|
||||
if (getglobal(L, "random")) {
|
||||
if (getglobal(L, "__vc_Random")) {
|
||||
setfield(L, "Random");
|
||||
}
|
||||
pop(L);
|
||||
}
|
||||
return L;
|
||||
}
|
||||
|
||||
@ -275,6 +275,15 @@ namespace lua {
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T& require_userdata(lua::State* L, int idx) {
|
||||
if (void* rawptr = lua_touserdata(L, idx)) {
|
||||
return *static_cast<T*>(rawptr);
|
||||
}
|
||||
throw std::runtime_error("invalid 'self' value");
|
||||
}
|
||||
|
||||
template <class T, typename... Args>
|
||||
inline int newuserdata(lua::State* L, Args&&... args) {
|
||||
const auto& found = usertypeNames.find(typeid(T));
|
||||
|
||||
56
src/logic/scripting/lua/usertypes/lua_type_random.cpp
Normal file
56
src/logic/scripting/lua/usertypes/lua_type_random.cpp
Normal file
@ -0,0 +1,56 @@
|
||||
#include "../lua_custom_types.hpp"
|
||||
#include "../lua_util.hpp"
|
||||
|
||||
#include <chrono>
|
||||
|
||||
using namespace lua;
|
||||
using namespace std::chrono;
|
||||
|
||||
static int l_random(lua::State* L) {
|
||||
std::uniform_int_distribution<> dist(0, std::numeric_limits<int>::max());
|
||||
|
||||
auto& rng = require_userdata<LuaRandom>(L, 1).rng;
|
||||
size_t n = touinteger(L, 2);
|
||||
createtable(L, n, 0);
|
||||
|
||||
for (size_t i = 0; i < n; i++) {
|
||||
pushnumber(L, dist(rng) / (double)std::numeric_limits<int>::max());
|
||||
rawseti(L, i + 1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int l_seed(lua::State* L) {
|
||||
require_userdata<LuaRandom>(L, 1).rng = std::mt19937(lua::touinteger(L, 2));
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int l_meta_meta_call(lua::State* L) {
|
||||
integer_t seed;
|
||||
if (lua::isnoneornil(L, 1)) {
|
||||
seed = system_clock::now().time_since_epoch().count();
|
||||
} else {
|
||||
seed = tointeger(L, 1);
|
||||
}
|
||||
return newuserdata<LuaRandom>(L, seed);
|
||||
}
|
||||
|
||||
int LuaRandom::createMetatable(lua::State* L) {
|
||||
createtable(L, 0, 3);
|
||||
|
||||
requireglobal(L, "__vc_create_random_methods");
|
||||
createtable(L, 0, 0);
|
||||
pushcfunction(L, wrap<l_random>);
|
||||
setfield(L, "random");
|
||||
pushcfunction(L, wrap<l_seed>);
|
||||
setfield(L, "seed");
|
||||
call(L, 1, 1);
|
||||
|
||||
setfield(L, "__index");
|
||||
|
||||
createtable(L, 0, 1);
|
||||
pushcfunction(L, wrap<l_meta_meta_call>);
|
||||
setfield(L, "__call");
|
||||
setmetatable(L);
|
||||
return 1;
|
||||
}
|
||||
@ -620,6 +620,26 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
static sockaddr_in resolve_address_dgram(const std::string& address, int port) {
|
||||
sockaddr_in serverAddr{};
|
||||
addrinfo hints {};
|
||||
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
|
||||
addrinfo* addrinfo = nullptr;
|
||||
if (int res = getaddrinfo(
|
||||
address.c_str(), nullptr, &hints, &addrinfo
|
||||
)) {
|
||||
throw std::runtime_error(gai_strerror(res));
|
||||
}
|
||||
|
||||
std::memcpy(&serverAddr, addrinfo->ai_addr, sizeof(sockaddr_in));
|
||||
serverAddr.sin_port = htons(port);
|
||||
freeaddrinfo(addrinfo);
|
||||
return serverAddr;
|
||||
}
|
||||
|
||||
class SocketUdpConnection : public UdpConnection {
|
||||
u64id_t id;
|
||||
SOCKET descriptor;
|
||||
@ -652,13 +672,7 @@ public:
|
||||
throw std::runtime_error("could not create udp socket");
|
||||
}
|
||||
|
||||
sockaddr_in serverAddr{};
|
||||
serverAddr.sin_family = AF_INET;
|
||||
if (inet_pton(AF_INET, address.c_str(), &serverAddr.sin_addr) <= 0) {
|
||||
closesocket(descriptor);
|
||||
throw std::runtime_error("invalid udp address: " + address);
|
||||
}
|
||||
serverAddr.sin_port = htons(port);
|
||||
sockaddr_in serverAddr = resolve_address_dgram(address, port);
|
||||
|
||||
if (::connect(descriptor, (sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
|
||||
auto err = handle_socket_error("udp connect failed");
|
||||
@ -683,6 +697,7 @@ public:
|
||||
while (open) {
|
||||
int size = recv(descriptor, buffer.data(), buffer.size(), 0);
|
||||
if (size <= 0) {
|
||||
logger.error() <<id <<"udp connection " << id << handle_socket_error(" recv error").what();
|
||||
if (!open) break;
|
||||
closesocket(descriptor);
|
||||
state = ConnectionState::CLOSED;
|
||||
@ -697,11 +712,12 @@ public:
|
||||
}
|
||||
|
||||
int send(const char* buffer, size_t length) override {
|
||||
int len = sendto(descriptor, buffer, length, 0,
|
||||
(sockaddr*)&addr, sizeof(addr));
|
||||
int len = ::send(descriptor, buffer, length, 0);
|
||||
if (len < 0) {
|
||||
auto err = handle_socket_error(" send failed");
|
||||
closesocket(descriptor);
|
||||
state = ConnectionState::CLOSED;
|
||||
logger.error() << "udp connection " << id << err.what();
|
||||
} else totalUpload += len;
|
||||
|
||||
return len;
|
||||
@ -710,6 +726,7 @@ public:
|
||||
void close(bool discardAll=false) override {
|
||||
if (!open) return;
|
||||
open = false;
|
||||
logger.info() << "closing udp connection "<< id;
|
||||
|
||||
if (state != ConnectionState::CLOSED) {
|
||||
shutdown(descriptor, 2);
|
||||
@ -789,13 +806,11 @@ public:
|
||||
}
|
||||
|
||||
void sendTo(const std::string& addr, int port, const char* buffer, size_t length) override {
|
||||
sockaddr_in client{};
|
||||
client.sin_family = AF_INET;
|
||||
inet_pton(AF_INET, addr.c_str(), &client.sin_addr);
|
||||
client.sin_port = htons(port);
|
||||
|
||||
sendto(descriptor, buffer, length, 0,
|
||||
reinterpret_cast<sockaddr*>(&client), sizeof(client));
|
||||
sockaddr_in client = resolve_address_dgram(addr, port);
|
||||
if (sendto(descriptor, buffer, length, 0,
|
||||
reinterpret_cast<sockaddr*>(&client), sizeof(client)) < 0) {
|
||||
logger.error() << handle_socket_error("sendto").what();
|
||||
}
|
||||
}
|
||||
|
||||
void close() override {
|
||||
|
||||
@ -1,6 +1,9 @@
|
||||
#include "command_line.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
|
||||
#include "io/engine_paths.hpp"
|
||||
#include "util/ArgsReader.hpp"
|
||||
@ -8,45 +11,69 @@
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
class ArgC {
|
||||
public:
|
||||
std::string keyword;
|
||||
std::function<bool()> execute;
|
||||
std::string help;
|
||||
ArgC(const std::string& keyword, std::function<bool()> execute, const std::string& help) {
|
||||
this->keyword = keyword;
|
||||
this->execute = execute;
|
||||
this->help = help;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
static bool perform_keyword(
|
||||
util::ArgsReader& reader, const std::string& keyword, CoreParameters& params
|
||||
) {
|
||||
if (keyword == "--res") {
|
||||
params.resFolder = reader.next();
|
||||
} else if (keyword == "--dir") {
|
||||
params.userFolder = reader.next();
|
||||
} else if (keyword == "--project") {
|
||||
params.projectFolder = reader.next();
|
||||
} else if (keyword == "--help" || keyword == "-h") {
|
||||
std::cout << "VoxelCore v" << ENGINE_VERSION_STRING << "\n\n";
|
||||
std::cout << "command-line arguments:\n";
|
||||
std::cout << " --help - display this help\n";
|
||||
std::cout << " --version - display engine version\n";
|
||||
std::cout << " --res <path> - set resources directory\n";
|
||||
std::cout << " --dir <path> - set userfiles directory\n";
|
||||
std::cout << " --project <path> - set project directory\n";
|
||||
std::cout << " --headless - run in headless mode\n";
|
||||
std::cout << " --test <path> - test script file\n";
|
||||
std::cout << " --script <path> - main script file\n";
|
||||
std::cout << std::endl;
|
||||
return false;
|
||||
} else if (keyword == "--version") {
|
||||
std::cout << ENGINE_VERSION_STRING << std::endl;
|
||||
return false;
|
||||
} else if (keyword == "--headless") {
|
||||
params.headless = true;
|
||||
} else if (keyword == "--test") {
|
||||
auto token = reader.next();
|
||||
params.testMode = true;
|
||||
params.scriptFile = token;
|
||||
} else if (keyword == "--script") {
|
||||
auto token = reader.next();
|
||||
params.testMode = false;
|
||||
params.scriptFile = token;
|
||||
} else {
|
||||
throw std::runtime_error("unknown argument " + keyword);
|
||||
static const std::vector<ArgC> argumentsCommandline = {
|
||||
ArgC("--res", [¶ms, &reader]() -> bool {
|
||||
params.resFolder = reader.next();
|
||||
return true;
|
||||
}, "<path> - set resources directory."),
|
||||
ArgC("--dir", [¶ms, &reader]() -> bool {
|
||||
params.userFolder = reader.next();
|
||||
return true;
|
||||
}, "<path> - set userfiles directory."),
|
||||
ArgC("--project", [¶ms, &reader]() -> bool {
|
||||
params.projectFolder = reader.next();
|
||||
return true;
|
||||
}, "<path> - set project directory."),
|
||||
ArgC("--test", [¶ms, &reader]() -> bool {
|
||||
params.testMode = true;
|
||||
params.scriptFile = reader.next();
|
||||
return true;
|
||||
}, "<path> - test script file."),
|
||||
ArgC("--script", [¶ms, &reader]() -> bool {
|
||||
params.testMode = false;
|
||||
params.scriptFile = reader.next();
|
||||
return true;
|
||||
}, "<path> - main script file."),
|
||||
ArgC("--headless", [¶ms]() -> bool {
|
||||
params.headless = true;
|
||||
return true;
|
||||
}, "- run in headless mode."),
|
||||
ArgC("--version", []() -> bool {
|
||||
std::cout << ENGINE_VERSION_STRING << std::endl;
|
||||
return false;
|
||||
}, "- display the engine version."),
|
||||
ArgC("--help", []() -> bool {
|
||||
std::cout << "VoxelCore v" << ENGINE_VERSION_STRING << "\n\n";
|
||||
std::cout << "Command-line arguments:\n";
|
||||
for (auto& a : argumentsCommandline) {
|
||||
std::cout << a.keyword << " " << a.help << std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
return false;
|
||||
}, "- display this help.")
|
||||
};
|
||||
for (auto& a : argumentsCommandline) {
|
||||
if (a.keyword == keyword) {
|
||||
return a.execute();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
throw std::runtime_error("unknown argument " + keyword);
|
||||
}
|
||||
|
||||
bool parse_cmdline(int argc, char** argv, CoreParameters& params) {
|
||||
|
||||
@ -40,7 +40,7 @@ std::string util::escape(std::string_view s, bool escapeUnicode) {
|
||||
uint cpsize;
|
||||
int codepoint = decode_utf8(cpsize, s.data() + pos);
|
||||
if (escapeUnicode) {
|
||||
ss << "\\u" << std::hex << codepoint;
|
||||
ss << "\\u" << std::setw(4) << std::setfill('0') << std::hex << codepoint;
|
||||
} else {
|
||||
ss << std::string(s.data() + pos, cpsize);
|
||||
}
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
static debug::Logger logger("window");
|
||||
|
||||
static std::unordered_set<std::string> supported_gl_extensions;
|
||||
static void window_size_callback(GLFWwindow* window, int width, int height);
|
||||
|
||||
static void init_gl_extensions_list() {
|
||||
GLint numExtensions = 0;
|
||||
@ -418,7 +419,7 @@ public:
|
||||
if (fullscreen) {
|
||||
glfwGetWindowPos(window, &posX, &posY);
|
||||
glfwSetWindowMonitor(
|
||||
window, monitor, 0, 0, mode->width, mode->height, GLFW_DONT_CARE
|
||||
window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate
|
||||
);
|
||||
} else {
|
||||
glfwSetWindowMonitor(
|
||||
@ -430,6 +431,7 @@ public:
|
||||
settings->height.get(),
|
||||
GLFW_DONT_CARE
|
||||
);
|
||||
window_size_callback(window, settings->width.get(), settings->height.get());
|
||||
}
|
||||
|
||||
double xPos, yPos;
|
||||
@ -596,6 +598,17 @@ static void cursor_pos_callback(GLFWwindow* window, double xpos, double ypos) {
|
||||
handler->input.setCursorPosition(xpos, ypos);
|
||||
}
|
||||
|
||||
static void iconify_callback(GLFWwindow* window, int iconified) {
|
||||
auto handler = static_cast<GLFWWindow*>(glfwGetWindowUserPointer(window));
|
||||
if (handler->isFullscreen() && iconified == 0) {
|
||||
GLFWmonitor* monitor = glfwGetPrimaryMonitor();
|
||||
const GLFWvidmode* mode = glfwGetVideoMode(monitor);
|
||||
glfwSetWindowMonitor(
|
||||
window, monitor, 0, 0, mode->width, mode->height, mode->refreshRate
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
static void create_standard_cursors() {
|
||||
for (int i = 0; i <= static_cast<int>(CursorShape::LAST); i++) {
|
||||
int cursor = GLFW_ARROW_CURSOR + i;
|
||||
@ -615,6 +628,7 @@ static void setup_callbacks(GLFWwindow* window) {
|
||||
glfwSetWindowSizeCallback(window, window_size_callback);
|
||||
glfwSetCharCallback(window, character_callback);
|
||||
glfwSetScrollCallback(window, scroll_callback);
|
||||
glfwSetWindowIconifyCallback(window, iconify_callback);
|
||||
}
|
||||
|
||||
std::tuple<
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
#include "util/stringutil.hpp"
|
||||
#include "coders/BasicParser.hpp"
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
@ -16,6 +17,25 @@ TEST(stringutil, utf8) {
|
||||
EXPECT_EQ(str, str2);
|
||||
}
|
||||
|
||||
static std::wstring gen_random_unicode_wstring(int n) {
|
||||
std::wstring str;
|
||||
str.resize(n);
|
||||
for (int i = 0; i < n; i++) {
|
||||
// wstring is 16 bit in some systems
|
||||
str[i] = rand() & 0xFFFF;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
TEST(stringutil, utf8_random) {
|
||||
srand(5436324);
|
||||
|
||||
auto str = gen_random_unicode_wstring(10'000);
|
||||
auto utf8str = util::wstr2str_utf8(str);
|
||||
auto back = util::str2wstr_utf8(utf8str);
|
||||
EXPECT_EQ(str, back);
|
||||
}
|
||||
|
||||
TEST(stringutil, base64) {
|
||||
srand(2019);
|
||||
for (size_t size = 0; size < 30; size++) {
|
||||
@ -47,3 +67,37 @@ TEST(stringutil, base64_urlsafe) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class StringParser : BasicParser<char> {
|
||||
public:
|
||||
StringParser(std::string_view source) : BasicParser("<string>", source) {}
|
||||
|
||||
std::string parse() {
|
||||
++pos;
|
||||
return parseString(source[0], true);
|
||||
}
|
||||
};
|
||||
|
||||
TEST(stringutil, escape_cases) {
|
||||
auto escaped = util::escape("тест5", true);
|
||||
auto expected = "\"\\u0442\\u0435\\u0441\\u04425\"";
|
||||
ASSERT_EQ(expected, escaped);
|
||||
|
||||
srand(345873458);
|
||||
for (int i = 0; i < 36; i++) {
|
||||
rand();
|
||||
}
|
||||
|
||||
auto str = gen_random_unicode_wstring(40);
|
||||
auto utf8str = util::wstr2str_utf8(str);
|
||||
escaped = util::escape(utf8str, true);
|
||||
|
||||
StringParser parser(escaped);
|
||||
auto restored = parser.parse();
|
||||
for (int i = 0; i < utf8str.length(); i++) {
|
||||
if (utf8str[i] != restored[i]) {
|
||||
std::cout << i << ": " << (int)utf8str[i] << " " << (int)restored[i] << std::endl;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(utf8str, restored);
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user