mirror of
https://github.com/MihailRis/voxelcore.git
synced 2026-10-04 18:41: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 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
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
||||
```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
|
||||
player.set_spawnpoint(playerid: int, x: number, y: number, z: 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
|
||||
|
||||
```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\\` добавлять не нужно - движок делает это автоматически.
|
||||
|
||||
Доступные режимы такие же, как и в `file.open`, за исключением `+`
|
||||
Доступные режимы такие же, как и в `file.open`, за исключением `+`
|
||||
|
|
|
|||
|
|
@ -93,6 +93,12 @@ socket:is_connected() --> bool
|
|||
|
||||
-- Возвращает адрес и порт соединения.
|
||||
socket:get_address() --> str, int
|
||||
|
||||
-- Возвращает состояние NoDelay
|
||||
socket:is_nodelay() --> bool
|
||||
|
||||
-- Устанавливает состояние NoDelay
|
||||
socket:set_nodelay(state: bool)
|
||||
```
|
||||
|
||||
```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
|
||||
player.set_spawnpoint(playerid: int, x: number, y: number, z: 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)`
|
||||
- `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` - отображаемая текстура для всех сторон по-умолчанию.
|
||||
- `shading` определяет возможность затенения на примитиве. Пример: `shading off`
|
||||
- `delete` удаляет стороны по именам (top, bottom, east, west, north, south)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"id": "base",
|
||||
"title": "Base",
|
||||
"version": "0.29",
|
||||
"version": "0.30",
|
||||
"description": "basic content package"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -53,10 +53,19 @@ function create_checkbox(id, name, tooltip)
|
|||
))
|
||||
end
|
||||
|
||||
|
||||
function on_open()
|
||||
create_setting("camera.fov", "FOV", 1, "°")
|
||||
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.inertia", "Camera Inertia")
|
||||
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_connected=function(self) return network.__is_connected(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={
|
||||
|
|
|
|||
|
|
@ -587,6 +587,7 @@ function __process_post_runnables()
|
|||
__vc_named_coroutines[name] = nil
|
||||
end
|
||||
|
||||
debug.pull_events()
|
||||
network.__process_events()
|
||||
block.__process_register_events()
|
||||
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
|
||||
-- may be reused one global ffi buffer per lua_State
|
||||
local canvas_ffi_buffer
|
||||
|
|
@ -473,8 +614,6 @@ function file.readlines(path)
|
|||
return lines
|
||||
end
|
||||
|
||||
local _debug_getinfo = debug.getinfo
|
||||
|
||||
function debug.count_frames()
|
||||
local frames = 1
|
||||
while true do
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ Grant %{0} pack modification permission?=Выдаць дазвол на мады
|
|||
Error at line %{0}=Памылка ў радку %{0}
|
||||
Run=Запусціць
|
||||
Filter=Фільтр
|
||||
Are you sure you want to open the link: =Ці вы ўпэўненыя, што хочаце адкрыць спасылку:
|
||||
|
||||
editor.info.tooltip=CTRL+S - Захаваць\nCTRL+R - Запусціць\nCTRL+Z - Скасаваць\nCTRL+Y - Паўтарыць
|
||||
devtools.traceback=Стэк выклікаў (ад апошняга)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ Back=Zurück
|
|||
Continue=Weitermachen
|
||||
Add=Hinzufügen
|
||||
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.dependency-not-found=Die verwendete Abhängigkeit wurde nicht gefunden
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ Problems=Ongelmia
|
|||
Monitor=Valvonta
|
||||
Debug=Virheenkorjaus
|
||||
File=Tiedosto
|
||||
Are you sure you want to open the link: =Haluatko varmasti avata linkin:
|
||||
|
||||
devtools.traceback=Puhelupino (viimeisestä)
|
||||
error.pack-not-found=Pakettia ei löytynyt!
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ Back=Powrót
|
|||
Continue=Kontynuacja
|
||||
Add=Dodać
|
||||
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
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ Grant %{0} pack modification permission?=Выдать разрешение на
|
|||
Error at line %{0}=Ошибка на строке %{0}
|
||||
Run=Запустить
|
||||
Filter=Фильтр
|
||||
Are you sure you want to open the link: =Вы уверены, что хотите открыть ссылку:
|
||||
|
||||
editor.info.tooltip=CTRL+S - Сохранить\nCTRL+R - Запустить\nCTRL+Z - Отменить\nCTRL+Y - Повторить
|
||||
devtools.traceback=Стек вызовов (от последнего)
|
||||
|
|
@ -102,6 +103,8 @@ settings.Limit Background FPS=Ограничить фоновую частоту
|
|||
settings.Advanced render=Продвинутый рендер
|
||||
settings.Shadows quality=Качество теней
|
||||
settings.Conflict=Найдены возможные конфликты
|
||||
settings.Windowed=Оконный
|
||||
settings.Borderless=Безрамочный
|
||||
|
||||
# Управление
|
||||
chunks.reload=Перезагрузить Чанки
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ devtools.traceback=Стек викликів (від останнього)
|
|||
error.pack-not-found=Не вдалося знайти пакет
|
||||
error.dependency-not-found=Використовувана залежність не знайдена
|
||||
pack.remove-confirm=Видалити весь контент, що постачається паком/паками зі світу (безповоротно)?
|
||||
Are you sure you want to open the link: =Ви впевнені, що хочете відкрити посилання:
|
||||
|
||||
# Меню
|
||||
menu.Apply=Застосувати
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ Save=Saqlash
|
|||
Grant %{0} pack modification permission?=%{0} to‘plamini o‘zgartirish ruxsatini berilsinmi?
|
||||
Error at line %{0}=%{0}-qatorida xatolik
|
||||
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
|
||||
devtools.traceback=Chaqiruvlar steki (so`nggisidan boshlab)
|
||||
|
|
|
|||
|
|
@ -91,7 +91,9 @@ target_compile_options(
|
|||
/wd4245 # conversion from 'int' to 'const size_t', signed/unsigned
|
||||
# mismatch
|
||||
/wd4100 # unreferenced formal parameter
|
||||
/wd4457 # declaration of 'var' hides function parameter
|
||||
/wd4458 # declaration of 'var' hides class member
|
||||
/wd4459 # declaration of 'var' hides global declaration
|
||||
/wd4101 # 'var': unreferenced local variable
|
||||
/wd4388 # 'token' : signed/unsigned mismatch
|
||||
/wd4018 # '>': signed/unsigned mismatch
|
||||
|
|
@ -101,6 +103,7 @@ target_compile_options(
|
|||
-Wextra
|
||||
# additional warnings
|
||||
-Wformat-nonliteral
|
||||
#-Wsign-conversion
|
||||
-Wcast-align
|
||||
-Wpointer-arith
|
||||
-Wundef
|
||||
|
|
@ -108,6 +111,10 @@ target_compile_options(
|
|||
-Wno-unused-parameter
|
||||
-Wno-sign-compare
|
||||
-Wno-unknown-pragmas
|
||||
>
|
||||
$<$<CXX_COMPILER_ID:Clang>:
|
||||
-Wduplicated-branches
|
||||
-Wduplicated-cond
|
||||
>)
|
||||
|
||||
target_link_options(
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include "vcm.hpp"
|
||||
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
#include "xml.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 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] {};
|
||||
regions[0].scale(to.x - from.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] {};
|
||||
enabled[i] = true;
|
||||
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>
|
||||
|
||||
inline constexpr int ENGINE_VERSION_MAJOR = 0;
|
||||
inline constexpr int ENGINE_VERSION_MINOR = 29;
|
||||
inline constexpr int ENGINE_VERSION_MINOR = 30;
|
||||
|
||||
#ifdef NDEBUG
|
||||
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;
|
||||
#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
|
||||
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 "devtools/Editor.hpp"
|
||||
#include "devtools/Project.hpp"
|
||||
#include "devtools/DebuggingServer.hpp"
|
||||
#include "content/ContentControl.hpp"
|
||||
#include "core_defs.hpp"
|
||||
#include "io/io.hpp"
|
||||
|
|
@ -115,6 +116,9 @@ void Engine::initializeClient() {
|
|||
if (ENGINE_DEBUG_BUILD) {
|
||||
title += " [debug]";
|
||||
}
|
||||
if (debuggingServer) {
|
||||
title = "[debugging] " + title;
|
||||
}
|
||||
auto [window, input] = Window::initialize(&settings.display, title);
|
||||
if (!window || !input){
|
||||
throw initialize_error("could not initialize window");
|
||||
|
|
@ -135,10 +139,11 @@ void Engine::initializeClient() {
|
|||
if (ENGINE_DEBUG_BUILD) {
|
||||
menus::create_version_label(*gui);
|
||||
}
|
||||
keepAlive(settings.display.fullscreen.observe(
|
||||
[this](bool value) {
|
||||
if (value != this->window->isFullscreen()) {
|
||||
this->window->toggleFullscreen();
|
||||
keepAlive(settings.display.windowMode.observe(
|
||||
[this](int value) {
|
||||
WindowMode mode = static_cast<WindowMode>(value);
|
||||
if (mode != this->window->getMode()) {
|
||||
this->window->setMode(mode);
|
||||
}
|
||||
},
|
||||
true
|
||||
|
|
@ -172,6 +177,18 @@ void Engine::initialize(CoreParameters coreParameters) {
|
|||
cmd = std::make_unique<cmd::CommandsInterpreter>();
|
||||
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()) {
|
||||
paths.setScriptFolder(params.scriptFile.parent_path());
|
||||
}
|
||||
|
|
@ -237,7 +254,11 @@ void Engine::updateHotkeys() {
|
|||
gui->toggleDebug();
|
||||
}
|
||||
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();
|
||||
postRunnables.run();
|
||||
scripting::process_post_runnables();
|
||||
|
||||
if (debuggingServer) {
|
||||
debuggingServer->update();
|
||||
}
|
||||
}
|
||||
|
||||
void Engine::detachDebugger() {
|
||||
debuggingServer.reset();
|
||||
}
|
||||
|
||||
void Engine::updateFrontend() {
|
||||
|
|
@ -282,6 +311,30 @@ void Engine::nextFrame() {
|
|||
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() {
|
||||
screen->draw(time.getDelta());
|
||||
|
||||
|
|
@ -294,7 +347,11 @@ void Engine::saveSettings() {
|
|||
io::write_string(EnginePaths::SETTINGS_FILE, toml::stringify(*settingsHandler));
|
||||
if (!params.headless) {
|
||||
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";
|
||||
}
|
||||
audio::close();
|
||||
debuggingServer.reset();
|
||||
network.reset();
|
||||
clearKeepedObjects();
|
||||
project.reset();
|
||||
|
|
|
|||
|
|
@ -36,6 +36,7 @@ namespace network {
|
|||
|
||||
namespace devtools {
|
||||
class Editor;
|
||||
class DebuggingServer;
|
||||
}
|
||||
|
||||
class initialize_error : public std::runtime_error {
|
||||
|
|
@ -50,6 +51,8 @@ struct CoreParameters {
|
|||
std::filesystem::path userFolder = ".";
|
||||
std::filesystem::path scriptFile;
|
||||
std::filesystem::path projectFolder;
|
||||
std::string debugServerString;
|
||||
int tps = 20;
|
||||
};
|
||||
|
||||
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<gui::GUI> gui;
|
||||
std::unique_ptr<devtools::Editor> editor;
|
||||
std::unique_ptr<devtools::DebuggingServer> debuggingServer;
|
||||
PostRunnables postRunnables;
|
||||
Time time;
|
||||
OnWorldOpen levelConsumer;
|
||||
|
|
@ -104,6 +108,7 @@ public:
|
|||
void updateFrontend();
|
||||
void renderFrame();
|
||||
void nextFrame();
|
||||
void startPauseLoop();
|
||||
|
||||
/// @brief Set screen (scene).
|
||||
/// nullptr may be used to delete previous screen before creating new one,
|
||||
|
|
@ -181,4 +186,10 @@ public:
|
|||
const Project& getProject() {
|
||||
return *project;
|
||||
}
|
||||
|
||||
devtools::DebuggingServer* getDebuggingServer() {
|
||||
return debuggingServer.get();
|
||||
}
|
||||
|
||||
void detachDebugger();
|
||||
};
|
||||
|
|
|
|||
|
|
@ -15,8 +15,6 @@ using namespace std::chrono;
|
|||
|
||||
static debug::Logger logger("mainloop");
|
||||
|
||||
inline constexpr int TPS = 20;
|
||||
|
||||
ServerMainloop::ServerMainloop(Engine& engine) : engine(engine) {
|
||||
}
|
||||
|
||||
|
|
@ -39,7 +37,7 @@ void ServerMainloop::run() {
|
|||
"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;
|
||||
auto begin = system_clock::now();
|
||||
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 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(
|
||||
const glm::vec3& pos,
|
||||
const glm::vec3& right,
|
||||
|
|
@ -39,6 +24,23 @@ void Mesh::addPlane(
|
|||
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(
|
||||
const glm::vec3& pos,
|
||||
const glm::vec3& right,
|
||||
|
|
@ -56,14 +58,15 @@ void Mesh::addRect(
|
|||
}
|
||||
|
||||
void Mesh::addBox(const glm::vec3& pos, const glm::vec3& size) {
|
||||
addPlane(pos+Z*size, X*size, Y*size, Z);
|
||||
addPlane(pos-Z*size, -X*size, Y*size, -Z);
|
||||
UVRegion fullRegion (0, 0, 1, 1);
|
||||
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);
|
||||
addPlane(pos+Y*size, X*size, -Z*size, Y, fullRegion);
|
||||
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);
|
||||
addPlane(pos+X*size, -Z*size, Y*size, X, fullRegion);
|
||||
addPlane(pos-X*size, Z*size, Y*size, -X, fullRegion);
|
||||
}
|
||||
|
||||
void Mesh::addBox(
|
||||
|
|
@ -71,19 +74,29 @@ void Mesh::addBox(
|
|||
const glm::vec3& size,
|
||||
const UVRegion (&uvs)[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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -22,14 +22,16 @@ namespace model {
|
|||
const glm::vec3& pos,
|
||||
const glm::vec3& right,
|
||||
const glm::vec3& up,
|
||||
const glm::vec3& norm
|
||||
const glm::vec3& norm,
|
||||
const UVRegion& region
|
||||
);
|
||||
void addPlane(
|
||||
const glm::vec3& pos,
|
||||
const glm::vec3& right,
|
||||
const glm::vec3& up,
|
||||
const glm::vec3& norm,
|
||||
const UVRegion& region
|
||||
const UVRegion& region,
|
||||
const glm::mat4& transform
|
||||
);
|
||||
void addRect(
|
||||
const glm::vec3& pos,
|
||||
|
|
@ -45,6 +47,13 @@ namespace model {
|
|||
const UVRegion (&texfaces)[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);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ Cubemap::Cubemap(uint width, uint height, ImageFormat imageFormat)
|
|||
0,
|
||||
format,
|
||||
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 <GL/glew.h>
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
#include "assets/Assets.hpp"
|
||||
#include "graphics/core/DrawContext.hpp"
|
||||
|
|
@ -23,7 +26,7 @@ public:
|
|||
glGenTextures(1, &depthMap);
|
||||
glBindTexture(GL_TEXTURE_2D, depthMap);
|
||||
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_MAG_FILTER, GL_LINEAR);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
#include "Emitter.hpp"
|
||||
|
||||
#include <glm/gtc/random.hpp>
|
||||
#include <glm/gtc/constants.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
|
|
|
|||
|
|
@ -103,9 +103,14 @@ void guiutil::confirm(
|
|||
|
||||
panel->setGravity(Gravity::center_center);
|
||||
container->add(panel);
|
||||
|
||||
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));
|
||||
subpanel->setColor(glm::vec4(0));
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,7 @@ namespace guiutil {
|
|||
/// @brief Create element from XML
|
||||
/// @param source XML
|
||||
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(
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
#include <stack>
|
||||
#include "typedefs.hpp"
|
||||
#include "util/stringutil.hpp"
|
||||
#include "util/platform.hpp"
|
||||
#include <utility>
|
||||
|
||||
#include "io/devices/StdfsDevice.hpp"
|
||||
|
|
@ -44,6 +45,7 @@ void EnginePaths::prepare() {
|
|||
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() << "user files folder: " << fs::canonical(userFilesFolder).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("samples", &settings.display.samples);
|
||||
builder.add("framerate", &settings.display.framerate);
|
||||
builder.add("fullscreen", &settings.display.fullscreen);
|
||||
builder.add("limit-fps-iconified", &settings.display.limitFpsIconified);
|
||||
builder.add("window-mode", &settings.display.windowMode);
|
||||
|
||||
builder.section("camera");
|
||||
builder.add("sensitivity", &settings.camera.sensitivity);
|
||||
|
|
|
|||
|
|
@ -488,7 +488,10 @@ void PlayerController::updateInteraction(const Input& inputEvents, float delta)
|
|||
}
|
||||
const auto& bindings = inputEvents.getBindings();
|
||||
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 lclick = bindings.jactive(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 quatlib[];
|
||||
extern const luaL_Reg randomlib[];
|
||||
extern const luaL_Reg compressionlib[];
|
||||
extern const luaL_Reg text3dlib[]; // gfx.text3d
|
||||
extern const luaL_Reg timelib[];
|
||||
extern const luaL_Reg tomllib[];
|
||||
|
|
|
|||
|
|
@ -169,4 +169,5 @@ const luaL_Reg rigidbodylib[] = {
|
|||
{"set_crouching", lua::wrap<l_set_crouching>},
|
||||
{"get_body_type", lua::wrap<l_get_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>},
|
||||
{"set_interpolated", lua::wrap<l_set_interpolated>},
|
||||
{"exists", lua::wrap<l_exists>},
|
||||
{NULL, NULL}
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -51,4 +51,5 @@ const luaL_Reg transformlib[] = {
|
|||
{"set_size", lua::wrap<l_set_size>},
|
||||
{"get_rot", lua::wrap<l_get_rot>},
|
||||
{"set_rot", lua::wrap<l_set_rot>},
|
||||
{NULL, NULL}};
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,5 +2,5 @@
|
|||
|
||||
const luaL_Reg applib[] = {
|
||||
// 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[] = {
|
||||
{"load_texture", lua::wrap<l_load_texture>},
|
||||
{"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>},
|
||||
{"count_speakers", lua::wrap<l_audio_count_speakers>},
|
||||
{"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>>},
|
||||
{"encode_urlsafe", lua::wrap<l_encode<util::base64_urlsafe_encode>>},
|
||||
{"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[] = {
|
||||
{"tobytes", lua::wrap<l_tobytes>},
|
||||
{"frombytes", lua::wrap<l_frombytes>},
|
||||
{NULL, NULL}
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -783,5 +783,5 @@ const luaL_Reg blocklib[] = {
|
|||
{"has_tag", lua::wrap<l_has_tag>},
|
||||
{"__get_tags", lua::wrap<l_get_tags>},
|
||||
{"__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>},
|
||||
{"set_pos", lua::wrap<l_set_pos>},
|
||||
{"set_texture", lua::wrap<l_set_texture>},
|
||||
{NULL, NULL}
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -208,5 +208,5 @@ const luaL_Reg byteutillib[] = {
|
|||
{"tpack", lua::wrap<l_tpack>},
|
||||
{"unpack", lua::wrap<l_unpack>},
|
||||
{"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_up", lua::wrap<l_camera_getter<getter_up>>},
|
||||
{"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>},
|
||||
{"get_commands_list", lua::wrap<l_get_commands_list>},
|
||||
{"get_command_info", lua::wrap<l_get_command_info>},
|
||||
{NULL, NULL}};
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -20,6 +20,11 @@
|
|||
#include "util/platform.hpp"
|
||||
#include "world/Level.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;
|
||||
|
||||
|
|
@ -229,6 +234,24 @@ static int l_open_folder(lua::State* L) {
|
|||
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
|
||||
static int l_quit(lua::State*) {
|
||||
engine->quit();
|
||||
|
|
@ -284,7 +307,8 @@ const luaL_Reg corelib[] = {
|
|||
{"str_setting", lua::wrap<l_str_setting>},
|
||||
{"get_setting_info", lua::wrap<l_get_setting_info>},
|
||||
{"open_folder", lua::wrap<l_open_folder>},
|
||||
{"open_url", lua::wrap<l_open_url>},
|
||||
{"quit", lua::wrap<l_quit>},
|
||||
{"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>},
|
||||
{"raycast", lua::wrap<l_raycast>},
|
||||
{"reload_component", lua::wrap<l_reload_component>},
|
||||
{NULL, NULL}
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -188,34 +188,6 @@ static int l_list(lua::State* L) {
|
|||
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) {
|
||||
std::string path = lua::require_string(L, 1);
|
||||
if (path.find(':') != std::string::npos) {
|
||||
|
|
@ -419,8 +391,6 @@ const luaL_Reg filelib[] = {
|
|||
{"resolve", lua::wrap<l_resolve>},
|
||||
{"write_bytes", lua::wrap<l_write_bytes>},
|
||||
{"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_object", lua::wrap<l_read_combined_object>},
|
||||
{"is_writeable", lua::wrap<l_is_writeable>},
|
||||
|
|
@ -434,5 +404,6 @@ const luaL_Reg filelib[] = {
|
|||
{"__flush_descriptor", lua::wrap<l_flush_descriptor>},
|
||||
{"__close_descriptor", lua::wrap<l_close_descriptor>},
|
||||
{"__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>},
|
||||
{"get_generators", lua::wrap<l_get_generators>},
|
||||
{"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>},
|
||||
{"load_document", lua::wrap<l_gui_load_document>},
|
||||
{"__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_allow_pause", wrap_hud<l_set_allow_pause>},
|
||||
{"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>},
|
||||
{"reset_bindings", lua::wrap<l_reset_bindings>},
|
||||
{"set_enabled", lua::wrap<l_set_enabled>},
|
||||
{NULL, NULL}
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -256,5 +256,5 @@ const luaL_Reg inventorylib[] = {
|
|||
{"create", lua::wrap<l_create>},
|
||||
{"remove", lua::wrap<l_remove>},
|
||||
{"clone", lua::wrap<l_clone>},
|
||||
{NULL, NULL}
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -147,5 +147,5 @@ const luaL_Reg itemlib[] = {
|
|||
{"reload_script", lua::wrap<l_reload_script>},
|
||||
{"has_tag", lua::wrap<l_has_tag>},
|
||||
{"__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[] = {
|
||||
{"tostring", lua::wrap<l_json_stringify>},
|
||||
{"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>},
|
||||
{"from_quat", lua::wrap<l_from_quat>},
|
||||
{"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) {
|
||||
u64id_t id = lua::tointeger(L, 1);
|
||||
if (auto connection = network.getConnection(id)) {
|
||||
if (auto connection = network.getConnection(id, false)) {
|
||||
connection->close(true);
|
||||
}
|
||||
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) {
|
||||
u64id_t id = lua::tointeger(L, 1);
|
||||
if (auto server = network.getServer(id)) {
|
||||
if (auto server = network.getServer(id, false)) {
|
||||
server->close();
|
||||
}
|
||||
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) {
|
||||
u64id_t id = lua::tointeger(L, 1);
|
||||
auto connection = network.getConnection(id);
|
||||
auto connection = network.getConnection(id, false);
|
||||
if (connection == nullptr ||
|
||||
connection->getState() == network::ConnectionState::CLOSED) {
|
||||
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) {
|
||||
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)
|
||||
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);
|
||||
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) {
|
||||
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) {
|
||||
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());
|
||||
}
|
||||
|
||||
|
|
@ -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) {
|
||||
u64id_t id = lua::tointeger(L, 1);
|
||||
if (auto connection = network.getConnection(id)) {
|
||||
if (auto connection = network.getConnection(id, false)) {
|
||||
return lua::pushboolean(
|
||||
L,
|
||||
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) {
|
||||
u64id_t id = lua::tointeger(L, 1);
|
||||
if (auto connection = network.getConnection(id)) {
|
||||
if (auto connection = network.getConnection(id, false)) {
|
||||
return lua::pushboolean(
|
||||
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) {
|
||||
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::pushinteger(L, connection->getPort());
|
||||
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) {
|
||||
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, 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) {
|
||||
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 0;
|
||||
|
|
@ -402,6 +402,28 @@ static int l_get_total_download(lua::State* L, network::Network& network) {
|
|||
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) {
|
||||
std::vector<NetworkEvent> local_queue;
|
||||
{
|
||||
|
|
@ -517,5 +539,7 @@ const luaL_Reg networklib[] = {
|
|||
{"__get_address", wrap<l_get_address>},
|
||||
{"__is_serveropen", wrap<l_is_serveropen>},
|
||||
{"__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>},
|
||||
{"assemble", lua::wrap<l_pack_assemble>},
|
||||
{"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>},
|
||||
{"get_origin", wrap_hud<l_get_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_jump_height", lua::wrap<l_set_jump_height>},
|
||||
{"avoid_tag", lua::wrap<l_avoid_tag>},
|
||||
{NULL, NULL}
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,14 +2,14 @@
|
|||
#include <glm/glm.hpp>
|
||||
|
||||
#include "items/Inventory.hpp"
|
||||
#include "libentity.hpp"
|
||||
#include "objects/Entities.hpp"
|
||||
#include "objects/Entity.hpp"
|
||||
#include "objects/Player.hpp"
|
||||
#include "objects/Players.hpp"
|
||||
#include "objects/Entity.hpp"
|
||||
#include "physics/Hitbox.hpp"
|
||||
#include "window/Camera.hpp"
|
||||
#include "world/Level.hpp"
|
||||
#include "libentity.hpp"
|
||||
|
||||
using namespace scripting;
|
||||
|
||||
|
|
@ -53,7 +53,7 @@ static int l_set_vel(lua::State* L) {
|
|||
auto x = lua::tonumber(L, 2);
|
||||
auto y = lua::tonumber(L, 3);
|
||||
auto z = lua::tonumber(L, 4);
|
||||
|
||||
|
||||
if (auto hitbox = player->getHitbox()) {
|
||||
hitbox->velocity = glm::vec3(x, y, z);
|
||||
}
|
||||
|
|
@ -181,6 +181,21 @@ static int l_set_loading_chunks(lua::State* L) {
|
|||
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) {
|
||||
if (auto player = get_player(L, 1)) {
|
||||
if (player->selection.vox.id == BLOCK_VOID) {
|
||||
|
|
@ -307,6 +322,38 @@ static int l_set_suspended(lua::State* L) {
|
|||
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[] = {
|
||||
{"get_pos", lua::wrap<l_get_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>},
|
||||
{"is_loading_chunks", lua::wrap<l_is_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>},
|
||||
{"get_selected_block", lua::wrap<l_get_selected_block>},
|
||||
{"get_selected_entity", lua::wrap<l_get_selected_entity>},
|
||||
|
|
@ -341,5 +390,8 @@ const luaL_Reg playerlib[] = {
|
|||
{"set_name", lua::wrap<l_set_name>},
|
||||
{"create", lua::wrap<l_create>},
|
||||
{"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>},
|
||||
{"set_params", lua::wrap<l_set_params>},
|
||||
{"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>},
|
||||
{"slerp", lua::wrap<l_slerp>},
|
||||
{"tostring", lua::wrap<l_tostring>},
|
||||
{NULL, NULL}};
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -42,5 +42,5 @@ const luaL_Reg randomlib[] = {
|
|||
{"random", lua::wrap<l_random>},
|
||||
{"bytes", lua::wrap<l_bytes>},
|
||||
{"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_rotation", wrap_hud<l_set_rotation>},
|
||||
{"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_offset", lua::wrap<l_utc_offset>},
|
||||
{"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[] = {
|
||||
{"tostring", lua::wrap<l_toml_stringify>},
|
||||
{"parse", lua::wrap<l_toml_parse>},
|
||||
{NULL, NULL}
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -99,5 +99,5 @@ const luaL_Reg utf8lib[] = {
|
|||
{"lower", lua::wrap<l_lower>},
|
||||
{"encode", lua::wrap<l_encode>},
|
||||
{"escape", lua::wrap<l_escape>},
|
||||
{NULL, NULL}
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -228,7 +228,8 @@ const luaL_Reg vec2lib[] = {
|
|||
{"angle", lua::wrap<l_vec2_angle>},
|
||||
{"mix", lua::wrap<l_mix<2>>},
|
||||
{"rotate", lua::wrap<l_vec2_rotate>},
|
||||
{NULL, NULL}};
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
const luaL_Reg vec3lib[] = {
|
||||
{"add", lua::wrap<l_binop<3, std::plus>>},
|
||||
|
|
@ -246,7 +247,8 @@ const luaL_Reg vec3lib[] = {
|
|||
{"dot", lua::wrap<l_dot<3>>},
|
||||
{"spherical_rand", lua::wrap<l_spherical_rand>},
|
||||
{"mix", lua::wrap<l_mix<3>>},
|
||||
{NULL, NULL}};
|
||||
{nullptr, nullptr}
|
||||
};
|
||||
|
||||
const luaL_Reg vec4lib[] = {
|
||||
{"add", lua::wrap<l_binop<4, std::plus>>},
|
||||
|
|
@ -263,4 +265,5 @@ const luaL_Reg vec4lib[] = {
|
|||
{"pow", lua::wrap<l_pow<4>>},
|
||||
{"dot", lua::wrap<l_dot<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_fall_intensity", lua::wrap<l_get_fall_intensity>},
|
||||
{"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>},
|
||||
{"count_chunks", lua::wrap<l_count_chunks>},
|
||||
{"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[] = {
|
||||
{"tostring", lua::wrap<l_stringify>},
|
||||
{"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, "quat", quatlib);
|
||||
openlib(L, "random", randomlib);
|
||||
openlib(L, "compression", compressionlib);
|
||||
openlib(L, "toml", tomllib);
|
||||
openlib(L, "utf8", utf8lib);
|
||||
openlib(L, "vec2", vec2lib);
|
||||
|
|
|
|||
|
|
@ -1,11 +1,15 @@
|
|||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <random>
|
||||
#include <sstream>
|
||||
|
||||
#include "libs/api_lua.hpp"
|
||||
#include "debug/Logger.hpp"
|
||||
#include "engine/Engine.hpp"
|
||||
#include "devtools/DebuggingServer.hpp"
|
||||
#include "logic/scripting/scripting.hpp"
|
||||
|
||||
using namespace devtools;
|
||||
using namespace scripting;
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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) {
|
||||
if (lua::getglobal(L, "debug")) {
|
||||
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::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);
|
||||
}
|
||||
if (lua::getglobal(L, "math")) {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ int l_print(lua::State* L) {
|
|||
lua::pushvalue(L, i); /* value to print */
|
||||
lua::call(L, 1, 1);
|
||||
const char* s = lua::tostring(L, -1); /* get result */
|
||||
if (s == NULL)
|
||||
if (s == nullptr)
|
||||
return luaL_error(
|
||||
L,
|
||||
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) {
|
||||
#ifndef NDEBUG
|
||||
if (lua_type(L, idx) != LUA_TNUMBER && !lua_isnoneornil(L, idx)) {
|
||||
throw std::runtime_error("integer expected");
|
||||
throw std::runtime_error("number expected");
|
||||
}
|
||||
#endif
|
||||
return lua_tonumber(L, idx);
|
||||
|
|
@ -617,7 +617,9 @@ namespace lua {
|
|||
void remove_environment(lua::State*, int id);
|
||||
|
||||
inline void close(lua::State* L) {
|
||||
lua_close(L);
|
||||
if (L) {
|
||||
lua_close(L);
|
||||
}
|
||||
}
|
||||
|
||||
inline void addfunc(
|
||||
|
|
|
|||
|
|
@ -50,8 +50,8 @@ namespace lua {
|
|||
inline int type(lua::State* L, int idx) {
|
||||
return lua_type(L, idx);
|
||||
}
|
||||
inline const char* type_name(lua::State* L, int idx) {
|
||||
return lua_typename(L, idx);
|
||||
inline const char* type_name(lua::State* L, int tp) {
|
||||
return lua_typename(L, tp);
|
||||
}
|
||||
inline int rawget(lua::State* L, int idx = -2) {
|
||||
lua_rawget(L, idx);
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ int main(int argc, char** argv) {
|
|||
}
|
||||
std::signal(SIGTERM, sigterm_handler);
|
||||
|
||||
debug::Logger::init(coreParameters.userFolder.string()+"/latest.log");
|
||||
debug::Logger::init(coreParameters.userFolder.string() + "/latest.log");
|
||||
platform::configure_encoding();
|
||||
|
||||
auto& engine = Engine::getInstance();
|
||||
|
|
@ -33,7 +33,8 @@ int main(int argc, char** argv) {
|
|||
engine.initialize(std::move(coreParameters));
|
||||
engine.run();
|
||||
} 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)
|
||||
catch (const std::exception& err) {
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ namespace util {
|
|||
PseudoRandom(unsigned short seed) : seed(seed) {}
|
||||
|
||||
PseudoRandom() {
|
||||
seed = static_cast<unsigned short>(time(0));
|
||||
seed = static_cast<unsigned short>(time(nullptr));
|
||||
}
|
||||
|
||||
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"
|
||||
|
||||
#pragma comment(lib, "Ws2_32.lib")
|
||||
|
||||
#define NOMINMAX
|
||||
#include <curl/curl.h>
|
||||
#include <stdexcept>
|
||||
#include <limits>
|
||||
#include <queue>
|
||||
#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 "util/stringutil.hpp"
|
||||
|
||||
using namespace network;
|
||||
|
||||
inline constexpr int HTTP_OK = 200;
|
||||
inline constexpr int HTTP_BAD_GATEWAY = 502;
|
||||
|
||||
static debug::Logger logger("network");
|
||||
|
||||
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;
|
||||
}
|
||||
namespace network {
|
||||
std::unique_ptr<Requests> create_curl_requests();
|
||||
|
||||
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 = 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(
|
||||
std::shared_ptr<TcpConnection> connect_tcp(
|
||||
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;
|
||||
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(
|
||||
std::shared_ptr<TcpServer> open_tcp_server(
|
||||
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(
|
||||
std::shared_ptr<UdpConnection> connect_udp(
|
||||
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);
|
||||
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)
|
||||
: 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);
|
||||
|
||||
const auto& found = connections.find(id);
|
||||
if (found == connections.end()) {
|
||||
if (found == connections.end() || (!includePrivate && found->second->isPrivate())) {
|
||||
return nullptr;
|
||||
}
|
||||
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);
|
||||
if (found == servers.end()) {
|
||||
if (found == servers.end() || (!includePrivate && found->second->isPrivate())) {
|
||||
return nullptr;
|
||||
}
|
||||
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);
|
||||
|
||||
u64id_t id = nextConnection++;
|
||||
auto socket = SocketTcpConnection::connect(address, port, [id, callback]() {
|
||||
auto socket = connect_tcp(address, port, [id, callback]() {
|
||||
callback(id);
|
||||
});
|
||||
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 id = nextServer++;
|
||||
auto server = SocketTcpServer::openServer(id, this, port, handler);
|
||||
auto server = open_tcp_server(id, this, port, handler);
|
||||
servers[id] = std::move(server);
|
||||
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);
|
||||
|
||||
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);
|
||||
});
|
||||
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 id = nextServer++;
|
||||
auto server = SocketUdpServer::openServer(id, this, port, handler);
|
||||
auto server = open_udp_server(id, this, port, handler);
|
||||
servers[id] = std::move(server);
|
||||
return id;
|
||||
}
|
||||
|
|
@ -968,19 +164,19 @@ void Network::update() {
|
|||
}
|
||||
++socketiter;
|
||||
}
|
||||
auto serveriter = servers.begin();
|
||||
while (serveriter != servers.end()) {
|
||||
auto server = serveriter->second.get();
|
||||
if (!server->isOpen()) {
|
||||
serveriter = servers.erase(serveriter);
|
||||
continue;
|
||||
}
|
||||
++serveriter;
|
||||
}
|
||||
auto serveriter = servers.begin();
|
||||
while (serveriter != servers.end()) {
|
||||
auto server = serveriter->second.get();
|
||||
if (!server->isOpen()) {
|
||||
serveriter = servers.erase(serveriter);
|
||||
continue;
|
||||
}
|
||||
server->update();
|
||||
++serveriter;
|
||||
}
|
||||
}
|
||||
|
||||
std::unique_ptr<Network> Network::create(const NetworkSettings& settings) {
|
||||
auto requests = CurlRequests::create();
|
||||
return std::make_unique<Network>(std::move(requests));
|
||||
return std::make_unique<Network>(network::create_curl_requests());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,83 +1,16 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <mutex>
|
||||
|
||||
#include "typedefs.hpp"
|
||||
#include "settings.hpp"
|
||||
#include "util/Buffer.hpp"
|
||||
#include "delegates.hpp"
|
||||
#include "commons.hpp"
|
||||
|
||||
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;
|
||||
|
||||
[[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 {
|
||||
class TcpConnection : public ReadableConnection {
|
||||
public:
|
||||
~TcpConnection() override = default;
|
||||
|
||||
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 {
|
||||
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 {
|
||||
public:
|
||||
~TcpServer() override {}
|
||||
|
|
@ -112,6 +36,8 @@ namespace network {
|
|||
[[nodiscard]] TransportType getTransportType() const noexcept override {
|
||||
return TransportType::TCP;
|
||||
}
|
||||
|
||||
virtual void setMaxClientsConnected(int count) = 0;
|
||||
};
|
||||
|
||||
class UdpServer : public Server {
|
||||
|
|
@ -159,8 +85,8 @@ namespace network {
|
|||
long maxSize=0
|
||||
);
|
||||
|
||||
[[nodiscard]] Connection* getConnection(u64id_t id);
|
||||
[[nodiscard]] Server* getServer(u64id_t id) const;
|
||||
[[nodiscard]] Connection* getConnection(u64id_t id, bool includePrivate);
|
||||
[[nodiscard]] Server* getServer(u64id_t id, bool includePrivate) const;
|
||||
|
||||
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);
|
||||
|
|
|
|||
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;
|
||||
}
|
||||
|
||||
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 {
|
||||
return eid;
|
||||
}
|
||||
|
|
@ -250,6 +258,7 @@ dv::value Player::serialize() const {
|
|||
root["rotation"] = dv::to_value(rotation);
|
||||
root["spawnpoint"] = dv::to_value(spawnpoint);
|
||||
|
||||
root["interaction-distance"] = interactionDistance;
|
||||
root["flight"] = flight;
|
||||
root["noclip"] = noclip;
|
||||
root["suspended"] = suspended;
|
||||
|
|
@ -283,6 +292,8 @@ void Player::deserialize(const dv::value& src) {
|
|||
const auto& sparr = src["spawnpoint"];
|
||||
setSpawnPoint(glm::vec3(
|
||||
sparr[0].asNumber(), sparr[1].asNumber(), sparr[2].asNumber()));
|
||||
|
||||
src.at("interaction-distance").get(interactionDistance);
|
||||
|
||||
flight = src["flight"].asBoolean();
|
||||
noclip = src["noclip"].asBoolean();
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ class Player : public Serializable {
|
|||
int64_t id;
|
||||
std::string name;
|
||||
float speed;
|
||||
|
||||
int chosenSlot;
|
||||
glm::vec3 position;
|
||||
glm::vec3 spawnpoint {};
|
||||
|
|
@ -56,6 +57,8 @@ class Player : public Serializable {
|
|||
bool infiniteItems = true;
|
||||
bool instantDestruction = true;
|
||||
bool loadingChunks = true;
|
||||
float interactionDistance = 10.0f;
|
||||
|
||||
entityid_t eid = ENTITY_AUTO;
|
||||
entityid_t selectedEid = 0;
|
||||
|
||||
|
|
@ -90,6 +93,7 @@ public:
|
|||
void setChosenSlot(int index);
|
||||
|
||||
int getChosenSlot() const;
|
||||
|
||||
float getSpeed() const;
|
||||
|
||||
bool isSuspended() const;
|
||||
|
|
@ -110,6 +114,9 @@ public:
|
|||
bool isLoadingChunks() const;
|
||||
void setLoadingChunks(bool flag);
|
||||
|
||||
float getMaxInteractionDistance() const;
|
||||
void setMaxInteractionDistance(float distance);
|
||||
|
||||
entityid_t getEntity() const;
|
||||
void setEntity(entityid_t eid);
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
#include "Players.hpp"
|
||||
|
||||
#define GLM_ENABLE_EXPERIMENTAL
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
#include "Player.hpp"
|
||||
#include "items/Inventories.hpp"
|
||||
#include "world/Level.hpp"
|
||||
|
|
@ -20,6 +23,48 @@ Player* Players::get(int64_t id) const {
|
|||
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) {
|
||||
int64_t& nextPlayerID = level.getWorld()->getInfo().nextPlayerId;
|
||||
if (id == NONE) {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <glm/vec3.hpp>
|
||||
|
||||
#include "typedefs.hpp"
|
||||
#include "interfaces/Serializable.hpp"
|
||||
|
|
@ -34,6 +36,10 @@ public:
|
|||
|
||||
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;
|
||||
|
||||
void deserialize(const dv::value& src) override;
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
#include "data/dv_util.hpp"
|
||||
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
|
||||
void Transform::refresh() {
|
||||
combined = glm::mat4(1.0f);
|
||||
combined = glm::translate(combined, pos);
|
||||
|
|
|
|||
|
|
@ -19,8 +19,8 @@ struct AudioSettings {
|
|||
};
|
||||
|
||||
struct DisplaySettings {
|
||||
/// @brief Is window in full screen mode
|
||||
FlagSetting fullscreen {false};
|
||||
/// @brief Window mode (windowed/fullscreen/borderless)
|
||||
IntegerSetting windowMode {0, 0, 2};
|
||||
/// @brief Window width (pixels)
|
||||
IntegerSetting width {1280};
|
||||
/// @brief Window height (pixels)
|
||||
|
|
|
|||
|
|
@ -33,5 +33,14 @@ namespace util {
|
|||
last = 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 <vector>
|
||||
#include <string>
|
||||
#include <iomanip>
|
||||
|
||||
#include "io/engine_paths.hpp"
|
||||
#include "util/ArgsReader.hpp"
|
||||
|
|
@ -15,10 +16,17 @@ class ArgC {
|
|||
public:
|
||||
std::string keyword;
|
||||
std::function<bool()> execute;
|
||||
std::string args;
|
||||
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->execute = execute;
|
||||
this->args = args;
|
||||
this->help = help;
|
||||
}
|
||||
};
|
||||
|
|
@ -31,42 +39,51 @@ static bool perform_keyword(
|
|||
ArgC("--res", [¶ms, &reader]() -> bool {
|
||||
params.resFolder = reader.next();
|
||||
return true;
|
||||
}, "<path> - set resources directory."),
|
||||
}, "<path>", "set resources directory."),
|
||||
ArgC("--dir", [¶ms, &reader]() -> bool {
|
||||
params.userFolder = reader.next();
|
||||
return true;
|
||||
}, "<path> - set userfiles directory."),
|
||||
}, "<path>", "set userfiles directory."),
|
||||
ArgC("--project", [¶ms, &reader]() -> bool {
|
||||
params.projectFolder = reader.next();
|
||||
return true;
|
||||
}, "<path> - set project directory."),
|
||||
}, "<path>", "set project directory."),
|
||||
ArgC("--test", [¶ms, &reader]() -> bool {
|
||||
params.testMode = true;
|
||||
params.scriptFile = reader.next();
|
||||
return true;
|
||||
}, "<path> - test script file."),
|
||||
}, "<path>", "test script file."),
|
||||
ArgC("--script", [¶ms, &reader]() -> bool {
|
||||
params.testMode = false;
|
||||
params.scriptFile = reader.next();
|
||||
return true;
|
||||
}, "<path> - main script file."),
|
||||
}, "<path>", "main script file."),
|
||||
ArgC("--headless", [¶ms]() -> bool {
|
||||
params.headless = 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 {
|
||||
std::cout << ENGINE_VERSION_STRING << std::endl;
|
||||
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 {
|
||||
std::cout << "VoxelCore v" << ENGINE_VERSION_STRING << "\n\n";
|
||||
std::cout << "Command-line arguments:\n";
|
||||
for (auto& a : argumentsCommandline) {
|
||||
std::cout << a.keyword << " " << a.help << std::endl;
|
||||
std::cout << std::setw(24) << std::left << (a.keyword + " " + a.args);
|
||||
std::cout << "- " << a.help << std::endl;
|
||||
}
|
||||
std::cout << std::endl;
|
||||
return false;
|
||||
}, "- display this help.")
|
||||
}, "", "display this help.")
|
||||
};
|
||||
for (auto& a : argumentsCommandline) {
|
||||
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