mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-11 22:11:51 +00:00
Merge remote-tracking branch 'mih/dev' into sdl-try-2
This commit is contained in:
commit
8cd75ad790
106 changed files with 2838 additions and 1121 deletions
|
|
@ -1,6 +1,8 @@
|
||||||
# Documentation
|
# Documentation
|
||||||
|
|
||||||
Documentation for 0.29.
|
Documentation for 0.30 (in development).
|
||||||
|
|
||||||
|
[Documentation for 0.29.](https://github.com/MihailRis/voxelcore/blob/release-0.29/doc/ru/main-page.md)
|
||||||
|
|
||||||
## Sections
|
## Sections
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,13 @@ player.set_loading_chunks(playerid: int, bool)
|
||||||
|
|
||||||
Getter and setter of the property that determines whether the player is loading chunks.
|
Getter and setter of the property that determines whether the player is loading chunks.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
player.get_interaction_distance(playerid: int) -> float
|
||||||
|
player.set_interaction_distance(playerid: int, distance: float)
|
||||||
|
```
|
||||||
|
|
||||||
|
Getter and setter of the property for max interaction distance.
|
||||||
|
|
||||||
``` lua
|
``` lua
|
||||||
player.set_spawnpoint(playerid: int, x: number, y: number, z: number)
|
player.set_spawnpoint(playerid: int, x: number, y: number, z: number)
|
||||||
player.get_spawnpoint(playerid: int) -> number, number, number
|
player.get_spawnpoint(playerid: int) -> number, number, number
|
||||||
|
|
@ -135,3 +142,21 @@ player.get_entity(playerid: int) -> int
|
||||||
```
|
```
|
||||||
|
|
||||||
Returns unique identifier of the player entity
|
Returns unique identifier of the player entity
|
||||||
|
|
||||||
|
```lua
|
||||||
|
player.get_all_in_radius(center: vec3, radius: number) -> table<int>
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns an array of player IDs within a sphere with center `center` and radius `radius`.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
player.get_all() -> table<int>
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns an array of all active player IDs.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
player.get_nearest(position: vec3) -> int
|
||||||
|
```
|
||||||
|
|
||||||
|
Returns the ID of the player closest to the specified position, or nil if there are no players.
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
# Документация
|
# Документация
|
||||||
|
|
||||||
Документация версии 0.29.
|
Документация версии 0.30 (в разработке).
|
||||||
|
|
||||||
|
[Документация 0.29.](https://github.com/MihailRis/voxelcore/blob/release-0.29/doc/ru/main-page.md)
|
||||||
|
|
||||||
## Разделы
|
## Разделы
|
||||||
|
|
||||||
|
|
|
||||||
25
doc/ru/scripting/builtins/libcompression.md
Normal file
25
doc/ru/scripting/builtins/libcompression.md
Normal file
|
|
@ -0,0 +1,25 @@
|
||||||
|
# Библиотека *compression*
|
||||||
|
|
||||||
|
Библиотека функций для работы сжатия/разжатия массивов байт
|
||||||
|
|
||||||
|
```lua
|
||||||
|
-- Сжимает массив байт.
|
||||||
|
compression.encode(
|
||||||
|
-- Массив байт
|
||||||
|
data: array of integers,
|
||||||
|
-- Алгоритм сжатия (поддерживается только gzip)
|
||||||
|
[опционально] algorithm="gzip",
|
||||||
|
-- Вернуть результат в table?
|
||||||
|
[опционально] usetable=false
|
||||||
|
) -> array of integers
|
||||||
|
|
||||||
|
-- Разжимает массив байт.
|
||||||
|
compression.decode(
|
||||||
|
-- Массив байт
|
||||||
|
data: array of integers,
|
||||||
|
-- Алгоритм разжатия (поддерживается только gzip)
|
||||||
|
[опционально] algorithm="gzip",
|
||||||
|
-- Вернуть результат в table?
|
||||||
|
[опционально] usetable=false
|
||||||
|
) -> array of integers
|
||||||
|
```
|
||||||
|
|
@ -205,4 +205,4 @@ file.open_named_pipe(имя: str, режим: str) -> io_stream
|
||||||
|
|
||||||
`/tmp/` или `\\\\.\\pipe\\` добавлять не нужно - движок делает это автоматически.
|
`/tmp/` или `\\\\.\\pipe\\` добавлять не нужно - движок делает это автоматически.
|
||||||
|
|
||||||
Доступные режимы такие же, как и в `file.open`, за исключением `+`
|
Доступные режимы такие же, как и в `file.open`, за исключением `+`
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,12 @@ socket:is_connected() --> bool
|
||||||
|
|
||||||
-- Возвращает адрес и порт соединения.
|
-- Возвращает адрес и порт соединения.
|
||||||
socket:get_address() --> str, int
|
socket:get_address() --> str, int
|
||||||
|
|
||||||
|
-- Возвращает состояние NoDelay
|
||||||
|
socket:is_nodelay() --> bool
|
||||||
|
|
||||||
|
-- Устанавливает состояние NoDelay
|
||||||
|
socket:set_nodelay(state: bool)
|
||||||
```
|
```
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
|
|
|
||||||
|
|
@ -89,6 +89,13 @@ player.set_loading_chunks(playerid: int, bool)
|
||||||
|
|
||||||
Геттер и сеттер свойства, определяющего, прогружает ли игрок чанки вокруг.
|
Геттер и сеттер свойства, определяющего, прогружает ли игрок чанки вокруг.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
player.get_interaction_distance(playerid: int) -> float
|
||||||
|
player.set_interaction_distance(playerid: int, distance: float)
|
||||||
|
```
|
||||||
|
|
||||||
|
Геттер и сеттер свойства, определяющего максимальную дистанцию взаимодействия.
|
||||||
|
|
||||||
```lua
|
```lua
|
||||||
player.set_spawnpoint(playerid: int, x: number, y: number, z: number)
|
player.set_spawnpoint(playerid: int, x: number, y: number, z: number)
|
||||||
player.get_spawnpoint(playerid: int) -> number, number, number
|
player.get_spawnpoint(playerid: int) -> number, number, number
|
||||||
|
|
@ -135,3 +142,21 @@ player.get_entity(playerid: int) -> int
|
||||||
```
|
```
|
||||||
|
|
||||||
Возвращает уникальный идентификатор сущности игрока
|
Возвращает уникальный идентификатор сущности игрока
|
||||||
|
|
||||||
|
```lua
|
||||||
|
player.get_all_in_radius(center: vec3, radius: number) -> table<int>
|
||||||
|
```
|
||||||
|
|
||||||
|
Возвращает массив id игроков в пределах сферы с центром `center` и радиусом `radius`.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
player.get_all() -> table<int>
|
||||||
|
```
|
||||||
|
|
||||||
|
Возвращает массив id всех активных игроков.
|
||||||
|
|
||||||
|
```lua
|
||||||
|
player.get_nearest(position: vec3) -> int
|
||||||
|
```
|
||||||
|
|
||||||
|
Возвращает id ближайшего к указанной позиции игрока, либо nil если игроков нет.
|
||||||
|
|
|
||||||
|
|
@ -42,6 +42,8 @@
|
||||||
|
|
||||||
- `from` - точка начала примитива. Пример: `from (0,0,0)`
|
- `from` - точка начала примитива. Пример: `from (0,0,0)`
|
||||||
- `to` - противоположная от начала точка. Пример: `to (1,1,1)`
|
- `to` - противоположная от начала точка. Пример: `to (1,1,1)`
|
||||||
|
- `origin` - точка, относительно которой будет применено вращение. По-умолчанию: центр примитива. Пример: `origin (0.5,0.5,0.5)`
|
||||||
|
- `rotate` - вращение вокруг осей (x,y,z) в градусах, или кватернион (x,y,z,w). Пример: `rotate (45,0,0)` или `rotate (0.3826834, 0, 0, 0.9238795)`
|
||||||
- `texture` - отображаемая текстура для всех сторон по-умолчанию.
|
- `texture` - отображаемая текстура для всех сторон по-умолчанию.
|
||||||
- `shading` определяет возможность затенения на примитиве. Пример: `shading off`
|
- `shading` определяет возможность затенения на примитиве. Пример: `shading off`
|
||||||
- `delete` удаляет стороны по именам (top, bottom, east, west, north, south)
|
- `delete` удаляет стороны по именам (top, bottom, east, west, north, south)
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"id": "base",
|
"id": "base",
|
||||||
"title": "Base",
|
"title": "Base",
|
||||||
"version": "0.29",
|
"version": "0.30",
|
||||||
"description": "basic content package"
|
"description": "basic content package"
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -53,10 +53,19 @@ function create_checkbox(id, name, tooltip)
|
||||||
))
|
))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
function on_open()
|
function on_open()
|
||||||
create_setting("camera.fov", "FOV", 1, "°")
|
create_setting("camera.fov", "FOV", 1, "°")
|
||||||
create_setting("display.framerate", "Framerate", 1, "", "", true)
|
create_setting("display.framerate", "Framerate", 1, "", "", true)
|
||||||
create_checkbox("display.fullscreen", "Fullscreen")
|
|
||||||
|
document.root:add(string.format(
|
||||||
|
"<select context='settings' onselect='function(opt) core.set_setting(\"display.window-mode\", tonumber(opt)) end' selected='%s'>"..
|
||||||
|
"<option value='0'>@Windowed</option>"..
|
||||||
|
"<option value='1'>@Fullscreen</option>"..
|
||||||
|
"<option value='2'>@Borderless</option>"..
|
||||||
|
"</select>", core.get_setting("display.window-mode"))
|
||||||
|
)
|
||||||
|
|
||||||
create_checkbox("camera.shaking", "Camera Shaking")
|
create_checkbox("camera.shaking", "Camera Shaking")
|
||||||
create_checkbox("camera.inertia", "Camera Inertia")
|
create_checkbox("camera.inertia", "Camera Inertia")
|
||||||
create_checkbox("camera.fov-effects", "Camera FOV Effects")
|
create_checkbox("camera.fov-effects", "Camera FOV Effects")
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,8 @@ local Socket = {__index={
|
||||||
is_alive=function(self) return network.__is_alive(self.id) end,
|
is_alive=function(self) return network.__is_alive(self.id) end,
|
||||||
is_connected=function(self) return network.__is_connected(self.id) end,
|
is_connected=function(self) return network.__is_connected(self.id) end,
|
||||||
get_address=function(self) return network.__get_address(self.id) end,
|
get_address=function(self) return network.__get_address(self.id) end,
|
||||||
|
set_nodelay=function(self, nodelay) return network.__set_nodelay(self.id, nodelay or false) end,
|
||||||
|
is_nodelay=function(self) return network.__is_nodelay(self.id) end,
|
||||||
}}
|
}}
|
||||||
|
|
||||||
local WriteableSocket = {__index={
|
local WriteableSocket = {__index={
|
||||||
|
|
|
||||||
|
|
@ -587,6 +587,7 @@ function __process_post_runnables()
|
||||||
__vc_named_coroutines[name] = nil
|
__vc_named_coroutines[name] = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
debug.pull_events()
|
||||||
network.__process_events()
|
network.__process_events()
|
||||||
block.__process_register_events()
|
block.__process_register_events()
|
||||||
block.__perform_ticks(time.delta())
|
block.__perform_ticks(time.delta())
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,144 @@
|
||||||
|
local breakpoints = {}
|
||||||
|
local dbg_steps_mode = false
|
||||||
|
local dbg_step_into_func = false
|
||||||
|
local hook_lock = false
|
||||||
|
local current_func
|
||||||
|
local current_func_stack_size
|
||||||
|
|
||||||
|
local _debug_getinfo = debug.getinfo
|
||||||
|
local _debug_getlocal = debug.getlocal
|
||||||
|
local __pause = debug.pause
|
||||||
|
local __error = error
|
||||||
|
local __sethook = debug.sethook
|
||||||
|
|
||||||
|
-- 'return' hook not called for some functions
|
||||||
|
-- todo: speedup
|
||||||
|
local function calc_stack_size()
|
||||||
|
local s = debug.traceback("", 2)
|
||||||
|
local count = 0
|
||||||
|
for i in s:gmatch("\n") do
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
|
return count
|
||||||
|
end
|
||||||
|
|
||||||
|
local is_debugging = debug.is_debugging()
|
||||||
|
if is_debugging then
|
||||||
|
__sethook(function (e, line)
|
||||||
|
if e == "return" then
|
||||||
|
local info = _debug_getinfo(2)
|
||||||
|
if info.func == current_func then
|
||||||
|
current_func = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
if dbg_steps_mode and not hook_lock then
|
||||||
|
hook_lock = true
|
||||||
|
|
||||||
|
if not dbg_step_into_func then
|
||||||
|
local func = _debug_getinfo(2).func
|
||||||
|
if func ~= current_func then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if current_func_stack_size ~= calc_stack_size() then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
current_func = func
|
||||||
|
__pause("step")
|
||||||
|
debug.pull_events()
|
||||||
|
end
|
||||||
|
hook_lock = false
|
||||||
|
local bps = breakpoints[line]
|
||||||
|
if not bps then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local source = _debug_getinfo(2).source
|
||||||
|
if not bps[source] then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
current_func = _debug_getinfo(2).func
|
||||||
|
current_func_stack_size = calc_stack_size()
|
||||||
|
__pause("breakpoint")
|
||||||
|
debug.pull_events()
|
||||||
|
end, "lr")
|
||||||
|
end
|
||||||
|
|
||||||
|
local DBG_EVENT_SET_BREAKPOINT = 1
|
||||||
|
local DBG_EVENT_RM_BREAKPOINT = 2
|
||||||
|
local DBG_EVENT_STEP = 3
|
||||||
|
local DBG_EVENT_STEP_INTO_FUNCTION = 4
|
||||||
|
local DBG_EVENT_RESUME = 5
|
||||||
|
local DBG_EVENT_GET_VALUE = 6
|
||||||
|
local __pull_events = debug.__pull_events
|
||||||
|
local __sendvalue = debug.__sendvalue
|
||||||
|
debug.__pull_events = nil
|
||||||
|
debug.__sendvalue = nil
|
||||||
|
|
||||||
|
function debug.pull_events()
|
||||||
|
if not is_debugging then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
if not debug.is_debugging() then
|
||||||
|
is_debugging = false
|
||||||
|
__sethook()
|
||||||
|
end
|
||||||
|
local events = __pull_events()
|
||||||
|
if not events then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
for i, event in ipairs(events) do
|
||||||
|
if event[1] == DBG_EVENT_SET_BREAKPOINT then
|
||||||
|
debug.set_breakpoint(event[2], event[3])
|
||||||
|
elseif event[1] == DBG_EVENT_RM_BREAKPOINT then
|
||||||
|
debug.remove_breakpoint(event[2], event[3])
|
||||||
|
elseif event[1] == DBG_EVENT_STEP then
|
||||||
|
dbg_steps_mode = true
|
||||||
|
dbg_step_into_func = false
|
||||||
|
elseif event[1] == DBG_EVENT_STEP_INTO_FUNCTION then
|
||||||
|
dbg_steps_mode = true
|
||||||
|
dbg_step_into_func = true
|
||||||
|
elseif event[1] == DBG_EVENT_RESUME then
|
||||||
|
dbg_steps_mode = false
|
||||||
|
dbg_step_into_func = false
|
||||||
|
elseif event[1] == DBG_EVENT_GET_VALUE then
|
||||||
|
local _, value = _debug_getlocal(event[2] + 3, event[3])
|
||||||
|
for _, key in ipairs(event[4]) do
|
||||||
|
if value == nil then
|
||||||
|
value = "error: index nil value"
|
||||||
|
break
|
||||||
|
end
|
||||||
|
value = value[key]
|
||||||
|
end
|
||||||
|
__sendvalue(value, event[2], event[3], event[4])
|
||||||
|
__pause()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function debug.set_breakpoint(source, line)
|
||||||
|
local bps = breakpoints[line]
|
||||||
|
if not bps then
|
||||||
|
bps = {}
|
||||||
|
breakpoints[line] = bps
|
||||||
|
end
|
||||||
|
bps[source] = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function debug.remove_breakpoint(source, line)
|
||||||
|
local bps = breakpoints[line]
|
||||||
|
if not bps then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
bps[source] = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
function error(message, level)
|
||||||
|
if is_debugging then
|
||||||
|
__pause("exception", message)
|
||||||
|
end
|
||||||
|
__error(message, level)
|
||||||
|
end
|
||||||
|
|
||||||
-- Lua has no parallelizm, also _set_data does not call any lua functions so
|
-- Lua has no parallelizm, also _set_data does not call any lua functions so
|
||||||
-- may be reused one global ffi buffer per lua_State
|
-- may be reused one global ffi buffer per lua_State
|
||||||
local canvas_ffi_buffer
|
local canvas_ffi_buffer
|
||||||
|
|
@ -473,8 +614,6 @@ function file.readlines(path)
|
||||||
return lines
|
return lines
|
||||||
end
|
end
|
||||||
|
|
||||||
local _debug_getinfo = debug.getinfo
|
|
||||||
|
|
||||||
function debug.count_frames()
|
function debug.count_frames()
|
||||||
local frames = 1
|
local frames = 1
|
||||||
while true do
|
while true do
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ Grant %{0} pack modification permission?=Выдаць дазвол на мады
|
||||||
Error at line %{0}=Памылка ў радку %{0}
|
Error at line %{0}=Памылка ў радку %{0}
|
||||||
Run=Запусціць
|
Run=Запусціць
|
||||||
Filter=Фільтр
|
Filter=Фільтр
|
||||||
|
Are you sure you want to open the link: =Ці вы ўпэўненыя, што хочаце адкрыць спасылку:
|
||||||
|
|
||||||
editor.info.tooltip=CTRL+S - Захаваць\nCTRL+R - Запусціць\nCTRL+Z - Скасаваць\nCTRL+Y - Паўтарыць
|
editor.info.tooltip=CTRL+S - Захаваць\nCTRL+R - Запусціць\nCTRL+Z - Скасаваць\nCTRL+Y - Паўтарыць
|
||||||
devtools.traceback=Стэк выклікаў (ад апошняга)
|
devtools.traceback=Стэк выклікаў (ад апошняга)
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ Back=Zurück
|
||||||
Continue=Weitermachen
|
Continue=Weitermachen
|
||||||
Add=Hinzufügen
|
Add=Hinzufügen
|
||||||
Converting world...=Weltkonvertierung im Gange...
|
Converting world...=Weltkonvertierung im Gange...
|
||||||
|
Are you sure you want to open the link: =Sind Sie sicher, dass Sie den Link öffnen möchten:
|
||||||
|
|
||||||
error.pack-not-found=Paket konnte nicht gefunden werden
|
error.pack-not-found=Paket konnte nicht gefunden werden
|
||||||
error.dependency-not-found=Die verwendete Abhängigkeit wurde nicht gefunden
|
error.dependency-not-found=Die verwendete Abhängigkeit wurde nicht gefunden
|
||||||
|
|
|
||||||
|
|
@ -19,6 +19,7 @@ Problems=Ongelmia
|
||||||
Monitor=Valvonta
|
Monitor=Valvonta
|
||||||
Debug=Virheenkorjaus
|
Debug=Virheenkorjaus
|
||||||
File=Tiedosto
|
File=Tiedosto
|
||||||
|
Are you sure you want to open the link: =Haluatko varmasti avata linkin:
|
||||||
|
|
||||||
devtools.traceback=Puhelupino (viimeisestä)
|
devtools.traceback=Puhelupino (viimeisestä)
|
||||||
error.pack-not-found=Pakettia ei löytynyt!
|
error.pack-not-found=Pakettia ei löytynyt!
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ Back=Powrót
|
||||||
Continue=Kontynuacja
|
Continue=Kontynuacja
|
||||||
Add=Dodać
|
Add=Dodać
|
||||||
Converting world...=Konwersja świata w toku...
|
Converting world...=Konwersja świata w toku...
|
||||||
|
Are you sure you want to open the link: =Czy na pewno chcesz otworzyć link:
|
||||||
|
|
||||||
error.pack-not-found=Nie udało się znaleźć pakietu
|
error.pack-not-found=Nie udało się znaleźć pakietu
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ Grant %{0} pack modification permission?=Выдать разрешение на
|
||||||
Error at line %{0}=Ошибка на строке %{0}
|
Error at line %{0}=Ошибка на строке %{0}
|
||||||
Run=Запустить
|
Run=Запустить
|
||||||
Filter=Фильтр
|
Filter=Фильтр
|
||||||
|
Are you sure you want to open the link: =Вы уверены, что хотите открыть ссылку:
|
||||||
|
|
||||||
editor.info.tooltip=CTRL+S - Сохранить\nCTRL+R - Запустить\nCTRL+Z - Отменить\nCTRL+Y - Повторить
|
editor.info.tooltip=CTRL+S - Сохранить\nCTRL+R - Запустить\nCTRL+Z - Отменить\nCTRL+Y - Повторить
|
||||||
devtools.traceback=Стек вызовов (от последнего)
|
devtools.traceback=Стек вызовов (от последнего)
|
||||||
|
|
@ -102,6 +103,8 @@ settings.Limit Background FPS=Ограничить фоновую частоту
|
||||||
settings.Advanced render=Продвинутый рендер
|
settings.Advanced render=Продвинутый рендер
|
||||||
settings.Shadows quality=Качество теней
|
settings.Shadows quality=Качество теней
|
||||||
settings.Conflict=Найдены возможные конфликты
|
settings.Conflict=Найдены возможные конфликты
|
||||||
|
settings.Windowed=Оконный
|
||||||
|
settings.Borderless=Безрамочный
|
||||||
|
|
||||||
# Управление
|
# Управление
|
||||||
chunks.reload=Перезагрузить Чанки
|
chunks.reload=Перезагрузить Чанки
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ devtools.traceback=Стек викликів (від останнього)
|
||||||
error.pack-not-found=Не вдалося знайти пакет
|
error.pack-not-found=Не вдалося знайти пакет
|
||||||
error.dependency-not-found=Використовувана залежність не знайдена
|
error.dependency-not-found=Використовувана залежність не знайдена
|
||||||
pack.remove-confirm=Видалити весь контент, що постачається паком/паками зі світу (безповоротно)?
|
pack.remove-confirm=Видалити весь контент, що постачається паком/паками зі світу (безповоротно)?
|
||||||
|
Are you sure you want to open the link: =Ви впевнені, що хочете відкрити посилання:
|
||||||
|
|
||||||
# Меню
|
# Меню
|
||||||
menu.Apply=Застосувати
|
menu.Apply=Застосувати
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,7 @@ Save=Saqlash
|
||||||
Grant %{0} pack modification permission?=%{0} to‘plamini o‘zgartirish ruxsatini berilsinmi?
|
Grant %{0} pack modification permission?=%{0} to‘plamini o‘zgartirish ruxsatini berilsinmi?
|
||||||
Error at line %{0}=%{0}-qatorida xatolik
|
Error at line %{0}=%{0}-qatorida xatolik
|
||||||
Run=Ishga tushirish
|
Run=Ishga tushirish
|
||||||
|
Are you sure you want to open the link: =Haqiqatan ham havolani ochmoqchimisiz:
|
||||||
|
|
||||||
editor.info.tooltip=CTRL+S - Saqlash\nCTRL+R - Ishga tushirish\nCTRL+Z - Bekor qilish\nCTRL+Y - Qayta bajarish
|
editor.info.tooltip=CTRL+S - Saqlash\nCTRL+R - Ishga tushirish\nCTRL+Z - Bekor qilish\nCTRL+Y - Qayta bajarish
|
||||||
devtools.traceback=Chaqiruvlar steki (so`nggisidan boshlab)
|
devtools.traceback=Chaqiruvlar steki (so`nggisidan boshlab)
|
||||||
|
|
|
||||||
|
|
@ -91,7 +91,9 @@ target_compile_options(
|
||||||
/wd4245 # conversion from 'int' to 'const size_t', signed/unsigned
|
/wd4245 # conversion from 'int' to 'const size_t', signed/unsigned
|
||||||
# mismatch
|
# mismatch
|
||||||
/wd4100 # unreferenced formal parameter
|
/wd4100 # unreferenced formal parameter
|
||||||
|
/wd4457 # declaration of 'var' hides function parameter
|
||||||
/wd4458 # declaration of 'var' hides class member
|
/wd4458 # declaration of 'var' hides class member
|
||||||
|
/wd4459 # declaration of 'var' hides global declaration
|
||||||
/wd4101 # 'var': unreferenced local variable
|
/wd4101 # 'var': unreferenced local variable
|
||||||
/wd4388 # 'token' : signed/unsigned mismatch
|
/wd4388 # 'token' : signed/unsigned mismatch
|
||||||
/wd4018 # '>': signed/unsigned mismatch
|
/wd4018 # '>': signed/unsigned mismatch
|
||||||
|
|
@ -101,6 +103,7 @@ target_compile_options(
|
||||||
-Wextra
|
-Wextra
|
||||||
# additional warnings
|
# additional warnings
|
||||||
-Wformat-nonliteral
|
-Wformat-nonliteral
|
||||||
|
#-Wsign-conversion
|
||||||
-Wcast-align
|
-Wcast-align
|
||||||
-Wpointer-arith
|
-Wpointer-arith
|
||||||
-Wundef
|
-Wundef
|
||||||
|
|
@ -108,6 +111,10 @@ target_compile_options(
|
||||||
-Wno-unused-parameter
|
-Wno-unused-parameter
|
||||||
-Wno-sign-compare
|
-Wno-sign-compare
|
||||||
-Wno-unknown-pragmas
|
-Wno-unknown-pragmas
|
||||||
|
>
|
||||||
|
$<$<CXX_COMPILER_ID:Clang>:
|
||||||
|
-Wduplicated-branches
|
||||||
|
-Wduplicated-cond
|
||||||
>)
|
>)
|
||||||
|
|
||||||
target_link_options(
|
target_link_options(
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#include "vcm.hpp"
|
#include "vcm.hpp"
|
||||||
|
|
||||||
#include <iostream>
|
#include <algorithm>
|
||||||
|
#include <glm/gtc/quaternion.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
#include "xml.hpp"
|
#include "xml.hpp"
|
||||||
#include "util/stringutil.hpp"
|
#include "util/stringutil.hpp"
|
||||||
|
|
@ -73,6 +75,29 @@ static void perform_box(const xmlelement& root, model::Model& model) {
|
||||||
auto from = root.attr("from").asVec3();
|
auto from = root.attr("from").asVec3();
|
||||||
auto to = root.attr("to").asVec3();
|
auto to = root.attr("to").asVec3();
|
||||||
|
|
||||||
|
glm::vec3 origin = (from + to) * 0.5f;
|
||||||
|
if (root.has("origin")) {
|
||||||
|
origin = root.attr("origin").asVec3();
|
||||||
|
}
|
||||||
|
|
||||||
|
glm::mat4 tsf(1.0f);
|
||||||
|
from -= origin;
|
||||||
|
to -= origin;
|
||||||
|
tsf = glm::translate(tsf, origin);
|
||||||
|
|
||||||
|
if (root.has("rotate")) {
|
||||||
|
auto text = root.attr("rotate").getText();
|
||||||
|
if (std::count(text.begin(), text.end(), ',') == 3) {
|
||||||
|
auto quat = root.attr("rotate").asVec4();
|
||||||
|
tsf *= glm::mat4_cast(glm::quat(quat.w, quat.x, quat.y, quat.z));
|
||||||
|
} else {
|
||||||
|
auto rot = root.attr("rotate").asVec3();
|
||||||
|
tsf = glm::rotate(tsf, glm::radians(rot.x), glm::vec3(1, 0, 0));
|
||||||
|
tsf = glm::rotate(tsf, glm::radians(rot.y), glm::vec3(0, 1, 0));
|
||||||
|
tsf = glm::rotate(tsf, glm::radians(rot.z), glm::vec3(0, 0, 1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
UVRegion regions[6] {};
|
UVRegion regions[6] {};
|
||||||
regions[0].scale(to.x - from.x, to.y - from.y);
|
regions[0].scale(to.x - from.x, to.y - from.y);
|
||||||
regions[1].scale(from.x - to.x, to.y - from.y);
|
regions[1].scale(from.x - to.x, to.y - from.y);
|
||||||
|
|
@ -142,7 +167,7 @@ static void perform_box(const xmlelement& root, model::Model& model) {
|
||||||
bool enabled[6] {};
|
bool enabled[6] {};
|
||||||
enabled[i] = true;
|
enabled[i] = true;
|
||||||
auto& mesh = model.addMesh(texfaces[i], shading);
|
auto& mesh = model.addMesh(texfaces[i], shading);
|
||||||
mesh.addBox(center, halfsize, regions, enabled);
|
mesh.addBox(center, halfsize, regions, enabled, tsf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
inline constexpr int ENGINE_VERSION_MAJOR = 0;
|
inline constexpr int ENGINE_VERSION_MAJOR = 0;
|
||||||
inline constexpr int ENGINE_VERSION_MINOR = 29;
|
inline constexpr int ENGINE_VERSION_MINOR = 30;
|
||||||
|
|
||||||
#ifdef NDEBUG
|
#ifdef NDEBUG
|
||||||
inline constexpr bool ENGINE_DEBUG_BUILD = false;
|
inline constexpr bool ENGINE_DEBUG_BUILD = false;
|
||||||
|
|
@ -14,7 +14,7 @@ inline constexpr bool ENGINE_DEBUG_BUILD = false;
|
||||||
inline constexpr bool ENGINE_DEBUG_BUILD = true;
|
inline constexpr bool ENGINE_DEBUG_BUILD = true;
|
||||||
#endif // NDEBUG
|
#endif // NDEBUG
|
||||||
|
|
||||||
inline const std::string ENGINE_VERSION_STRING = "0.29";
|
inline const std::string ENGINE_VERSION_STRING = "0.30";
|
||||||
|
|
||||||
/// @brief world regions format version
|
/// @brief world regions format version
|
||||||
inline constexpr uint REGION_FORMAT_VERSION = 3;
|
inline constexpr uint REGION_FORMAT_VERSION = 3;
|
||||||
|
|
|
||||||
315
src/devtools/DebuggingServer.cpp
Normal file
315
src/devtools/DebuggingServer.cpp
Normal file
|
|
@ -0,0 +1,315 @@
|
||||||
|
#include "DebuggingServer.hpp"
|
||||||
|
|
||||||
|
#include "engine/Engine.hpp"
|
||||||
|
#include "network/Network.hpp"
|
||||||
|
#include "debug/Logger.hpp"
|
||||||
|
#include "coders/json.hpp"
|
||||||
|
|
||||||
|
using namespace devtools;
|
||||||
|
|
||||||
|
static debug::Logger logger("debug-server");
|
||||||
|
|
||||||
|
ClientConnection::~ClientConnection() {
|
||||||
|
if (auto connection = dynamic_cast<network::ReadableConnection*>(
|
||||||
|
network.getConnection(this->connection, true)
|
||||||
|
)) {
|
||||||
|
connection->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClientConnection::initiate(network::ReadableConnection* connection) {
|
||||||
|
if (connection->available() < 8) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
char buffer[8] {};
|
||||||
|
char expected[8] {};
|
||||||
|
std::memcpy(expected, VCDBG_MAGIC, sizeof(VCDBG_MAGIC));
|
||||||
|
expected[6] = VCDBG_VERSION >> 8;
|
||||||
|
expected[7] = VCDBG_VERSION & 0xFF;
|
||||||
|
connection->recv(buffer, sizeof(VCDBG_MAGIC));
|
||||||
|
|
||||||
|
connection->send(expected, sizeof(VCDBG_MAGIC));
|
||||||
|
if (std::memcmp(expected, buffer, sizeof(VCDBG_MAGIC)) == 0) {
|
||||||
|
initiated = true;
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
connection->close(true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string ClientConnection::read() {
|
||||||
|
auto connection = dynamic_cast<network::ReadableConnection*>(
|
||||||
|
network.getConnection(this->connection, true)
|
||||||
|
);
|
||||||
|
if (connection == nullptr) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
if (!initiated) {
|
||||||
|
if (initiate(connection)) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (messageLength == 0) {
|
||||||
|
if (connection->available() >= sizeof(int32_t)) {
|
||||||
|
int32_t length = 0;
|
||||||
|
connection->recv(reinterpret_cast<char*>(&length), sizeof(int32_t));
|
||||||
|
if (length <= 0) {
|
||||||
|
logger.error() << "invalid message length " << length;
|
||||||
|
} else {
|
||||||
|
logger.info() << "message length " << length;
|
||||||
|
messageLength = length;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (connection->available() >= messageLength) {
|
||||||
|
std::string string(messageLength, 0);
|
||||||
|
connection->recv(string.data(), messageLength);
|
||||||
|
messageLength = 0;
|
||||||
|
return string;
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientConnection::send(const dv::value& object) {
|
||||||
|
auto connection = dynamic_cast<network::ReadableConnection*>(
|
||||||
|
network.getConnection(this->connection, true)
|
||||||
|
);
|
||||||
|
if (connection == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto message = json::stringify(object, false);
|
||||||
|
int32_t length = message.length();
|
||||||
|
connection->send(reinterpret_cast<char*>(&length), sizeof(int32_t));
|
||||||
|
connection->send(message.data(), length);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ClientConnection::sendResponse(const std::string& type) {
|
||||||
|
send(dv::object({{"type", type}}));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ClientConnection::alive() const {
|
||||||
|
return network.getConnection(this->connection, true) != nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
static network::Server& create_tcp_server(
|
||||||
|
DebuggingServer& dbgServer, Engine& engine, int port
|
||||||
|
) {
|
||||||
|
auto& network = engine.getNetwork();
|
||||||
|
u64id_t serverId = network.openTcpServer(
|
||||||
|
port,
|
||||||
|
[&network, &dbgServer](u64id_t sid, u64id_t id) {
|
||||||
|
auto& connection = dynamic_cast<network::ReadableConnection&>(
|
||||||
|
*network.getConnection(id, true)
|
||||||
|
);
|
||||||
|
connection.setPrivate(true);
|
||||||
|
logger.info() << "connected client " << id << ": "
|
||||||
|
<< connection.getAddress() << ":"
|
||||||
|
<< connection.getPort();
|
||||||
|
dbgServer.setClient(id);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
auto& server = *network.getServer(serverId, true);
|
||||||
|
server.setPrivate(true);
|
||||||
|
|
||||||
|
auto& tcpServer = dynamic_cast<network::TcpServer&>(server);
|
||||||
|
tcpServer.setMaxClientsConnected(1);
|
||||||
|
|
||||||
|
logger.info() << "tcp debugging server open at port " << server.getPort();
|
||||||
|
|
||||||
|
return tcpServer;
|
||||||
|
}
|
||||||
|
|
||||||
|
static network::Server& create_server(
|
||||||
|
DebuggingServer& dbgServer, Engine& engine, const std::string& serverString
|
||||||
|
) {
|
||||||
|
logger.info() << "starting debugging server";
|
||||||
|
|
||||||
|
size_t sepPos = serverString.find(':');
|
||||||
|
if (sepPos == std::string::npos) {
|
||||||
|
throw std::runtime_error("invalid debugging server configuration string");
|
||||||
|
}
|
||||||
|
auto transport = serverString.substr(0, sepPos);
|
||||||
|
if (transport == "tcp") {
|
||||||
|
int port;
|
||||||
|
try {
|
||||||
|
port = std::stoi(serverString.substr(sepPos + 1));
|
||||||
|
} catch (const std::exception& err) {
|
||||||
|
throw std::runtime_error("invalid tcp port");
|
||||||
|
}
|
||||||
|
return create_tcp_server(dbgServer, engine, port);
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"unsupported debugging server transport '" + transport + "'"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
DebuggingServer::DebuggingServer(
|
||||||
|
Engine& engine, const std::string& serverString
|
||||||
|
)
|
||||||
|
: engine(engine),
|
||||||
|
server(create_server(*this, engine, serverString)),
|
||||||
|
connection(nullptr) {
|
||||||
|
}
|
||||||
|
|
||||||
|
DebuggingServer::~DebuggingServer() {
|
||||||
|
logger.info() << "stopping debugging server";
|
||||||
|
server.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool DebuggingServer::update() {
|
||||||
|
if (connection == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
std::string message = connection->read();
|
||||||
|
if (message.empty()) {
|
||||||
|
if (!connection->alive()) {
|
||||||
|
bool status = performCommand(disconnectAction, dv::object());
|
||||||
|
connection.reset();
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
logger.debug() << "received: " << message;
|
||||||
|
try {
|
||||||
|
auto obj = json::parse(message);
|
||||||
|
if (!obj.has("type")) {
|
||||||
|
logger.error() << "missing message type";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const auto& type = obj["type"].asString();
|
||||||
|
if (performCommand(type, obj)) {
|
||||||
|
connection->sendResponse("resumed");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} catch (const std::runtime_error& err) {
|
||||||
|
logger.error() << "could not to parse message: " << err.what();
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DebuggingServer::performCommand(
|
||||||
|
const std::string& type, const dv::value& map
|
||||||
|
) {
|
||||||
|
if (!connectionEstablished && type == "connect") {
|
||||||
|
map.at("disconnect-action").get(disconnectAction);
|
||||||
|
connectionEstablished = true;
|
||||||
|
logger.info() << "client connection established";
|
||||||
|
connection->sendResponse("success");
|
||||||
|
}
|
||||||
|
if (!connectionEstablished) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (type == "terminate") {
|
||||||
|
engine.quit();
|
||||||
|
connection->sendResponse("success");
|
||||||
|
} else if (type == "detach") {
|
||||||
|
connection->sendResponse("success");
|
||||||
|
connection.reset();
|
||||||
|
engine.detachDebugger();
|
||||||
|
return false;
|
||||||
|
} else if (type == "set-breakpoint" || type == "remove-breakpoint") {
|
||||||
|
if (!map.has("source") || !map.has("line"))
|
||||||
|
return false;
|
||||||
|
breakpointEvents.push_back(DebuggingEvent {
|
||||||
|
type[0] == 's'
|
||||||
|
? DebuggingEventType::SET_BREAKPOINT
|
||||||
|
: DebuggingEventType::REMOVE_BREAKPOINT,
|
||||||
|
BreakpointEventDto {
|
||||||
|
map["source"].asString(),
|
||||||
|
static_cast<int>(map["line"].asInteger()),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (type == "step" || type == "step-into-function") {
|
||||||
|
breakpointEvents.push_back(DebuggingEvent {
|
||||||
|
type == "step"
|
||||||
|
? DebuggingEventType::STEP
|
||||||
|
: DebuggingEventType::STEP_INTO_FUNCTION,
|
||||||
|
SignalEventDto {}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} else if (type == "resume") {
|
||||||
|
breakpointEvents.push_back(DebuggingEvent {
|
||||||
|
DebuggingEventType::RESUME, SignalEventDto {}});
|
||||||
|
return true;
|
||||||
|
} else if (type == "get-value") {
|
||||||
|
if (!map.has("frame") || !map.has("local") || !map.has("path"))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
int frame = map["frame"].asInteger();
|
||||||
|
int localIndex = map["local"].asInteger();
|
||||||
|
|
||||||
|
ValuePath path;
|
||||||
|
for (const auto& segment : map["path"]) {
|
||||||
|
if (segment.isString()) {
|
||||||
|
path.emplace_back(segment.asString());
|
||||||
|
} else {
|
||||||
|
path.emplace_back(static_cast<int>(segment.asInteger()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
breakpointEvents.push_back(DebuggingEvent {
|
||||||
|
DebuggingEventType::GET_VALUE, GetValueEventDto {
|
||||||
|
frame, localIndex, std::move(path)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
logger.error() << "unsupported command '" << type << "'";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebuggingServer::pause(
|
||||||
|
std::string&& reason, std::string&& message, dv::value&& stackTrace
|
||||||
|
) {
|
||||||
|
if (connection == nullptr) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
auto response = dv::object({{"type", std::string("paused")}});
|
||||||
|
if (!reason.empty()) {
|
||||||
|
response["reason"] = std::move(reason);
|
||||||
|
}
|
||||||
|
if (!message.empty()) {
|
||||||
|
response["message"] = std::move(message);
|
||||||
|
}
|
||||||
|
if (stackTrace != nullptr) {
|
||||||
|
response["stack"] = std::move(stackTrace);
|
||||||
|
}
|
||||||
|
connection->send(std::move(response));
|
||||||
|
engine.startPauseLoop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebuggingServer::sendValue(
|
||||||
|
dv::value&& value, int frame, int local, ValuePath&& path
|
||||||
|
) {
|
||||||
|
auto pathValue = dv::list();
|
||||||
|
for (const auto& segment : path) {
|
||||||
|
if (auto string = std::get_if<std::string>(&segment)) {
|
||||||
|
pathValue.add(*string);
|
||||||
|
} else {
|
||||||
|
pathValue.add(std::get<int>(segment));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connection->send(dv::object({
|
||||||
|
{"type", std::string("value")},
|
||||||
|
{"frame", frame},
|
||||||
|
{"local", local},
|
||||||
|
{"path", std::move(pathValue)},
|
||||||
|
{"value", std::move(value)},
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebuggingServer::setClient(u64id_t client) {
|
||||||
|
connection =
|
||||||
|
std::make_unique<ClientConnection>(engine.getNetwork(), client);
|
||||||
|
connectionEstablished = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<DebuggingEvent> DebuggingServer::pullEvents() {
|
||||||
|
return std::move(breakpointEvents);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebuggingServer::setDisconnectAction(const std::string& action) {
|
||||||
|
disconnectAction = action;
|
||||||
|
}
|
||||||
106
src/devtools/DebuggingServer.hpp
Normal file
106
src/devtools/DebuggingServer.hpp
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <variant>
|
||||||
|
|
||||||
|
#include "typedefs.hpp"
|
||||||
|
|
||||||
|
namespace network {
|
||||||
|
class Server;
|
||||||
|
class Connection;
|
||||||
|
class ReadableConnection;
|
||||||
|
class Network;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace dv {
|
||||||
|
class value;
|
||||||
|
}
|
||||||
|
|
||||||
|
class Engine;
|
||||||
|
|
||||||
|
namespace devtools {
|
||||||
|
inline constexpr const char VCDBG_MAGIC[8] = "vc-dbg\0";
|
||||||
|
inline constexpr int VCDBG_VERSION = 1;
|
||||||
|
|
||||||
|
class ClientConnection {
|
||||||
|
public:
|
||||||
|
ClientConnection(network::Network& network, u64id_t connection)
|
||||||
|
: network(network), connection(connection) {
|
||||||
|
}
|
||||||
|
~ClientConnection();
|
||||||
|
|
||||||
|
std::string read();
|
||||||
|
void send(const dv::value& message);
|
||||||
|
void sendResponse(const std::string& type);
|
||||||
|
|
||||||
|
bool alive() const;
|
||||||
|
private:
|
||||||
|
network::Network& network;
|
||||||
|
size_t messageLength = 0;
|
||||||
|
u64id_t connection;
|
||||||
|
bool initiated = false;
|
||||||
|
|
||||||
|
bool initiate(network::ReadableConnection* connection);
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class DebuggingEventType {
|
||||||
|
SET_BREAKPOINT = 1,
|
||||||
|
REMOVE_BREAKPOINT,
|
||||||
|
STEP,
|
||||||
|
STEP_INTO_FUNCTION,
|
||||||
|
RESUME,
|
||||||
|
GET_VALUE,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BreakpointEventDto {
|
||||||
|
std::string source;
|
||||||
|
int line;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SignalEventDto {
|
||||||
|
};
|
||||||
|
|
||||||
|
using ValuePath = std::vector<std::variant<std::string, int>>;
|
||||||
|
|
||||||
|
struct GetValueEventDto {
|
||||||
|
int frame;
|
||||||
|
int localIndex;
|
||||||
|
ValuePath path;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DebuggingEvent {
|
||||||
|
DebuggingEventType type;
|
||||||
|
std::variant<BreakpointEventDto, SignalEventDto, GetValueEventDto> data;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DebuggingServer {
|
||||||
|
public:
|
||||||
|
DebuggingServer(Engine& engine, const std::string& serverString);
|
||||||
|
~DebuggingServer();
|
||||||
|
|
||||||
|
bool update();
|
||||||
|
void pause(
|
||||||
|
std::string&& reason, std::string&& message, dv::value&& stackTrace
|
||||||
|
);
|
||||||
|
|
||||||
|
void sendValue(dv::value&& value, int frame, int local, ValuePath&& path);
|
||||||
|
|
||||||
|
void setClient(u64id_t client);
|
||||||
|
std::vector<DebuggingEvent> pullEvents();
|
||||||
|
|
||||||
|
void setDisconnectAction(const std::string& action);
|
||||||
|
private:
|
||||||
|
Engine& engine;
|
||||||
|
network::Server& server;
|
||||||
|
std::unique_ptr<ClientConnection> connection;
|
||||||
|
bool connectionEstablished = false;
|
||||||
|
std::vector<DebuggingEvent> breakpointEvents;
|
||||||
|
std::string disconnectAction = "resume";
|
||||||
|
|
||||||
|
bool performCommand(
|
||||||
|
const std::string& type, const dv::value& map
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
#include "coders/commons.hpp"
|
#include "coders/commons.hpp"
|
||||||
#include "devtools/Editor.hpp"
|
#include "devtools/Editor.hpp"
|
||||||
#include "devtools/Project.hpp"
|
#include "devtools/Project.hpp"
|
||||||
|
#include "devtools/DebuggingServer.hpp"
|
||||||
#include "content/ContentControl.hpp"
|
#include "content/ContentControl.hpp"
|
||||||
#include "core_defs.hpp"
|
#include "core_defs.hpp"
|
||||||
#include "io/io.hpp"
|
#include "io/io.hpp"
|
||||||
|
|
@ -115,6 +116,9 @@ void Engine::initializeClient() {
|
||||||
if (ENGINE_DEBUG_BUILD) {
|
if (ENGINE_DEBUG_BUILD) {
|
||||||
title += " [debug]";
|
title += " [debug]";
|
||||||
}
|
}
|
||||||
|
if (debuggingServer) {
|
||||||
|
title = "[debugging] " + title;
|
||||||
|
}
|
||||||
auto [window, input] = Window::initialize(&settings.display, title);
|
auto [window, input] = Window::initialize(&settings.display, title);
|
||||||
if (!window || !input){
|
if (!window || !input){
|
||||||
throw initialize_error("could not initialize window");
|
throw initialize_error("could not initialize window");
|
||||||
|
|
@ -135,10 +139,11 @@ void Engine::initializeClient() {
|
||||||
if (ENGINE_DEBUG_BUILD) {
|
if (ENGINE_DEBUG_BUILD) {
|
||||||
menus::create_version_label(*gui);
|
menus::create_version_label(*gui);
|
||||||
}
|
}
|
||||||
keepAlive(settings.display.fullscreen.observe(
|
keepAlive(settings.display.windowMode.observe(
|
||||||
[this](bool value) {
|
[this](int value) {
|
||||||
if (value != this->window->isFullscreen()) {
|
WindowMode mode = static_cast<WindowMode>(value);
|
||||||
this->window->toggleFullscreen();
|
if (mode != this->window->getMode()) {
|
||||||
|
this->window->setMode(mode);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
true
|
true
|
||||||
|
|
@ -172,6 +177,18 @@ void Engine::initialize(CoreParameters coreParameters) {
|
||||||
cmd = std::make_unique<cmd::CommandsInterpreter>();
|
cmd = std::make_unique<cmd::CommandsInterpreter>();
|
||||||
network = network::Network::create(settings.network);
|
network = network::Network::create(settings.network);
|
||||||
|
|
||||||
|
if (!params.debugServerString.empty()) {
|
||||||
|
try {
|
||||||
|
debuggingServer = std::make_unique<devtools::DebuggingServer>(
|
||||||
|
*this, params.debugServerString
|
||||||
|
);
|
||||||
|
} catch (const std::runtime_error& err) {
|
||||||
|
throw initialize_error(
|
||||||
|
"debugging server error: " + std::string(err.what())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!params.scriptFile.empty()) {
|
if (!params.scriptFile.empty()) {
|
||||||
paths.setScriptFolder(params.scriptFile.parent_path());
|
paths.setScriptFolder(params.scriptFile.parent_path());
|
||||||
}
|
}
|
||||||
|
|
@ -237,7 +254,11 @@ void Engine::updateHotkeys() {
|
||||||
gui->toggleDebug();
|
gui->toggleDebug();
|
||||||
}
|
}
|
||||||
if (input->jpressed(Keycode::F11)) {
|
if (input->jpressed(Keycode::F11)) {
|
||||||
settings.display.fullscreen.toggle();
|
if (settings.display.windowMode.get() != static_cast<int>(WindowMode::FULLSCREEN)) {
|
||||||
|
settings.display.windowMode.set(static_cast<int>(WindowMode::FULLSCREEN));
|
||||||
|
} else {
|
||||||
|
settings.display.windowMode.set(static_cast<int>(WindowMode::WINDOWED));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -261,6 +282,14 @@ void Engine::postUpdate() {
|
||||||
network->update();
|
network->update();
|
||||||
postRunnables.run();
|
postRunnables.run();
|
||||||
scripting::process_post_runnables();
|
scripting::process_post_runnables();
|
||||||
|
|
||||||
|
if (debuggingServer) {
|
||||||
|
debuggingServer->update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Engine::detachDebugger() {
|
||||||
|
debuggingServer.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Engine::updateFrontend() {
|
void Engine::updateFrontend() {
|
||||||
|
|
@ -282,6 +311,30 @@ void Engine::nextFrame() {
|
||||||
input->pollEvents();
|
input->pollEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Engine::startPauseLoop() {
|
||||||
|
bool initialCursorLocked = false;
|
||||||
|
if (!isHeadless()) {
|
||||||
|
initialCursorLocked = input->isCursorLocked();
|
||||||
|
if (initialCursorLocked) {
|
||||||
|
input->toggleCursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (!isQuitSignal() && debuggingServer) {
|
||||||
|
network->update();
|
||||||
|
if (debuggingServer->update()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (isHeadless()) {
|
||||||
|
platform::sleep(1.0 / params.tps * 1000);
|
||||||
|
} else {
|
||||||
|
nextFrame();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (initialCursorLocked) {
|
||||||
|
input->toggleCursor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Engine::renderFrame() {
|
void Engine::renderFrame() {
|
||||||
screen->draw(time.getDelta());
|
screen->draw(time.getDelta());
|
||||||
|
|
||||||
|
|
@ -294,7 +347,11 @@ void Engine::saveSettings() {
|
||||||
io::write_string(EnginePaths::SETTINGS_FILE, toml::stringify(*settingsHandler));
|
io::write_string(EnginePaths::SETTINGS_FILE, toml::stringify(*settingsHandler));
|
||||||
if (!params.headless) {
|
if (!params.headless) {
|
||||||
logger.info() << "saving bindings";
|
logger.info() << "saving bindings";
|
||||||
io::write_string(EnginePaths::CONTROLS_FILE, input->getBindings().write());
|
if (input) {
|
||||||
|
io::write_string(
|
||||||
|
EnginePaths::CONTROLS_FILE, input->getBindings().write()
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -313,6 +370,7 @@ void Engine::close() {
|
||||||
logger.info() << "gui finished";
|
logger.info() << "gui finished";
|
||||||
}
|
}
|
||||||
audio::close();
|
audio::close();
|
||||||
|
debuggingServer.reset();
|
||||||
network.reset();
|
network.reset();
|
||||||
clearKeepedObjects();
|
clearKeepedObjects();
|
||||||
project.reset();
|
project.reset();
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ namespace network {
|
||||||
|
|
||||||
namespace devtools {
|
namespace devtools {
|
||||||
class Editor;
|
class Editor;
|
||||||
|
class DebuggingServer;
|
||||||
}
|
}
|
||||||
|
|
||||||
class initialize_error : public std::runtime_error {
|
class initialize_error : public std::runtime_error {
|
||||||
|
|
@ -50,6 +51,8 @@ struct CoreParameters {
|
||||||
std::filesystem::path userFolder = ".";
|
std::filesystem::path userFolder = ".";
|
||||||
std::filesystem::path scriptFile;
|
std::filesystem::path scriptFile;
|
||||||
std::filesystem::path projectFolder;
|
std::filesystem::path projectFolder;
|
||||||
|
std::string debugServerString;
|
||||||
|
int tps = 20;
|
||||||
};
|
};
|
||||||
|
|
||||||
using OnWorldOpen = std::function<void(std::unique_ptr<Level>, int64_t)>;
|
using OnWorldOpen = std::function<void(std::unique_ptr<Level>, int64_t)>;
|
||||||
|
|
@ -71,6 +74,7 @@ class Engine : public util::ObjectsKeeper {
|
||||||
std::unique_ptr<Input> input;
|
std::unique_ptr<Input> input;
|
||||||
std::unique_ptr<gui::GUI> gui;
|
std::unique_ptr<gui::GUI> gui;
|
||||||
std::unique_ptr<devtools::Editor> editor;
|
std::unique_ptr<devtools::Editor> editor;
|
||||||
|
std::unique_ptr<devtools::DebuggingServer> debuggingServer;
|
||||||
PostRunnables postRunnables;
|
PostRunnables postRunnables;
|
||||||
Time time;
|
Time time;
|
||||||
OnWorldOpen levelConsumer;
|
OnWorldOpen levelConsumer;
|
||||||
|
|
@ -104,6 +108,7 @@ public:
|
||||||
void updateFrontend();
|
void updateFrontend();
|
||||||
void renderFrame();
|
void renderFrame();
|
||||||
void nextFrame();
|
void nextFrame();
|
||||||
|
void startPauseLoop();
|
||||||
|
|
||||||
/// @brief Set screen (scene).
|
/// @brief Set screen (scene).
|
||||||
/// nullptr may be used to delete previous screen before creating new one,
|
/// nullptr may be used to delete previous screen before creating new one,
|
||||||
|
|
@ -181,4 +186,10 @@ public:
|
||||||
const Project& getProject() {
|
const Project& getProject() {
|
||||||
return *project;
|
return *project;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
devtools::DebuggingServer* getDebuggingServer() {
|
||||||
|
return debuggingServer.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
void detachDebugger();
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -15,8 +15,6 @@ using namespace std::chrono;
|
||||||
|
|
||||||
static debug::Logger logger("mainloop");
|
static debug::Logger logger("mainloop");
|
||||||
|
|
||||||
inline constexpr int TPS = 20;
|
|
||||||
|
|
||||||
ServerMainloop::ServerMainloop(Engine& engine) : engine(engine) {
|
ServerMainloop::ServerMainloop(Engine& engine) : engine(engine) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -39,7 +37,7 @@ void ServerMainloop::run() {
|
||||||
"script:" + coreParams.scriptFile.filename().u8string()
|
"script:" + coreParams.scriptFile.filename().u8string()
|
||||||
);
|
);
|
||||||
|
|
||||||
double targetDelta = 1.0 / static_cast<double>(TPS);
|
double targetDelta = 1.0 / static_cast<double>(coreParams.tps);
|
||||||
double delta = targetDelta;
|
double delta = targetDelta;
|
||||||
auto begin = system_clock::now();
|
auto begin = system_clock::now();
|
||||||
auto startupTime = begin;
|
auto startupTime = begin;
|
||||||
|
|
|
||||||
|
|
@ -8,21 +8,6 @@ inline constexpr glm::vec3 X(1, 0, 0);
|
||||||
inline constexpr glm::vec3 Y(0, 1, 0);
|
inline constexpr glm::vec3 Y(0, 1, 0);
|
||||||
inline constexpr glm::vec3 Z(0, 0, 1);
|
inline constexpr glm::vec3 Z(0, 0, 1);
|
||||||
|
|
||||||
void Mesh::addPlane(
|
|
||||||
const glm::vec3& pos,
|
|
||||||
const glm::vec3& right,
|
|
||||||
const glm::vec3& up,
|
|
||||||
const glm::vec3& norm
|
|
||||||
) {
|
|
||||||
vertices.push_back({pos-right-up, {0,0}, norm});
|
|
||||||
vertices.push_back({pos+right-up, {1,0}, norm});
|
|
||||||
vertices.push_back({pos+right+up, {1,1}, norm});
|
|
||||||
|
|
||||||
vertices.push_back({pos-right-up, {0,0}, norm});
|
|
||||||
vertices.push_back({pos+right+up, {1,1}, norm});
|
|
||||||
vertices.push_back({pos-right+up, {0,1}, norm});
|
|
||||||
}
|
|
||||||
|
|
||||||
void Mesh::addPlane(
|
void Mesh::addPlane(
|
||||||
const glm::vec3& pos,
|
const glm::vec3& pos,
|
||||||
const glm::vec3& right,
|
const glm::vec3& right,
|
||||||
|
|
@ -39,6 +24,23 @@ void Mesh::addPlane(
|
||||||
vertices.push_back({pos-right+up, {uv.u1, uv.v2}, norm});
|
vertices.push_back({pos-right+up, {uv.u1, uv.v2}, norm});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Mesh::addPlane(
|
||||||
|
const glm::vec3& pos,
|
||||||
|
const glm::vec3& right,
|
||||||
|
const glm::vec3& up,
|
||||||
|
const glm::vec3& norm,
|
||||||
|
const UVRegion& region,
|
||||||
|
const glm::mat4& transform
|
||||||
|
) {
|
||||||
|
addPlane(
|
||||||
|
glm::vec3(transform * glm::vec4(pos, 1.0f)),
|
||||||
|
glm::vec3(transform * glm::vec4(right, 0.0f)),
|
||||||
|
glm::vec3(transform * glm::vec4(up, 0.0f)),
|
||||||
|
glm::normalize(glm::vec3(transform * glm::vec4(norm, 0.0f))),
|
||||||
|
region
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
void Mesh::addRect(
|
void Mesh::addRect(
|
||||||
const glm::vec3& pos,
|
const glm::vec3& pos,
|
||||||
const glm::vec3& right,
|
const glm::vec3& right,
|
||||||
|
|
@ -56,14 +58,15 @@ void Mesh::addRect(
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mesh::addBox(const glm::vec3& pos, const glm::vec3& size) {
|
void Mesh::addBox(const glm::vec3& pos, const glm::vec3& size) {
|
||||||
addPlane(pos+Z*size, X*size, Y*size, Z);
|
UVRegion fullRegion (0, 0, 1, 1);
|
||||||
addPlane(pos-Z*size, -X*size, Y*size, -Z);
|
addPlane(pos+Z*size, X*size, Y*size, Z, fullRegion);
|
||||||
|
addPlane(pos-Z*size, -X*size, Y*size, -Z, fullRegion);
|
||||||
|
|
||||||
addPlane(pos+Y*size, X*size, -Z*size, Y);
|
addPlane(pos+Y*size, X*size, -Z*size, Y, fullRegion);
|
||||||
addPlane(pos-Y*size, X*size, Z*size, -Y);
|
addPlane(pos-Y*size, X*size, Z*size, -Y, fullRegion);
|
||||||
|
|
||||||
addPlane(pos+X*size, -Z*size, Y*size, X);
|
addPlane(pos+X*size, -Z*size, Y*size, X, fullRegion);
|
||||||
addPlane(pos-X*size, Z*size, Y*size, -X);
|
addPlane(pos-X*size, Z*size, Y*size, -X, fullRegion);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mesh::addBox(
|
void Mesh::addBox(
|
||||||
|
|
@ -71,19 +74,29 @@ void Mesh::addBox(
|
||||||
const glm::vec3& size,
|
const glm::vec3& size,
|
||||||
const UVRegion (&uvs)[6],
|
const UVRegion (&uvs)[6],
|
||||||
const bool enabledSides[6]
|
const bool enabledSides[6]
|
||||||
|
) {
|
||||||
|
addBox(pos, size, uvs, enabledSides, glm::mat4(1.0f));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mesh::addBox(
|
||||||
|
const glm::vec3& pos,
|
||||||
|
const glm::vec3& size,
|
||||||
|
const UVRegion (&uvs)[6],
|
||||||
|
const bool enabledSides[6],
|
||||||
|
const glm::mat4& transform
|
||||||
) {
|
) {
|
||||||
if (enabledSides[0]) // north
|
if (enabledSides[0]) // north
|
||||||
addPlane(pos+Z*size, X*size, Y*size, Z, uvs[0]);
|
addPlane(pos+Z*size, X*size, Y*size, Z, uvs[0], transform);
|
||||||
if (enabledSides[1]) // south
|
if (enabledSides[1]) // south
|
||||||
addPlane(pos-Z*size, -X*size, Y*size, -Z, uvs[1]);
|
addPlane(pos-Z*size, -X*size, Y*size, -Z, uvs[1], transform);
|
||||||
if (enabledSides[2]) // top
|
if (enabledSides[2]) // top
|
||||||
addPlane(pos+Y*size, X*size, -Z*size, Y, uvs[2] * glm::vec2(-1));
|
addPlane(pos+Y*size, X*size, -Z*size, Y, uvs[2] * glm::vec2(-1), transform);
|
||||||
if (enabledSides[3]) // bottom
|
if (enabledSides[3]) // bottom
|
||||||
addPlane(pos-Y*size, X*size, Z*size, -Y, uvs[3] * glm::vec2(-1, 1));
|
addPlane(pos-Y*size, X*size, Z*size, -Y, uvs[3] * glm::vec2(-1, 1), transform);
|
||||||
if (enabledSides[4]) // west
|
if (enabledSides[4]) // west
|
||||||
addPlane(pos+X*size, -Z*size, Y*size, X, uvs[4]);
|
addPlane(pos+X*size, -Z*size, Y*size, X, uvs[4], transform);
|
||||||
if (enabledSides[5]) // east
|
if (enabledSides[5]) // east
|
||||||
addPlane(pos-X*size, Z*size, Y*size, -X, uvs[5] * glm::vec2(-1, 1));
|
addPlane(pos-X*size, Z*size, Y*size, -X, uvs[5] * glm::vec2(-1, 1), transform);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Mesh::scale(const glm::vec3& size) {
|
void Mesh::scale(const glm::vec3& size) {
|
||||||
|
|
|
||||||
|
|
@ -22,14 +22,16 @@ namespace model {
|
||||||
const glm::vec3& pos,
|
const glm::vec3& pos,
|
||||||
const glm::vec3& right,
|
const glm::vec3& right,
|
||||||
const glm::vec3& up,
|
const glm::vec3& up,
|
||||||
const glm::vec3& norm
|
const glm::vec3& norm,
|
||||||
|
const UVRegion& region
|
||||||
);
|
);
|
||||||
void addPlane(
|
void addPlane(
|
||||||
const glm::vec3& pos,
|
const glm::vec3& pos,
|
||||||
const glm::vec3& right,
|
const glm::vec3& right,
|
||||||
const glm::vec3& up,
|
const glm::vec3& up,
|
||||||
const glm::vec3& norm,
|
const glm::vec3& norm,
|
||||||
const UVRegion& region
|
const UVRegion& region,
|
||||||
|
const glm::mat4& transform
|
||||||
);
|
);
|
||||||
void addRect(
|
void addRect(
|
||||||
const glm::vec3& pos,
|
const glm::vec3& pos,
|
||||||
|
|
@ -45,6 +47,13 @@ namespace model {
|
||||||
const UVRegion (&texfaces)[6],
|
const UVRegion (&texfaces)[6],
|
||||||
const bool enabledSides[6]
|
const bool enabledSides[6]
|
||||||
);
|
);
|
||||||
|
void addBox(
|
||||||
|
const glm::vec3& pos,
|
||||||
|
const glm::vec3& size,
|
||||||
|
const UVRegion (&texfaces)[6],
|
||||||
|
const bool enabledSides[6],
|
||||||
|
const glm::mat4& transform
|
||||||
|
);
|
||||||
void scale(const glm::vec3& size);
|
void scale(const glm::vec3& size);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ Cubemap::Cubemap(uint width, uint height, ImageFormat imageFormat)
|
||||||
0,
|
0,
|
||||||
format,
|
format,
|
||||||
GL_UNSIGNED_BYTE,
|
GL_UNSIGNED_BYTE,
|
||||||
NULL
|
nullptr
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
|
#include <glm/gtx/norm.hpp>
|
||||||
|
#include <glm/gtc/constants.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
#include "Shadows.hpp"
|
#include "Shadows.hpp"
|
||||||
|
|
||||||
#include <GL/glew.h>
|
#include <GL/glew.h>
|
||||||
#define GLM_ENABLE_EXPERIMENTAL
|
|
||||||
#include <glm/gtx/norm.hpp>
|
|
||||||
|
|
||||||
#include "assets/Assets.hpp"
|
#include "assets/Assets.hpp"
|
||||||
#include "graphics/core/DrawContext.hpp"
|
#include "graphics/core/DrawContext.hpp"
|
||||||
|
|
@ -23,7 +26,7 @@ public:
|
||||||
glGenTextures(1, &depthMap);
|
glGenTextures(1, &depthMap);
|
||||||
glBindTexture(GL_TEXTURE_2D, depthMap);
|
glBindTexture(GL_TEXTURE_2D, depthMap);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT,
|
||||||
resolution, resolution, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
|
resolution, resolution, 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,8 @@
|
||||||
#include "Emitter.hpp"
|
#include "Emitter.hpp"
|
||||||
|
|
||||||
#include <glm/gtc/random.hpp>
|
#include <glm/gtc/random.hpp>
|
||||||
|
#include <glm/gtc/constants.hpp>
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
#define GLM_ENABLE_EXPERIMENTAL
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
#include <glm/gtx/norm.hpp>
|
#include <glm/gtx/norm.hpp>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -103,9 +103,14 @@ void guiutil::confirm(
|
||||||
|
|
||||||
panel->setGravity(Gravity::center_center);
|
panel->setGravity(Gravity::center_center);
|
||||||
container->add(panel);
|
container->add(panel);
|
||||||
|
|
||||||
panel->setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.5f));
|
panel->setColor(glm::vec4(0.0f, 0.0f, 0.0f, 0.5f));
|
||||||
panel->add(std::make_shared<Label>(gui, text));
|
|
||||||
|
auto label = std::make_shared<Label>(gui, text);
|
||||||
|
label->setSize(glm::vec2(600, 50));
|
||||||
|
label->setMultiline(true);
|
||||||
|
label->setTextWrapping(true);
|
||||||
|
|
||||||
|
panel->add(label);
|
||||||
auto subpanel = std::make_shared<Panel>(gui, glm::vec2(600, 53));
|
auto subpanel = std::make_shared<Panel>(gui, glm::vec2(600, 53));
|
||||||
subpanel->setColor(glm::vec4(0));
|
subpanel->setColor(glm::vec4(0));
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -17,7 +17,7 @@ namespace guiutil {
|
||||||
/// @brief Create element from XML
|
/// @brief Create element from XML
|
||||||
/// @param source XML
|
/// @param source XML
|
||||||
std::shared_ptr<gui::UINode> create(
|
std::shared_ptr<gui::UINode> create(
|
||||||
gui::GUI& gui, const std::string& source, scriptenv env = 0
|
gui::GUI& gui, const std::string& source, scriptenv env = nullptr
|
||||||
);
|
);
|
||||||
|
|
||||||
void alert(
|
void alert(
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@
|
||||||
#include <stack>
|
#include <stack>
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
#include "util/stringutil.hpp"
|
#include "util/stringutil.hpp"
|
||||||
|
#include "util/platform.hpp"
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
#include "io/devices/StdfsDevice.hpp"
|
#include "io/devices/StdfsDevice.hpp"
|
||||||
|
|
@ -44,6 +45,7 @@ void EnginePaths::prepare() {
|
||||||
resourcesFolder.string() + " is not a directory"
|
resourcesFolder.string() + " is not a directory"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
logger.info() << "executable path: " << platform::get_executable_path().string();
|
||||||
logger.info() << "resources folder: " << fs::canonical(resourcesFolder).u8string();
|
logger.info() << "resources folder: " << fs::canonical(resourcesFolder).u8string();
|
||||||
logger.info() << "user files folder: " << fs::canonical(userFilesFolder).u8string();
|
logger.info() << "user files folder: " << fs::canonical(userFilesFolder).u8string();
|
||||||
logger.info() << "project folder: " << fs::canonical(projectFolder).u8string();
|
logger.info() << "project folder: " << fs::canonical(projectFolder).u8string();
|
||||||
|
|
|
||||||
|
|
@ -49,8 +49,8 @@ SettingsHandler::SettingsHandler(EngineSettings& settings) {
|
||||||
builder.add("height", &settings.display.height);
|
builder.add("height", &settings.display.height);
|
||||||
builder.add("samples", &settings.display.samples);
|
builder.add("samples", &settings.display.samples);
|
||||||
builder.add("framerate", &settings.display.framerate);
|
builder.add("framerate", &settings.display.framerate);
|
||||||
builder.add("fullscreen", &settings.display.fullscreen);
|
|
||||||
builder.add("limit-fps-iconified", &settings.display.limitFpsIconified);
|
builder.add("limit-fps-iconified", &settings.display.limitFpsIconified);
|
||||||
|
builder.add("window-mode", &settings.display.windowMode);
|
||||||
|
|
||||||
builder.section("camera");
|
builder.section("camera");
|
||||||
builder.add("sensitivity", &settings.camera.sensitivity);
|
builder.add("sensitivity", &settings.camera.sensitivity);
|
||||||
|
|
|
||||||
|
|
@ -488,7 +488,10 @@ void PlayerController::updateInteraction(const Input& inputEvents, float delta)
|
||||||
}
|
}
|
||||||
const auto& bindings = inputEvents.getBindings();
|
const auto& bindings = inputEvents.getBindings();
|
||||||
bool xkey = bindings.active(BIND_PLAYER_FAST_INTERACTOIN);
|
bool xkey = bindings.active(BIND_PLAYER_FAST_INTERACTOIN);
|
||||||
float maxDistance = xkey ? 200.0f : 10.0f;
|
float maxDistance = player.getMaxInteractionDistance();
|
||||||
|
if (xkey) {
|
||||||
|
maxDistance *= 100.0;
|
||||||
|
}
|
||||||
bool longInteraction = interactionTimer <= 0 || xkey;
|
bool longInteraction = interactionTimer <= 0 || xkey;
|
||||||
bool lclick = bindings.jactive(BIND_PLAYER_DESTROY) ||
|
bool lclick = bindings.jactive(BIND_PLAYER_DESTROY) ||
|
||||||
(longInteraction && bindings.active(BIND_PLAYER_DESTROY));
|
(longInteraction && bindings.active(BIND_PLAYER_DESTROY));
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ extern const luaL_Reg playerlib[];
|
||||||
extern const luaL_Reg posteffectslib[]; // gfx.posteffects
|
extern const luaL_Reg posteffectslib[]; // gfx.posteffects
|
||||||
extern const luaL_Reg quatlib[];
|
extern const luaL_Reg quatlib[];
|
||||||
extern const luaL_Reg randomlib[];
|
extern const luaL_Reg randomlib[];
|
||||||
|
extern const luaL_Reg compressionlib[];
|
||||||
extern const luaL_Reg text3dlib[]; // gfx.text3d
|
extern const luaL_Reg text3dlib[]; // gfx.text3d
|
||||||
extern const luaL_Reg timelib[];
|
extern const luaL_Reg timelib[];
|
||||||
extern const luaL_Reg tomllib[];
|
extern const luaL_Reg tomllib[];
|
||||||
|
|
|
||||||
|
|
@ -169,4 +169,5 @@ const luaL_Reg rigidbodylib[] = {
|
||||||
{"set_crouching", lua::wrap<l_set_crouching>},
|
{"set_crouching", lua::wrap<l_set_crouching>},
|
||||||
{"get_body_type", lua::wrap<l_get_body_type>},
|
{"get_body_type", lua::wrap<l_get_body_type>},
|
||||||
{"set_body_type", lua::wrap<l_set_body_type>},
|
{"set_body_type", lua::wrap<l_set_body_type>},
|
||||||
{NULL, NULL}};
|
{nullptr, nullptr}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -169,5 +169,5 @@ const luaL_Reg skeletonlib[] = {
|
||||||
{"is_interpolated", lua::wrap<l_is_interpolated>},
|
{"is_interpolated", lua::wrap<l_is_interpolated>},
|
||||||
{"set_interpolated", lua::wrap<l_set_interpolated>},
|
{"set_interpolated", lua::wrap<l_set_interpolated>},
|
||||||
{"exists", lua::wrap<l_exists>},
|
{"exists", lua::wrap<l_exists>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -51,4 +51,5 @@ const luaL_Reg transformlib[] = {
|
||||||
{"set_size", lua::wrap<l_set_size>},
|
{"set_size", lua::wrap<l_set_size>},
|
||||||
{"get_rot", lua::wrap<l_get_rot>},
|
{"get_rot", lua::wrap<l_get_rot>},
|
||||||
{"set_rot", lua::wrap<l_set_rot>},
|
{"set_rot", lua::wrap<l_set_rot>},
|
||||||
{NULL, NULL}};
|
{nullptr, nullptr}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,5 +2,5 @@
|
||||||
|
|
||||||
const luaL_Reg applib[] = {
|
const luaL_Reg applib[] = {
|
||||||
// see libcore.cpp an stdlib.lua
|
// see libcore.cpp an stdlib.lua
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -67,5 +67,5 @@ static int l_parse_model(lua::State* L) {
|
||||||
const luaL_Reg assetslib[] = {
|
const luaL_Reg assetslib[] = {
|
||||||
{"load_texture", lua::wrap<l_load_texture>},
|
{"load_texture", lua::wrap<l_load_texture>},
|
||||||
{"parse_model", lua::wrap<l_parse_model>},
|
{"parse_model", lua::wrap<l_parse_model>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -394,4 +394,5 @@ const luaL_Reg audiolib[] = {
|
||||||
{"get_velocity", lua::wrap<l_audio_get_velocity>},
|
{"get_velocity", lua::wrap<l_audio_get_velocity>},
|
||||||
{"count_speakers", lua::wrap<l_audio_count_speakers>},
|
{"count_speakers", lua::wrap<l_audio_count_speakers>},
|
||||||
{"count_streams", lua::wrap<l_audio_count_streams>},
|
{"count_streams", lua::wrap<l_audio_count_streams>},
|
||||||
{NULL, NULL}};
|
{nullptr, nullptr}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -49,5 +49,5 @@ const luaL_Reg base64lib[] = {
|
||||||
{"decode", lua::wrap<l_decode<util::base64_decode>>},
|
{"decode", lua::wrap<l_decode<util::base64_decode>>},
|
||||||
{"encode_urlsafe", lua::wrap<l_encode<util::base64_urlsafe_encode>>},
|
{"encode_urlsafe", lua::wrap<l_encode<util::base64_urlsafe_encode>>},
|
||||||
{"decode_urlsafe", lua::wrap<l_decode<util::base64_urlsafe_decode>>},
|
{"decode_urlsafe", lua::wrap<l_decode<util::base64_urlsafe_decode>>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -35,5 +35,5 @@ static int l_frombytes(lua::State* L) {
|
||||||
const luaL_Reg bjsonlib[] = {
|
const luaL_Reg bjsonlib[] = {
|
||||||
{"tobytes", lua::wrap<l_tobytes>},
|
{"tobytes", lua::wrap<l_tobytes>},
|
||||||
{"frombytes", lua::wrap<l_frombytes>},
|
{"frombytes", lua::wrap<l_frombytes>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -783,5 +783,5 @@ const luaL_Reg blocklib[] = {
|
||||||
{"has_tag", lua::wrap<l_has_tag>},
|
{"has_tag", lua::wrap<l_has_tag>},
|
||||||
{"__get_tags", lua::wrap<l_get_tags>},
|
{"__get_tags", lua::wrap<l_get_tags>},
|
||||||
{"__pull_register_events", lua::wrap<l_pull_register_events>},
|
{"__pull_register_events", lua::wrap<l_pull_register_events>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -39,5 +39,5 @@ const luaL_Reg blockwrapslib[] = {
|
||||||
{"unwrap", lua::wrap<l_unwrap>},
|
{"unwrap", lua::wrap<l_unwrap>},
|
||||||
{"set_pos", lua::wrap<l_set_pos>},
|
{"set_pos", lua::wrap<l_set_pos>},
|
||||||
{"set_texture", lua::wrap<l_set_texture>},
|
{"set_texture", lua::wrap<l_set_texture>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -208,5 +208,5 @@ const luaL_Reg byteutillib[] = {
|
||||||
{"tpack", lua::wrap<l_tpack>},
|
{"tpack", lua::wrap<l_tpack>},
|
||||||
{"unpack", lua::wrap<l_unpack>},
|
{"unpack", lua::wrap<l_unpack>},
|
||||||
{"get_size", lua::wrap<l_get_size>},
|
{"get_size", lua::wrap<l_get_size>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -125,4 +125,5 @@ const luaL_Reg cameralib[] = {
|
||||||
{"get_right", lua::wrap<l_camera_getter<getter_right>>},
|
{"get_right", lua::wrap<l_camera_getter<getter_right>>},
|
||||||
{"get_up", lua::wrap<l_camera_getter<getter_up>>},
|
{"get_up", lua::wrap<l_camera_getter<getter_up>>},
|
||||||
{"look_at", lua::wrap<l_look_at>},
|
{"look_at", lua::wrap<l_look_at>},
|
||||||
{NULL, NULL}};
|
{nullptr, nullptr}
|
||||||
|
};
|
||||||
|
|
|
||||||
84
src/logic/scripting/lua/libs/libcompression.cpp
Normal file
84
src/logic/scripting/lua/libs/libcompression.cpp
Normal file
|
|
@ -0,0 +1,84 @@
|
||||||
|
#include "api_lua.hpp"
|
||||||
|
#include "coders/gzip.hpp"
|
||||||
|
#include "../lua_engine.hpp"
|
||||||
|
|
||||||
|
static int l_encode(lua::State* L) {
|
||||||
|
char argc = lua::gettop(L);
|
||||||
|
std::vector<unsigned char> compressedBytes;
|
||||||
|
|
||||||
|
std::string algo = "gzip";
|
||||||
|
if (argc >= 2) {
|
||||||
|
if (!lua::isstring(L, 2)) {
|
||||||
|
throw std::runtime_error("compression algorithm must be a string");
|
||||||
|
}
|
||||||
|
algo = lua::require_lstring(L, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (algo == "gzip") {
|
||||||
|
auto str = lua::bytearray_as_string(L, 1);
|
||||||
|
compressedBytes = gzip::compress(
|
||||||
|
reinterpret_cast<const ubyte*>(str.data()),
|
||||||
|
str.size()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("unsupported compression algorithm");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc < 3 || !lua::toboolean(L, 3)) {
|
||||||
|
lua::create_bytearray(L, std::move(compressedBytes));
|
||||||
|
} else {
|
||||||
|
size_t length = compressedBytes.size();
|
||||||
|
lua::createtable(L, length, 0);
|
||||||
|
int newTable = lua::gettop(L);
|
||||||
|
for (size_t i = 0; i < length; i++) {
|
||||||
|
lua::pushinteger(L, compressedBytes.data()[i]);
|
||||||
|
lua::rawseti(L, i + 1, newTable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_decode(lua::State* L) {
|
||||||
|
char argc = lua::gettop(L);
|
||||||
|
std::vector<unsigned char> decompressedBytes;
|
||||||
|
|
||||||
|
std::string algo = "gzip";
|
||||||
|
if (argc >= 2) {
|
||||||
|
if (!lua::isstring(L, 2)) {
|
||||||
|
throw std::runtime_error("compression algorithm must be a string");
|
||||||
|
}
|
||||||
|
algo = lua::require_lstring(L, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (algo == "gzip") {
|
||||||
|
auto str = lua::bytearray_as_string(L, 1);
|
||||||
|
decompressedBytes = gzip::decompress(
|
||||||
|
reinterpret_cast<const ubyte*>(str.data()),
|
||||||
|
str.size()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
throw std::runtime_error("unsupported compression algorithm");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc < 3 || !lua::toboolean(L, 3)) {
|
||||||
|
lua::create_bytearray(L, std::move(decompressedBytes));
|
||||||
|
} else {
|
||||||
|
size_t length = decompressedBytes.size();
|
||||||
|
lua::createtable(L, length, 0);
|
||||||
|
int newTable = lua::gettop(L);
|
||||||
|
for (size_t i = 0; i < length; i++) {
|
||||||
|
lua::pushinteger(L, decompressedBytes.data()[i]);
|
||||||
|
lua::rawseti(L, i + 1, newTable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
const luaL_Reg compressionlib[] = {
|
||||||
|
{"encode", lua::wrap<l_encode>},
|
||||||
|
{"decode", lua::wrap<l_decode>},
|
||||||
|
{nullptr, nullptr}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
@ -126,4 +126,5 @@ const luaL_Reg consolelib[] = {
|
||||||
{"set", lua::wrap<l_set>},
|
{"set", lua::wrap<l_set>},
|
||||||
{"get_commands_list", lua::wrap<l_get_commands_list>},
|
{"get_commands_list", lua::wrap<l_get_commands_list>},
|
||||||
{"get_command_info", lua::wrap<l_get_command_info>},
|
{"get_command_info", lua::wrap<l_get_command_info>},
|
||||||
{NULL, NULL}};
|
{nullptr, nullptr}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,11 @@
|
||||||
#include "util/platform.hpp"
|
#include "util/platform.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "world/generator/WorldGenerator.hpp"
|
#include "world/generator/WorldGenerator.hpp"
|
||||||
|
#include "util/platform.hpp"
|
||||||
|
#include "frontend/locale.hpp"
|
||||||
|
#include "graphics/ui/gui_util.hpp"
|
||||||
|
#include "graphics/ui/GUI.hpp"
|
||||||
|
#include "graphics/ui/elements/Menu.hpp"
|
||||||
|
|
||||||
using namespace scripting;
|
using namespace scripting;
|
||||||
|
|
||||||
|
|
@ -229,6 +234,24 @@ static int l_open_folder(lua::State* L) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_open_url(lua::State* L) {
|
||||||
|
auto url = lua::require_string(L, 1);
|
||||||
|
|
||||||
|
std::wstring msg = langs::get(L"Are you sure you want to open the link:") +
|
||||||
|
L"\n" + util::str2wstr_utf8(url) +
|
||||||
|
std::wstring(L"?");
|
||||||
|
|
||||||
|
auto menu = engine->getGUI().getMenu();
|
||||||
|
|
||||||
|
guiutil::confirm(*engine, msg, [url, menu]() {
|
||||||
|
platform::open_url(url);
|
||||||
|
if (!menu->back()) {
|
||||||
|
menu->reset();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// @brief Quit the game
|
/// @brief Quit the game
|
||||||
static int l_quit(lua::State*) {
|
static int l_quit(lua::State*) {
|
||||||
engine->quit();
|
engine->quit();
|
||||||
|
|
@ -284,7 +307,8 @@ const luaL_Reg corelib[] = {
|
||||||
{"str_setting", lua::wrap<l_str_setting>},
|
{"str_setting", lua::wrap<l_str_setting>},
|
||||||
{"get_setting_info", lua::wrap<l_get_setting_info>},
|
{"get_setting_info", lua::wrap<l_get_setting_info>},
|
||||||
{"open_folder", lua::wrap<l_open_folder>},
|
{"open_folder", lua::wrap<l_open_folder>},
|
||||||
|
{"open_url", lua::wrap<l_open_url>},
|
||||||
{"quit", lua::wrap<l_quit>},
|
{"quit", lua::wrap<l_quit>},
|
||||||
{"capture_output", lua::wrap<l_capture_output>},
|
{"capture_output", lua::wrap<l_capture_output>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -255,5 +255,5 @@ const luaL_Reg entitylib[] = {
|
||||||
{"get_all_in_radius", lua::wrap<l_get_all_in_radius>},
|
{"get_all_in_radius", lua::wrap<l_get_all_in_radius>},
|
||||||
{"raycast", lua::wrap<l_raycast>},
|
{"raycast", lua::wrap<l_raycast>},
|
||||||
{"reload_component", lua::wrap<l_reload_component>},
|
{"reload_component", lua::wrap<l_reload_component>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -188,34 +188,6 @@ static int l_list(lua::State* L) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int l_gzip_compress(lua::State* L) {
|
|
||||||
std::vector<ubyte> bytes;
|
|
||||||
|
|
||||||
lua::read_bytes_from_table(L, 1, bytes);
|
|
||||||
auto compressed_bytes = gzip::compress(bytes.data(), bytes.size());
|
|
||||||
int newTable = lua::gettop(L);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < compressed_bytes.size(); i++) {
|
|
||||||
lua::pushinteger(L, compressed_bytes.data()[i]);
|
|
||||||
lua::rawseti(L, i + 1, newTable);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int l_gzip_decompress(lua::State* L) {
|
|
||||||
std::vector<ubyte> bytes;
|
|
||||||
|
|
||||||
lua::read_bytes_from_table(L, 1, bytes);
|
|
||||||
auto decompressed_bytes = gzip::decompress(bytes.data(), bytes.size());
|
|
||||||
int newTable = lua::gettop(L);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < decompressed_bytes.size(); i++) {
|
|
||||||
lua::pushinteger(L, decompressed_bytes.data()[i]);
|
|
||||||
lua::rawseti(L, i + 1, newTable);
|
|
||||||
}
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int l_read_combined_list(lua::State* L) {
|
static int l_read_combined_list(lua::State* L) {
|
||||||
std::string path = lua::require_string(L, 1);
|
std::string path = lua::require_string(L, 1);
|
||||||
if (path.find(':') != std::string::npos) {
|
if (path.find(':') != std::string::npos) {
|
||||||
|
|
@ -419,8 +391,6 @@ const luaL_Reg filelib[] = {
|
||||||
{"resolve", lua::wrap<l_resolve>},
|
{"resolve", lua::wrap<l_resolve>},
|
||||||
{"write_bytes", lua::wrap<l_write_bytes>},
|
{"write_bytes", lua::wrap<l_write_bytes>},
|
||||||
{"write", lua::wrap<l_write>},
|
{"write", lua::wrap<l_write>},
|
||||||
{"gzip_compress", lua::wrap<l_gzip_compress>},
|
|
||||||
{"gzip_decompress", lua::wrap<l_gzip_decompress>},
|
|
||||||
{"read_combined_list", lua::wrap<l_read_combined_list>},
|
{"read_combined_list", lua::wrap<l_read_combined_list>},
|
||||||
{"read_combined_object", lua::wrap<l_read_combined_object>},
|
{"read_combined_object", lua::wrap<l_read_combined_object>},
|
||||||
{"is_writeable", lua::wrap<l_is_writeable>},
|
{"is_writeable", lua::wrap<l_is_writeable>},
|
||||||
|
|
@ -434,5 +404,6 @@ const luaL_Reg filelib[] = {
|
||||||
{"__flush_descriptor", lua::wrap<l_flush_descriptor>},
|
{"__flush_descriptor", lua::wrap<l_flush_descriptor>},
|
||||||
{"__close_descriptor", lua::wrap<l_close_descriptor>},
|
{"__close_descriptor", lua::wrap<l_close_descriptor>},
|
||||||
{"__close_all_descriptors", lua::wrap<l_close_all_descriptors>},
|
{"__close_all_descriptors", lua::wrap<l_close_all_descriptors>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -104,5 +104,5 @@ const luaL_Reg generationlib[] = {
|
||||||
{"load_fragment", lua::wrap<l_load_fragment>},
|
{"load_fragment", lua::wrap<l_load_fragment>},
|
||||||
{"get_generators", lua::wrap<l_get_generators>},
|
{"get_generators", lua::wrap<l_get_generators>},
|
||||||
{"get_default_generator", lua::wrap<l_get_default_generator>},
|
{"get_default_generator", lua::wrap<l_get_default_generator>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1053,5 +1053,5 @@ const luaL_Reg guilib[] = {
|
||||||
{"alert", lua::wrap<l_gui_alert>},
|
{"alert", lua::wrap<l_gui_alert>},
|
||||||
{"load_document", lua::wrap<l_gui_load_document>},
|
{"load_document", lua::wrap<l_gui_load_document>},
|
||||||
{"__reindex", lua::wrap<l_gui_reindex>},
|
{"__reindex", lua::wrap<l_gui_reindex>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -208,5 +208,5 @@ const luaL_Reg hudlib[] = {
|
||||||
{"_set_debug_cheats", wrap_hud<l_set_debug_cheats>},
|
{"_set_debug_cheats", wrap_hud<l_set_debug_cheats>},
|
||||||
{"set_allow_pause", wrap_hud<l_set_allow_pause>},
|
{"set_allow_pause", wrap_hud<l_set_allow_pause>},
|
||||||
{"reload_script", wrap_hud<l_reload_script>},
|
{"reload_script", wrap_hud<l_reload_script>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -177,5 +177,5 @@ const luaL_Reg inputlib[] = {
|
||||||
{"is_pressed", lua::wrap<l_is_pressed>},
|
{"is_pressed", lua::wrap<l_is_pressed>},
|
||||||
{"reset_bindings", lua::wrap<l_reset_bindings>},
|
{"reset_bindings", lua::wrap<l_reset_bindings>},
|
||||||
{"set_enabled", lua::wrap<l_set_enabled>},
|
{"set_enabled", lua::wrap<l_set_enabled>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -256,5 +256,5 @@ const luaL_Reg inventorylib[] = {
|
||||||
{"create", lua::wrap<l_create>},
|
{"create", lua::wrap<l_create>},
|
||||||
{"remove", lua::wrap<l_remove>},
|
{"remove", lua::wrap<l_remove>},
|
||||||
{"clone", lua::wrap<l_clone>},
|
{"clone", lua::wrap<l_clone>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -147,5 +147,5 @@ const luaL_Reg itemlib[] = {
|
||||||
{"reload_script", lua::wrap<l_reload_script>},
|
{"reload_script", lua::wrap<l_reload_script>},
|
||||||
{"has_tag", lua::wrap<l_has_tag>},
|
{"has_tag", lua::wrap<l_has_tag>},
|
||||||
{"__get_tags", lua::wrap<l_get_tags>},
|
{"__get_tags", lua::wrap<l_get_tags>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -19,5 +19,5 @@ static int l_json_parse(lua::State* L) {
|
||||||
const luaL_Reg jsonlib[] = {
|
const luaL_Reg jsonlib[] = {
|
||||||
{"tostring", lua::wrap<l_json_stringify>},
|
{"tostring", lua::wrap<l_json_stringify>},
|
||||||
{"parse", lua::wrap<l_json_parse>},
|
{"parse", lua::wrap<l_json_parse>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -282,4 +282,5 @@ const luaL_Reg mat4lib[] = {
|
||||||
{"look_at", lua::wrap<l_look_at>},
|
{"look_at", lua::wrap<l_look_at>},
|
||||||
{"from_quat", lua::wrap<l_from_quat>},
|
{"from_quat", lua::wrap<l_from_quat>},
|
||||||
{"tostring", lua::wrap<l_tostring>},
|
{"tostring", lua::wrap<l_tostring>},
|
||||||
{NULL, NULL}};
|
{nullptr, nullptr}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -155,7 +155,7 @@ static int l_post(lua::State* L, network::Network& network) {
|
||||||
|
|
||||||
static int l_close(lua::State* L, network::Network& network) {
|
static int l_close(lua::State* L, network::Network& network) {
|
||||||
u64id_t id = lua::tointeger(L, 1);
|
u64id_t id = lua::tointeger(L, 1);
|
||||||
if (auto connection = network.getConnection(id)) {
|
if (auto connection = network.getConnection(id, false)) {
|
||||||
connection->close(true);
|
connection->close(true);
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -163,7 +163,7 @@ static int l_close(lua::State* L, network::Network& network) {
|
||||||
|
|
||||||
static int l_closeserver(lua::State* L, network::Network& network) {
|
static int l_closeserver(lua::State* L, network::Network& network) {
|
||||||
u64id_t id = lua::tointeger(L, 1);
|
u64id_t id = lua::tointeger(L, 1);
|
||||||
if (auto server = network.getServer(id)) {
|
if (auto server = network.getServer(id, false)) {
|
||||||
server->close();
|
server->close();
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -171,7 +171,7 @@ static int l_closeserver(lua::State* L, network::Network& network) {
|
||||||
|
|
||||||
static int l_send(lua::State* L, network::Network& network) {
|
static int l_send(lua::State* L, network::Network& network) {
|
||||||
u64id_t id = lua::tointeger(L, 1);
|
u64id_t id = lua::tointeger(L, 1);
|
||||||
auto connection = network.getConnection(id);
|
auto connection = network.getConnection(id, false);
|
||||||
if (connection == nullptr ||
|
if (connection == nullptr ||
|
||||||
connection->getState() == network::ConnectionState::CLOSED) {
|
connection->getState() == network::ConnectionState::CLOSED) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -201,7 +201,7 @@ static int l_send(lua::State* L, network::Network& network) {
|
||||||
static int l_udp_server_send_to(lua::State* L, network::Network& network) {
|
static int l_udp_server_send_to(lua::State* L, network::Network& network) {
|
||||||
u64id_t id = lua::tointeger(L, 1);
|
u64id_t id = lua::tointeger(L, 1);
|
||||||
|
|
||||||
if (auto server = network.getServer(id)) {
|
if (auto server = network.getServer(id, false)) {
|
||||||
if (server->getTransportType() != network::TransportType::UDP)
|
if (server->getTransportType() != network::TransportType::UDP)
|
||||||
throw std::runtime_error("the server must work on UDP transport");
|
throw std::runtime_error("the server must work on UDP transport");
|
||||||
|
|
||||||
|
|
@ -238,7 +238,7 @@ static int l_recv(lua::State* L, network::Network& network) {
|
||||||
u64id_t id = lua::tointeger(L, 1);
|
u64id_t id = lua::tointeger(L, 1);
|
||||||
int length = lua::tointeger(L, 2);
|
int length = lua::tointeger(L, 2);
|
||||||
|
|
||||||
auto connection = engine->getNetwork().getConnection(id);
|
auto connection = engine->getNetwork().getConnection(id, false);
|
||||||
|
|
||||||
if (connection == nullptr || connection->getTransportType() != network::TransportType::TCP) {
|
if (connection == nullptr || connection->getTransportType() != network::TransportType::TCP) {
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -268,7 +268,7 @@ static int l_recv(lua::State* L, network::Network& network) {
|
||||||
static int l_available(lua::State* L, network::Network& network) {
|
static int l_available(lua::State* L, network::Network& network) {
|
||||||
u64id_t id = lua::tointeger(L, 1);
|
u64id_t id = lua::tointeger(L, 1);
|
||||||
|
|
||||||
if (auto connection = network.getConnection(id)) {
|
if (auto connection = network.getConnection(id, false)) {
|
||||||
return lua::pushinteger(L, dynamic_cast<network::TcpConnection*>(connection)->available());
|
return lua::pushinteger(L, dynamic_cast<network::TcpConnection*>(connection)->available());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -345,7 +345,7 @@ static int l_open_udp(lua::State* L, network::Network& network) {
|
||||||
|
|
||||||
static int l_is_alive(lua::State* L, network::Network& network) {
|
static int l_is_alive(lua::State* L, network::Network& network) {
|
||||||
u64id_t id = lua::tointeger(L, 1);
|
u64id_t id = lua::tointeger(L, 1);
|
||||||
if (auto connection = network.getConnection(id)) {
|
if (auto connection = network.getConnection(id, false)) {
|
||||||
return lua::pushboolean(
|
return lua::pushboolean(
|
||||||
L,
|
L,
|
||||||
connection->getState() != network::ConnectionState::CLOSED ||
|
connection->getState() != network::ConnectionState::CLOSED ||
|
||||||
|
|
@ -360,7 +360,7 @@ static int l_is_alive(lua::State* L, network::Network& network) {
|
||||||
|
|
||||||
static int l_is_connected(lua::State* L, network::Network& network) {
|
static int l_is_connected(lua::State* L, network::Network& network) {
|
||||||
u64id_t id = lua::tointeger(L, 1);
|
u64id_t id = lua::tointeger(L, 1);
|
||||||
if (auto connection = network.getConnection(id)) {
|
if (auto connection = network.getConnection(id, false)) {
|
||||||
return lua::pushboolean(
|
return lua::pushboolean(
|
||||||
L, connection->getState() == network::ConnectionState::CONNECTED
|
L, connection->getState() == network::ConnectionState::CONNECTED
|
||||||
);
|
);
|
||||||
|
|
@ -370,7 +370,7 @@ static int l_is_connected(lua::State* L, network::Network& network) {
|
||||||
|
|
||||||
static int l_get_address(lua::State* L, network::Network& network) {
|
static int l_get_address(lua::State* L, network::Network& network) {
|
||||||
u64id_t id = lua::tointeger(L, 1);
|
u64id_t id = lua::tointeger(L, 1);
|
||||||
if (auto connection = network.getConnection(id)) {
|
if (auto connection = network.getConnection(id, false)) {
|
||||||
lua::pushstring(L, connection->getAddress());
|
lua::pushstring(L, connection->getAddress());
|
||||||
lua::pushinteger(L, connection->getPort());
|
lua::pushinteger(L, connection->getPort());
|
||||||
return 2;
|
return 2;
|
||||||
|
|
@ -380,7 +380,7 @@ static int l_get_address(lua::State* L, network::Network& network) {
|
||||||
|
|
||||||
static int l_is_serveropen(lua::State* L, network::Network& network) {
|
static int l_is_serveropen(lua::State* L, network::Network& network) {
|
||||||
u64id_t id = lua::tointeger(L, 1);
|
u64id_t id = lua::tointeger(L, 1);
|
||||||
if (auto server = network.getServer(id)) {
|
if (auto server = network.getServer(id, false)) {
|
||||||
return lua::pushboolean(L, server->isOpen());
|
return lua::pushboolean(L, server->isOpen());
|
||||||
}
|
}
|
||||||
return lua::pushboolean(L, false);
|
return lua::pushboolean(L, false);
|
||||||
|
|
@ -388,7 +388,7 @@ static int l_is_serveropen(lua::State* L, network::Network& network) {
|
||||||
|
|
||||||
static int l_get_serverport(lua::State* L, network::Network& network) {
|
static int l_get_serverport(lua::State* L, network::Network& network) {
|
||||||
u64id_t id = lua::tointeger(L, 1);
|
u64id_t id = lua::tointeger(L, 1);
|
||||||
if (auto server = network.getServer(id)) {
|
if (auto server = network.getServer(id, false)) {
|
||||||
return lua::pushinteger(L, server->getPort());
|
return lua::pushinteger(L, server->getPort());
|
||||||
}
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
@ -402,6 +402,28 @@ static int l_get_total_download(lua::State* L, network::Network& network) {
|
||||||
return lua::pushinteger(L, network.getTotalDownload());
|
return lua::pushinteger(L, network.getTotalDownload());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_set_nodelay(lua::State* L, network::Network& network) {
|
||||||
|
u64id_t id = lua::tointeger(L, 1);
|
||||||
|
bool noDelay = lua::toboolean(L, 2);
|
||||||
|
if (auto connection = network.getConnection(id, false)) {
|
||||||
|
if (connection->getTransportType() == network::TransportType::TCP) {
|
||||||
|
dynamic_cast<network::TcpConnection*>(connection)->setNoDelay(noDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_is_nodelay(lua::State* L, network::Network& network) {
|
||||||
|
u64id_t id = lua::tointeger(L, 1);
|
||||||
|
if (auto connection = network.getConnection(id, false)) {
|
||||||
|
if (connection->getTransportType() == network::TransportType::TCP) {
|
||||||
|
bool noDelay = dynamic_cast<network::TcpConnection*>(connection)->isNoDelay();
|
||||||
|
return lua::pushboolean(L, noDelay);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lua::pushboolean(L, false);
|
||||||
|
}
|
||||||
|
|
||||||
static int l_pull_events(lua::State* L, network::Network& network) {
|
static int l_pull_events(lua::State* L, network::Network& network) {
|
||||||
std::vector<NetworkEvent> local_queue;
|
std::vector<NetworkEvent> local_queue;
|
||||||
{
|
{
|
||||||
|
|
@ -517,5 +539,7 @@ const luaL_Reg networklib[] = {
|
||||||
{"__get_address", wrap<l_get_address>},
|
{"__get_address", wrap<l_get_address>},
|
||||||
{"__is_serveropen", wrap<l_is_serveropen>},
|
{"__is_serveropen", wrap<l_is_serveropen>},
|
||||||
{"__get_serverport", wrap<l_get_serverport>},
|
{"__get_serverport", wrap<l_get_serverport>},
|
||||||
{NULL, NULL}
|
{"__set_nodelay", wrap<l_set_nodelay>},
|
||||||
|
{"__is_nodelay", wrap<l_is_nodelay>},
|
||||||
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -268,5 +268,5 @@ const luaL_Reg packlib[] = {
|
||||||
{"get_base_packs", lua::wrap<l_pack_get_base_packs>},
|
{"get_base_packs", lua::wrap<l_pack_get_base_packs>},
|
||||||
{"assemble", lua::wrap<l_pack_assemble>},
|
{"assemble", lua::wrap<l_pack_assemble>},
|
||||||
{"request_writeable", lua::wrap<l_pack_request_writeable>},
|
{"request_writeable", lua::wrap<l_pack_request_writeable>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -82,5 +82,5 @@ const luaL_Reg particleslib[] = {
|
||||||
{"is_alive", wrap_hud<l_is_alive>},
|
{"is_alive", wrap_hud<l_is_alive>},
|
||||||
{"get_origin", wrap_hud<l_get_origin>},
|
{"get_origin", wrap_hud<l_get_origin>},
|
||||||
{"set_origin", wrap_hud<l_set_origin>},
|
{"set_origin", wrap_hud<l_set_origin>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -126,5 +126,5 @@ const luaL_Reg pathfindinglib[] = {
|
||||||
{"set_max_visited", lua::wrap<l_set_max_visited_blocks>},
|
{"set_max_visited", lua::wrap<l_set_max_visited_blocks>},
|
||||||
{"set_jump_height", lua::wrap<l_set_jump_height>},
|
{"set_jump_height", lua::wrap<l_set_jump_height>},
|
||||||
{"avoid_tag", lua::wrap<l_avoid_tag>},
|
{"avoid_tag", lua::wrap<l_avoid_tag>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,14 +2,14 @@
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
|
|
||||||
#include "items/Inventory.hpp"
|
#include "items/Inventory.hpp"
|
||||||
|
#include "libentity.hpp"
|
||||||
#include "objects/Entities.hpp"
|
#include "objects/Entities.hpp"
|
||||||
|
#include "objects/Entity.hpp"
|
||||||
#include "objects/Player.hpp"
|
#include "objects/Player.hpp"
|
||||||
#include "objects/Players.hpp"
|
#include "objects/Players.hpp"
|
||||||
#include "objects/Entity.hpp"
|
|
||||||
#include "physics/Hitbox.hpp"
|
#include "physics/Hitbox.hpp"
|
||||||
#include "window/Camera.hpp"
|
#include "window/Camera.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
#include "libentity.hpp"
|
|
||||||
|
|
||||||
using namespace scripting;
|
using namespace scripting;
|
||||||
|
|
||||||
|
|
@ -53,7 +53,7 @@ static int l_set_vel(lua::State* L) {
|
||||||
auto x = lua::tonumber(L, 2);
|
auto x = lua::tonumber(L, 2);
|
||||||
auto y = lua::tonumber(L, 3);
|
auto y = lua::tonumber(L, 3);
|
||||||
auto z = lua::tonumber(L, 4);
|
auto z = lua::tonumber(L, 4);
|
||||||
|
|
||||||
if (auto hitbox = player->getHitbox()) {
|
if (auto hitbox = player->getHitbox()) {
|
||||||
hitbox->velocity = glm::vec3(x, y, z);
|
hitbox->velocity = glm::vec3(x, y, z);
|
||||||
}
|
}
|
||||||
|
|
@ -181,6 +181,21 @@ static int l_set_loading_chunks(lua::State* L) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_get_interaction_distance(lua::State* L) {
|
||||||
|
if (auto player = get_player(L, 1)) {
|
||||||
|
return lua::pushnumber(L, player->getMaxInteractionDistance());
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_set_interaction_distance(lua::State* L) {
|
||||||
|
if (auto player = get_player(L, 1)) {
|
||||||
|
player->setMaxInteractionDistance(
|
||||||
|
static_cast<float>(lua::tonumber(L, 2)));
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static int l_get_selected_block(lua::State* L) {
|
static int l_get_selected_block(lua::State* L) {
|
||||||
if (auto player = get_player(L, 1)) {
|
if (auto player = get_player(L, 1)) {
|
||||||
if (player->selection.vox.id == BLOCK_VOID) {
|
if (player->selection.vox.id == BLOCK_VOID) {
|
||||||
|
|
@ -307,6 +322,38 @@ static int l_set_suspended(lua::State* L) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int l_get_all_in_radius(lua::State* L) {
|
||||||
|
auto center = lua::tovec3(L, 1);
|
||||||
|
auto radius = static_cast<float>(lua::tonumber(L, 2));
|
||||||
|
|
||||||
|
auto players = level->players->getAllInRadius(center, radius);
|
||||||
|
lua::createtable(L, players.size(), 0);
|
||||||
|
for (size_t i = 0; i < players.size(); i++) {
|
||||||
|
lua::pushinteger(L, players[i]->getId());
|
||||||
|
lua::rawseti(L, i + 1);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_get_all(lua::State* L) {
|
||||||
|
auto players = level->players->getAll();
|
||||||
|
lua::createtable(L, players.size(), 0);
|
||||||
|
for (size_t i = 0; i < players.size(); i++) {
|
||||||
|
lua::pushinteger(L, players[i]->getId());
|
||||||
|
lua::rawseti(L, i + 1);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_get_nearest(lua::State* L) {
|
||||||
|
auto position = lua::tovec3(L, 1);
|
||||||
|
if (auto player = level->players->getNearest(position)) {
|
||||||
|
lua::pushinteger(L, player->getId());
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
const luaL_Reg playerlib[] = {
|
const luaL_Reg playerlib[] = {
|
||||||
{"get_pos", lua::wrap<l_get_pos>},
|
{"get_pos", lua::wrap<l_get_pos>},
|
||||||
{"set_pos", lua::wrap<l_set_pos>},
|
{"set_pos", lua::wrap<l_set_pos>},
|
||||||
|
|
@ -328,6 +375,8 @@ const luaL_Reg playerlib[] = {
|
||||||
{"set_instant_destruction", lua::wrap<l_set_instant_destruction>},
|
{"set_instant_destruction", lua::wrap<l_set_instant_destruction>},
|
||||||
{"is_loading_chunks", lua::wrap<l_is_loading_chunks>},
|
{"is_loading_chunks", lua::wrap<l_is_loading_chunks>},
|
||||||
{"set_loading_chunks", lua::wrap<l_set_loading_chunks>},
|
{"set_loading_chunks", lua::wrap<l_set_loading_chunks>},
|
||||||
|
{"get_interaction_distance", lua::wrap<l_get_interaction_distance>},
|
||||||
|
{"set_interaction_distance", lua::wrap<l_set_interaction_distance>},
|
||||||
{"set_selected_slot", lua::wrap<l_set_selected_slot>},
|
{"set_selected_slot", lua::wrap<l_set_selected_slot>},
|
||||||
{"get_selected_block", lua::wrap<l_get_selected_block>},
|
{"get_selected_block", lua::wrap<l_get_selected_block>},
|
||||||
{"get_selected_entity", lua::wrap<l_get_selected_entity>},
|
{"get_selected_entity", lua::wrap<l_get_selected_entity>},
|
||||||
|
|
@ -341,5 +390,8 @@ const luaL_Reg playerlib[] = {
|
||||||
{"set_name", lua::wrap<l_set_name>},
|
{"set_name", lua::wrap<l_set_name>},
|
||||||
{"create", lua::wrap<l_create>},
|
{"create", lua::wrap<l_create>},
|
||||||
{"delete", lua::wrap<l_delete>},
|
{"delete", lua::wrap<l_delete>},
|
||||||
{NULL, NULL}
|
{"get_all_in_radius", lua::wrap<l_get_all_in_radius>},
|
||||||
|
{"get_all", lua::wrap<l_get_all>},
|
||||||
|
{"get_nearest", lua::wrap<l_get_nearest>},
|
||||||
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -84,5 +84,5 @@ const luaL_Reg posteffectslib[] = {
|
||||||
{"is_active", lua::wrap<l_is_active>},
|
{"is_active", lua::wrap<l_is_active>},
|
||||||
{"set_params", lua::wrap<l_set_params>},
|
{"set_params", lua::wrap<l_set_params>},
|
||||||
{"set_array", lua::wrap<l_set_array>},
|
{"set_array", lua::wrap<l_set_array>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -48,4 +48,5 @@ const luaL_Reg quatlib[] = {
|
||||||
{"from_mat4", lua::wrap<l_from_mat4>},
|
{"from_mat4", lua::wrap<l_from_mat4>},
|
||||||
{"slerp", lua::wrap<l_slerp>},
|
{"slerp", lua::wrap<l_slerp>},
|
||||||
{"tostring", lua::wrap<l_tostring>},
|
{"tostring", lua::wrap<l_tostring>},
|
||||||
{NULL, NULL}};
|
{nullptr, nullptr}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -42,5 +42,5 @@ const luaL_Reg randomlib[] = {
|
||||||
{"random", lua::wrap<l_random>},
|
{"random", lua::wrap<l_random>},
|
||||||
{"bytes", lua::wrap<l_bytes>},
|
{"bytes", lua::wrap<l_bytes>},
|
||||||
{"uuid", lua::wrap<l_uuid>},
|
{"uuid", lua::wrap<l_uuid>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -108,5 +108,5 @@ const luaL_Reg text3dlib[] = {
|
||||||
{"set_axis_y", wrap_hud<l_set_axis_y>},
|
{"set_axis_y", wrap_hud<l_set_axis_y>},
|
||||||
{"set_rotation", wrap_hud<l_set_rotation>},
|
{"set_rotation", wrap_hud<l_set_rotation>},
|
||||||
{"update_settings", wrap_hud<l_update_settings>},
|
{"update_settings", wrap_hud<l_update_settings>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -68,5 +68,5 @@ const luaL_Reg timelib[] = {
|
||||||
{"utc_time", lua::wrap<l_utc_time>},
|
{"utc_time", lua::wrap<l_utc_time>},
|
||||||
{"utc_offset", lua::wrap<l_utc_offset>},
|
{"utc_offset", lua::wrap<l_utc_offset>},
|
||||||
{"local_time", lua::wrap<l_local_time>},
|
{"local_time", lua::wrap<l_local_time>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -23,5 +23,5 @@ static int l_toml_parse(lua::State* L) {
|
||||||
const luaL_Reg tomllib[] = {
|
const luaL_Reg tomllib[] = {
|
||||||
{"tostring", lua::wrap<l_toml_stringify>},
|
{"tostring", lua::wrap<l_toml_stringify>},
|
||||||
{"parse", lua::wrap<l_toml_parse>},
|
{"parse", lua::wrap<l_toml_parse>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -99,5 +99,5 @@ const luaL_Reg utf8lib[] = {
|
||||||
{"lower", lua::wrap<l_lower>},
|
{"lower", lua::wrap<l_lower>},
|
||||||
{"encode", lua::wrap<l_encode>},
|
{"encode", lua::wrap<l_encode>},
|
||||||
{"escape", lua::wrap<l_escape>},
|
{"escape", lua::wrap<l_escape>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -228,7 +228,8 @@ const luaL_Reg vec2lib[] = {
|
||||||
{"angle", lua::wrap<l_vec2_angle>},
|
{"angle", lua::wrap<l_vec2_angle>},
|
||||||
{"mix", lua::wrap<l_mix<2>>},
|
{"mix", lua::wrap<l_mix<2>>},
|
||||||
{"rotate", lua::wrap<l_vec2_rotate>},
|
{"rotate", lua::wrap<l_vec2_rotate>},
|
||||||
{NULL, NULL}};
|
{nullptr, nullptr}
|
||||||
|
};
|
||||||
|
|
||||||
const luaL_Reg vec3lib[] = {
|
const luaL_Reg vec3lib[] = {
|
||||||
{"add", lua::wrap<l_binop<3, std::plus>>},
|
{"add", lua::wrap<l_binop<3, std::plus>>},
|
||||||
|
|
@ -246,7 +247,8 @@ const luaL_Reg vec3lib[] = {
|
||||||
{"dot", lua::wrap<l_dot<3>>},
|
{"dot", lua::wrap<l_dot<3>>},
|
||||||
{"spherical_rand", lua::wrap<l_spherical_rand>},
|
{"spherical_rand", lua::wrap<l_spherical_rand>},
|
||||||
{"mix", lua::wrap<l_mix<3>>},
|
{"mix", lua::wrap<l_mix<3>>},
|
||||||
{NULL, NULL}};
|
{nullptr, nullptr}
|
||||||
|
};
|
||||||
|
|
||||||
const luaL_Reg vec4lib[] = {
|
const luaL_Reg vec4lib[] = {
|
||||||
{"add", lua::wrap<l_binop<4, std::plus>>},
|
{"add", lua::wrap<l_binop<4, std::plus>>},
|
||||||
|
|
@ -263,4 +265,5 @@ const luaL_Reg vec4lib[] = {
|
||||||
{"pow", lua::wrap<l_pow<4>>},
|
{"pow", lua::wrap<l_pow<4>>},
|
||||||
{"dot", lua::wrap<l_dot<4>>},
|
{"dot", lua::wrap<l_dot<4>>},
|
||||||
{"mix", lua::wrap<l_mix<4>>},
|
{"mix", lua::wrap<l_mix<4>>},
|
||||||
{NULL, NULL}};
|
{nullptr, nullptr}
|
||||||
|
};
|
||||||
|
|
|
||||||
|
|
@ -64,5 +64,5 @@ const luaL_Reg weatherlib[] = {
|
||||||
{"get_current_data", lua::wrap<l_get_current_data>},
|
{"get_current_data", lua::wrap<l_get_current_data>},
|
||||||
{"get_fall_intensity", lua::wrap<l_get_fall_intensity>},
|
{"get_fall_intensity", lua::wrap<l_get_fall_intensity>},
|
||||||
{"is_transition", lua::wrap<l_is_transition>},
|
{"is_transition", lua::wrap<l_is_transition>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -251,5 +251,5 @@ const luaL_Reg worldlib[] = {
|
||||||
{"save_chunk_data", lua::wrap<l_save_chunk_data>},
|
{"save_chunk_data", lua::wrap<l_save_chunk_data>},
|
||||||
{"count_chunks", lua::wrap<l_count_chunks>},
|
{"count_chunks", lua::wrap<l_count_chunks>},
|
||||||
{"reload_script", lua::wrap<l_reload_script>},
|
{"reload_script", lua::wrap<l_reload_script>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -16,5 +16,5 @@ static int l_parse(lua::State* L) {
|
||||||
const luaL_Reg yamllib[] = {
|
const luaL_Reg yamllib[] = {
|
||||||
{"tostring", lua::wrap<l_stringify>},
|
{"tostring", lua::wrap<l_stringify>},
|
||||||
{"parse", lua::wrap<l_parse>},
|
{"parse", lua::wrap<l_parse>},
|
||||||
{NULL, NULL}
|
{nullptr, nullptr}
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,7 @@ static void create_libs(State* L, StateType stateType) {
|
||||||
openlib(L, "pack", packlib);
|
openlib(L, "pack", packlib);
|
||||||
openlib(L, "quat", quatlib);
|
openlib(L, "quat", quatlib);
|
||||||
openlib(L, "random", randomlib);
|
openlib(L, "random", randomlib);
|
||||||
|
openlib(L, "compression", compressionlib);
|
||||||
openlib(L, "toml", tomllib);
|
openlib(L, "toml", tomllib);
|
||||||
openlib(L, "utf8", utf8lib);
|
openlib(L, "utf8", utf8lib);
|
||||||
openlib(L, "vec2", vec2lib);
|
openlib(L, "vec2", vec2lib);
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <random>
|
#include <random>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
#include "libs/api_lua.hpp"
|
#include "libs/api_lua.hpp"
|
||||||
#include "debug/Logger.hpp"
|
#include "debug/Logger.hpp"
|
||||||
|
#include "engine/Engine.hpp"
|
||||||
|
#include "devtools/DebuggingServer.hpp"
|
||||||
#include "logic/scripting/scripting.hpp"
|
#include "logic/scripting/scripting.hpp"
|
||||||
|
|
||||||
|
using namespace devtools;
|
||||||
using namespace scripting;
|
using namespace scripting;
|
||||||
|
|
||||||
static debug::Logger logger("lua-debug");
|
static debug::Logger logger("lua-debug");
|
||||||
|
|
@ -159,6 +163,223 @@ static int l_math_normal_random(lua::State* L) {
|
||||||
return lua::pushnumber(L, randomFloats(generator));
|
return lua::pushnumber(L, randomFloats(generator));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
constexpr inline int MAX_SHORT_STRING_LEN = 50;
|
||||||
|
|
||||||
|
static std::string get_short_value(lua::State* L, int idx, int type) {
|
||||||
|
switch (type) {
|
||||||
|
case LUA_TNIL:
|
||||||
|
return "nil";
|
||||||
|
case LUA_TBOOLEAN:
|
||||||
|
return lua::toboolean(L, idx) ? "true" : "false";
|
||||||
|
case LUA_TNUMBER: {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << lua::tonumber(L, idx);
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
case LUA_TSTRING: {
|
||||||
|
const char* str = lua::tostring(L, idx);
|
||||||
|
if (strlen(str) > MAX_SHORT_STRING_LEN) {
|
||||||
|
return std::string(str, MAX_SHORT_STRING_LEN);
|
||||||
|
} else {
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case LUA_TTABLE:
|
||||||
|
return "{...}";
|
||||||
|
case LUA_TFUNCTION: {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "function: 0x" << std::hex
|
||||||
|
<< reinterpret_cast<ptrdiff_t>(lua::topointer(L, idx));
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
case LUA_TUSERDATA: {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "userdata: 0x" << std::hex
|
||||||
|
<< reinterpret_cast<ptrdiff_t>(lua::topointer(L, idx));
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
case LUA_TTHREAD: {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "thread: 0x" << std::hex
|
||||||
|
<< reinterpret_cast<ptrdiff_t>(lua::topointer(L, idx));
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
default: {
|
||||||
|
std::stringstream ss;
|
||||||
|
ss << "cdata: 0x" << std::hex
|
||||||
|
<< reinterpret_cast<ptrdiff_t>(lua::topointer(L, idx));
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static dv::value collect_locals(lua::State* L, lua_Debug& frame) {
|
||||||
|
auto locals = dv::list();
|
||||||
|
|
||||||
|
int localIndex = 1;
|
||||||
|
const char* name;
|
||||||
|
while ((name = lua_getlocal(L, &frame, localIndex++))) {
|
||||||
|
if (name[0] == '(') {
|
||||||
|
lua::pop(L);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto local = dv::object();
|
||||||
|
local["name"] = name;
|
||||||
|
local["index"] = localIndex - 1;
|
||||||
|
|
||||||
|
int type = lua::type(L, -1);
|
||||||
|
local["type"] = lua::type_name(L, type);
|
||||||
|
local["short"] = get_short_value(L, -1, type);
|
||||||
|
locals.add(std::move(local));
|
||||||
|
lua::pop(L);
|
||||||
|
}
|
||||||
|
return locals;
|
||||||
|
}
|
||||||
|
|
||||||
|
static dv::value create_stack_trace(lua::State* L, int initFrame = 2) {
|
||||||
|
auto entriesList = dv::list();
|
||||||
|
|
||||||
|
lua_Debug frame;
|
||||||
|
int level = initFrame;
|
||||||
|
|
||||||
|
while (lua_getstack(L, level, &frame)) {
|
||||||
|
auto entry = dv::object();
|
||||||
|
if (lua_getinfo(L, "nSlf", &frame) == 0) {
|
||||||
|
level++;
|
||||||
|
entriesList.add(std::move(entry));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (frame.name) {
|
||||||
|
entry["function"] = frame.name;
|
||||||
|
}
|
||||||
|
if (frame.source) {
|
||||||
|
const char* src =
|
||||||
|
(frame.source[0] == '@') ? frame.source + 1 : frame.source;
|
||||||
|
entry["source"] = src;
|
||||||
|
entry["line"] = frame.currentline;
|
||||||
|
}
|
||||||
|
entry["what"] = frame.what;
|
||||||
|
entry["locals"] = collect_locals(L, frame);
|
||||||
|
entriesList.add(std::move(entry));
|
||||||
|
level++;
|
||||||
|
}
|
||||||
|
return entriesList;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_debug_pause(lua::State* L) {
|
||||||
|
if (auto server = engine->getDebuggingServer()) {
|
||||||
|
std::string reason;
|
||||||
|
std::string message;
|
||||||
|
if (lua::isstring(L, 1)) {
|
||||||
|
reason = lua::tolstring(L, 1);
|
||||||
|
}
|
||||||
|
if (lua::isstring(L, 2)) {
|
||||||
|
message = lua::tolstring(L, 2);
|
||||||
|
}
|
||||||
|
server->pause(
|
||||||
|
std::move(reason), std::move(message), create_stack_trace(L)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_debug_sendvalue(lua::State* L) {
|
||||||
|
auto server = engine->getDebuggingServer();
|
||||||
|
if (!server) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int frame = lua::tointeger(L, 2);
|
||||||
|
int local = lua::tointeger(L, 3);
|
||||||
|
|
||||||
|
ValuePath path;
|
||||||
|
int pathSectors = lua::objlen(L, 4);
|
||||||
|
for (int i = 0; i < pathSectors; i++) {
|
||||||
|
lua::rawgeti(L, i + 1, 4);
|
||||||
|
if (lua::isstring(L, -1)) {
|
||||||
|
path.emplace_back(lua::tostring(L, -1));
|
||||||
|
} else {
|
||||||
|
path.emplace_back(static_cast<int>(lua::tointeger(L, -1)));
|
||||||
|
}
|
||||||
|
lua::pop(L);
|
||||||
|
}
|
||||||
|
|
||||||
|
dv::value value = nullptr;
|
||||||
|
if (lua::istable(L, 1)) {
|
||||||
|
auto table = dv::object();
|
||||||
|
|
||||||
|
lua::pushnil(L);
|
||||||
|
while (lua::next(L, 1)) {
|
||||||
|
auto key = lua::tolstring(L, -2);
|
||||||
|
|
||||||
|
int type = lua::type(L, -1);
|
||||||
|
table[std::string(key)] = dv::object({
|
||||||
|
{"type", std::string(lua::type_name(L, type))},
|
||||||
|
{"short", get_short_value(L, -1, type)},
|
||||||
|
});
|
||||||
|
lua::pop(L);
|
||||||
|
}
|
||||||
|
lua::pop(L);
|
||||||
|
value = std::move(table);
|
||||||
|
} else {
|
||||||
|
value = lua::tovalue(L, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
server->sendValue(std::move(value), frame, local, std::move(path));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_debug_pull_events(lua::State* L) {
|
||||||
|
auto server = engine->getDebuggingServer();
|
||||||
|
if (!server) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
auto events = server->pullEvents();
|
||||||
|
if (events.empty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
lua::createtable(L, events.size(), 0);
|
||||||
|
for (int i = 0; i < events.size(); i++) {
|
||||||
|
const auto& event = events[i];
|
||||||
|
lua::createtable(L, 3, 0);
|
||||||
|
|
||||||
|
lua::pushinteger(L, static_cast<int>(event.type));
|
||||||
|
lua::rawseti(L, 1);
|
||||||
|
|
||||||
|
if (auto dto = std::get_if<BreakpointEventDto>(&event.data)) {
|
||||||
|
lua::pushstring(L, dto->source);
|
||||||
|
lua::rawseti(L, 2);
|
||||||
|
|
||||||
|
lua::pushinteger(L, dto->line);
|
||||||
|
lua::rawseti(L, 3);
|
||||||
|
} else if (auto dto = std::get_if<GetValueEventDto>(&event.data)) {
|
||||||
|
lua::pushinteger(L, dto->frame);
|
||||||
|
lua::rawseti(L, 2);
|
||||||
|
|
||||||
|
lua::pushinteger(L, dto->localIndex);
|
||||||
|
lua::rawseti(L, 3);
|
||||||
|
|
||||||
|
lua::createtable(L, dto->path.size(), 0);
|
||||||
|
for (int i = 0; i < dto->path.size(); i++) {
|
||||||
|
const auto& segment = dto->path[i];
|
||||||
|
if (auto string = std::get_if<std::string>(&segment)) {
|
||||||
|
lua::pushstring(L, *string);
|
||||||
|
} else {
|
||||||
|
lua::pushinteger(L, std::get<int>(segment));
|
||||||
|
}
|
||||||
|
lua::rawseti(L, i + 1);
|
||||||
|
}
|
||||||
|
lua::rawseti(L, 4);
|
||||||
|
}
|
||||||
|
|
||||||
|
lua::rawseti(L, i + 1);
|
||||||
|
}
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int l_debug_is_debugging(lua::State* L) {
|
||||||
|
return lua::pushboolean(L, engine->getDebuggingServer() != nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
void initialize_libs_extends(lua::State* L) {
|
void initialize_libs_extends(lua::State* L) {
|
||||||
if (lua::getglobal(L, "debug")) {
|
if (lua::getglobal(L, "debug")) {
|
||||||
lua::pushcfunction(L, lua::wrap<l_debug_error>);
|
lua::pushcfunction(L, lua::wrap<l_debug_error>);
|
||||||
|
|
@ -173,6 +394,18 @@ void initialize_libs_extends(lua::State* L) {
|
||||||
lua::pushcfunction(L, lua::wrap<l_debug_print>);
|
lua::pushcfunction(L, lua::wrap<l_debug_print>);
|
||||||
lua::setfield(L, "print");
|
lua::setfield(L, "print");
|
||||||
|
|
||||||
|
lua::pushcfunction(L, lua::wrap<l_debug_pause>);
|
||||||
|
lua::setfield(L, "pause");
|
||||||
|
|
||||||
|
lua::pushcfunction(L, lua::wrap<l_debug_pull_events>);
|
||||||
|
lua::setfield(L, "__pull_events");
|
||||||
|
|
||||||
|
lua::pushcfunction(L, lua::wrap<l_debug_sendvalue>);
|
||||||
|
lua::setfield(L, "__sendvalue");
|
||||||
|
|
||||||
|
lua::pushcfunction(L, lua::wrap<l_debug_is_debugging>);
|
||||||
|
lua::setfield(L, "is_debugging");
|
||||||
|
|
||||||
lua::pop(L);
|
lua::pop(L);
|
||||||
}
|
}
|
||||||
if (lua::getglobal(L, "math")) {
|
if (lua::getglobal(L, "math")) {
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ int l_print(lua::State* L) {
|
||||||
lua::pushvalue(L, i); /* value to print */
|
lua::pushvalue(L, i); /* value to print */
|
||||||
lua::call(L, 1, 1);
|
lua::call(L, 1, 1);
|
||||||
const char* s = lua::tostring(L, -1); /* get result */
|
const char* s = lua::tostring(L, -1); /* get result */
|
||||||
if (s == NULL)
|
if (s == nullptr)
|
||||||
return luaL_error(
|
return luaL_error(
|
||||||
L,
|
L,
|
||||||
LUA_QL("tostring") " must return a string to " LUA_QL("print")
|
LUA_QL("tostring") " must return a string to " LUA_QL("print")
|
||||||
|
|
|
||||||
|
|
@ -249,7 +249,7 @@ namespace lua {
|
||||||
inline lua::Number tonumber(lua::State* L, int idx) {
|
inline lua::Number tonumber(lua::State* L, int idx) {
|
||||||
#ifndef NDEBUG
|
#ifndef NDEBUG
|
||||||
if (lua_type(L, idx) != LUA_TNUMBER && !lua_isnoneornil(L, idx)) {
|
if (lua_type(L, idx) != LUA_TNUMBER && !lua_isnoneornil(L, idx)) {
|
||||||
throw std::runtime_error("integer expected");
|
throw std::runtime_error("number expected");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
return lua_tonumber(L, idx);
|
return lua_tonumber(L, idx);
|
||||||
|
|
@ -617,7 +617,9 @@ namespace lua {
|
||||||
void remove_environment(lua::State*, int id);
|
void remove_environment(lua::State*, int id);
|
||||||
|
|
||||||
inline void close(lua::State* L) {
|
inline void close(lua::State* L) {
|
||||||
lua_close(L);
|
if (L) {
|
||||||
|
lua_close(L);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void addfunc(
|
inline void addfunc(
|
||||||
|
|
|
||||||
|
|
@ -50,8 +50,8 @@ namespace lua {
|
||||||
inline int type(lua::State* L, int idx) {
|
inline int type(lua::State* L, int idx) {
|
||||||
return lua_type(L, idx);
|
return lua_type(L, idx);
|
||||||
}
|
}
|
||||||
inline const char* type_name(lua::State* L, int idx) {
|
inline const char* type_name(lua::State* L, int tp) {
|
||||||
return lua_typename(L, idx);
|
return lua_typename(L, tp);
|
||||||
}
|
}
|
||||||
inline int rawget(lua::State* L, int idx = -2) {
|
inline int rawget(lua::State* L, int idx = -2) {
|
||||||
lua_rawget(L, idx);
|
lua_rawget(L, idx);
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
std::signal(SIGTERM, sigterm_handler);
|
std::signal(SIGTERM, sigterm_handler);
|
||||||
|
|
||||||
debug::Logger::init(coreParameters.userFolder.string()+"/latest.log");
|
debug::Logger::init(coreParameters.userFolder.string() + "/latest.log");
|
||||||
platform::configure_encoding();
|
platform::configure_encoding();
|
||||||
|
|
||||||
auto& engine = Engine::getInstance();
|
auto& engine = Engine::getInstance();
|
||||||
|
|
@ -33,7 +33,8 @@ int main(int argc, char** argv) {
|
||||||
engine.initialize(std::move(coreParameters));
|
engine.initialize(std::move(coreParameters));
|
||||||
engine.run();
|
engine.run();
|
||||||
} catch (const initialize_error& err) {
|
} catch (const initialize_error& err) {
|
||||||
logger.error() << "could not to initialize engine\n" << err.what();
|
logger.error() << err.what();
|
||||||
|
logger.error() << "could not to initialize engine";
|
||||||
}
|
}
|
||||||
#if defined(NDEBUG) and defined(_WIN32)
|
#if defined(NDEBUG) and defined(_WIN32)
|
||||||
catch (const std::exception& err) {
|
catch (const std::exception& err) {
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ namespace util {
|
||||||
PseudoRandom(unsigned short seed) : seed(seed) {}
|
PseudoRandom(unsigned short seed) : seed(seed) {}
|
||||||
|
|
||||||
PseudoRandom() {
|
PseudoRandom() {
|
||||||
seed = static_cast<unsigned short>(time(0));
|
seed = static_cast<unsigned short>(time(nullptr));
|
||||||
}
|
}
|
||||||
|
|
||||||
int rand() {
|
int rand() {
|
||||||
|
|
|
||||||
239
src/network/Curl.cpp
Normal file
239
src/network/Curl.cpp
Normal file
|
|
@ -0,0 +1,239 @@
|
||||||
|
#include "commons.hpp"
|
||||||
|
|
||||||
|
#include "debug/Logger.hpp"
|
||||||
|
|
||||||
|
#define NOMINMAX
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#include <queue>
|
||||||
|
|
||||||
|
using namespace network;
|
||||||
|
|
||||||
|
static debug::Logger logger("curl");
|
||||||
|
|
||||||
|
inline constexpr int HTTP_OK = 200;
|
||||||
|
inline constexpr int HTTP_BAD_GATEWAY = 502;
|
||||||
|
|
||||||
|
static size_t write_callback(
|
||||||
|
char* ptr, size_t size, size_t nmemb, void* userdata
|
||||||
|
) {
|
||||||
|
auto& buffer = *reinterpret_cast<std::vector<char>*>(userdata);
|
||||||
|
size_t psize = buffer.size();
|
||||||
|
buffer.resize(psize + size * nmemb);
|
||||||
|
std::memcpy(buffer.data() + psize, ptr, size * nmemb);
|
||||||
|
return size * nmemb;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class RequestType {
|
||||||
|
GET, POST
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Request {
|
||||||
|
RequestType type;
|
||||||
|
std::string url;
|
||||||
|
OnResponse onResponse;
|
||||||
|
OnReject onReject;
|
||||||
|
long maxSize;
|
||||||
|
bool followLocation = false;
|
||||||
|
std::string data;
|
||||||
|
std::vector<std::string> headers;
|
||||||
|
};
|
||||||
|
|
||||||
|
class CurlRequests : public Requests {
|
||||||
|
CURLM* multiHandle;
|
||||||
|
CURL* curl;
|
||||||
|
|
||||||
|
size_t totalUpload = 0;
|
||||||
|
size_t totalDownload = 0;
|
||||||
|
|
||||||
|
OnResponse onResponse;
|
||||||
|
OnReject onReject;
|
||||||
|
std::vector<char> buffer;
|
||||||
|
std::string url;
|
||||||
|
|
||||||
|
std::queue<Request> requests;
|
||||||
|
public:
|
||||||
|
CurlRequests(CURLM* multiHandle, CURL* curl)
|
||||||
|
: multiHandle(multiHandle), curl(curl) {
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~CurlRequests() {
|
||||||
|
curl_multi_remove_handle(multiHandle, curl);
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
curl_multi_cleanup(multiHandle);
|
||||||
|
}
|
||||||
|
void get(
|
||||||
|
const std::string& url,
|
||||||
|
OnResponse onResponse,
|
||||||
|
OnReject onReject,
|
||||||
|
std::vector<std::string> headers,
|
||||||
|
long maxSize
|
||||||
|
) override {
|
||||||
|
Request request {
|
||||||
|
RequestType::GET,
|
||||||
|
url,
|
||||||
|
onResponse,
|
||||||
|
onReject,
|
||||||
|
maxSize,
|
||||||
|
true,
|
||||||
|
"",
|
||||||
|
std::move(headers)};
|
||||||
|
processRequest(std::move(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
void post(
|
||||||
|
const std::string& url,
|
||||||
|
const std::string& data,
|
||||||
|
OnResponse onResponse,
|
||||||
|
OnReject onReject=nullptr,
|
||||||
|
std::vector<std::string> headers = {},
|
||||||
|
long maxSize=0
|
||||||
|
) override {
|
||||||
|
Request request {
|
||||||
|
RequestType::POST,
|
||||||
|
url,
|
||||||
|
onResponse,
|
||||||
|
onReject,
|
||||||
|
maxSize,
|
||||||
|
false,
|
||||||
|
"",
|
||||||
|
std::move(headers)};
|
||||||
|
request.data = data;
|
||||||
|
processRequest(std::move(request));
|
||||||
|
}
|
||||||
|
|
||||||
|
void processRequest(Request request) {
|
||||||
|
if (!url.empty()) {
|
||||||
|
requests.push(request);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
onResponse = request.onResponse;
|
||||||
|
onReject = request.onReject;
|
||||||
|
url = request.url;
|
||||||
|
|
||||||
|
buffer.clear();
|
||||||
|
|
||||||
|
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POST, request.type == RequestType::POST);
|
||||||
|
|
||||||
|
curl_slist* hs = nullptr;
|
||||||
|
|
||||||
|
for (const auto& header : request.headers) {
|
||||||
|
hs = curl_slist_append(hs, header.c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (request.type) {
|
||||||
|
case RequestType::GET:
|
||||||
|
break;
|
||||||
|
case RequestType::POST:
|
||||||
|
hs = curl_slist_append(hs, "Content-Type: application/json");
|
||||||
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, request.data.length());
|
||||||
|
curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, request.data.c_str());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw std::runtime_error("not implemented");
|
||||||
|
}
|
||||||
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hs);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, request.followLocation);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
|
||||||
|
curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/7.81.0");
|
||||||
|
if (request.maxSize == 0) {
|
||||||
|
curl_easy_setopt(
|
||||||
|
curl, CURLOPT_MAXFILESIZE, std::numeric_limits<long>::max()
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
curl_easy_setopt(curl, CURLOPT_MAXFILESIZE, request.maxSize);
|
||||||
|
}
|
||||||
|
curl_multi_add_handle(multiHandle, curl);
|
||||||
|
int running;
|
||||||
|
CURLMcode res = curl_multi_perform(multiHandle, &running);
|
||||||
|
if (res != CURLM_OK) {
|
||||||
|
auto message = curl_multi_strerror(res);
|
||||||
|
logger.error() << message << " (" << url << ")";
|
||||||
|
if (onReject) {
|
||||||
|
onReject(HTTP_BAD_GATEWAY, {});
|
||||||
|
}
|
||||||
|
url = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void update() override {
|
||||||
|
int messagesLeft;
|
||||||
|
int running;
|
||||||
|
CURLMsg* msg;
|
||||||
|
CURLMcode res = curl_multi_perform(multiHandle, &running);
|
||||||
|
if (res != CURLM_OK) {
|
||||||
|
auto message = curl_multi_strerror(res);
|
||||||
|
logger.error() << message << " (" << url << ")";
|
||||||
|
if (onReject) {
|
||||||
|
onReject(HTTP_BAD_GATEWAY, {});
|
||||||
|
}
|
||||||
|
curl_multi_remove_handle(multiHandle, curl);
|
||||||
|
url = "";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if ((msg = curl_multi_info_read(multiHandle, &messagesLeft)) != nullptr) {
|
||||||
|
if(msg->msg == CURLMSG_DONE) {
|
||||||
|
curl_multi_remove_handle(multiHandle, curl);
|
||||||
|
}
|
||||||
|
int response;
|
||||||
|
curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &response);
|
||||||
|
if (response == HTTP_OK) {
|
||||||
|
long size;
|
||||||
|
if (!curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &size)) {
|
||||||
|
totalUpload += size;
|
||||||
|
}
|
||||||
|
if (!curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &size)) {
|
||||||
|
totalDownload += size;
|
||||||
|
}
|
||||||
|
totalDownload += buffer.size();
|
||||||
|
if (onResponse) {
|
||||||
|
onResponse(std::move(buffer));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
logger.error()
|
||||||
|
<< "response code " << response << " (" << url << ")"
|
||||||
|
<< (buffer.empty()
|
||||||
|
? ""
|
||||||
|
: std::to_string(buffer.size()) + " byte(s)");
|
||||||
|
totalDownload += buffer.size();
|
||||||
|
if (onReject) {
|
||||||
|
onReject(response, std::move(buffer));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
url = "";
|
||||||
|
}
|
||||||
|
if (url.empty() && !requests.empty()) {
|
||||||
|
auto request = std::move(requests.front());
|
||||||
|
requests.pop();
|
||||||
|
processRequest(std::move(request));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getTotalUpload() const override {
|
||||||
|
return totalUpload;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t getTotalDownload() const override {
|
||||||
|
return totalDownload;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::unique_ptr<CurlRequests> create() {
|
||||||
|
auto curl = curl_easy_init();
|
||||||
|
if (curl == nullptr) {
|
||||||
|
throw std::runtime_error("could not initialzie cURL");
|
||||||
|
}
|
||||||
|
auto multiHandle = curl_multi_init();
|
||||||
|
if (multiHandle == nullptr) {
|
||||||
|
curl_easy_cleanup(curl);
|
||||||
|
throw std::runtime_error("could not initialzie cURL-multi");
|
||||||
|
}
|
||||||
|
return std::make_unique<CurlRequests>(multiHandle, curl);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace network {
|
||||||
|
std::unique_ptr<Requests> create_curl_requests() {
|
||||||
|
return CurlRequests::create();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,853 +1,44 @@
|
||||||
#include "Network.hpp"
|
#include "Network.hpp"
|
||||||
|
|
||||||
#pragma comment(lib, "Ws2_32.lib")
|
|
||||||
|
|
||||||
#define NOMINMAX
|
|
||||||
#include <curl/curl.h>
|
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <limits>
|
#include <limits>
|
||||||
#include <queue>
|
#include <queue>
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
#ifdef _WIN32
|
|
||||||
/// included in curl.h
|
|
||||||
#else
|
|
||||||
#include <sys/types.h>
|
|
||||||
#include <sys/socket.h>
|
|
||||||
#include <netdb.h>
|
|
||||||
#include <netinet/in.h>
|
|
||||||
#include <netinet/tcp.h>
|
|
||||||
#include <arpa/inet.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
#include <fcntl.h>
|
|
||||||
#include <errno.h>
|
|
||||||
|
|
||||||
using SOCKET = int;
|
|
||||||
#endif // _WIN32
|
|
||||||
|
|
||||||
#include "debug/Logger.hpp"
|
#include "debug/Logger.hpp"
|
||||||
#include "util/stringutil.hpp"
|
#include "util/stringutil.hpp"
|
||||||
|
|
||||||
using namespace network;
|
using namespace network;
|
||||||
|
|
||||||
inline constexpr int HTTP_OK = 200;
|
|
||||||
inline constexpr int HTTP_BAD_GATEWAY = 502;
|
|
||||||
|
|
||||||
static debug::Logger logger("network");
|
static debug::Logger logger("network");
|
||||||
|
|
||||||
static size_t write_callback(
|
namespace network {
|
||||||
char* ptr, size_t size, size_t nmemb, void* userdata
|
std::unique_ptr<Requests> create_curl_requests();
|
||||||
) {
|
|
||||||
auto& buffer = *reinterpret_cast<std::vector<char>*>(userdata);
|
|
||||||
size_t psize = buffer.size();
|
|
||||||
buffer.resize(psize + size * nmemb);
|
|
||||||
std::memcpy(buffer.data() + psize, ptr, size * nmemb);
|
|
||||||
return size * nmemb;
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class RequestType {
|
std::shared_ptr<TcpConnection> connect_tcp(
|
||||||
GET, POST
|
|
||||||
};
|
|
||||||
|
|
||||||
struct Request {
|
|
||||||
RequestType type;
|
|
||||||
std::string url;
|
|
||||||
OnResponse onResponse;
|
|
||||||
OnReject onReject;
|
|
||||||
long maxSize;
|
|
||||||
bool followLocation = false;
|
|
||||||
std::string data;
|
|
||||||
std::vector<std::string> headers;
|
|
||||||
};
|
|
||||||
|
|
||||||
class CurlRequests : public Requests {
|
|
||||||
CURLM* multiHandle;
|
|
||||||
CURL* curl;
|
|
||||||
|
|
||||||
size_t totalUpload = 0;
|
|
||||||
size_t totalDownload = 0;
|
|
||||||
|
|
||||||
OnResponse onResponse;
|
|
||||||
OnReject onReject;
|
|
||||||
std::vector<char> buffer;
|
|
||||||
std::string url;
|
|
||||||
|
|
||||||
std::queue<Request> requests;
|
|
||||||
public:
|
|
||||||
CurlRequests(CURLM* multiHandle, CURL* curl)
|
|
||||||
: multiHandle(multiHandle), curl(curl) {
|
|
||||||
}
|
|
||||||
|
|
||||||
virtual ~CurlRequests() {
|
|
||||||
curl_multi_remove_handle(multiHandle, curl);
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
curl_multi_cleanup(multiHandle);
|
|
||||||
}
|
|
||||||
void get(
|
|
||||||
const std::string& url,
|
|
||||||
OnResponse onResponse,
|
|
||||||
OnReject onReject,
|
|
||||||
std::vector<std::string> headers,
|
|
||||||
long maxSize
|
|
||||||
) override {
|
|
||||||
Request request {
|
|
||||||
RequestType::GET,
|
|
||||||
url,
|
|
||||||
onResponse,
|
|
||||||
onReject,
|
|
||||||
maxSize,
|
|
||||||
true,
|
|
||||||
"",
|
|
||||||
std::move(headers)};
|
|
||||||
processRequest(std::move(request));
|
|
||||||
}
|
|
||||||
|
|
||||||
void post(
|
|
||||||
const std::string& url,
|
|
||||||
const std::string& data,
|
|
||||||
OnResponse onResponse,
|
|
||||||
OnReject onReject=nullptr,
|
|
||||||
std::vector<std::string> headers = {},
|
|
||||||
long maxSize=0
|
|
||||||
) override {
|
|
||||||
Request request {
|
|
||||||
RequestType::POST,
|
|
||||||
url,
|
|
||||||
onResponse,
|
|
||||||
onReject,
|
|
||||||
maxSize,
|
|
||||||
false,
|
|
||||||
"",
|
|
||||||
std::move(headers)};
|
|
||||||
request.data = data;
|
|
||||||
processRequest(std::move(request));
|
|
||||||
}
|
|
||||||
|
|
||||||
void processRequest(Request request) {
|
|
||||||
if (!url.empty()) {
|
|
||||||
requests.push(request);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
onResponse = request.onResponse;
|
|
||||||
onReject = request.onReject;
|
|
||||||
url = request.url;
|
|
||||||
|
|
||||||
buffer.clear();
|
|
||||||
|
|
||||||
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
|
||||||
curl_easy_setopt(curl, CURLOPT_POST, request.type == RequestType::POST);
|
|
||||||
|
|
||||||
curl_slist* hs = NULL;
|
|
||||||
|
|
||||||
for (const auto& header : request.headers) {
|
|
||||||
hs = curl_slist_append(hs, header.c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (request.type) {
|
|
||||||
case RequestType::GET:
|
|
||||||
break;
|
|
||||||
case RequestType::POST:
|
|
||||||
hs = curl_slist_append(hs, "Content-Type: application/json");
|
|
||||||
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, request.data.length());
|
|
||||||
curl_easy_setopt(curl, CURLOPT_COPYPOSTFIELDS, request.data.c_str());
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw std::runtime_error("not implemented");
|
|
||||||
}
|
|
||||||
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, hs);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, request.followLocation);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &buffer);
|
|
||||||
curl_easy_setopt(curl, CURLOPT_USERAGENT, "curl/7.81.0");
|
|
||||||
if (request.maxSize == 0) {
|
|
||||||
curl_easy_setopt(
|
|
||||||
curl, CURLOPT_MAXFILESIZE, std::numeric_limits<long>::max()
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
curl_easy_setopt(curl, CURLOPT_MAXFILESIZE, request.maxSize);
|
|
||||||
}
|
|
||||||
curl_multi_add_handle(multiHandle, curl);
|
|
||||||
int running;
|
|
||||||
CURLMcode res = curl_multi_perform(multiHandle, &running);
|
|
||||||
if (res != CURLM_OK) {
|
|
||||||
auto message = curl_multi_strerror(res);
|
|
||||||
logger.error() << message << " (" << url << ")";
|
|
||||||
if (onReject) {
|
|
||||||
onReject(HTTP_BAD_GATEWAY, {});
|
|
||||||
}
|
|
||||||
url = "";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void update() override {
|
|
||||||
int messagesLeft;
|
|
||||||
int running;
|
|
||||||
CURLMsg* msg;
|
|
||||||
CURLMcode res = curl_multi_perform(multiHandle, &running);
|
|
||||||
if (res != CURLM_OK) {
|
|
||||||
auto message = curl_multi_strerror(res);
|
|
||||||
logger.error() << message << " (" << url << ")";
|
|
||||||
if (onReject) {
|
|
||||||
onReject(HTTP_BAD_GATEWAY, {});
|
|
||||||
}
|
|
||||||
curl_multi_remove_handle(multiHandle, curl);
|
|
||||||
url = "";
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if ((msg = curl_multi_info_read(multiHandle, &messagesLeft)) != NULL) {
|
|
||||||
if(msg->msg == CURLMSG_DONE) {
|
|
||||||
curl_multi_remove_handle(multiHandle, curl);
|
|
||||||
}
|
|
||||||
int response;
|
|
||||||
curl_easy_getinfo(msg->easy_handle, CURLINFO_RESPONSE_CODE, &response);
|
|
||||||
if (response == HTTP_OK) {
|
|
||||||
long size;
|
|
||||||
if (!curl_easy_getinfo(curl, CURLINFO_REQUEST_SIZE, &size)) {
|
|
||||||
totalUpload += size;
|
|
||||||
}
|
|
||||||
if (!curl_easy_getinfo(curl, CURLINFO_HEADER_SIZE, &size)) {
|
|
||||||
totalDownload += size;
|
|
||||||
}
|
|
||||||
totalDownload += buffer.size();
|
|
||||||
if (onResponse) {
|
|
||||||
onResponse(std::move(buffer));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
logger.error()
|
|
||||||
<< "response code " << response << " (" << url << ")"
|
|
||||||
<< (buffer.empty()
|
|
||||||
? ""
|
|
||||||
: std::to_string(buffer.size()) + " byte(s)");
|
|
||||||
totalDownload += buffer.size();
|
|
||||||
if (onReject) {
|
|
||||||
onReject(response, std::move(buffer));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
url = "";
|
|
||||||
}
|
|
||||||
if (url.empty() && !requests.empty()) {
|
|
||||||
auto request = std::move(requests.front());
|
|
||||||
requests.pop();
|
|
||||||
processRequest(std::move(request));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t getTotalUpload() const override {
|
|
||||||
return totalUpload;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t getTotalDownload() const override {
|
|
||||||
return totalDownload;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::unique_ptr<CurlRequests> create() {
|
|
||||||
auto curl = curl_easy_init();
|
|
||||||
if (curl == nullptr) {
|
|
||||||
throw std::runtime_error("could not initialzie cURL");
|
|
||||||
}
|
|
||||||
auto multiHandle = curl_multi_init();
|
|
||||||
if (multiHandle == nullptr) {
|
|
||||||
curl_easy_cleanup(curl);
|
|
||||||
throw std::runtime_error("could not initialzie cURL-multi");
|
|
||||||
}
|
|
||||||
return std::make_unique<CurlRequests>(multiHandle, curl);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
|
||||||
static inline int closesocket(int descriptor) noexcept {
|
|
||||||
return close(descriptor);
|
|
||||||
}
|
|
||||||
static inline std::runtime_error handle_socket_error(const std::string& message) {
|
|
||||||
int err = errno;
|
|
||||||
return std::runtime_error(
|
|
||||||
message+" [errno=" + std::to_string(err) + "]: " +
|
|
||||||
std::string(strerror(err))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
static inline std::runtime_error handle_socket_error(const std::string& message) {
|
|
||||||
int errorCode = WSAGetLastError();
|
|
||||||
wchar_t* s = nullptr;
|
|
||||||
size_t size = FormatMessageW(
|
|
||||||
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
|
||||||
FORMAT_MESSAGE_IGNORE_INSERTS,
|
|
||||||
nullptr,
|
|
||||||
errorCode,
|
|
||||||
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
|
||||||
(LPWSTR)&s,
|
|
||||||
0,
|
|
||||||
nullptr
|
|
||||||
);
|
|
||||||
assert(s != nullptr);
|
|
||||||
while (size && isspace(s[size-1])) {
|
|
||||||
s[--size] = 0;
|
|
||||||
}
|
|
||||||
auto errorString = util::wstr2str_utf8(std::wstring(s));
|
|
||||||
LocalFree(s);
|
|
||||||
return std::runtime_error(message+" [WSA error=" +
|
|
||||||
std::to_string(errorCode) + "]: "+errorString);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static inline int connectsocket(
|
|
||||||
int descriptor, const sockaddr* addr, socklen_t len
|
|
||||||
) noexcept {
|
|
||||||
return connect(descriptor, addr, len);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int recvsocket(
|
|
||||||
int descriptor, char* buf, size_t len
|
|
||||||
) noexcept {
|
|
||||||
return recv(descriptor, buf, len, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
static inline int sendsocket(
|
|
||||||
int descriptor, const char* buf, size_t len, int flags
|
|
||||||
) noexcept {
|
|
||||||
return send(descriptor, buf, len, flags);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::string to_string(const sockaddr_in& addr, bool port=true) {
|
|
||||||
char ip[INET_ADDRSTRLEN];
|
|
||||||
if (inet_ntop(AF_INET, &(addr.sin_addr), ip, INET_ADDRSTRLEN)) {
|
|
||||||
return std::string(ip) +
|
|
||||||
(port ? (":" + std::to_string(htons(addr.sin_port))) : "");
|
|
||||||
}
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
|
|
||||||
class SocketTcpConnection : public TcpConnection {
|
|
||||||
SOCKET descriptor;
|
|
||||||
sockaddr_in addr;
|
|
||||||
size_t totalUpload = 0;
|
|
||||||
size_t totalDownload = 0;
|
|
||||||
ConnectionState state = ConnectionState::INITIAL;
|
|
||||||
std::unique_ptr<std::thread> thread = nullptr;
|
|
||||||
std::vector<char> readBatch;
|
|
||||||
util::Buffer<char> buffer;
|
|
||||||
std::mutex mutex;
|
|
||||||
|
|
||||||
void connectSocket() {
|
|
||||||
state = ConnectionState::CONNECTING;
|
|
||||||
logger.info() << "connecting to " << to_string(addr);
|
|
||||||
int res = connectsocket(descriptor, (const sockaddr*)&addr, sizeof(sockaddr_in));
|
|
||||||
if (res < 0) {
|
|
||||||
auto error = handle_socket_error("Connect failed");
|
|
||||||
closesocket(descriptor);
|
|
||||||
state = ConnectionState::CLOSED;
|
|
||||||
logger.error() << error.what();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logger.info() << "connected to " << to_string(addr);
|
|
||||||
state = ConnectionState::CONNECTED;
|
|
||||||
}
|
|
||||||
public:
|
|
||||||
SocketTcpConnection(SOCKET descriptor, sockaddr_in addr)
|
|
||||||
: descriptor(descriptor), addr(std::move(addr)), buffer(16'384) {}
|
|
||||||
|
|
||||||
~SocketTcpConnection() {
|
|
||||||
if (state != ConnectionState::CLOSED) {
|
|
||||||
shutdown(descriptor, 2);
|
|
||||||
}
|
|
||||||
if (thread) {
|
|
||||||
thread->join();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void startListen() {
|
|
||||||
while (state == ConnectionState::CONNECTED) {
|
|
||||||
int size = recvsocket(descriptor, buffer.data(), buffer.size());
|
|
||||||
if (size == 0) {
|
|
||||||
logger.info() << "closed connection with " << to_string(addr);
|
|
||||||
closesocket(descriptor);
|
|
||||||
state = ConnectionState::CLOSED;
|
|
||||||
break;
|
|
||||||
} else if (size < 0) {
|
|
||||||
logger.warning() << "an error ocurred while receiving from "
|
|
||||||
<< to_string(addr);
|
|
||||||
auto error = handle_socket_error("recv(...) error");
|
|
||||||
closesocket(descriptor);
|
|
||||||
state = ConnectionState::CLOSED;
|
|
||||||
logger.error() << error.what();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
std::lock_guard lock(mutex);
|
|
||||||
for (size_t i = 0; i < size; i++) {
|
|
||||||
readBatch.emplace_back(buffer[i]);
|
|
||||||
}
|
|
||||||
totalDownload += size;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void startClient() {
|
|
||||||
state = ConnectionState::CONNECTED;
|
|
||||||
thread = std::make_unique<std::thread>([this]() { startListen();});
|
|
||||||
}
|
|
||||||
|
|
||||||
void connect(runnable callback) override {
|
|
||||||
thread = std::make_unique<std::thread>([this, callback]() {
|
|
||||||
connectSocket();
|
|
||||||
if (state == ConnectionState::CONNECTED) {
|
|
||||||
callback();
|
|
||||||
}
|
|
||||||
startListen();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
int recv(char* buffer, size_t length) override {
|
|
||||||
std::lock_guard lock(mutex);
|
|
||||||
|
|
||||||
if (state != ConnectionState::CONNECTED && readBatch.empty()) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
int size = std::min(readBatch.size(), length);
|
|
||||||
std::memcpy(buffer, readBatch.data(), size);
|
|
||||||
readBatch.erase(readBatch.begin(), readBatch.begin() + size);
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
int send(const char* buffer, size_t length) override {
|
|
||||||
if (state == ConnectionState::CLOSED) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
int len = sendsocket(descriptor, buffer, length, 0);
|
|
||||||
if (len == -1) {
|
|
||||||
int err = errno;
|
|
||||||
close();
|
|
||||||
throw std::runtime_error(
|
|
||||||
"Send failed [errno=" + std::to_string(err) + "]: "
|
|
||||||
+ std::string(strerror(err))
|
|
||||||
);
|
|
||||||
}
|
|
||||||
totalUpload += len;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
int available() override {
|
|
||||||
std::lock_guard lock(mutex);
|
|
||||||
return readBatch.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
void close(bool discardAll=false) override {
|
|
||||||
{
|
|
||||||
std::lock_guard lock(mutex);
|
|
||||||
readBatch.clear();
|
|
||||||
|
|
||||||
if (state != ConnectionState::CLOSED) {
|
|
||||||
shutdown(descriptor, 2);
|
|
||||||
closesocket(descriptor);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (thread) {
|
|
||||||
thread->join();
|
|
||||||
thread = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t pullUpload() override {
|
|
||||||
size_t size = totalUpload;
|
|
||||||
totalUpload = 0;
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t pullDownload() override {
|
|
||||||
size_t size = totalDownload;
|
|
||||||
totalDownload = 0;
|
|
||||||
return size;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getPort() const override {
|
|
||||||
return htons(addr.sin_port);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string getAddress() const override {
|
|
||||||
return to_string(addr, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::shared_ptr<SocketTcpConnection> connect(
|
|
||||||
const std::string& address, int port, runnable callback
|
const std::string& address, int port, runnable callback
|
||||||
) {
|
);
|
||||||
addrinfo hints {};
|
|
||||||
|
|
||||||
hints.ai_family = AF_INET;
|
std::shared_ptr<TcpServer> open_tcp_server(
|
||||||
hints.ai_socktype = SOCK_STREAM;
|
|
||||||
|
|
||||||
addrinfo* addrinfo = nullptr;
|
|
||||||
if (int res = getaddrinfo(
|
|
||||||
address.c_str(), nullptr, &hints, &addrinfo
|
|
||||||
)) {
|
|
||||||
throw std::runtime_error(gai_strerror(res));
|
|
||||||
}
|
|
||||||
|
|
||||||
sockaddr_in serverAddress;
|
|
||||||
std::memcpy(&serverAddress, addrinfo->ai_addr, sizeof(sockaddr_in));
|
|
||||||
serverAddress.sin_port = htons(port);
|
|
||||||
freeaddrinfo(addrinfo);
|
|
||||||
|
|
||||||
SOCKET descriptor = socket(AF_INET, SOCK_STREAM, 0);
|
|
||||||
if (descriptor == -1) {
|
|
||||||
throw std::runtime_error("Could not create socket");
|
|
||||||
}
|
|
||||||
auto socket = std::make_shared<SocketTcpConnection>(descriptor, std::move(serverAddress));
|
|
||||||
socket->connect(std::move(callback));
|
|
||||||
return socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
ConnectionState getState() const override {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class SocketTcpServer : public TcpServer {
|
|
||||||
u64id_t id;
|
|
||||||
Network* network;
|
|
||||||
SOCKET descriptor;
|
|
||||||
std::vector<u64id_t> clients;
|
|
||||||
std::mutex clientsMutex;
|
|
||||||
bool open = true;
|
|
||||||
std::unique_ptr<std::thread> thread = nullptr;
|
|
||||||
int port;
|
|
||||||
public:
|
|
||||||
SocketTcpServer(u64id_t id, Network* network, SOCKET descriptor, int port)
|
|
||||||
: id(id), network(network), descriptor(descriptor), port(port) {}
|
|
||||||
|
|
||||||
~SocketTcpServer() {
|
|
||||||
closeSocket();
|
|
||||||
}
|
|
||||||
|
|
||||||
void startListen(ConnectCallback handler) override {
|
|
||||||
thread = std::make_unique<std::thread>([this, handler]() {
|
|
||||||
while (open) {
|
|
||||||
logger.info() << "listening for connections";
|
|
||||||
if (listen(descriptor, 2) < 0) {
|
|
||||||
close();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
socklen_t addrlen = sizeof(sockaddr_in);
|
|
||||||
SOCKET clientDescriptor;
|
|
||||||
sockaddr_in address;
|
|
||||||
logger.info() << "accepting clients";
|
|
||||||
if ((clientDescriptor = accept(descriptor, (sockaddr*)&address, &addrlen)) == -1) {
|
|
||||||
close();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
logger.info() << "client connected: " << to_string(address);
|
|
||||||
auto socket = std::make_shared<SocketTcpConnection>(
|
|
||||||
clientDescriptor, address
|
|
||||||
);
|
|
||||||
socket->startClient();
|
|
||||||
u64id_t id = network->addConnection(socket);
|
|
||||||
{
|
|
||||||
std::lock_guard lock(clientsMutex);
|
|
||||||
clients.push_back(id);
|
|
||||||
}
|
|
||||||
handler(this->id, id);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void closeSocket() {
|
|
||||||
if (!open) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
logger.info() << "closing server";
|
|
||||||
open = false;
|
|
||||||
|
|
||||||
{
|
|
||||||
std::lock_guard lock(clientsMutex);
|
|
||||||
for (u64id_t clientid : clients) {
|
|
||||||
if (auto client = network->getConnection(clientid)) {
|
|
||||||
client->close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
clients.clear();
|
|
||||||
|
|
||||||
shutdown(descriptor, 2);
|
|
||||||
closesocket(descriptor);
|
|
||||||
thread->join();
|
|
||||||
}
|
|
||||||
|
|
||||||
void close() override {
|
|
||||||
closeSocket();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isOpen() override {
|
|
||||||
return open;
|
|
||||||
}
|
|
||||||
|
|
||||||
int getPort() const override {
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::shared_ptr<SocketTcpServer> openServer(
|
|
||||||
u64id_t id, Network* network, int port, ConnectCallback handler
|
u64id_t id, Network* network, int port, ConnectCallback handler
|
||||||
) {
|
);
|
||||||
SOCKET descriptor = socket(
|
|
||||||
AF_INET, SOCK_STREAM, 0
|
|
||||||
);
|
|
||||||
if (descriptor == -1) {
|
|
||||||
throw std::runtime_error("Could not create server socket");
|
|
||||||
}
|
|
||||||
int opt = 1;
|
|
||||||
int flags = SO_REUSEADDR;
|
|
||||||
# if !defined(_WIN32) && !defined(__APPLE__)
|
|
||||||
flags |= SO_REUSEPORT;
|
|
||||||
# endif
|
|
||||||
if (setsockopt(descriptor, SOL_SOCKET, flags, (const char*)&opt, sizeof(opt))) {
|
|
||||||
logger.error() << "setsockopt(SO_REUSEADDR) failed with errno: "
|
|
||||||
<< errno << "(" << std::strerror(errno) << ")";
|
|
||||||
closesocket(descriptor);
|
|
||||||
throw std::runtime_error("setsockopt");
|
|
||||||
}
|
|
||||||
sockaddr_in address;
|
|
||||||
address.sin_family = AF_INET;
|
|
||||||
address.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
address.sin_port = htons(port);
|
|
||||||
if (bind(descriptor, (sockaddr*)&address, sizeof(address)) < 0) {
|
|
||||||
closesocket(descriptor);
|
|
||||||
throw std::runtime_error("could not bind port "+std::to_string(port));
|
|
||||||
}
|
|
||||||
logger.info() << "opened server at port " << port;
|
|
||||||
auto server =
|
|
||||||
std::make_shared<SocketTcpServer>(id, network, descriptor, port);
|
|
||||||
server->startListen(std::move(handler));
|
|
||||||
return server;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
static sockaddr_in resolve_address_dgram(const std::string& address, int port) {
|
std::shared_ptr<UdpConnection> connect_udp(
|
||||||
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;
|
|
||||||
sockaddr_in addr{};
|
|
||||||
bool open = true;
|
|
||||||
std::unique_ptr<std::thread> thread;
|
|
||||||
ClientDatagramCallback callback;
|
|
||||||
|
|
||||||
size_t totalUpload = 0;
|
|
||||||
size_t totalDownload = 0;
|
|
||||||
ConnectionState state = ConnectionState::INITIAL;
|
|
||||||
|
|
||||||
public:
|
|
||||||
SocketUdpConnection(u64id_t id, SOCKET descriptor, sockaddr_in addr)
|
|
||||||
: id(id), descriptor(descriptor), addr(std::move(addr)) {}
|
|
||||||
|
|
||||||
~SocketUdpConnection() override {
|
|
||||||
SocketUdpConnection::close();
|
|
||||||
}
|
|
||||||
|
|
||||||
static std::shared_ptr<SocketUdpConnection> connect(
|
|
||||||
u64id_t id,
|
u64id_t id,
|
||||||
const std::string& address,
|
const std::string& address,
|
||||||
int port,
|
int port,
|
||||||
ClientDatagramCallback handler,
|
ClientDatagramCallback handler,
|
||||||
runnable callback
|
runnable callback
|
||||||
) {
|
);
|
||||||
SOCKET descriptor = socket(AF_INET, SOCK_DGRAM, 0);
|
|
||||||
if (descriptor == -1) {
|
|
||||||
throw std::runtime_error("could not create udp socket");
|
|
||||||
}
|
|
||||||
|
|
||||||
sockaddr_in serverAddr = resolve_address_dgram(address, port);
|
std::shared_ptr<UdpServer> open_udp_server(
|
||||||
|
u64id_t id,
|
||||||
|
Network* network,
|
||||||
|
int port,
|
||||||
|
const ServerDatagramCallback& handler
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (::connect(descriptor, (sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
|
|
||||||
auto err = handle_socket_error("udp connect failed");
|
|
||||||
closesocket(descriptor);
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
|
|
||||||
auto socket = std::make_shared<SocketUdpConnection>(id, descriptor, serverAddr);
|
|
||||||
socket->connect(std::move(handler));
|
|
||||||
|
|
||||||
callback();
|
|
||||||
|
|
||||||
return socket;
|
|
||||||
}
|
|
||||||
|
|
||||||
void connect(ClientDatagramCallback handler) override {
|
|
||||||
callback = std::move(handler);
|
|
||||||
state = ConnectionState::CONNECTED;
|
|
||||||
|
|
||||||
thread = std::make_unique<std::thread>([this]() {
|
|
||||||
util::Buffer<char> buffer(16'384);
|
|
||||||
while (open) {
|
|
||||||
int size = recv(descriptor, buffer.data(), buffer.size(), 0);
|
|
||||||
if (size <= 0) {
|
|
||||||
logger.error() << "udp connection " << id
|
|
||||||
<< handle_socket_error(" recv error").what();
|
|
||||||
if (!open) break;
|
|
||||||
closesocket(descriptor);
|
|
||||||
state = ConnectionState::CLOSED;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
totalDownload += size;
|
|
||||||
if (callback) {
|
|
||||||
callback(id, buffer.data(), size);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
int send(const char* buffer, size_t length) override {
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
void close(bool discardAll=false) override {
|
|
||||||
if (!open) return;
|
|
||||||
open = false;
|
|
||||||
logger.info() << "closing udp connection "<< id;
|
|
||||||
|
|
||||||
if (state != ConnectionState::CLOSED) {
|
|
||||||
shutdown(descriptor, 2);
|
|
||||||
closesocket(descriptor);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (thread) {
|
|
||||||
thread->join();
|
|
||||||
thread.reset();
|
|
||||||
}
|
|
||||||
state = ConnectionState::CLOSED;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t pullUpload() override {
|
|
||||||
size_t s = totalUpload;
|
|
||||||
totalUpload = 0;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t pullDownload() override {
|
|
||||||
size_t s = totalDownload;
|
|
||||||
totalDownload = 0;
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] int getPort() const override {
|
|
||||||
return ntohs(addr.sin_port);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] std::string getAddress() const override {
|
|
||||||
return to_string(addr, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] ConnectionState getState() const override {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class SocketUdpServer : public UdpServer {
|
|
||||||
u64id_t id;
|
|
||||||
SOCKET descriptor;
|
|
||||||
bool open = true;
|
|
||||||
std::unique_ptr<std::thread> thread = nullptr;
|
|
||||||
int port;
|
|
||||||
ServerDatagramCallback callback;
|
|
||||||
|
|
||||||
public:
|
|
||||||
SocketUdpServer(u64id_t id, Network* network, SOCKET descriptor, int port)
|
|
||||||
: id(id), descriptor(descriptor), port(port) {}
|
|
||||||
|
|
||||||
~SocketUdpServer() override {
|
|
||||||
SocketUdpServer::close();
|
|
||||||
}
|
|
||||||
|
|
||||||
void startListen(ServerDatagramCallback handler) override {
|
|
||||||
callback = std::move(handler);
|
|
||||||
|
|
||||||
thread = std::make_unique<std::thread>([this]() {
|
|
||||||
util::Buffer<char> buffer(16384);
|
|
||||||
sockaddr_in clientAddr{};
|
|
||||||
socklen_t addrlen = sizeof(clientAddr);
|
|
||||||
|
|
||||||
while (open) {
|
|
||||||
int size = recvfrom(descriptor, buffer.data(), buffer.size(), 0,
|
|
||||||
reinterpret_cast<sockaddr*>(&clientAddr), &addrlen);
|
|
||||||
if (size <= 0) {
|
|
||||||
if (!open) break;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string addrStr = to_string(clientAddr, false);
|
|
||||||
int port = ntohs(clientAddr.sin_port);
|
|
||||||
|
|
||||||
callback(id, addrStr, port, buffer.data(), size);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
void sendTo(const std::string& addr, int port, const char* buffer, size_t length) override {
|
|
||||||
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 {
|
|
||||||
if (!open) return;
|
|
||||||
open = false;
|
|
||||||
shutdown(descriptor, 2);
|
|
||||||
closesocket(descriptor);
|
|
||||||
if (thread) {
|
|
||||||
thread->join();
|
|
||||||
thread = nullptr;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isOpen() override { return open; }
|
|
||||||
int getPort() const override { return port; }
|
|
||||||
|
|
||||||
static std::shared_ptr<SocketUdpServer> openServer(
|
|
||||||
u64id_t id, Network* network, int port, const ServerDatagramCallback& handler
|
|
||||||
) {
|
|
||||||
SOCKET descriptor = socket(AF_INET, SOCK_DGRAM, 0);
|
|
||||||
if (descriptor == -1) throw std::runtime_error("could not create udp socket");
|
|
||||||
|
|
||||||
sockaddr_in address{};
|
|
||||||
address.sin_family = AF_INET;
|
|
||||||
address.sin_addr.s_addr = INADDR_ANY;
|
|
||||||
address.sin_port = htons(port);
|
|
||||||
|
|
||||||
if (bind(descriptor, (sockaddr*)&address, sizeof(address)) < 0) {
|
|
||||||
closesocket(descriptor);
|
|
||||||
throw std::runtime_error("could not bind udp port " + std::to_string(port));
|
|
||||||
}
|
|
||||||
|
|
||||||
auto server = std::make_shared<SocketUdpServer>(id, network, descriptor, port);
|
|
||||||
server->startListen(std::move(handler));
|
|
||||||
return server;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
Network::Network(std::unique_ptr<Requests> requests)
|
Network::Network(std::unique_ptr<Requests> requests)
|
||||||
: requests(std::move(requests)) {
|
: requests(std::move(requests)) {
|
||||||
|
|
@ -878,19 +69,19 @@ void Network::post(
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Connection* Network::getConnection(u64id_t id) {
|
Connection* Network::getConnection(u64id_t id, bool includePrivate) {
|
||||||
std::lock_guard lock(connectionsMutex);
|
std::lock_guard lock(connectionsMutex);
|
||||||
|
|
||||||
const auto& found = connections.find(id);
|
const auto& found = connections.find(id);
|
||||||
if (found == connections.end()) {
|
if (found == connections.end() || (!includePrivate && found->second->isPrivate())) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return found->second.get();
|
return found->second.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
Server* Network::getServer(u64id_t id) const {
|
Server* Network::getServer(u64id_t id, bool includePrivate) const {
|
||||||
const auto& found = servers.find(id);
|
const auto& found = servers.find(id);
|
||||||
if (found == servers.end()) {
|
if (found == servers.end() || (!includePrivate && found->second->isPrivate())) {
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
return found->second.get();
|
return found->second.get();
|
||||||
|
|
@ -900,7 +91,7 @@ u64id_t Network::connectTcp(const std::string& address, int port, consumer<u64id
|
||||||
std::lock_guard lock(connectionsMutex);
|
std::lock_guard lock(connectionsMutex);
|
||||||
|
|
||||||
u64id_t id = nextConnection++;
|
u64id_t id = nextConnection++;
|
||||||
auto socket = SocketTcpConnection::connect(address, port, [id, callback]() {
|
auto socket = connect_tcp(address, port, [id, callback]() {
|
||||||
callback(id);
|
callback(id);
|
||||||
});
|
});
|
||||||
connections[id] = std::move(socket);
|
connections[id] = std::move(socket);
|
||||||
|
|
@ -909,16 +100,21 @@ u64id_t Network::connectTcp(const std::string& address, int port, consumer<u64id
|
||||||
|
|
||||||
u64id_t Network::openTcpServer(int port, ConnectCallback handler) {
|
u64id_t Network::openTcpServer(int port, ConnectCallback handler) {
|
||||||
u64id_t id = nextServer++;
|
u64id_t id = nextServer++;
|
||||||
auto server = SocketTcpServer::openServer(id, this, port, handler);
|
auto server = open_tcp_server(id, this, port, handler);
|
||||||
servers[id] = std::move(server);
|
servers[id] = std::move(server);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
u64id_t Network::connectUdp(const std::string& address, int port, const consumer<u64id_t>& callback, ClientDatagramCallback handler) {
|
u64id_t Network::connectUdp(
|
||||||
|
const std::string& address,
|
||||||
|
int port,
|
||||||
|
const consumer<u64id_t>& callback,
|
||||||
|
ClientDatagramCallback handler
|
||||||
|
) {
|
||||||
std::lock_guard lock(connectionsMutex);
|
std::lock_guard lock(connectionsMutex);
|
||||||
|
|
||||||
u64id_t id = nextConnection++;
|
u64id_t id = nextConnection++;
|
||||||
auto socket = SocketUdpConnection::connect(id, address, port, std::move(handler), [id, callback]() {
|
auto socket = connect_udp(id, address, port, std::move(handler), [id, callback]() {
|
||||||
callback(id);
|
callback(id);
|
||||||
});
|
});
|
||||||
connections[id] = std::move(socket);
|
connections[id] = std::move(socket);
|
||||||
|
|
@ -927,7 +123,7 @@ u64id_t Network::connectUdp(const std::string& address, int port, const consumer
|
||||||
|
|
||||||
u64id_t Network::openUdpServer(int port, const ServerDatagramCallback& handler) {
|
u64id_t Network::openUdpServer(int port, const ServerDatagramCallback& handler) {
|
||||||
u64id_t id = nextServer++;
|
u64id_t id = nextServer++;
|
||||||
auto server = SocketUdpServer::openServer(id, this, port, handler);
|
auto server = open_udp_server(id, this, port, handler);
|
||||||
servers[id] = std::move(server);
|
servers[id] = std::move(server);
|
||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
@ -968,19 +164,19 @@ void Network::update() {
|
||||||
}
|
}
|
||||||
++socketiter;
|
++socketiter;
|
||||||
}
|
}
|
||||||
auto serveriter = servers.begin();
|
}
|
||||||
while (serveriter != servers.end()) {
|
auto serveriter = servers.begin();
|
||||||
auto server = serveriter->second.get();
|
while (serveriter != servers.end()) {
|
||||||
if (!server->isOpen()) {
|
auto server = serveriter->second.get();
|
||||||
serveriter = servers.erase(serveriter);
|
if (!server->isOpen()) {
|
||||||
continue;
|
serveriter = servers.erase(serveriter);
|
||||||
}
|
continue;
|
||||||
++serveriter;
|
|
||||||
}
|
}
|
||||||
|
server->update();
|
||||||
|
++serveriter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Network> Network::create(const NetworkSettings& settings) {
|
std::unique_ptr<Network> Network::create(const NetworkSettings& settings) {
|
||||||
auto requests = CurlRequests::create();
|
return std::make_unique<Network>(network::create_curl_requests());
|
||||||
return std::make_unique<Network>(std::move(requests));
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,83 +1,16 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include "commons.hpp"
|
||||||
#include <vector>
|
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
#include "typedefs.hpp"
|
|
||||||
#include "settings.hpp"
|
|
||||||
#include "util/Buffer.hpp"
|
|
||||||
#include "delegates.hpp"
|
|
||||||
|
|
||||||
namespace network {
|
namespace network {
|
||||||
using OnResponse = std::function<void(std::vector<char>)>;
|
class TcpConnection : public ReadableConnection {
|
||||||
using OnReject = std::function<void(int, std::vector<char>)>;
|
|
||||||
using ConnectCallback = std::function<void(u64id_t, u64id_t)>;
|
|
||||||
using ServerDatagramCallback = std::function<void(u64id_t sid, const std::string& addr, int port, const char* buffer, size_t length)>;
|
|
||||||
using ClientDatagramCallback = std::function<void(u64id_t cid, const char* buffer, size_t length)>;
|
|
||||||
|
|
||||||
|
|
||||||
class Requests {
|
|
||||||
public:
|
|
||||||
virtual ~Requests() {}
|
|
||||||
|
|
||||||
virtual void get(
|
|
||||||
const std::string& url,
|
|
||||||
OnResponse onResponse,
|
|
||||||
OnReject onReject=nullptr,
|
|
||||||
std::vector<std::string> headers = {},
|
|
||||||
long maxSize=0
|
|
||||||
) = 0;
|
|
||||||
|
|
||||||
virtual void post(
|
|
||||||
const std::string& url,
|
|
||||||
const std::string& data,
|
|
||||||
OnResponse onResponse,
|
|
||||||
OnReject onReject=nullptr,
|
|
||||||
std::vector<std::string> headers = {},
|
|
||||||
long maxSize=0
|
|
||||||
) = 0;
|
|
||||||
|
|
||||||
[[nodiscard]] virtual size_t getTotalUpload() const = 0;
|
|
||||||
[[nodiscard]] virtual size_t getTotalDownload() const = 0;
|
|
||||||
|
|
||||||
virtual void update() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class ConnectionState {
|
|
||||||
INITIAL, CONNECTING, CONNECTED, CLOSED
|
|
||||||
};
|
|
||||||
|
|
||||||
enum class TransportType {
|
|
||||||
TCP, UDP
|
|
||||||
};
|
|
||||||
|
|
||||||
class Connection {
|
|
||||||
public:
|
|
||||||
virtual ~Connection() = default;
|
|
||||||
|
|
||||||
virtual void close(bool discardAll=false) = 0;
|
|
||||||
|
|
||||||
virtual int send(const char* buffer, size_t length) = 0;
|
|
||||||
|
|
||||||
virtual size_t pullUpload() = 0;
|
|
||||||
virtual size_t pullDownload() = 0;
|
|
||||||
|
|
||||||
[[nodiscard]] virtual int getPort() const = 0;
|
|
||||||
[[nodiscard]] virtual std::string getAddress() const = 0;
|
|
||||||
|
|
||||||
[[nodiscard]] virtual ConnectionState getState() const = 0;
|
|
||||||
|
|
||||||
[[nodiscard]] virtual TransportType getTransportType() const noexcept = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class TcpConnection : public Connection {
|
|
||||||
public:
|
public:
|
||||||
~TcpConnection() override = default;
|
~TcpConnection() override = default;
|
||||||
|
|
||||||
virtual void connect(runnable callback) = 0;
|
virtual void connect(runnable callback) = 0;
|
||||||
virtual int recv(char* buffer, size_t length) = 0;
|
|
||||||
virtual int available() = 0;
|
virtual void setNoDelay(bool noDelay) = 0;
|
||||||
|
[[nodiscard]] virtual bool isNoDelay() const = 0;
|
||||||
|
|
||||||
[[nodiscard]] TransportType getTransportType() const noexcept override {
|
[[nodiscard]] TransportType getTransportType() const noexcept override {
|
||||||
return TransportType::TCP;
|
return TransportType::TCP;
|
||||||
|
|
@ -95,15 +28,6 @@ namespace network {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class Server {
|
|
||||||
public:
|
|
||||||
virtual ~Server() = default;
|
|
||||||
virtual void close() = 0;
|
|
||||||
virtual bool isOpen() = 0;
|
|
||||||
[[nodiscard]] virtual TransportType getTransportType() const noexcept = 0;
|
|
||||||
[[nodiscard]] virtual int getPort() const = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
class TcpServer : public Server {
|
class TcpServer : public Server {
|
||||||
public:
|
public:
|
||||||
~TcpServer() override {}
|
~TcpServer() override {}
|
||||||
|
|
@ -112,6 +36,8 @@ namespace network {
|
||||||
[[nodiscard]] TransportType getTransportType() const noexcept override {
|
[[nodiscard]] TransportType getTransportType() const noexcept override {
|
||||||
return TransportType::TCP;
|
return TransportType::TCP;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
virtual void setMaxClientsConnected(int count) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
class UdpServer : public Server {
|
class UdpServer : public Server {
|
||||||
|
|
@ -159,8 +85,8 @@ namespace network {
|
||||||
long maxSize=0
|
long maxSize=0
|
||||||
);
|
);
|
||||||
|
|
||||||
[[nodiscard]] Connection* getConnection(u64id_t id);
|
[[nodiscard]] Connection* getConnection(u64id_t id, bool includePrivate);
|
||||||
[[nodiscard]] Server* getServer(u64id_t id) const;
|
[[nodiscard]] Server* getServer(u64id_t id, bool includePrivate) const;
|
||||||
|
|
||||||
u64id_t connectTcp(const std::string& address, int port, consumer<u64id_t> callback);
|
u64id_t connectTcp(const std::string& address, int port, consumer<u64id_t> callback);
|
||||||
u64id_t connectUdp(const std::string& address, int port, const consumer<u64id_t>& callback, ClientDatagramCallback handler);
|
u64id_t connectUdp(const std::string& address, int port, const consumer<u64id_t>& callback, ClientDatagramCallback handler);
|
||||||
|
|
|
||||||
704
src/network/Sockets.cpp
Normal file
704
src/network/Sockets.cpp
Normal file
|
|
@ -0,0 +1,704 @@
|
||||||
|
#include "commons.hpp"
|
||||||
|
|
||||||
|
#pragma comment(lib, "Ws2_32.lib")
|
||||||
|
|
||||||
|
#define NOMINMAX
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <limits>
|
||||||
|
#include <queue>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <curl/curl.h>
|
||||||
|
#define SHUT_RDWR SD_BOTH
|
||||||
|
#else
|
||||||
|
#include <sys/types.h>
|
||||||
|
#include <sys/socket.h>
|
||||||
|
#include <netdb.h>
|
||||||
|
#include <netinet/in.h>
|
||||||
|
#include <netinet/tcp.h>
|
||||||
|
#include <arpa/inet.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
|
using SOCKET = int;
|
||||||
|
#endif // _WIN32
|
||||||
|
|
||||||
|
#include "Network.hpp"
|
||||||
|
#include "util/stringutil.hpp"
|
||||||
|
#include "debug/Logger.hpp"
|
||||||
|
|
||||||
|
using namespace network;
|
||||||
|
|
||||||
|
static debug::Logger logger("sockets");
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
static inline int closesocket(int descriptor) noexcept {
|
||||||
|
return close(descriptor);
|
||||||
|
}
|
||||||
|
static inline std::runtime_error handle_socket_error(const std::string& message) {
|
||||||
|
int err = errno;
|
||||||
|
return std::runtime_error(
|
||||||
|
message+" [errno=" + std::to_string(err) + "]: " +
|
||||||
|
std::string(strerror(err))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
static inline std::runtime_error handle_socket_error(const std::string& message) {
|
||||||
|
int errorCode = WSAGetLastError();
|
||||||
|
wchar_t* s = nullptr;
|
||||||
|
size_t size = FormatMessageW(
|
||||||
|
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
|
||||||
|
FORMAT_MESSAGE_IGNORE_INSERTS,
|
||||||
|
nullptr,
|
||||||
|
errorCode,
|
||||||
|
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
|
||||||
|
(LPWSTR)&s,
|
||||||
|
0,
|
||||||
|
nullptr
|
||||||
|
);
|
||||||
|
assert(s != nullptr);
|
||||||
|
while (size && isspace(s[size-1])) {
|
||||||
|
s[--size] = 0;
|
||||||
|
}
|
||||||
|
auto errorString = util::wstr2str_utf8(std::wstring(s));
|
||||||
|
LocalFree(s);
|
||||||
|
return std::runtime_error(message+" [WSA error=" +
|
||||||
|
std::to_string(errorCode) + "]: "+errorString);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static inline int connectsocket(
|
||||||
|
int descriptor, const sockaddr* addr, socklen_t len
|
||||||
|
) noexcept {
|
||||||
|
return connect(descriptor, addr, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int recvsocket(
|
||||||
|
int descriptor, char* buf, size_t len
|
||||||
|
) noexcept {
|
||||||
|
return recv(descriptor, buf, len, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline int sendsocket(
|
||||||
|
int descriptor, const char* buf, size_t len, int flags
|
||||||
|
) noexcept {
|
||||||
|
return send(descriptor, buf, len, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string to_string(const sockaddr_in& addr, bool port=true) {
|
||||||
|
char ip[INET_ADDRSTRLEN];
|
||||||
|
if (inet_ntop(AF_INET, &(addr.sin_addr), ip, INET_ADDRSTRLEN)) {
|
||||||
|
return std::string(ip) +
|
||||||
|
(port ? (":" + std::to_string(htons(addr.sin_port))) : "");
|
||||||
|
}
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
class SocketTcpConnection : public TcpConnection {
|
||||||
|
SOCKET descriptor;
|
||||||
|
sockaddr_in addr;
|
||||||
|
size_t totalUpload = 0;
|
||||||
|
size_t totalDownload = 0;
|
||||||
|
ConnectionState state = ConnectionState::INITIAL;
|
||||||
|
std::unique_ptr<std::thread> thread = nullptr;
|
||||||
|
std::vector<char> readBatch;
|
||||||
|
util::Buffer<char> buffer;
|
||||||
|
std::mutex mutex;
|
||||||
|
|
||||||
|
void connectSocket() {
|
||||||
|
state = ConnectionState::CONNECTING;
|
||||||
|
logger.info() << "connecting to " << to_string(addr);
|
||||||
|
int res = connectsocket(descriptor, (const sockaddr*)&addr, sizeof(sockaddr_in));
|
||||||
|
if (res < 0) {
|
||||||
|
auto error = handle_socket_error("Connect failed");
|
||||||
|
closesocket(descriptor);
|
||||||
|
state = ConnectionState::CLOSED;
|
||||||
|
logger.error() << error.what();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logger.info() << "connected to " << to_string(addr);
|
||||||
|
state = ConnectionState::CONNECTED;
|
||||||
|
}
|
||||||
|
public:
|
||||||
|
SocketTcpConnection(SOCKET descriptor, sockaddr_in addr)
|
||||||
|
: descriptor(descriptor), addr(std::move(addr)), buffer(16'384) {}
|
||||||
|
|
||||||
|
~SocketTcpConnection() {
|
||||||
|
if (state != ConnectionState::CLOSED) {
|
||||||
|
shutdown(descriptor, 2);
|
||||||
|
}
|
||||||
|
if (thread) {
|
||||||
|
thread->join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setNoDelay(bool noDelay) override {
|
||||||
|
int opt = noDelay ? 1 : 0;
|
||||||
|
if (setsockopt(descriptor, IPPROTO_TCP, TCP_NODELAY, (char*)&opt, sizeof(opt)) < 0) {
|
||||||
|
throw handle_socket_error("setsockopt(TCP_NODELAY) failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isNoDelay() const override {
|
||||||
|
int opt = 0;
|
||||||
|
socklen_t len = sizeof(opt);
|
||||||
|
if (getsockopt(descriptor, IPPROTO_TCP, TCP_NODELAY, (char*)&opt, &len) < 0) {
|
||||||
|
throw handle_socket_error("getsockopt(TCP_NODELAY) failed");
|
||||||
|
}
|
||||||
|
return opt != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void startListen() {
|
||||||
|
while (state == ConnectionState::CONNECTED) {
|
||||||
|
int size = recvsocket(descriptor, buffer.data(), buffer.size());
|
||||||
|
if (size == 0) {
|
||||||
|
logger.info() << "closed connection with " << to_string(addr);
|
||||||
|
closesocket(descriptor);
|
||||||
|
state = ConnectionState::CLOSED;
|
||||||
|
break;
|
||||||
|
} else if (size < 0) {
|
||||||
|
logger.warning() << "an error ocurred while receiving from "
|
||||||
|
<< to_string(addr);
|
||||||
|
auto error = handle_socket_error("recv(...) error");
|
||||||
|
closesocket(descriptor);
|
||||||
|
state = ConnectionState::CLOSED;
|
||||||
|
logger.error() << error.what();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
readBatch.emplace_back(buffer[i]);
|
||||||
|
}
|
||||||
|
totalDownload += size;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void startClient() {
|
||||||
|
state = ConnectionState::CONNECTED;
|
||||||
|
thread = std::make_unique<std::thread>([this]() { startListen();});
|
||||||
|
}
|
||||||
|
|
||||||
|
void connect(runnable callback) override {
|
||||||
|
thread = std::make_unique<std::thread>([this, callback]() {
|
||||||
|
connectSocket();
|
||||||
|
if (state == ConnectionState::CONNECTED) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
startListen();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int recv(char* buffer, size_t length) override {
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
|
||||||
|
if (state != ConnectionState::CONNECTED && readBatch.empty()) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
int size = std::min(readBatch.size(), length);
|
||||||
|
std::memcpy(buffer, readBatch.data(), size);
|
||||||
|
readBatch.erase(readBatch.begin(), readBatch.begin() + size);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int send(const char* buffer, size_t length) override {
|
||||||
|
if (state == ConnectionState::CLOSED) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
int len = sendsocket(descriptor, buffer, length, 0);
|
||||||
|
if (len == -1) {
|
||||||
|
int err = errno;
|
||||||
|
close();
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Send failed [errno=" + std::to_string(err) + "]: "
|
||||||
|
+ std::string(strerror(err))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
totalUpload += len;
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int available() override {
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
return readBatch.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
void close(bool discardAll=false) override {
|
||||||
|
{
|
||||||
|
std::lock_guard lock(mutex);
|
||||||
|
readBatch.clear();
|
||||||
|
|
||||||
|
if (state != ConnectionState::CLOSED) {
|
||||||
|
shutdown(descriptor, SHUT_RDWR);
|
||||||
|
closesocket(descriptor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (thread) {
|
||||||
|
thread->join();
|
||||||
|
thread = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pullUpload() override {
|
||||||
|
size_t size = totalUpload;
|
||||||
|
totalUpload = 0;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pullDownload() override {
|
||||||
|
size_t size = totalDownload;
|
||||||
|
totalDownload = 0;
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getPort() const override {
|
||||||
|
return htons(addr.sin_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getAddress() const override {
|
||||||
|
return to_string(addr, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::shared_ptr<SocketTcpConnection> connect(
|
||||||
|
const std::string& address, int port, runnable callback
|
||||||
|
) {
|
||||||
|
addrinfo hints {};
|
||||||
|
|
||||||
|
hints.ai_family = AF_INET;
|
||||||
|
hints.ai_socktype = SOCK_STREAM;
|
||||||
|
|
||||||
|
addrinfo* addrinfo = nullptr;
|
||||||
|
if (int res = getaddrinfo(
|
||||||
|
address.c_str(), nullptr, &hints, &addrinfo
|
||||||
|
)) {
|
||||||
|
throw std::runtime_error(gai_strerror(res));
|
||||||
|
}
|
||||||
|
|
||||||
|
sockaddr_in serverAddress;
|
||||||
|
std::memcpy(&serverAddress, addrinfo->ai_addr, sizeof(sockaddr_in));
|
||||||
|
serverAddress.sin_port = htons(port);
|
||||||
|
freeaddrinfo(addrinfo);
|
||||||
|
|
||||||
|
SOCKET descriptor = socket(AF_INET, SOCK_STREAM, 0);
|
||||||
|
if (descriptor == -1) {
|
||||||
|
throw std::runtime_error("Could not create socket");
|
||||||
|
}
|
||||||
|
auto socket = std::make_shared<SocketTcpConnection>(descriptor, std::move(serverAddress));
|
||||||
|
socket->connect(std::move(callback));
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
ConnectionState getState() const override {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SocketTcpServer : public TcpServer {
|
||||||
|
u64id_t id;
|
||||||
|
Network* network;
|
||||||
|
SOCKET descriptor;
|
||||||
|
std::vector<u64id_t> clients;
|
||||||
|
std::mutex clientsMutex;
|
||||||
|
bool open = true;
|
||||||
|
std::unique_ptr<std::thread> thread = nullptr;
|
||||||
|
int port;
|
||||||
|
int maxConnected = -1;
|
||||||
|
public:
|
||||||
|
SocketTcpServer(u64id_t id, Network* network, SOCKET descriptor, int port)
|
||||||
|
: id(id), network(network), descriptor(descriptor), port(port) {}
|
||||||
|
|
||||||
|
~SocketTcpServer() {
|
||||||
|
closeSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
void setMaxClientsConnected(int count) override {
|
||||||
|
maxConnected = count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update() override {
|
||||||
|
std::vector<u64id_t> clients;
|
||||||
|
for (u64id_t cid : this->clients) {
|
||||||
|
if (auto client = network->getConnection(cid, true)) {
|
||||||
|
if (client->getState() != ConnectionState::CLOSED) {
|
||||||
|
clients.emplace_back(cid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::swap(clients, this->clients);
|
||||||
|
}
|
||||||
|
|
||||||
|
void startListen(ConnectCallback handler) override {
|
||||||
|
thread = std::make_unique<std::thread>([this, handler]() {
|
||||||
|
while (open) {
|
||||||
|
logger.info() << "listening for connections";
|
||||||
|
if (listen(descriptor, 2) < 0) {
|
||||||
|
close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
socklen_t addrlen = sizeof(sockaddr_in);
|
||||||
|
SOCKET clientDescriptor;
|
||||||
|
sockaddr_in address;
|
||||||
|
logger.info() << "accepting clients";
|
||||||
|
if ((clientDescriptor = accept(descriptor, (sockaddr*)&address, &addrlen)) == -1) {
|
||||||
|
close();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (maxConnected >= 0 && clients.size() >= maxConnected) {
|
||||||
|
logger.info() << "refused connection attempt from " << to_string(address);
|
||||||
|
closesocket(clientDescriptor);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
logger.info() << "client connected: " << to_string(address);
|
||||||
|
auto socket = std::make_shared<SocketTcpConnection>(
|
||||||
|
clientDescriptor, address
|
||||||
|
);
|
||||||
|
socket->startClient();
|
||||||
|
u64id_t id = network->addConnection(socket);
|
||||||
|
{
|
||||||
|
std::lock_guard lock(clientsMutex);
|
||||||
|
clients.push_back(id);
|
||||||
|
}
|
||||||
|
handler(this->id, id);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void closeSocket() {
|
||||||
|
if (!open) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
logger.info() << "closing server";
|
||||||
|
open = false;
|
||||||
|
|
||||||
|
{
|
||||||
|
std::lock_guard lock(clientsMutex);
|
||||||
|
for (u64id_t clientid : clients) {
|
||||||
|
if (auto client = network->getConnection(clientid, true)) {
|
||||||
|
client->close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
clients.clear();
|
||||||
|
|
||||||
|
shutdown(descriptor, 2);
|
||||||
|
closesocket(descriptor);
|
||||||
|
thread->join();
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() override {
|
||||||
|
closeSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isOpen() override {
|
||||||
|
return open;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getPort() const override {
|
||||||
|
return port;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::shared_ptr<SocketTcpServer> openServer(
|
||||||
|
u64id_t id, Network* network, int port, ConnectCallback handler
|
||||||
|
) {
|
||||||
|
SOCKET descriptor = socket(
|
||||||
|
AF_INET, SOCK_STREAM, 0
|
||||||
|
);
|
||||||
|
if (descriptor == -1) {
|
||||||
|
throw std::runtime_error("Could not create server socket");
|
||||||
|
}
|
||||||
|
int opt = 1;
|
||||||
|
int flags = SO_REUSEADDR;
|
||||||
|
# if !defined(_WIN32) && !defined(__APPLE__)
|
||||||
|
flags |= SO_REUSEPORT;
|
||||||
|
# endif
|
||||||
|
if (setsockopt(descriptor, SOL_SOCKET, flags, (const char*)&opt, sizeof(opt))) {
|
||||||
|
logger.error() << "setsockopt(SO_REUSEADDR) failed with errno: "
|
||||||
|
<< errno << "(" << std::strerror(errno) << ")";
|
||||||
|
closesocket(descriptor);
|
||||||
|
throw std::runtime_error("setsockopt");
|
||||||
|
}
|
||||||
|
sockaddr_in address;
|
||||||
|
address.sin_family = AF_INET;
|
||||||
|
address.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
address.sin_port = htons(port);
|
||||||
|
if (bind(descriptor, (sockaddr*)&address, sizeof(address)) < 0) {
|
||||||
|
closesocket(descriptor);
|
||||||
|
throw std::runtime_error("could not bind port "+std::to_string(port));
|
||||||
|
}
|
||||||
|
logger.info() << "opened server at port " << port;
|
||||||
|
auto server =
|
||||||
|
std::make_shared<SocketTcpServer>(id, network, descriptor, port);
|
||||||
|
server->startListen(std::move(handler));
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
sockaddr_in addr{};
|
||||||
|
bool open = true;
|
||||||
|
std::unique_ptr<std::thread> thread;
|
||||||
|
ClientDatagramCallback callback;
|
||||||
|
|
||||||
|
size_t totalUpload = 0;
|
||||||
|
size_t totalDownload = 0;
|
||||||
|
ConnectionState state = ConnectionState::INITIAL;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SocketUdpConnection(u64id_t id, SOCKET descriptor, sockaddr_in addr)
|
||||||
|
: id(id), descriptor(descriptor), addr(std::move(addr)) {}
|
||||||
|
|
||||||
|
~SocketUdpConnection() override {
|
||||||
|
SocketUdpConnection::close();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::shared_ptr<SocketUdpConnection> connect(
|
||||||
|
u64id_t id,
|
||||||
|
const std::string& address,
|
||||||
|
int port,
|
||||||
|
ClientDatagramCallback handler,
|
||||||
|
runnable callback
|
||||||
|
) {
|
||||||
|
SOCKET descriptor = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
if (descriptor == -1) {
|
||||||
|
throw std::runtime_error("could not create udp socket");
|
||||||
|
}
|
||||||
|
|
||||||
|
sockaddr_in serverAddr = resolve_address_dgram(address, port);
|
||||||
|
|
||||||
|
if (::connect(descriptor, (sockaddr*)&serverAddr, sizeof(serverAddr)) < 0) {
|
||||||
|
auto err = handle_socket_error("udp connect failed");
|
||||||
|
closesocket(descriptor);
|
||||||
|
throw err;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto socket = std::make_shared<SocketUdpConnection>(id, descriptor, serverAddr);
|
||||||
|
socket->connect(std::move(handler));
|
||||||
|
|
||||||
|
callback();
|
||||||
|
|
||||||
|
return socket;
|
||||||
|
}
|
||||||
|
|
||||||
|
void connect(ClientDatagramCallback handler) override {
|
||||||
|
callback = std::move(handler);
|
||||||
|
state = ConnectionState::CONNECTED;
|
||||||
|
|
||||||
|
thread = std::make_unique<std::thread>([this]() {
|
||||||
|
util::Buffer<char> buffer(16'384);
|
||||||
|
while (open) {
|
||||||
|
int size = recv(descriptor, buffer.data(), buffer.size(), 0);
|
||||||
|
if (size <= 0) {
|
||||||
|
logger.error() << "udp connection " << id
|
||||||
|
<< handle_socket_error(" recv error").what();
|
||||||
|
if (!open) break;
|
||||||
|
closesocket(descriptor);
|
||||||
|
state = ConnectionState::CLOSED;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
totalDownload += size;
|
||||||
|
if (callback) {
|
||||||
|
callback(id, buffer.data(), size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
int send(const char* buffer, size_t length) override {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void close(bool discardAll=false) override {
|
||||||
|
if (!open) return;
|
||||||
|
open = false;
|
||||||
|
logger.info() << "closing udp connection "<< id;
|
||||||
|
|
||||||
|
if (state != ConnectionState::CLOSED) {
|
||||||
|
shutdown(descriptor, 2);
|
||||||
|
closesocket(descriptor);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (thread) {
|
||||||
|
thread->join();
|
||||||
|
thread.reset();
|
||||||
|
}
|
||||||
|
state = ConnectionState::CLOSED;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pullUpload() override {
|
||||||
|
size_t s = totalUpload;
|
||||||
|
totalUpload = 0;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t pullDownload() override {
|
||||||
|
size_t s = totalDownload;
|
||||||
|
totalDownload = 0;
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] int getPort() const override {
|
||||||
|
return ntohs(addr.sin_port);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::string getAddress() const override {
|
||||||
|
return to_string(addr, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] ConnectionState getState() const override {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class SocketUdpServer : public UdpServer {
|
||||||
|
u64id_t id;
|
||||||
|
SOCKET descriptor;
|
||||||
|
bool open = true;
|
||||||
|
std::unique_ptr<std::thread> thread = nullptr;
|
||||||
|
int port;
|
||||||
|
ServerDatagramCallback callback;
|
||||||
|
|
||||||
|
public:
|
||||||
|
SocketUdpServer(u64id_t id, Network* network, SOCKET descriptor, int port)
|
||||||
|
: id(id), descriptor(descriptor), port(port) {}
|
||||||
|
|
||||||
|
~SocketUdpServer() override {
|
||||||
|
SocketUdpServer::close();
|
||||||
|
}
|
||||||
|
|
||||||
|
void update() override {}
|
||||||
|
|
||||||
|
void startListen(ServerDatagramCallback handler) override {
|
||||||
|
callback = std::move(handler);
|
||||||
|
|
||||||
|
thread = std::make_unique<std::thread>([this]() {
|
||||||
|
util::Buffer<char> buffer(16384);
|
||||||
|
sockaddr_in clientAddr{};
|
||||||
|
socklen_t addrlen = sizeof(clientAddr);
|
||||||
|
|
||||||
|
while (open) {
|
||||||
|
int size = recvfrom(descriptor, buffer.data(), buffer.size(), 0,
|
||||||
|
reinterpret_cast<sockaddr*>(&clientAddr), &addrlen);
|
||||||
|
if (size <= 0) {
|
||||||
|
if (!open) break;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string addrStr = to_string(clientAddr, false);
|
||||||
|
int port = ntohs(clientAddr.sin_port);
|
||||||
|
|
||||||
|
callback(id, addrStr, port, buffer.data(), size);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void sendTo(const std::string& addr, int port, const char* buffer, size_t length) override {
|
||||||
|
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 {
|
||||||
|
if (!open) return;
|
||||||
|
open = false;
|
||||||
|
shutdown(descriptor, 2);
|
||||||
|
closesocket(descriptor);
|
||||||
|
if (thread) {
|
||||||
|
thread->join();
|
||||||
|
thread = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isOpen() override { return open; }
|
||||||
|
int getPort() const override { return port; }
|
||||||
|
|
||||||
|
static std::shared_ptr<SocketUdpServer> openServer(
|
||||||
|
u64id_t id, Network* network, int port, const ServerDatagramCallback& handler
|
||||||
|
) {
|
||||||
|
SOCKET descriptor = socket(AF_INET, SOCK_DGRAM, 0);
|
||||||
|
if (descriptor == -1) throw std::runtime_error("could not create udp socket");
|
||||||
|
|
||||||
|
sockaddr_in address{};
|
||||||
|
address.sin_family = AF_INET;
|
||||||
|
address.sin_addr.s_addr = INADDR_ANY;
|
||||||
|
address.sin_port = htons(port);
|
||||||
|
|
||||||
|
if (bind(descriptor, (sockaddr*)&address, sizeof(address)) < 0) {
|
||||||
|
closesocket(descriptor);
|
||||||
|
throw std::runtime_error("could not bind udp port " + std::to_string(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
auto server = std::make_shared<SocketUdpServer>(id, network, descriptor, port);
|
||||||
|
server->startListen(std::move(handler));
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace network {
|
||||||
|
std::shared_ptr<TcpConnection> connect_tcp(
|
||||||
|
const std::string& address, int port, runnable callback
|
||||||
|
) {
|
||||||
|
return SocketTcpConnection::connect(address, port, std::move(callback));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<TcpServer> open_tcp_server(
|
||||||
|
u64id_t id, Network* network, int port, ConnectCallback handler
|
||||||
|
) {
|
||||||
|
return SocketTcpServer::openServer(id, network, port, std::move(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<UdpConnection> connect_udp(
|
||||||
|
u64id_t id,
|
||||||
|
const std::string& address,
|
||||||
|
int port,
|
||||||
|
ClientDatagramCallback handler,
|
||||||
|
runnable callback
|
||||||
|
) {
|
||||||
|
return SocketUdpConnection::connect(
|
||||||
|
id, address, port, std::move(handler), std::move(callback)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<UdpServer> open_udp_server(
|
||||||
|
u64id_t id,
|
||||||
|
Network* network,
|
||||||
|
int port,
|
||||||
|
const ServerDatagramCallback& handler
|
||||||
|
) {
|
||||||
|
return SocketUdpServer::openServer(id, network, port, handler);
|
||||||
|
}
|
||||||
|
}
|
||||||
99
src/network/commons.hpp
Normal file
99
src/network/commons.hpp
Normal file
|
|
@ -0,0 +1,99 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "typedefs.hpp"
|
||||||
|
#include "settings.hpp"
|
||||||
|
#include "util/Buffer.hpp"
|
||||||
|
#include "delegates.hpp"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <mutex>
|
||||||
|
|
||||||
|
namespace network {
|
||||||
|
using OnResponse = std::function<void(std::vector<char>)>;
|
||||||
|
using OnReject = std::function<void(int, std::vector<char>)>;
|
||||||
|
using ConnectCallback = std::function<void(u64id_t, u64id_t)>;
|
||||||
|
using ServerDatagramCallback = std::function<void(u64id_t sid, const std::string& addr, int port, const char* buffer, size_t length)>;
|
||||||
|
using ClientDatagramCallback = std::function<void(u64id_t cid, const char* buffer, size_t length)>;
|
||||||
|
|
||||||
|
class Requests {
|
||||||
|
public:
|
||||||
|
virtual ~Requests() {}
|
||||||
|
|
||||||
|
virtual void get(
|
||||||
|
const std::string& url,
|
||||||
|
OnResponse onResponse,
|
||||||
|
OnReject onReject=nullptr,
|
||||||
|
std::vector<std::string> headers = {},
|
||||||
|
long maxSize=0
|
||||||
|
) = 0;
|
||||||
|
|
||||||
|
virtual void post(
|
||||||
|
const std::string& url,
|
||||||
|
const std::string& data,
|
||||||
|
OnResponse onResponse,
|
||||||
|
OnReject onReject=nullptr,
|
||||||
|
std::vector<std::string> headers = {},
|
||||||
|
long maxSize=0
|
||||||
|
) = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual size_t getTotalUpload() const = 0;
|
||||||
|
[[nodiscard]] virtual size_t getTotalDownload() const = 0;
|
||||||
|
|
||||||
|
virtual void update() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class ConnectionState {
|
||||||
|
INITIAL, CONNECTING, CONNECTED, CLOSED
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class TransportType {
|
||||||
|
TCP, UDP
|
||||||
|
};
|
||||||
|
|
||||||
|
class Connection {
|
||||||
|
public:
|
||||||
|
virtual ~Connection() = default;
|
||||||
|
|
||||||
|
virtual void close(bool discardAll=false) = 0;
|
||||||
|
|
||||||
|
virtual int send(const char* buffer, size_t length) = 0;
|
||||||
|
|
||||||
|
virtual size_t pullUpload() = 0;
|
||||||
|
virtual size_t pullDownload() = 0;
|
||||||
|
|
||||||
|
bool isPrivate() const { return isprivate; }
|
||||||
|
void setPrivate(bool flag) {isprivate = flag;}
|
||||||
|
|
||||||
|
[[nodiscard]] virtual int getPort() const = 0;
|
||||||
|
[[nodiscard]] virtual std::string getAddress() const = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual ConnectionState getState() const = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual TransportType getTransportType() const noexcept = 0;
|
||||||
|
protected:
|
||||||
|
bool isprivate = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
class ReadableConnection : public Connection {
|
||||||
|
public:
|
||||||
|
virtual int recv(char* buffer, size_t length) = 0;
|
||||||
|
virtual int available() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
class Server {
|
||||||
|
public:
|
||||||
|
virtual ~Server() = default;
|
||||||
|
|
||||||
|
virtual void update() = 0;
|
||||||
|
virtual void close() = 0;
|
||||||
|
virtual bool isOpen() = 0;
|
||||||
|
[[nodiscard]] virtual TransportType getTransportType() const noexcept = 0;
|
||||||
|
[[nodiscard]] virtual int getPort() const = 0;
|
||||||
|
|
||||||
|
bool isPrivate() const { return isprivate; }
|
||||||
|
void setPrivate(bool flag) { isprivate = flag; }
|
||||||
|
protected:
|
||||||
|
bool isprivate = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
@ -196,6 +196,14 @@ void Player::setLoadingChunks(bool flag) {
|
||||||
loadingChunks = flag;
|
loadingChunks = flag;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float Player::getMaxInteractionDistance() const {
|
||||||
|
return interactionDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Player::setMaxInteractionDistance(float distance) {
|
||||||
|
interactionDistance = std::max(1.0f, std::min(200.0f, distance));
|
||||||
|
}
|
||||||
|
|
||||||
entityid_t Player::getEntity() const {
|
entityid_t Player::getEntity() const {
|
||||||
return eid;
|
return eid;
|
||||||
}
|
}
|
||||||
|
|
@ -250,6 +258,7 @@ dv::value Player::serialize() const {
|
||||||
root["rotation"] = dv::to_value(rotation);
|
root["rotation"] = dv::to_value(rotation);
|
||||||
root["spawnpoint"] = dv::to_value(spawnpoint);
|
root["spawnpoint"] = dv::to_value(spawnpoint);
|
||||||
|
|
||||||
|
root["interaction-distance"] = interactionDistance;
|
||||||
root["flight"] = flight;
|
root["flight"] = flight;
|
||||||
root["noclip"] = noclip;
|
root["noclip"] = noclip;
|
||||||
root["suspended"] = suspended;
|
root["suspended"] = suspended;
|
||||||
|
|
@ -283,6 +292,8 @@ void Player::deserialize(const dv::value& src) {
|
||||||
const auto& sparr = src["spawnpoint"];
|
const auto& sparr = src["spawnpoint"];
|
||||||
setSpawnPoint(glm::vec3(
|
setSpawnPoint(glm::vec3(
|
||||||
sparr[0].asNumber(), sparr[1].asNumber(), sparr[2].asNumber()));
|
sparr[0].asNumber(), sparr[1].asNumber(), sparr[2].asNumber()));
|
||||||
|
|
||||||
|
src.at("interaction-distance").get(interactionDistance);
|
||||||
|
|
||||||
flight = src["flight"].asBoolean();
|
flight = src["flight"].asBoolean();
|
||||||
noclip = src["noclip"].asBoolean();
|
noclip = src["noclip"].asBoolean();
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@ class Player : public Serializable {
|
||||||
int64_t id;
|
int64_t id;
|
||||||
std::string name;
|
std::string name;
|
||||||
float speed;
|
float speed;
|
||||||
|
|
||||||
int chosenSlot;
|
int chosenSlot;
|
||||||
glm::vec3 position;
|
glm::vec3 position;
|
||||||
glm::vec3 spawnpoint {};
|
glm::vec3 spawnpoint {};
|
||||||
|
|
@ -56,6 +57,8 @@ class Player : public Serializable {
|
||||||
bool infiniteItems = true;
|
bool infiniteItems = true;
|
||||||
bool instantDestruction = true;
|
bool instantDestruction = true;
|
||||||
bool loadingChunks = true;
|
bool loadingChunks = true;
|
||||||
|
float interactionDistance = 10.0f;
|
||||||
|
|
||||||
entityid_t eid = ENTITY_AUTO;
|
entityid_t eid = ENTITY_AUTO;
|
||||||
entityid_t selectedEid = 0;
|
entityid_t selectedEid = 0;
|
||||||
|
|
||||||
|
|
@ -90,6 +93,7 @@ public:
|
||||||
void setChosenSlot(int index);
|
void setChosenSlot(int index);
|
||||||
|
|
||||||
int getChosenSlot() const;
|
int getChosenSlot() const;
|
||||||
|
|
||||||
float getSpeed() const;
|
float getSpeed() const;
|
||||||
|
|
||||||
bool isSuspended() const;
|
bool isSuspended() const;
|
||||||
|
|
@ -110,6 +114,9 @@ public:
|
||||||
bool isLoadingChunks() const;
|
bool isLoadingChunks() const;
|
||||||
void setLoadingChunks(bool flag);
|
void setLoadingChunks(bool flag);
|
||||||
|
|
||||||
|
float getMaxInteractionDistance() const;
|
||||||
|
void setMaxInteractionDistance(float distance);
|
||||||
|
|
||||||
entityid_t getEntity() const;
|
entityid_t getEntity() const;
|
||||||
void setEntity(entityid_t eid);
|
void setEntity(entityid_t eid);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,8 @@
|
||||||
#include "Players.hpp"
|
#include "Players.hpp"
|
||||||
|
|
||||||
|
#define GLM_ENABLE_EXPERIMENTAL
|
||||||
|
#include <glm/gtx/norm.hpp>
|
||||||
|
|
||||||
#include "Player.hpp"
|
#include "Player.hpp"
|
||||||
#include "items/Inventories.hpp"
|
#include "items/Inventories.hpp"
|
||||||
#include "world/Level.hpp"
|
#include "world/Level.hpp"
|
||||||
|
|
@ -20,6 +23,48 @@ Player* Players::get(int64_t id) const {
|
||||||
return found->second.get();
|
return found->second.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<Player*> Players::getAllInRadius(
|
||||||
|
const glm::vec3& center, float radius
|
||||||
|
) const {
|
||||||
|
std::vector<Player*> foundPlayers;
|
||||||
|
|
||||||
|
for (const auto& pair : players) {
|
||||||
|
auto player = pair.second.get();
|
||||||
|
auto relativePos = player->getPosition() - center;
|
||||||
|
if (!player->isSuspended() && glm::length2(relativePos) <= radius) {
|
||||||
|
foundPlayers.emplace_back(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return foundPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Player*> Players::getAll() const {
|
||||||
|
std::vector<Player*> allPlayers;
|
||||||
|
allPlayers.reserve(players.size());
|
||||||
|
for (const auto& pair : players) {
|
||||||
|
allPlayers.emplace_back(pair.second.get());
|
||||||
|
}
|
||||||
|
return allPlayers;
|
||||||
|
}
|
||||||
|
|
||||||
|
Player* Players::getNearest(const glm::vec3& position) const {
|
||||||
|
Player* nearest = nullptr;
|
||||||
|
float nearestDist2 = std::numeric_limits<float>::max();
|
||||||
|
for (const auto& pair : players) {
|
||||||
|
auto player = pair.second.get();
|
||||||
|
if (player->isSuspended()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto relativePos = player->getPosition() - position;
|
||||||
|
auto dist2 = glm::length2(relativePos);
|
||||||
|
if (dist2 < nearestDist2) {
|
||||||
|
nearestDist2 = dist2;
|
||||||
|
nearest = player;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nearest;
|
||||||
|
}
|
||||||
|
|
||||||
Player* Players::create(int64_t id) {
|
Player* Players::create(int64_t id) {
|
||||||
int64_t& nextPlayerID = level.getWorld()->getInfo().nextPlayerId;
|
int64_t& nextPlayerID = level.getWorld()->getInfo().nextPlayerId;
|
||||||
if (id == NONE) {
|
if (id == NONE) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,9 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <glm/vec3.hpp>
|
||||||
|
|
||||||
#include "typedefs.hpp"
|
#include "typedefs.hpp"
|
||||||
#include "interfaces/Serializable.hpp"
|
#include "interfaces/Serializable.hpp"
|
||||||
|
|
@ -34,6 +36,10 @@ public:
|
||||||
|
|
||||||
void remove(int64_t id);
|
void remove(int64_t id);
|
||||||
|
|
||||||
|
std::vector<Player*> getAllInRadius(const glm::vec3& center, float radius) const;
|
||||||
|
std::vector<Player*> getAll() const;
|
||||||
|
Player* getNearest(const glm::vec3& position) const;
|
||||||
|
|
||||||
dv::value serialize() const override;
|
dv::value serialize() const override;
|
||||||
|
|
||||||
void deserialize(const dv::value& src) override;
|
void deserialize(const dv::value& src) override;
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
#include "data/dv_util.hpp"
|
#include "data/dv_util.hpp"
|
||||||
|
|
||||||
|
#include <glm/gtc/matrix_transform.hpp>
|
||||||
|
|
||||||
void Transform::refresh() {
|
void Transform::refresh() {
|
||||||
combined = glm::mat4(1.0f);
|
combined = glm::mat4(1.0f);
|
||||||
combined = glm::translate(combined, pos);
|
combined = glm::translate(combined, pos);
|
||||||
|
|
|
||||||
|
|
@ -19,8 +19,8 @@ struct AudioSettings {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct DisplaySettings {
|
struct DisplaySettings {
|
||||||
/// @brief Is window in full screen mode
|
/// @brief Window mode (windowed/fullscreen/borderless)
|
||||||
FlagSetting fullscreen {false};
|
IntegerSetting windowMode {0, 0, 2};
|
||||||
/// @brief Window width (pixels)
|
/// @brief Window width (pixels)
|
||||||
IntegerSetting width {1280};
|
IntegerSetting width {1280};
|
||||||
/// @brief Window height (pixels)
|
/// @brief Window height (pixels)
|
||||||
|
|
|
||||||
|
|
@ -33,5 +33,14 @@ namespace util {
|
||||||
last = argv[pos];
|
last = argv[pos];
|
||||||
return argv[pos++];
|
return argv[pos++];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int nextInt() {
|
||||||
|
auto text = next();
|
||||||
|
try {
|
||||||
|
return std::stoi(text);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
throw std::runtime_error(e.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,7 @@
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <iomanip>
|
||||||
|
|
||||||
#include "io/engine_paths.hpp"
|
#include "io/engine_paths.hpp"
|
||||||
#include "util/ArgsReader.hpp"
|
#include "util/ArgsReader.hpp"
|
||||||
|
|
@ -15,10 +16,17 @@ class ArgC {
|
||||||
public:
|
public:
|
||||||
std::string keyword;
|
std::string keyword;
|
||||||
std::function<bool()> execute;
|
std::function<bool()> execute;
|
||||||
|
std::string args;
|
||||||
std::string help;
|
std::string help;
|
||||||
ArgC(const std::string& keyword, std::function<bool()> execute, const std::string& help) {
|
ArgC(
|
||||||
|
const std::string& keyword,
|
||||||
|
std::function<bool()> execute,
|
||||||
|
const std::string& args,
|
||||||
|
const std::string& help
|
||||||
|
) {
|
||||||
this->keyword = keyword;
|
this->keyword = keyword;
|
||||||
this->execute = execute;
|
this->execute = execute;
|
||||||
|
this->args = args;
|
||||||
this->help = help;
|
this->help = help;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -31,42 +39,51 @@ static bool perform_keyword(
|
||||||
ArgC("--res", [¶ms, &reader]() -> bool {
|
ArgC("--res", [¶ms, &reader]() -> bool {
|
||||||
params.resFolder = reader.next();
|
params.resFolder = reader.next();
|
||||||
return true;
|
return true;
|
||||||
}, "<path> - set resources directory."),
|
}, "<path>", "set resources directory."),
|
||||||
ArgC("--dir", [¶ms, &reader]() -> bool {
|
ArgC("--dir", [¶ms, &reader]() -> bool {
|
||||||
params.userFolder = reader.next();
|
params.userFolder = reader.next();
|
||||||
return true;
|
return true;
|
||||||
}, "<path> - set userfiles directory."),
|
}, "<path>", "set userfiles directory."),
|
||||||
ArgC("--project", [¶ms, &reader]() -> bool {
|
ArgC("--project", [¶ms, &reader]() -> bool {
|
||||||
params.projectFolder = reader.next();
|
params.projectFolder = reader.next();
|
||||||
return true;
|
return true;
|
||||||
}, "<path> - set project directory."),
|
}, "<path>", "set project directory."),
|
||||||
ArgC("--test", [¶ms, &reader]() -> bool {
|
ArgC("--test", [¶ms, &reader]() -> bool {
|
||||||
params.testMode = true;
|
params.testMode = true;
|
||||||
params.scriptFile = reader.next();
|
params.scriptFile = reader.next();
|
||||||
return true;
|
return true;
|
||||||
}, "<path> - test script file."),
|
}, "<path>", "test script file."),
|
||||||
ArgC("--script", [¶ms, &reader]() -> bool {
|
ArgC("--script", [¶ms, &reader]() -> bool {
|
||||||
params.testMode = false;
|
params.testMode = false;
|
||||||
params.scriptFile = reader.next();
|
params.scriptFile = reader.next();
|
||||||
return true;
|
return true;
|
||||||
}, "<path> - main script file."),
|
}, "<path>", "main script file."),
|
||||||
ArgC("--headless", [¶ms]() -> bool {
|
ArgC("--headless", [¶ms]() -> bool {
|
||||||
params.headless = true;
|
params.headless = true;
|
||||||
return true;
|
return true;
|
||||||
}, "- run in headless mode."),
|
}, "", "run in headless mode."),
|
||||||
|
ArgC("--tps", [¶ms, &reader]() -> bool {
|
||||||
|
params.tps = reader.nextInt();
|
||||||
|
return true;
|
||||||
|
}, "<tps>", "headless mode tick-rate (default - 20)."),
|
||||||
ArgC("--version", []() -> bool {
|
ArgC("--version", []() -> bool {
|
||||||
std::cout << ENGINE_VERSION_STRING << std::endl;
|
std::cout << ENGINE_VERSION_STRING << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}, "- display the engine version."),
|
}, "", "display the engine version."),
|
||||||
|
ArgC("--dbg-server", [¶ms, &reader]() -> bool {
|
||||||
|
params.debugServerString = reader.next();
|
||||||
|
return true;
|
||||||
|
}, "<serv>", "open debugging server where <serv> is {transport}:{port}"),
|
||||||
ArgC("--help", []() -> bool {
|
ArgC("--help", []() -> bool {
|
||||||
std::cout << "VoxelCore v" << ENGINE_VERSION_STRING << "\n\n";
|
std::cout << "VoxelCore v" << ENGINE_VERSION_STRING << "\n\n";
|
||||||
std::cout << "Command-line arguments:\n";
|
std::cout << "Command-line arguments:\n";
|
||||||
for (auto& a : argumentsCommandline) {
|
for (auto& a : argumentsCommandline) {
|
||||||
std::cout << a.keyword << " " << a.help << std::endl;
|
std::cout << std::setw(24) << std::left << (a.keyword + " " + a.args);
|
||||||
|
std::cout << "- " << a.help << std::endl;
|
||||||
}
|
}
|
||||||
std::cout << std::endl;
|
std::cout << std::endl;
|
||||||
return false;
|
return false;
|
||||||
}, "- display this help.")
|
}, "", "display this help.")
|
||||||
};
|
};
|
||||||
for (auto& a : argumentsCommandline) {
|
for (auto& a : argumentsCommandline) {
|
||||||
if (a.keyword == keyword) {
|
if (a.keyword == keyword) {
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue