mirror of https://github.com/minetest/minetest.git
Merge branch 'master' into doc-refactor-2
This commit is contained in:
commit
027d9f4217
|
@ -74,7 +74,7 @@ jobs:
|
|||
env:
|
||||
VCPKG_VERSION: 8eb57355a4ffb410a2e94c07b4dca2dffbee8e50
|
||||
# 2023.10.19
|
||||
vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp opengl-registry sdl2
|
||||
vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp sdl2
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
|
|
|
@ -71,7 +71,6 @@ files["builtin/mainmenu"] = {
|
|||
|
||||
read_globals = {
|
||||
"PLATFORM",
|
||||
"TOUCHSCREEN_GUI",
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -82,9 +81,3 @@ files["builtin/common/tests"] = {
|
|||
"assert",
|
||||
},
|
||||
}
|
||||
|
||||
files["builtin/fstk"] = {
|
||||
read_globals = {
|
||||
"TOUCHSCREEN_GUI",
|
||||
},
|
||||
}
|
||||
|
|
|
@ -42,6 +42,18 @@ set(DEFAULT_ENABLE_LTO TRUE)
|
|||
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
|
||||
set(DEFAULT_ENABLE_LTO FALSE)
|
||||
endif()
|
||||
#### LTO testing list ####
|
||||
# - Linux: seems to work always
|
||||
# - win32/msvc: works
|
||||
# - win32/gcc: fails to link
|
||||
# - win32/clang: works
|
||||
# - macOS on x86: seems to be fine
|
||||
# - macOS on ARM: crashes, see <https://github.com/minetest/minetest/issues/14397>
|
||||
# Note: since CMake has no easy architecture detection disabling for Mac entirely
|
||||
#### ####
|
||||
if((WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR APPLE)
|
||||
set(DEFAULT_ENABLE_LTO FALSE)
|
||||
endif()
|
||||
set(ENABLE_LTO ${DEFAULT_ENABLE_LTO} CACHE BOOL "Use Link Time Optimization")
|
||||
|
||||
set(DEFAULT_RUN_IN_PLACE FALSE)
|
||||
|
@ -66,20 +78,6 @@ if(NOT CMAKE_BUILD_TYPE)
|
|||
set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type: Debug or Release" FORCE)
|
||||
endif()
|
||||
|
||||
# FIXME: Windows build fails in multiple places to link, needs to be investigated.
|
||||
if (ENABLE_LTO AND NOT WIN32)
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT lto_supported OUTPUT lto_output)
|
||||
if(lto_supported)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
message(STATUS "LTO/IPO is enabled")
|
||||
else()
|
||||
message(STATUS "LTO/IPO requested but it is not supported by the compiler: ${lto_output}")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "LTO/IPO is not enabled")
|
||||
endif()
|
||||
|
||||
set(ENABLE_UPDATE_CHECKER (NOT ${DEVELOPMENT_BUILD}) CACHE BOOL
|
||||
"Whether to enable update checks by default")
|
||||
|
||||
|
@ -154,7 +152,7 @@ elseif(BUILD_CLIENT AND TARGET IrrlichtMt::IrrlichtMt)
|
|||
endif()
|
||||
message(STATUS "Found IrrlichtMt ${IrrlichtMt_VERSION}")
|
||||
|
||||
set(TARGET_VER_S 1.9.0mt14)
|
||||
set(TARGET_VER_S 1.9.0mt15)
|
||||
string(REPLACE "mt" "." TARGET_VER ${TARGET_VER_S})
|
||||
if(IrrlichtMt_VERSION VERSION_LESS ${TARGET_VER})
|
||||
message(FATAL_ERROR "At least IrrlichtMt ${TARGET_VER_S} is required to build")
|
||||
|
@ -163,6 +161,20 @@ elseif(BUILD_CLIENT AND TARGET IrrlichtMt::IrrlichtMt)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
if (ENABLE_LTO OR CMAKE_INTERPROCEDURAL_OPTIMIZATION)
|
||||
include(CheckIPOSupported)
|
||||
check_ipo_supported(RESULT lto_supported OUTPUT lto_output)
|
||||
if(lto_supported)
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE)
|
||||
message(STATUS "LTO/IPO is enabled")
|
||||
else()
|
||||
set(CMAKE_INTERPROCEDURAL_OPTIMIZATION FALSE)
|
||||
message(STATUS "LTO/IPO was requested but is not supported by the compiler: ${lto_output}")
|
||||
endif()
|
||||
else()
|
||||
message(STATUS "LTO/IPO is not enabled")
|
||||
endif()
|
||||
|
||||
if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
||||
if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "${GCC_MINIMUM_VERSION}")
|
||||
message(FATAL_ERROR "Insufficient gcc version, found ${CMAKE_CXX_COMPILER_VERSION}. "
|
||||
|
|
|
@ -18,7 +18,9 @@
|
|||
|
||||
|
||||
local BASE_SPACING = 0.1
|
||||
local SCROLL_BTN_WIDTH = TOUCHSCREEN_GUI and 0.8 or 0.5
|
||||
local function get_scroll_btn_width()
|
||||
return core.settings:get_bool("enable_touch") and 0.8 or 0.5
|
||||
end
|
||||
|
||||
local function buttonbar_formspec(self)
|
||||
if self.hidden then
|
||||
|
@ -39,7 +41,7 @@ local function buttonbar_formspec(self)
|
|||
|
||||
-- The number of buttons per page is always calculated as if the scroll
|
||||
-- buttons were visible.
|
||||
local avail_space = self.size.x - 2*BASE_SPACING - 2*SCROLL_BTN_WIDTH
|
||||
local avail_space = self.size.x - 2*BASE_SPACING - 2*get_scroll_btn_width()
|
||||
local btns_per_page = math.floor((avail_space - BASE_SPACING) / (btn_size + BASE_SPACING))
|
||||
|
||||
self.num_pages = math.ceil(#self.buttons / btns_per_page)
|
||||
|
@ -55,7 +57,7 @@ local function buttonbar_formspec(self)
|
|||
|
||||
local btn_start_x = self.pos.x + btn_spacing
|
||||
if show_scroll_btns then
|
||||
btn_start_x = btn_start_x + BASE_SPACING + SCROLL_BTN_WIDTH
|
||||
btn_start_x = btn_start_x + BASE_SPACING + get_scroll_btn_width()
|
||||
end
|
||||
|
||||
for i = first_btn, first_btn + btns_per_page - 1 do
|
||||
|
@ -80,7 +82,7 @@ local function buttonbar_formspec(self)
|
|||
y = self.pos.y + BASE_SPACING,
|
||||
}
|
||||
local btn_next_pos = {
|
||||
x = self.pos.x + self.size.x - BASE_SPACING - SCROLL_BTN_WIDTH,
|
||||
x = self.pos.x + self.size.x - BASE_SPACING - get_scroll_btn_width(),
|
||||
y = self.pos.y + BASE_SPACING,
|
||||
}
|
||||
|
||||
|
@ -88,11 +90,11 @@ local function buttonbar_formspec(self)
|
|||
self.btn_prev_name, self.btn_next_name))
|
||||
|
||||
table.insert(formspec, string.format("button[%f,%f;%f,%f;%s;<]",
|
||||
btn_prev_pos.x, btn_prev_pos.y, SCROLL_BTN_WIDTH, btn_size,
|
||||
btn_prev_pos.x, btn_prev_pos.y, get_scroll_btn_width(), btn_size,
|
||||
self.btn_prev_name))
|
||||
|
||||
table.insert(formspec, string.format("button[%f,%f;%f,%f;%s;>]",
|
||||
btn_next_pos.x, btn_next_pos.y, SCROLL_BTN_WIDTH, btn_size,
|
||||
btn_next_pos.x, btn_next_pos.y, get_scroll_btn_width(), btn_size,
|
||||
self.btn_next_name))
|
||||
end
|
||||
|
||||
|
|
|
@ -154,7 +154,9 @@ local function start_install(package, reason)
|
|||
|
||||
if conf_path then
|
||||
local conf = Settings(conf_path)
|
||||
conf:set("title", package.title)
|
||||
if not conf:get("title") then
|
||||
conf:set("title", package.title)
|
||||
end
|
||||
if not name_is_title then
|
||||
conf:set("name", package.name)
|
||||
end
|
||||
|
@ -642,8 +644,21 @@ local function fetch_pkgs()
|
|||
end
|
||||
end
|
||||
|
||||
local languages
|
||||
local current_language = core.get_language()
|
||||
if current_language ~= "" then
|
||||
languages = { current_language, "en;q=0.8" }
|
||||
else
|
||||
languages = { "en" }
|
||||
end
|
||||
|
||||
local http = core.get_http_api()
|
||||
local response = http.fetch_sync({ url = url })
|
||||
local response = http.fetch_sync({
|
||||
url = url,
|
||||
extra_headers = {
|
||||
"Accept-Language: " .. table.concat(languages, ", ")
|
||||
},
|
||||
})
|
||||
if not response.succeeded then
|
||||
return
|
||||
end
|
||||
|
@ -898,7 +913,7 @@ local function get_info_formspec(text)
|
|||
return table.concat({
|
||||
"formspec_version[6]",
|
||||
"size[15.75,9.5]",
|
||||
TOUCHSCREEN_GUI and "padding[0.01,0.01]" or "position[0.5,0.55]",
|
||||
core.settings:get_bool("enable_touch") and "padding[0.01,0.01]" or "position[0.5,0.55]",
|
||||
|
||||
"label[4,4.35;", text, "]",
|
||||
"container[0,", H - 0.8 - 0.375, "]",
|
||||
|
@ -928,7 +943,7 @@ function store.get_formspec(dlgdata)
|
|||
local formspec = {
|
||||
"formspec_version[6]",
|
||||
"size[15.75,9.5]",
|
||||
TOUCHSCREEN_GUI and "padding[0.01,0.01]" or "position[0.5,0.55]",
|
||||
core.settings:get_bool("enable_touch") and "padding[0.01,0.01]" or "position[0.5,0.55]",
|
||||
|
||||
"style[status,downloading,queued;border=false]",
|
||||
|
||||
|
@ -1175,8 +1190,8 @@ end
|
|||
|
||||
function store.handle_events(event)
|
||||
if event == "DialogShow" then
|
||||
-- On mobile, don't show the "MINETEST" header behind the dialog.
|
||||
mm_game_theme.set_engine(TOUCHSCREEN_GUI)
|
||||
-- On touchscreen, don't show the "MINETEST" header behind the dialog.
|
||||
mm_game_theme.set_engine(core.settings:get_bool("enable_touch"))
|
||||
|
||||
-- If the store is already loaded, auto-install packages here.
|
||||
do_auto_install()
|
||||
|
|
|
@ -150,6 +150,8 @@ function pkgmgr.get_mods(path, virtual_path, listing, modpack)
|
|||
toadd.virtual_path = mod_virtual_path
|
||||
toadd.type = "mod"
|
||||
|
||||
pkgmgr.update_translations({ toadd })
|
||||
|
||||
-- Check modpack.txt
|
||||
-- Note: modpack.conf is already checked above
|
||||
local modpackfile = io.open(mod_path .. DIR_DELIM .. "modpack.txt")
|
||||
|
@ -189,6 +191,8 @@ function pkgmgr.get_texture_packs()
|
|||
load_texture_packs(txtpath_system, retval)
|
||||
end
|
||||
|
||||
pkgmgr.update_translations(retval)
|
||||
|
||||
table.sort(retval, function(a, b)
|
||||
return a.title:lower() < b.title:lower()
|
||||
end)
|
||||
|
@ -775,6 +779,29 @@ function pkgmgr.update_gamelist()
|
|||
table.sort(pkgmgr.games, function(a, b)
|
||||
return a.title:lower() < b.title:lower()
|
||||
end)
|
||||
pkgmgr.update_translations(pkgmgr.games)
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
function pkgmgr.update_translations(list)
|
||||
for _, item in ipairs(list) do
|
||||
local info = core.get_content_info(item.path)
|
||||
assert(info.path)
|
||||
assert(info.textdomain)
|
||||
|
||||
assert(not item.is_translated)
|
||||
item.is_translated = true
|
||||
|
||||
if info.title and info.title ~= "" then
|
||||
item.title = core.get_content_translation(info.path, info.textdomain,
|
||||
core.translate(info.textdomain, info.title))
|
||||
end
|
||||
|
||||
if info.description and info.description ~= "" then
|
||||
item.description = core.get_content_translation(info.path, info.textdomain,
|
||||
core.translate(info.textdomain, info.description))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
|
|
@ -316,8 +316,8 @@ local function check_requirements(name, requires)
|
|||
local special = {
|
||||
android = PLATFORM == "Android",
|
||||
desktop = PLATFORM ~= "Android",
|
||||
touchscreen_gui = TOUCHSCREEN_GUI,
|
||||
keyboard_mouse = not TOUCHSCREEN_GUI,
|
||||
touchscreen_gui = core.settings:get_bool("enable_touch"),
|
||||
keyboard_mouse = not core.settings:get_bool("enable_touch"),
|
||||
shaders_support = shaders_support,
|
||||
shaders = core.settings:get_bool("enable_shaders") and shaders_support,
|
||||
opengl = video_driver == "opengl",
|
||||
|
@ -449,13 +449,14 @@ local function get_formspec(dialogdata)
|
|||
|
||||
local extra_h = 1 -- not included in tabsize.height
|
||||
local tabsize = {
|
||||
width = TOUCHSCREEN_GUI and 16.5 or 15.5,
|
||||
height = TOUCHSCREEN_GUI and (10 - extra_h) or 12,
|
||||
width = core.settings:get_bool("enable_touch") and 16.5 or 15.5,
|
||||
height = core.settings:get_bool("enable_touch") and (10 - extra_h) or 12,
|
||||
}
|
||||
|
||||
local scrollbar_w = TOUCHSCREEN_GUI and 0.6 or 0.4
|
||||
local scrollbar_w = core.settings:get_bool("enable_touch") and 0.6 or 0.4
|
||||
|
||||
local left_pane_width = TOUCHSCREEN_GUI and 4.5 or 4.25
|
||||
local left_pane_width = core.settings:get_bool("enable_touch") and 4.5 or 4.25
|
||||
local left_pane_padding = 0.25
|
||||
local search_width = left_pane_width + scrollbar_w - (0.75 * 2)
|
||||
|
||||
local back_w = 3
|
||||
|
@ -468,7 +469,7 @@ local function get_formspec(dialogdata)
|
|||
local fs = {
|
||||
"formspec_version[6]",
|
||||
"size[", tostring(tabsize.width), ",", tostring(tabsize.height + extra_h), "]",
|
||||
TOUCHSCREEN_GUI and "padding[0.01,0.01]" or "",
|
||||
core.settings:get_bool("enable_touch") and "padding[0.01,0.01]" or "",
|
||||
"bgcolor[#0000]",
|
||||
|
||||
-- HACK: this is needed to allow resubmitting the same formspec
|
||||
|
@ -516,9 +517,9 @@ local function get_formspec(dialogdata)
|
|||
y = y + 0.82
|
||||
end
|
||||
fs[#fs + 1] = ("box[0,%f;%f,0.8;%s]"):format(
|
||||
y, left_pane_width, other_page.id == page_id and "#467832FF" or "#3339")
|
||||
y, left_pane_width-left_pane_padding, other_page.id == page_id and "#467832FF" or "#3339")
|
||||
fs[#fs + 1] = ("button[0,%f;%f,0.8;page_%s;%s]")
|
||||
:format(y, left_pane_width, other_page.id, fgettext(other_page.title))
|
||||
:format(y, left_pane_width-left_pane_padding, other_page.id, fgettext(other_page.title))
|
||||
y = y + 0.82
|
||||
end
|
||||
|
||||
|
@ -641,11 +642,22 @@ local function buttonhandler(this, fields)
|
|||
local value = core.is_yes(fields.show_advanced)
|
||||
core.settings:set_bool("show_advanced", value)
|
||||
write_settings_early()
|
||||
end
|
||||
|
||||
-- enable_touch is a checkbox in a setting component. We handle this
|
||||
-- setting differently so we can hide/show pages using the next if-statement
|
||||
if fields.enable_touch ~= nil then
|
||||
local value = core.is_yes(fields.enable_touch)
|
||||
core.settings:set_bool("enable_touch", value)
|
||||
write_settings_early()
|
||||
end
|
||||
|
||||
if fields.show_advanced ~= nil or fields.enable_touch ~= nil then
|
||||
local suggested_page_id = update_filtered_pages(dialogdata.query)
|
||||
|
||||
dialogdata.components = nil
|
||||
|
||||
if not filtered_page_by_id[dialogdata.page_id] then
|
||||
dialogdata.components = nil
|
||||
dialogdata.leftscroll = 0
|
||||
dialogdata.rightscroll = 0
|
||||
|
||||
|
|
|
@ -114,12 +114,13 @@ local function get_formspec(tabview, name, tabdata)
|
|||
modscreenshot = defaulttexturedir .. "no_screenshot.png"
|
||||
end
|
||||
|
||||
local info = core.get_content_info(selected_pkg.path)
|
||||
local desc = fgettext("No package description available")
|
||||
if info.description and info.description:trim() ~= "" then
|
||||
desc = core.formspec_escape(info.description)
|
||||
if selected_pkg.description and selected_pkg.description:trim() ~= "" then
|
||||
desc = core.formspec_escape(selected_pkg.description)
|
||||
end
|
||||
|
||||
local info = core.get_content_info(selected_pkg.path)
|
||||
|
||||
local title_and_name
|
||||
if selected_pkg.type == "game" then
|
||||
title_and_name = selected_pkg.name
|
||||
|
|
|
@ -94,7 +94,7 @@ function singleplayer_refresh_gamebar()
|
|||
|
||||
local btnbar = buttonbar_create(
|
||||
"game_button_bar",
|
||||
TOUCHSCREEN_GUI and {x = 0, y = 7.25} or {x = 0, y = 7.475},
|
||||
core.settings:get_bool("enable_touch") and {x = 0, y = 7.25} or {x = 0, y = 7.475},
|
||||
{x = 15.5, y = 1.25},
|
||||
"#000000",
|
||||
game_buttonbar_button_handler)
|
||||
|
|
|
@ -148,6 +148,11 @@ invert_hotbar_mouse_wheel (Hotbar: Invert mouse wheel direction) bool false
|
|||
|
||||
[*Touchscreen]
|
||||
|
||||
# Enables touchscreen mode, allowing you to play the game with a touchscreen.
|
||||
#
|
||||
# Requires: !android
|
||||
enable_touch (Enable touchscreen) bool true
|
||||
|
||||
# The length in pixels it takes for touchscreen interaction to start.
|
||||
#
|
||||
# Requires: touchscreen_gui
|
||||
|
@ -2014,9 +2019,10 @@ ask_reconnect_on_crash (Ask to reconnect after crash) bool false
|
|||
|
||||
[**Server/Env Performance]
|
||||
|
||||
# Length of a server tick and the interval at which objects are generally updated over
|
||||
# network, stated in seconds.
|
||||
dedicated_server_step (Dedicated server step) float 0.09 0.0
|
||||
# Length of a server tick (the interval at which everything is generally updated),
|
||||
# stated in seconds.
|
||||
# Does not apply to sessions hosted from the client menu.
|
||||
dedicated_server_step (Dedicated server step) float 0.09 0.0 1.0
|
||||
|
||||
# Whether players are shown to clients without any range limit.
|
||||
# Deprecated, use the setting player_transfer_distance instead.
|
||||
|
@ -2086,8 +2092,7 @@ liquid_update (Liquid update tick) float 1.0 0.001
|
|||
# At this distance the server will aggressively optimize which blocks are sent to
|
||||
# clients.
|
||||
# Small values potentially improve performance a lot, at the expense of visible
|
||||
# rendering glitches (some blocks will not be rendered under water and in caves,
|
||||
# as well as sometimes on land).
|
||||
# rendering glitches (some blocks might not be rendered correctly in caves).
|
||||
# Setting this to a value greater than max_block_send_distance disables this
|
||||
# optimization.
|
||||
# Stated in MapBlocks (16 nodes).
|
||||
|
|
|
@ -27,7 +27,7 @@ General options and their default values:
|
|||
ENABLE_REDIS=ON - Build with libhiredis; Enables use of Redis map backend
|
||||
ENABLE_SPATIAL=ON - Build with LibSpatial; Speeds up AreaStores
|
||||
ENABLE_SOUND=ON - Build with OpenAL, libogg & libvorbis; in-game sounds
|
||||
ENABLE_LTO=ON - Build with IPO/LTO optimizations (smaller and more efficient than regular build)
|
||||
ENABLE_LTO=<varies> - Build with IPO/LTO optimizations (smaller and more efficient than regular build)
|
||||
ENABLE_LUAJIT=ON - Build with LuaJIT (much faster than non-JIT Lua)
|
||||
ENABLE_PROMETHEUS=OFF - Build with Prometheus metrics exporter (listens on tcp/30000 by default)
|
||||
ENABLE_SYSTEM_GMP=ON - Use GMP from system (much faster than bundled mini-gmp)
|
||||
|
@ -37,7 +37,7 @@ General options and their default values:
|
|||
INSTALL_DEVTEST=FALSE - Whether the Development Test game should be installed alongside Minetest
|
||||
USE_GPROF=FALSE - Enable profiling using GProf
|
||||
VERSION_EXTRA= - Text to append to version (e.g. VERSION_EXTRA=foobar -> Minetest 0.4.9-foobar)
|
||||
ENABLE_TOUCH=FALSE - Enable Touchscreen support (requires support by IrrlichtMt)
|
||||
ENABLE_TOUCH=FALSE - Enable touchscreen support by default (requires support by IrrlichtMt)
|
||||
|
||||
Library specific options:
|
||||
|
||||
|
|
|
@ -6,7 +6,9 @@
|
|||
|
||||
Install dependencies with homebrew:
|
||||
|
||||
brew install cmake freetype gettext gmp hiredis jpeg jsoncpp leveldb libogg libpng libvorbis luajit zstd gettext
|
||||
```bash
|
||||
brew install cmake freetype gettext gmp hiredis jpeg-turbo jsoncpp leveldb libogg libpng libvorbis luajit zstd gettext
|
||||
```
|
||||
|
||||
## Download
|
||||
|
||||
|
@ -43,6 +45,6 @@ codesign --force --deep -s - macos/minetest.app
|
|||
|
||||
## Run
|
||||
|
||||
```
|
||||
```bash
|
||||
open ./build/macos/minetest.app
|
||||
```
|
|
@ -14,7 +14,7 @@ It is highly recommended to use vcpkg as package manager.
|
|||
After you successfully built vcpkg you can easily install the required libraries:
|
||||
|
||||
```powershell
|
||||
vcpkg install zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp opengl-registry gettext sdl2 --triplet x64-windows
|
||||
vcpkg install zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp gettext sdl2 --triplet x64-windows
|
||||
```
|
||||
|
||||
* **Don't forget about IrrlichtMt.** The easiest way is to clone it to `lib/irrlichtmt`:
|
||||
|
|
|
@ -57,7 +57,8 @@ The game directory can contain the following files:
|
|||
* `game.conf`, with the following keys:
|
||||
* `title`: Required, a human-readable title to address the game, e.g. `title = Minetest Game`.
|
||||
* `name`: (Deprecated) same as title.
|
||||
* `description`: Short description to be shown in the content tab
|
||||
* `description`: Short description to be shown in the content tab.
|
||||
See [Translating content meta](#translating-content-meta).
|
||||
* `allowed_mapgens = <comma-separated mapgens>`
|
||||
e.g. `allowed_mapgens = v5,v6,flat`
|
||||
Mapgens not in this list are removed from the list of mapgens for the
|
||||
|
@ -83,10 +84,11 @@ The game directory can contain the following files:
|
|||
`enable_damage`, `creative_mode`, `enable_server`.
|
||||
* `map_persistent`: Specifies whether newly created worlds should use
|
||||
a persistent map backend. Defaults to `true` (= "sqlite3")
|
||||
* `author`: The author of the game. It only appears when downloaded from
|
||||
ContentDB.
|
||||
* `author`: The author's ContentDB
|
||||
* `release`: Ignore this: Should only ever be set by ContentDB, as it is
|
||||
an internal ID used to track versions.
|
||||
* `textdomain`: Textdomain used to translate description. Defaults to game ID.
|
||||
See [Translating content meta](#translating-content-meta).
|
||||
* `minetest.conf`:
|
||||
Used to set default settings when running this game.
|
||||
* `settingtypes.txt`:
|
||||
|
@ -148,8 +150,8 @@ The file is a key-value store of modpack details.
|
|||
|
||||
* `name`: The modpack name. Allows Minetest to determine the modpack name even
|
||||
if the folder is wrongly named.
|
||||
* `description`: Description of mod to be shown in the Mods tab of the main
|
||||
menu.
|
||||
* `title`: A human-readable title to address the modpack. See [Translating content meta](#translating-content-meta).
|
||||
* `description`: Description of mod to be shown in the Mods tab of the main menu.
|
||||
* `author`: The author of the modpack. It only appears when downloaded from
|
||||
ContentDB.
|
||||
* `release`: Ignore this: Should only ever be set by ContentDB, as it is an
|
||||
|
@ -193,17 +195,19 @@ A `Settings` file that provides meta information about the mod.
|
|||
|
||||
* `name`: The mod name. Allows Minetest to determine the mod name even if the
|
||||
folder is wrongly named.
|
||||
* `title`: A human-readable title to address the mod. See
|
||||
[Translating content meta](#translating-content-meta).
|
||||
* `description`: Description of mod to be shown in the Mods tab of the main
|
||||
menu.
|
||||
menu. See [Translating content meta](#translating-content-meta).
|
||||
* `depends`: A comma separated list of dependencies. These are mods that must be
|
||||
loaded before this mod.
|
||||
* `optional_depends`: A comma separated list of optional dependencies.
|
||||
Like a dependency, but no error if the mod doesn't exist.
|
||||
* `author`: The author of the mod. It only appears when downloaded from
|
||||
ContentDB.
|
||||
* `author`: The author's ContentDB username.
|
||||
* `release`: Ignore this: Should only ever be set by ContentDB, as it is an
|
||||
internal ID used to track versions.
|
||||
* `title`: A human-readable title to address the mod.
|
||||
* `textdomain`: Textdomain used to translate title and description. Defaults to modname.
|
||||
See [Translating content meta](#translating-content-meta).
|
||||
|
||||
### `screenshot.png`
|
||||
|
||||
|
@ -4078,10 +4082,41 @@ markup, e.g. `minetest.get_translated_string("fr", S("Hello"))`.
|
|||
The `lang_code` to use for a given player can be retrieved from
|
||||
the table returned by `minetest.get_player_information(name)`.
|
||||
|
||||
> [!WARNING]
|
||||
> This functionality should only be used for sorting, filtering or
|
||||
similar purposes. You do not need to use this to get translated strings to show
|
||||
up on the client.
|
||||
> [!NOTE]
|
||||
> This functionality should only be used for sorting, filtering
|
||||
or similar purposes. You do not need to use this to get translated strings
|
||||
to show up on the client.
|
||||
|
||||
## Translating Content Meta
|
||||
|
||||
You can translate content meta, such as `title` and `description`, by placing
|
||||
translations in a `locale/DOMAIN.LANG.tr` file. The textdomain defaults to the
|
||||
content name, but can be customised using `textdomain` in the content's .conf.
|
||||
|
||||
### Mods and Texture Packs
|
||||
|
||||
Say you have a mod called `mymod` with a short description in mod.conf:
|
||||
|
||||
description = This is the short description
|
||||
|
||||
Minetest will look for translations in the `mymod` textdomain as there's no
|
||||
textdomain specified in mod.conf. For example, `mymod/locale/mymod.fr.tr`:
|
||||
|
||||
# textdomain:mymod
|
||||
This is the short description=Voici la description succincte
|
||||
|
||||
### Games and Modpacks
|
||||
|
||||
For games and modpacks, Minetest will look for the textdomain in all mods.
|
||||
|
||||
Say you have a game called `mygame` with the following game.conf:
|
||||
|
||||
description = This is the game's short description
|
||||
textdomain = mygame
|
||||
|
||||
Minetest will then look for the textdomain `mygame` in all mods, for example,
|
||||
`mygame/mods/anymod/locale/mygame.fr.tr`. Note that it is still recommended that your
|
||||
textdomain match the mod name, but this isn't required.
|
||||
|
||||
|
||||
|
||||
|
@ -5367,6 +5402,9 @@ Minetest includes the following settings to control behavior of privileges:
|
|||
* `minetest.sha1(data, [raw])`: returns the sha1 hash of data
|
||||
* `data`: string of data to hash
|
||||
* `raw`: return raw bytes instead of hex digits, default: false
|
||||
* `minetest.sha256(data, [raw])`: returns the sha256 hash of data
|
||||
* `data`: string of data to hash
|
||||
* `raw`: return raw bytes instead of hex digits, default: false
|
||||
* `minetest.colorspec_to_colorstring(colorspec)`: Converts a ColorSpec to a
|
||||
ColorString. If the ColorSpec is invalid, returns `nil`.
|
||||
* `colorspec`: The ColorSpec to convert
|
||||
|
|
|
@ -318,6 +318,7 @@ Package - content which is downloadable from the content db, may or may not be i
|
|||
description = "description",
|
||||
author = "author",
|
||||
path = "path/to/content",
|
||||
textdomain = "textdomain", -- textdomain to translate title / description with
|
||||
depends = {"mod", "names"}, -- mods only
|
||||
optional_depends = {"mod", "names"}, -- mods only
|
||||
}
|
||||
|
@ -335,6 +336,13 @@ Package - content which is downloadable from the content db, may or may not be i
|
|||
error_message = "", -- message or nil
|
||||
}
|
||||
```
|
||||
* `core.get_content_translation(path, domain, string)`
|
||||
* Translates `string` using `domain` in content directory at `path`.
|
||||
* Textdomains will be found by looking through all locale folders.
|
||||
* String should contain translation markup from `core.translate(textdomain, ...)`.
|
||||
* Ex: `core.get_content_translation("mods/mymod", "mymod", core.translate("mymod", "Hello World"))`
|
||||
will translate "Hello World" into the current user's language
|
||||
using `mods/mymod/locale/mymod.fr.tr`.
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -25,8 +25,14 @@ texture pack. The name must not be "base".
|
|||
|
||||
A key-value config file with the following keys:
|
||||
|
||||
* `title` - human readable title
|
||||
* `name`: The texture pack name. Allows Minetest to determine the texture pack name even if
|
||||
the folder is wrongly named.
|
||||
* `title` - human-readable title
|
||||
* `description` - short description, shown in the content tab
|
||||
* `author`: The author's ContentDB username.
|
||||
* `textdomain`: Textdomain used to translate title and description.
|
||||
Defaults to the texture pack name.
|
||||
See [Translating content meta](lua_api.md#translating-content-meta).
|
||||
|
||||
### `description.txt`
|
||||
|
||||
|
@ -202,7 +208,8 @@ Here are targets you can choose from:
|
|||
Nodes support all targets, but other items only support 'inventory'
|
||||
and 'wield'.
|
||||
|
||||
¹ : `N` is an integer [0,255]. Sets align_style = "world" and scale = N on the tile, refer to lua_api.md for details.
|
||||
¹ : `N` is an integer [0,255]. Sets align_style = "world" and scale = N on the tile,
|
||||
refer to lua_api.md for details.
|
||||
|
||||
### Using the Special Targets
|
||||
|
||||
|
|
|
@ -77,6 +77,29 @@ local function test_metadata(meta)
|
|||
assert(not meta:equals(compare_meta))
|
||||
end
|
||||
|
||||
local function test_metadata_compat(meta)
|
||||
-- key/value removal using set_string (undocumented, deprecated way)
|
||||
meta:set_string("key", "value")
|
||||
assert(meta:get_string("key") == "value")
|
||||
meta:set_string("key", nil) -- ignore warning
|
||||
assert(meta:to_table().fields["key"] == nil)
|
||||
|
||||
-- undocumented but supported consequence of Lua's
|
||||
-- automatic string <--> number cast
|
||||
meta:set_string("key", 2)
|
||||
assert(meta:get_string("key") == "2")
|
||||
|
||||
-- from_table with non-string keys (supported)
|
||||
local values = meta:to_table()
|
||||
values.fields["new"] = 420
|
||||
meta:from_table(values)
|
||||
assert(meta:get_int("new") == 420)
|
||||
values.fields["new"] = nil
|
||||
meta:from_table(values)
|
||||
assert(meta:get("new") == nil)
|
||||
end
|
||||
|
||||
|
||||
local storage_a = core.get_mod_storage()
|
||||
local storage_b = core.get_mod_storage()
|
||||
local function test_mod_storage()
|
||||
|
@ -86,7 +109,9 @@ end
|
|||
unittests.register("test_mod_storage", test_mod_storage)
|
||||
|
||||
local function test_item_metadata()
|
||||
test_metadata(ItemStack("unittest:coal_lump"):get_meta())
|
||||
local meta = ItemStack("unittest:coal_lump"):get_meta()
|
||||
test_metadata(meta)
|
||||
test_metadata_compat(meta)
|
||||
end
|
||||
unittests.register("test_item_metadata", test_item_metadata)
|
||||
|
||||
|
|
|
@ -111,6 +111,13 @@ unittests.register("test_punch_node", function(_, pos)
|
|||
-- currently failing: assert(on_punch_called)
|
||||
end, {map=true})
|
||||
|
||||
local function test_hashing()
|
||||
local input = "hello\000world"
|
||||
assert(core.sha1(input) == "f85b420f1e43ebf88649dfcab302b898d889606c")
|
||||
assert(core.sha256(input) == "b206899bc103669c8e7b36de29d73f95b46795b508aa87d612b2ce84bfb29df2")
|
||||
end
|
||||
unittests.register("test_hashing", test_hashing)
|
||||
|
||||
local function test_compress()
|
||||
-- This text should be compressible, to make sure the results are... normal
|
||||
local text = "The\000 icey canoe couldn't move very well on the\128 lake. The\000 ice was too stiff and the icey canoe's paddles simply wouldn't punch through."
|
||||
|
|
|
@ -1 +1 @@
|
|||
1.9.0mt14
|
||||
1.9.0mt15
|
||||
|
|
|
@ -109,9 +109,10 @@ if(BUILD_CLIENT AND ENABLE_SOUND)
|
|||
endif()
|
||||
endif()
|
||||
|
||||
option(ENABLE_TOUCH "Enable Touchscreen support" FALSE)
|
||||
option(ENABLE_TOUCH "Enable touchscreen by default" FALSE)
|
||||
if(ENABLE_TOUCH)
|
||||
add_definitions(-DHAVE_TOUCHSCREENGUI)
|
||||
message(STATUS "Touchscreen support enabled by default.")
|
||||
add_definitions(-DENABLE_TOUCH)
|
||||
endif()
|
||||
|
||||
if(BUILD_CLIENT)
|
||||
|
|
|
@ -138,8 +138,8 @@ void Camera::notifyFovChange()
|
|||
// Returns the fractional part of x
|
||||
inline f32 my_modf(f32 x)
|
||||
{
|
||||
double dummy;
|
||||
return modf(x, &dummy);
|
||||
float dummy;
|
||||
return std::modf(x, &dummy);
|
||||
}
|
||||
|
||||
void Camera::step(f32 dtime)
|
||||
|
@ -407,10 +407,10 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio)
|
|||
f32 bobdir = (m_view_bobbing_anim < 0.5) ? 1.0 : -1.0;
|
||||
|
||||
f32 bobknob = 1.2;
|
||||
f32 bobtmp = sin(pow(bobfrac, bobknob) * M_PI);
|
||||
f32 bobtmp = std::sin(std::pow(bobfrac, bobknob) * M_PI);
|
||||
|
||||
v3f bobvec = v3f(
|
||||
0.3 * bobdir * sin(bobfrac * M_PI),
|
||||
0.3 * bobdir * std::sin(bobfrac * M_PI),
|
||||
-0.28 * bobtmp * bobtmp,
|
||||
0.);
|
||||
|
||||
|
@ -531,11 +531,9 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio)
|
|||
addArmInertia(yaw);
|
||||
|
||||
// Position the wielded item
|
||||
//v3f wield_position = v3f(45, -35, 65);
|
||||
v3f wield_position = v3f(m_wieldmesh_offset.X, m_wieldmesh_offset.Y, 65);
|
||||
//v3f wield_rotation = v3f(-100, 120, -100);
|
||||
v3f wield_rotation = v3f(-100, 120, -100);
|
||||
wield_position.Y += fabs(m_wield_change_timer)*320 - 40;
|
||||
wield_position.Y += std::abs(m_wield_change_timer)*320 - 40;
|
||||
if(m_digging_anim < 0.05 || m_digging_anim > 0.5)
|
||||
{
|
||||
f32 frac = 1.0;
|
||||
|
@ -543,33 +541,29 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio)
|
|||
frac = 2.0 * (m_digging_anim - 0.5);
|
||||
// This value starts from 1 and settles to 0
|
||||
f32 ratiothing = std::pow((1.0f - tool_reload_ratio), 0.5f);
|
||||
//f32 ratiothing2 = pow(ratiothing, 0.5f);
|
||||
f32 ratiothing2 = (easeCurve(ratiothing*0.5))*2.0;
|
||||
wield_position.Y -= frac * 25.0 * pow(ratiothing2, 1.7f);
|
||||
//wield_position.Z += frac * 5.0 * ratiothing2;
|
||||
wield_position.X -= frac * 35.0 * pow(ratiothing2, 1.1f);
|
||||
wield_rotation.Y += frac * 70.0 * pow(ratiothing2, 1.4f);
|
||||
//wield_rotation.X -= frac * 15.0 * pow(ratiothing2, 1.4f);
|
||||
//wield_rotation.Z += frac * 15.0 * pow(ratiothing2, 1.0f);
|
||||
wield_position.Y -= frac * 25.0f * std::pow(ratiothing2, 1.7f);
|
||||
wield_position.X -= frac * 35.0f * std::pow(ratiothing2, 1.1f);
|
||||
wield_rotation.Y += frac * 70.0f * std::pow(ratiothing2, 1.4f);
|
||||
}
|
||||
if (m_digging_button != -1)
|
||||
{
|
||||
f32 digfrac = m_digging_anim;
|
||||
wield_position.X -= 50 * sin(pow(digfrac, 0.8f) * M_PI);
|
||||
wield_position.Y += 24 * sin(digfrac * 1.8 * M_PI);
|
||||
wield_position.X -= 50 * std::sin(std::pow(digfrac, 0.8f) * M_PI);
|
||||
wield_position.Y += 24 * std::sin(digfrac * 1.8 * M_PI);
|
||||
wield_position.Z += 25 * 0.5;
|
||||
|
||||
// Euler angles are PURE EVIL, so why not use quaternions?
|
||||
core::quaternion quat_begin(wield_rotation * core::DEGTORAD);
|
||||
core::quaternion quat_end(v3f(80, 30, 100) * core::DEGTORAD);
|
||||
core::quaternion quat_slerp;
|
||||
quat_slerp.slerp(quat_begin, quat_end, sin(digfrac * M_PI));
|
||||
quat_slerp.slerp(quat_begin, quat_end, std::sin(digfrac * M_PI));
|
||||
quat_slerp.toEuler(wield_rotation);
|
||||
wield_rotation *= core::RADTODEG;
|
||||
} else {
|
||||
f32 bobfrac = my_modf(m_view_bobbing_anim);
|
||||
wield_position.X -= sin(bobfrac*M_PI*2.0) * 3.0;
|
||||
wield_position.Y += sin(my_modf(bobfrac*2.0)*M_PI) * 3.0;
|
||||
wield_position.X -= std::sin(bobfrac*M_PI*2.0) * 3.0;
|
||||
wield_position.Y += std::sin(my_modf(bobfrac*2.0)*M_PI) * 3.0;
|
||||
}
|
||||
m_wieldnode->setPosition(wield_position);
|
||||
m_wieldnode->setRotation(wield_rotation);
|
||||
|
@ -584,8 +578,8 @@ void Camera::update(LocalPlayer* player, f32 frametime, f32 tool_reload_ratio)
|
|||
// view bobbing is enabled and free_move is off,
|
||||
// start (or continue) the view bobbing animation.
|
||||
const v3f &speed = player->getSpeed();
|
||||
const bool movement_XZ = hypot(speed.X, speed.Z) > BS;
|
||||
const bool movement_Y = fabs(speed.Y) > BS;
|
||||
const bool movement_XZ = std::hypot(speed.X, speed.Z) > BS;
|
||||
const bool movement_Y = std::abs(speed.Y) > BS;
|
||||
|
||||
const bool walking = movement_XZ && player->touching_ground;
|
||||
const bool swimming = (movement_XZ || player->swimming_vertical) && player->in_liquid;
|
||||
|
|
|
@ -1806,7 +1806,7 @@ struct TextureUpdateArgs {
|
|||
void Client::showUpdateProgressTexture(void *args, u32 progress, u32 max_progress)
|
||||
{
|
||||
TextureUpdateArgs* targs = (TextureUpdateArgs*) args;
|
||||
u16 cur_percent = ceil(progress / (double) max_progress * 100.);
|
||||
u16 cur_percent = std::ceil(progress / max_progress * 100.f);
|
||||
|
||||
// update the loading menu -- if necessary
|
||||
bool do_draw = false;
|
||||
|
@ -2123,3 +2123,11 @@ const std::string &Client::getFormspecPrepend() const
|
|||
{
|
||||
return m_env.getLocalPlayer()->formspec_prepend;
|
||||
}
|
||||
|
||||
void Client::removeActiveObjectSounds(u16 id)
|
||||
{
|
||||
for (auto it : m_sounds_to_objects) {
|
||||
if (it.second == id)
|
||||
m_sound->stopSound(it.first);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -471,6 +471,9 @@ private:
|
|||
|
||||
bool canSendChatMessage() const;
|
||||
|
||||
// remove sounds attached to object
|
||||
void removeActiveObjectSounds(u16 id);
|
||||
|
||||
float m_packetcounter_timer = 0.0f;
|
||||
float m_connection_reinit_timer = 0.1f;
|
||||
float m_avg_rtt_timer = 0.0f;
|
||||
|
|
|
@ -182,7 +182,7 @@ void ClientEnvironment::step(float dtime)
|
|||
Stuff that has a maximum time increment
|
||||
*/
|
||||
|
||||
u32 steps = ceil(dtime / dtime_max_increment);
|
||||
u32 steps = std::ceil(dtime / dtime_max_increment);
|
||||
f32 dtime_part = dtime / steps;
|
||||
for (; steps > 0; --steps) {
|
||||
/*
|
||||
|
|
|
@ -249,10 +249,10 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
|
|||
m_rendering_engine->get_video_driver()->setTextureCreationFlag(
|
||||
video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map"));
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
receiver->m_touchscreengui = new TouchScreenGUI(m_rendering_engine->get_raw_device(), receiver);
|
||||
g_touchscreengui = receiver->m_touchscreengui;
|
||||
#endif
|
||||
if (g_settings->getBool("enable_touch")) {
|
||||
receiver->m_touchscreengui = new TouchScreenGUI(m_rendering_engine->get_raw_device(), receiver);
|
||||
g_touchscreengui = receiver->m_touchscreengui;
|
||||
}
|
||||
|
||||
the_game(
|
||||
kill,
|
||||
|
@ -283,11 +283,11 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
|
|||
|
||||
m_rendering_engine->get_scene_manager()->clear();
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
delete g_touchscreengui;
|
||||
g_touchscreengui = NULL;
|
||||
receiver->m_touchscreengui = NULL;
|
||||
#endif
|
||||
if (g_touchscreengui) {
|
||||
delete g_touchscreengui;
|
||||
g_touchscreengui = NULL;
|
||||
receiver->m_touchscreengui = NULL;
|
||||
}
|
||||
|
||||
/* Save the settings when leaving the game.
|
||||
* This makes sure that setting changes made in-game are persisted even
|
||||
|
|
|
@ -1305,7 +1305,7 @@ void ClientMap::updateTransparentMeshBuffers()
|
|||
ScopeProfiler sp(g_profiler, "CM::updateTransparentMeshBuffers", SPT_AVG);
|
||||
u32 sorted_blocks = 0;
|
||||
u32 unsorted_blocks = 0;
|
||||
f32 sorting_distance_sq = pow(m_cache_transparency_sorting_distance * BS, 2.0f);
|
||||
f32 sorting_distance_sq = std::pow(m_cache_transparency_sorting_distance * BS, 2.0f);
|
||||
|
||||
|
||||
// Update the order of transparent mesh buffers in each mesh
|
||||
|
|
|
@ -1216,7 +1216,7 @@ void GenericCAO::step(float dtime, ClientEnvironment *env)
|
|||
}
|
||||
}
|
||||
|
||||
if (node && fabs(m_prop.automatic_rotate) > 0.001f) {
|
||||
if (node && std::abs(m_prop.automatic_rotate) > 0.001f) {
|
||||
// This is the child node's rotation. It is only used for automatic_rotate.
|
||||
v3f local_rot = node->getRotation();
|
||||
local_rot.Y = modulo360f(local_rot.Y - dtime * core::RADTODEG *
|
||||
|
|
|
@ -77,7 +77,7 @@ MapblockMeshGenerator::MapblockMeshGenerator(MeshMakeData *input, MeshCollector
|
|||
scene::IMeshManipulator *mm):
|
||||
data(input),
|
||||
collector(output),
|
||||
nodedef(data->m_client->ndef()),
|
||||
nodedef(data->nodedef),
|
||||
meshmanip(mm),
|
||||
blockpos_nodes(data->m_blockpos * MAP_BLOCKSIZE),
|
||||
enable_mesh_cache(g_settings->getBool("enable_mesh_cache") &&
|
||||
|
@ -617,14 +617,14 @@ void MapblockMeshGenerator::calculateCornerLevels()
|
|||
cur_liquid.corner_levels[k][i] = getCornerLevel(i, k);
|
||||
}
|
||||
|
||||
f32 MapblockMeshGenerator::getCornerLevel(int i, int k)
|
||||
f32 MapblockMeshGenerator::getCornerLevel(int i, int k) const
|
||||
{
|
||||
float sum = 0;
|
||||
int count = 0;
|
||||
int air_count = 0;
|
||||
for (int dk = 0; dk < 2; dk++)
|
||||
for (int di = 0; di < 2; di++) {
|
||||
LiquidData::NeighborData &neighbor_data = cur_liquid.neighbors[k + dk][i + di];
|
||||
const LiquidData::NeighborData &neighbor_data = cur_liquid.neighbors[k + dk][i + di];
|
||||
content_t content = neighbor_data.content;
|
||||
|
||||
// If top is liquid, draw starting from top of node
|
||||
|
|
|
@ -138,7 +138,7 @@ private:
|
|||
void prepareLiquidNodeDrawing();
|
||||
void getLiquidNeighborhood();
|
||||
void calculateCornerLevels();
|
||||
f32 getCornerLevel(int i, int k);
|
||||
f32 getCornerLevel(int i, int k) const;
|
||||
void drawLiquidSides();
|
||||
void drawLiquidTop();
|
||||
void drawLiquidBottom();
|
||||
|
|
|
@ -664,11 +664,7 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
#define SIZE_TAG "size[11,5.5]"
|
||||
#else
|
||||
#define SIZE_TAG "size[11,5.5,true]" // Fixed size on desktop
|
||||
#endif
|
||||
#define SIZE_TAG "size[11,5.5,true]" // Fixed size (ignored in touchscreen mode)
|
||||
|
||||
/****************************************************************************
|
||||
****************************************************************************/
|
||||
|
@ -1021,13 +1017,11 @@ private:
|
|||
// this happens in pause menu in singleplayer
|
||||
bool m_is_paused = false;
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
bool m_cache_hold_aux1;
|
||||
bool m_touch_simulate_aux1 = false;
|
||||
bool m_touch_use_crosshair;
|
||||
inline bool isNoCrosshairAllowed() {
|
||||
inline bool isTouchCrosshairDisabled() {
|
||||
return !m_touch_use_crosshair && camera->getCameraMode() == CAMERA_MODE_FIRST;
|
||||
}
|
||||
#endif
|
||||
#ifdef __ANDROID__
|
||||
bool m_android_chat_open;
|
||||
#endif
|
||||
|
@ -1075,11 +1069,6 @@ Game::Game() :
|
|||
&settingChangedCallback, this);
|
||||
|
||||
readSettings();
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
m_cache_hold_aux1 = false; // This is initialised properly later
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
@ -1182,9 +1171,7 @@ bool Game::startup(bool *kill,
|
|||
|
||||
m_first_loop_after_window_activation = true;
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
m_touch_use_crosshair = g_settings->getBool("touch_use_crosshair");
|
||||
#endif
|
||||
|
||||
g_client_translations->clear();
|
||||
|
||||
|
@ -1219,10 +1206,8 @@ void Game::run()
|
|||
|
||||
set_light_table(g_settings->getFloat("display_gamma"));
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
m_cache_hold_aux1 = g_settings->getBool("fast_move")
|
||||
m_touch_simulate_aux1 = g_settings->getBool("fast_move")
|
||||
&& client->checkPrivilege("fast");
|
||||
#endif
|
||||
|
||||
const irr::core::dimension2du initial_screen_size(
|
||||
g_settings->getU16("screen_w"),
|
||||
|
@ -1308,9 +1293,8 @@ void Game::shutdown()
|
|||
// Clear text when exiting.
|
||||
m_game_ui->clearText();
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
g_touchscreengui->hide();
|
||||
#endif
|
||||
if (g_touchscreengui)
|
||||
g_touchscreengui->hide();
|
||||
|
||||
showOverlayMessage(N_("Shutting down..."), 0, 0, false);
|
||||
|
||||
|
@ -1520,11 +1504,10 @@ bool Game::createClient(const GameStartData &start_data)
|
|||
if (client->modsLoaded())
|
||||
client->getScript()->on_camera_ready(camera);
|
||||
client->setCamera(camera);
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
|
||||
if (g_touchscreengui) {
|
||||
g_touchscreengui->setUseCrosshair(!isNoCrosshairAllowed());
|
||||
g_touchscreengui->setUseCrosshair(!isTouchCrosshairDisabled());
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Clouds
|
||||
*/
|
||||
|
@ -1594,10 +1577,8 @@ bool Game::initGui()
|
|||
gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(),
|
||||
-1, chat_backend, client, &g_menumgr);
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui)
|
||||
g_touchscreengui->init(texture_src);
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -2026,18 +2007,17 @@ void Game::processUserInput(f32 dtime)
|
|||
} else {
|
||||
input->clear();
|
||||
}
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
g_touchscreengui->hide();
|
||||
#endif
|
||||
|
||||
if (g_touchscreengui)
|
||||
g_touchscreengui->hide();
|
||||
|
||||
} else {
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui) {
|
||||
/* on touchscreengui step may generate own input events which ain't
|
||||
* what we want in case we just did clear them */
|
||||
g_touchscreengui->show();
|
||||
g_touchscreengui->step(dtime);
|
||||
}
|
||||
#endif
|
||||
|
||||
m_game_focused = true;
|
||||
}
|
||||
|
@ -2229,13 +2209,11 @@ void Game::processItemSelection(u16 *new_playeritem)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui) {
|
||||
std::optional<u16> selection = g_touchscreengui->getHotbarSelection();
|
||||
if (selection)
|
||||
*new_playeritem = *selection;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Clamp selection again in case it wasn't changed but max_item was
|
||||
*new_playeritem = MYMIN(*new_playeritem, max_item);
|
||||
|
@ -2386,9 +2364,7 @@ void Game::toggleFast()
|
|||
m_game_ui->showTranslatedStatusText("Fast mode disabled");
|
||||
}
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
m_cache_hold_aux1 = fast_move && has_fast_privs;
|
||||
#endif
|
||||
m_touch_simulate_aux1 = fast_move && has_fast_privs;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2633,10 +2609,8 @@ void Game::updateCameraDirection(CameraOrientation *cam, float dtime)
|
|||
Since Minetest has its own code to synthesize mouse events from touch events,
|
||||
this results in duplicated input. To avoid that, we don't enable relative
|
||||
mouse mode if we're in touchscreen mode. */
|
||||
#ifndef HAVE_TOUCHSCREENGUI
|
||||
if (cur_control)
|
||||
cur_control->setRelativeMode(!isMenuActive());
|
||||
#endif
|
||||
cur_control->setRelativeMode(!g_touchscreengui && !isMenuActive());
|
||||
|
||||
if ((device->isWindowActive() && device->isWindowFocused()
|
||||
&& !isMenuActive()) || input->isRandom()) {
|
||||
|
@ -2674,17 +2648,15 @@ f32 Game::getSensitivityScaleFactor() const
|
|||
// Multiply by a constant such that it becomes 1.0 at 72 degree FOV and
|
||||
// 16:9 aspect ratio to minimize disruption of existing sensitivity
|
||||
// settings.
|
||||
return tan(fov_y / 2.0f) * 1.3763818698f;
|
||||
return std::tan(fov_y / 2.0f) * 1.3763819f;
|
||||
}
|
||||
|
||||
void Game::updateCameraOrientation(CameraOrientation *cam, float dtime)
|
||||
{
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui) {
|
||||
cam->camera_yaw += g_touchscreengui->getYawChange();
|
||||
cam->camera_pitch += g_touchscreengui->getPitchChange();
|
||||
} else {
|
||||
#endif
|
||||
v2s32 center(driver->getScreenSize().Width / 2, driver->getScreenSize().Height / 2);
|
||||
v2s32 dist = input->getMousePos() - center;
|
||||
|
||||
|
@ -2698,9 +2670,7 @@ void Game::updateCameraOrientation(CameraOrientation *cam, float dtime)
|
|||
|
||||
if (dist.X != 0 || dist.Y != 0)
|
||||
input->setMousePos(center.X, center.Y);
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
}
|
||||
#endif
|
||||
|
||||
if (m_cache_enable_joysticks) {
|
||||
f32 sens_scale = getSensitivityScaleFactor();
|
||||
|
@ -2741,20 +2711,18 @@ void Game::updatePlayerControl(const CameraOrientation &cam)
|
|||
client->activeObjectsReceived() && !player->isDead()) {
|
||||
control.movement_speed = 1.0f;
|
||||
// sideways movement only
|
||||
float dx = sin(control.movement_direction);
|
||||
control.movement_direction = atan2(dx, 1.0f);
|
||||
float dx = std::sin(control.movement_direction);
|
||||
control.movement_direction = std::atan2(dx, 1.0f);
|
||||
}
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
/* For touch, simulate holding down AUX1 (fast move) if the user has
|
||||
* the fast_move setting toggled on. If there is an aux1 key defined for
|
||||
* touch then its meaning is inverted (i.e. holding aux1 means walk and
|
||||
* not fast)
|
||||
*/
|
||||
if (m_cache_hold_aux1) {
|
||||
if (g_touchscreengui && m_touch_simulate_aux1) {
|
||||
control.aux1 = control.aux1 ^ true;
|
||||
}
|
||||
#endif
|
||||
|
||||
client->setPlayerControl(control);
|
||||
|
||||
|
@ -2783,10 +2751,16 @@ inline void Game::step(f32 dtime)
|
|||
g_settings->getFloat("fps_max_unfocused") :
|
||||
g_settings->getFloat("fps_max");
|
||||
fps_max = std::max(fps_max, 1.0f);
|
||||
float steplen = 1.0f / fps_max;
|
||||
/*
|
||||
* Unless you have a barebones game, running the server at more than 60Hz
|
||||
* is hardly realistic and you're at the point of diminishing returns.
|
||||
* fps_max is also not necessarily anywhere near the FPS actually achieved
|
||||
* (also due to vsync).
|
||||
*/
|
||||
fps_max = std::min(fps_max, 60.0f);
|
||||
|
||||
server->setStepSettings(Server::StepSettings{
|
||||
steplen,
|
||||
1.0f / fps_max,
|
||||
m_is_paused
|
||||
});
|
||||
|
||||
|
@ -3235,10 +3209,8 @@ void Game::updateCamera(f32 dtime)
|
|||
|
||||
camera->toggleCameraMode();
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui)
|
||||
g_touchscreengui->setUseCrosshair(!isNoCrosshairAllowed());
|
||||
#endif
|
||||
g_touchscreengui->setUseCrosshair(!isTouchCrosshairDisabled());
|
||||
|
||||
// Make the player visible depending on camera mode.
|
||||
playercao->updateMeshCulling();
|
||||
|
@ -3339,8 +3311,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud)
|
|||
}
|
||||
shootline.end = shootline.start + camera_direction * BS * d;
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui && isNoCrosshairAllowed()) {
|
||||
if (g_touchscreengui && isTouchCrosshairDisabled()) {
|
||||
shootline = g_touchscreengui->getShootline();
|
||||
// Scale shootline to the acual distance the player can reach
|
||||
shootline.end = shootline.start +
|
||||
|
@ -3348,7 +3319,6 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud)
|
|||
shootline.start += intToFloat(camera_offset, BS);
|
||||
shootline.end += intToFloat(camera_offset, BS);
|
||||
}
|
||||
#endif
|
||||
|
||||
PointedThing pointed = updatePointedThing(shootline,
|
||||
selected_def.liquids_pointable,
|
||||
|
@ -3359,10 +3329,8 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud)
|
|||
if (pointed != runData.pointed_old)
|
||||
infostream << "Pointing at " << pointed.dump() << std::endl;
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui)
|
||||
g_touchscreengui->applyContextControls(selected_def.touch_interaction.getMode(pointed));
|
||||
#endif
|
||||
|
||||
// Note that updating the selection mesh every frame is not particularly efficient,
|
||||
// but the halo rendering code is already inefficient so there's no point in optimizing it here
|
||||
|
@ -3740,11 +3708,11 @@ bool Game::nodePlacement(const ItemDefinition &selected_def,
|
|||
break;
|
||||
};
|
||||
case NDT_SIGNLIKE: {
|
||||
rotate90 = abs(pdir.X) < abs(pdir.Z);
|
||||
rotate90 = std::abs(pdir.X) < std::abs(pdir.Z);
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
rotate90 = abs(pdir.X) > abs(pdir.Z);
|
||||
rotate90 = std::abs(pdir.X) > std::abs(pdir.Z);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -4282,14 +4250,14 @@ void Game::updateShadows()
|
|||
if (!shadow)
|
||||
return;
|
||||
|
||||
float in_timeofday = fmod(runData.time_of_day_smooth, 1.0f);
|
||||
float in_timeofday = std::fmod(runData.time_of_day_smooth, 1.0f);
|
||||
|
||||
float timeoftheday = getWickedTimeOfDay(in_timeofday);
|
||||
bool is_day = timeoftheday > 0.25 && timeoftheday < 0.75;
|
||||
bool is_shadow_visible = is_day ? sky->getSunVisible() : sky->getMoonVisible();
|
||||
shadow->setShadowIntensity(is_shadow_visible ? client->getEnv().getLocalPlayer()->getLighting().shadow_intensity : 0.0f);
|
||||
|
||||
timeoftheday = fmod(timeoftheday + 0.75f, 0.5f) + 0.25f;
|
||||
timeoftheday = std::fmod(timeoftheday + 0.75f, 0.5f) + 0.25f;
|
||||
const float offset_constant = 10000.0f;
|
||||
|
||||
v3f light = is_day ? sky->getSunDirection() : sky->getMoonDirection();
|
||||
|
@ -4345,10 +4313,10 @@ void Game::drawScene(ProfilerGraph *graph, RunStats *stats)
|
|||
bool draw_crosshair = (
|
||||
(player->hud_flags & HUD_FLAG_CROSSHAIR_VISIBLE) &&
|
||||
(this->camera->getCameraMode() != CAMERA_MODE_THIRD_FRONT));
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (this->isNoCrosshairAllowed())
|
||||
|
||||
if (g_touchscreengui && isTouchCrosshairDisabled())
|
||||
draw_crosshair = false;
|
||||
#endif
|
||||
|
||||
this->m_rendering_engine->draw_scene(sky_color, this->m_game_ui->m_flags.show_hud,
|
||||
draw_wield_tool, draw_crosshair);
|
||||
|
||||
|
@ -4496,21 +4464,23 @@ void Game::showDeathFormspec()
|
|||
#define GET_KEY_NAME(KEY) gettext(getKeySetting(#KEY).name())
|
||||
void Game::showPauseMenu()
|
||||
{
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
static const std::string control_text = strgettext("Controls:\n"
|
||||
"No menu open:\n"
|
||||
"- slide finger: look around\n"
|
||||
"- tap: place/punch/use (default)\n"
|
||||
"- long tap: dig/use (default)\n"
|
||||
"Menu/inventory open:\n"
|
||||
"- double tap (outside):\n"
|
||||
" --> close\n"
|
||||
"- touch stack, touch slot:\n"
|
||||
" --> move stack\n"
|
||||
"- touch&drag, tap 2nd finger\n"
|
||||
" --> place single item to slot\n"
|
||||
);
|
||||
#endif
|
||||
std::string control_text;
|
||||
|
||||
if (g_touchscreengui) {
|
||||
control_text = strgettext("Controls:\n"
|
||||
"No menu open:\n"
|
||||
"- slide finger: look around\n"
|
||||
"- tap: place/punch/use (default)\n"
|
||||
"- long tap: dig/use (default)\n"
|
||||
"Menu/inventory open:\n"
|
||||
"- double tap (outside):\n"
|
||||
" --> close\n"
|
||||
"- touch stack, touch slot:\n"
|
||||
" --> move stack\n"
|
||||
"- touch&drag, tap 2nd finger\n"
|
||||
" --> place single item to slot\n"
|
||||
);
|
||||
}
|
||||
|
||||
float ypos = simple_singleplayer_mode ? 0.7f : 0.1f;
|
||||
std::ostringstream os;
|
||||
|
@ -4540,9 +4510,9 @@ void Game::showPauseMenu()
|
|||
<< strgettext("Exit to Menu") << "]";
|
||||
os << "button_exit[4," << (ypos++) << ";3,0.5;btn_exit_os;"
|
||||
<< strgettext("Exit to OS") << "]";
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (!control_text.empty()) {
|
||||
os << "textarea[7.5,0.25;3.9,6.25;;" << control_text << ";]";
|
||||
#endif
|
||||
}
|
||||
os << "textarea[0.4,0.25;3.9,6.25;;" << PROJECT_NAME_C " " VERSION_STRING "\n"
|
||||
<< "\n"
|
||||
<< strgettext("Game info:") << "\n";
|
||||
|
|
|
@ -39,10 +39,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "wieldmesh.h"
|
||||
#include "client/renderingengine.h"
|
||||
#include "client/minimap.h"
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
#include "gui/touchscreengui.h"
|
||||
#endif
|
||||
|
||||
#define OBJECT_CROSSHAIR_LINE_SIZE 8
|
||||
#define CROSSHAIR_LINE_SIZE 10
|
||||
|
@ -292,10 +289,8 @@ void Hud::drawItems(v2s32 upperleftpos, v2s32 screen_offset, s32 itemcount,
|
|||
|
||||
drawItem(mainlist->getItem(i), item_rect, (i + 1) == selectitem);
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (is_hotbar && g_touchscreengui)
|
||||
g_touchscreengui->registerHotbarRect(i, item_rect);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -749,10 +744,8 @@ void Hud::drawStatbar(v2s32 pos, u16 corner, u16 drawdir,
|
|||
|
||||
void Hud::drawHotbar(u16 playeritem)
|
||||
{
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui)
|
||||
g_touchscreengui->resetHotbarRects();
|
||||
#endif
|
||||
|
||||
InventoryList *mainlist = inventory->getList("main");
|
||||
if (mainlist == NULL) {
|
||||
|
|
|
@ -71,14 +71,14 @@ static void imageCleanTransparentWithInlining(video::IImage *src, u32 threshold)
|
|||
void *const src_data = src->getData();
|
||||
const core::dimension2d<u32> dim = src->getDimension();
|
||||
|
||||
auto get_pixel = [src, src_data, dim](u32 x, u32 y) -> video::SColor {
|
||||
auto get_pixel = [=](u32 x, u32 y) -> video::SColor {
|
||||
if constexpr (IS_A8R8G8B8) {
|
||||
return reinterpret_cast<u32 *>(src_data)[y*dim.Width + x];
|
||||
} else {
|
||||
return src->getPixel(x, y);
|
||||
}
|
||||
};
|
||||
auto set_pixel = [src, src_data, dim](u32 x, u32 y, video::SColor color) {
|
||||
auto set_pixel = [=](u32 x, u32 y, video::SColor color) {
|
||||
if constexpr (IS_A8R8G8B8) {
|
||||
u32 *dest = &reinterpret_cast<u32 *>(src_data)[y*dim.Width + x];
|
||||
*dest = color.color;
|
||||
|
|
|
@ -102,11 +102,9 @@ bool MyEventReceiver::OnEvent(const SEvent &event)
|
|||
React to nothing here if a menu is active
|
||||
*/
|
||||
if (isMenuActive()) {
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (m_touchscreengui) {
|
||||
m_touchscreengui->setVisible(false);
|
||||
}
|
||||
#endif
|
||||
return g_menumgr.preprocessEvent(event);
|
||||
}
|
||||
|
||||
|
@ -130,12 +128,10 @@ bool MyEventReceiver::OnEvent(const SEvent &event)
|
|||
return true;
|
||||
}
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
} else if (m_touchscreengui && event.EventType == irr::EET_TOUCH_INPUT_EVENT) {
|
||||
// In case of touchscreengui, we have to handle different events
|
||||
m_touchscreengui->translateEvent(event);
|
||||
return true;
|
||||
#endif
|
||||
|
||||
} else if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) {
|
||||
// joystick may be nullptr if game is launched with '--random-input' parameter
|
||||
|
|
|
@ -24,10 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include <list>
|
||||
#include "keycode.h"
|
||||
#include "renderingengine.h"
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
#include "gui/touchscreengui.h"
|
||||
#endif
|
||||
|
||||
class InputHandler;
|
||||
|
||||
|
@ -203,16 +200,12 @@ public:
|
|||
|
||||
MyEventReceiver()
|
||||
{
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
m_touchscreengui = NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
JoystickController *joystick = nullptr;
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
TouchScreenGUI *m_touchscreengui;
|
||||
#endif
|
||||
|
||||
private:
|
||||
s32 mouse_wheel = 0;
|
||||
|
@ -332,11 +325,9 @@ public:
|
|||
return 0.0f;
|
||||
return 1.0f; // If there is a keyboard event, assume maximum speed
|
||||
}
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
return m_receiver->m_touchscreengui->getMovementSpeed();
|
||||
#else
|
||||
if (m_receiver->m_touchscreengui && m_receiver->m_touchscreengui->getMovementSpeed())
|
||||
return m_receiver->m_touchscreengui->getMovementSpeed();
|
||||
return joystick.getMovementSpeed();
|
||||
#endif
|
||||
}
|
||||
|
||||
virtual float getMovementDirection()
|
||||
|
@ -355,12 +346,9 @@ public:
|
|||
|
||||
if (x != 0 || z != 0) /* If there is a keyboard event, it takes priority */
|
||||
return atan2(x, z);
|
||||
else
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
else if (m_receiver->m_touchscreengui && m_receiver->m_touchscreengui->getMovementDirection())
|
||||
return m_receiver->m_touchscreengui->getMovementDirection();
|
||||
#else
|
||||
return joystick.getMovementDirection();
|
||||
#endif
|
||||
return joystick.getMovementDirection();
|
||||
}
|
||||
|
||||
virtual bool cancelPressed()
|
||||
|
|
|
@ -318,12 +318,14 @@ float JoystickController::getAxisWithoutDead(JoystickAxis axis)
|
|||
|
||||
float JoystickController::getMovementDirection()
|
||||
{
|
||||
return atan2(getAxisWithoutDead(JA_SIDEWARD_MOVE), -getAxisWithoutDead(JA_FORWARD_MOVE));
|
||||
return std::atan2(getAxisWithoutDead(JA_SIDEWARD_MOVE),
|
||||
-getAxisWithoutDead(JA_FORWARD_MOVE));
|
||||
}
|
||||
|
||||
float JoystickController::getMovementSpeed()
|
||||
{
|
||||
float speed = sqrt(pow(getAxisWithoutDead(JA_FORWARD_MOVE), 2) + pow(getAxisWithoutDead(JA_SIDEWARD_MOVE), 2));
|
||||
float speed = std::sqrt(std::pow(getAxisWithoutDead(JA_FORWARD_MOVE), 2) +
|
||||
std::pow(getAxisWithoutDead(JA_SIDEWARD_MOVE), 2));
|
||||
if (speed > 1.0f)
|
||||
speed = 1.0f;
|
||||
return speed;
|
||||
|
|
|
@ -18,7 +18,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
*/
|
||||
|
||||
#include "keycode.h"
|
||||
#include "exceptions.h"
|
||||
#include "settings.h"
|
||||
#include "log.h"
|
||||
#include "debug.h"
|
||||
|
@ -26,13 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "util/string.h"
|
||||
#include "util/basic_macros.h"
|
||||
|
||||
class UnknownKeycode : public BaseException
|
||||
{
|
||||
public:
|
||||
UnknownKeycode(const char *s) :
|
||||
BaseException(s) {};
|
||||
};
|
||||
|
||||
struct table_key {
|
||||
const char *Name;
|
||||
irr::EKEY_CODE Key;
|
||||
|
|
|
@ -19,11 +19,19 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "exceptions.h"
|
||||
#include "irrlichttypes.h"
|
||||
#include "Keycodes.h"
|
||||
#include <IEventReceiver.h>
|
||||
#include <string>
|
||||
|
||||
class UnknownKeycode : public BaseException
|
||||
{
|
||||
public:
|
||||
UnknownKeycode(const char *s) :
|
||||
BaseException(s) {};
|
||||
};
|
||||
|
||||
/* A key press, consisting of either an Irrlicht keycode
|
||||
or an actual char */
|
||||
|
||||
|
|
|
@ -600,7 +600,8 @@ void LocalPlayer::applyControl(float dtime, Environment *env)
|
|||
}
|
||||
}
|
||||
|
||||
speedH = v3f(sin(control.movement_direction), 0.0f, cos(control.movement_direction));
|
||||
speedH = v3f(std::sin(control.movement_direction), 0.0f,
|
||||
std::cos(control.movement_direction));
|
||||
|
||||
if (m_autojump) {
|
||||
// release autojump after a given time
|
||||
|
|
|
@ -38,10 +38,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
MeshMakeData
|
||||
*/
|
||||
|
||||
MeshMakeData::MeshMakeData(Client *client, bool use_shaders):
|
||||
m_mesh_grid(client->getMeshGrid()),
|
||||
side_length(MAP_BLOCKSIZE * m_mesh_grid.cell_size),
|
||||
m_client(client),
|
||||
MeshMakeData::MeshMakeData(const NodeDefManager *ndef, u16 side_length, bool use_shaders):
|
||||
side_length(side_length),
|
||||
nodedef(ndef),
|
||||
m_use_shaders(use_shaders)
|
||||
{}
|
||||
|
||||
|
@ -147,7 +146,7 @@ u16 getFaceLight(MapNode n, MapNode n2, const NodeDefManager *ndef)
|
|||
static u16 getSmoothLightCombined(const v3s16 &p,
|
||||
const std::array<v3s16,8> &dirs, MeshMakeData *data)
|
||||
{
|
||||
const NodeDefManager *ndef = data->m_client->ndef();
|
||||
const NodeDefManager *ndef = data->nodedef;
|
||||
|
||||
u16 ambient_occlusion = 0;
|
||||
u16 light_count = 0;
|
||||
|
@ -360,7 +359,7 @@ static const v3s16 vertex_dirs_table[] = {
|
|||
*/
|
||||
void getNodeTileN(MapNode mn, const v3s16 &p, u8 tileindex, MeshMakeData *data, TileSpec &tile)
|
||||
{
|
||||
const NodeDefManager *ndef = data->m_client->ndef();
|
||||
const NodeDefManager *ndef = data->nodedef;
|
||||
const ContentFeatures &f = ndef->get(mn);
|
||||
tile = f.tiles[tileindex];
|
||||
bool has_crack = p == data->m_crack_pos_relative;
|
||||
|
@ -380,7 +379,7 @@ void getNodeTileN(MapNode mn, const v3s16 &p, u8 tileindex, MeshMakeData *data,
|
|||
*/
|
||||
void getNodeTile(MapNode mn, const v3s16 &p, const v3s16 &dir, MeshMakeData *data, TileSpec &tile)
|
||||
{
|
||||
const NodeDefManager *ndef = data->m_client->ndef();
|
||||
const NodeDefManager *ndef = data->nodedef;
|
||||
|
||||
// Direction must be (1,0,0), (-1,0,0), (0,1,0), (0,-1,0),
|
||||
// (0,0,1), (0,0,-1) or (0,0,0)
|
||||
|
@ -635,9 +634,9 @@ void PartialMeshBuffer::afterDraw() const
|
|||
MapBlockMesh
|
||||
*/
|
||||
|
||||
MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
|
||||
m_tsrc(data->m_client->getTextureSource()),
|
||||
m_shdrsrc(data->m_client->getShaderSource()),
|
||||
MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offset):
|
||||
m_tsrc(client->getTextureSource()),
|
||||
m_shdrsrc(client->getShaderSource()),
|
||||
m_bounding_sphere_center((data->side_length * 0.5f - 0.5f) * BS),
|
||||
m_animation_force_timer(0), // force initial animation
|
||||
m_last_crack(-1),
|
||||
|
@ -648,26 +647,27 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
|
|||
m_enable_shaders = data->m_use_shaders;
|
||||
m_enable_vbo = g_settings->getBool("enable_vbo");
|
||||
|
||||
auto mesh_grid = client->getMeshGrid();
|
||||
v3s16 bp = data->m_blockpos;
|
||||
// Only generate minimap mapblocks at even coordinates.
|
||||
if (data->m_mesh_grid.isMeshPos(bp) && data->m_client->getMinimap()) {
|
||||
m_minimap_mapblocks.resize(data->m_mesh_grid.getCellVolume(), nullptr);
|
||||
if (mesh_grid.isMeshPos(bp) && client->getMinimap()) {
|
||||
m_minimap_mapblocks.resize(mesh_grid.getCellVolume(), nullptr);
|
||||
v3s16 ofs;
|
||||
|
||||
// See also client.cpp for the code that reads the array of minimap blocks.
|
||||
for (ofs.Z = 0; ofs.Z < data->m_mesh_grid.cell_size; ofs.Z++)
|
||||
for (ofs.Y = 0; ofs.Y < data->m_mesh_grid.cell_size; ofs.Y++)
|
||||
for (ofs.X = 0; ofs.X < data->m_mesh_grid.cell_size; ofs.X++) {
|
||||
for (ofs.Z = 0; ofs.Z < mesh_grid.cell_size; ofs.Z++)
|
||||
for (ofs.Y = 0; ofs.Y < mesh_grid.cell_size; ofs.Y++)
|
||||
for (ofs.X = 0; ofs.X < mesh_grid.cell_size; ofs.X++) {
|
||||
v3s16 p = (bp + ofs) * MAP_BLOCKSIZE;
|
||||
if (data->m_vmanip.getNodeNoEx(p).getContent() != CONTENT_IGNORE) {
|
||||
MinimapMapblock *block = new MinimapMapblock;
|
||||
m_minimap_mapblocks[data->m_mesh_grid.getOffsetIndex(ofs)] = block;
|
||||
m_minimap_mapblocks[mesh_grid.getOffsetIndex(ofs)] = block;
|
||||
block->getMinimapNodes(&data->m_vmanip, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
v3f offset = intToFloat((data->m_blockpos - data->m_mesh_grid.getMeshPos(data->m_blockpos)) * MAP_BLOCKSIZE, BS);
|
||||
v3f offset = intToFloat((data->m_blockpos - mesh_grid.getMeshPos(data->m_blockpos)) * MAP_BLOCKSIZE, BS);
|
||||
MeshCollector collector(m_bounding_sphere_center, offset);
|
||||
/*
|
||||
Add special graphics:
|
||||
|
@ -679,7 +679,7 @@ MapBlockMesh::MapBlockMesh(MeshMakeData *data, v3s16 camera_offset):
|
|||
|
||||
{
|
||||
MapblockMeshGenerator(data, &collector,
|
||||
data->m_client->getSceneManager()->getMeshManipulator()).generate();
|
||||
client->getSceneManager()->getMeshManipulator()).generate();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -1011,7 +1011,7 @@ u8 get_solid_sides(MeshMakeData *data)
|
|||
std::unordered_map<v3s16, u8> results;
|
||||
v3s16 ofs;
|
||||
v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE;
|
||||
const NodeDefManager *ndef = data->m_client->ndef();
|
||||
const NodeDefManager *ndef = data->nodedef;
|
||||
|
||||
u8 result = 0x3F; // all sides solid;
|
||||
|
||||
|
|
|
@ -27,6 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include <unordered_map>
|
||||
|
||||
class Client;
|
||||
class NodeDefManager;
|
||||
class IShaderSource;
|
||||
|
||||
/*
|
||||
|
@ -43,13 +44,12 @@ struct MeshMakeData
|
|||
v3s16 m_blockpos = v3s16(-1337,-1337,-1337);
|
||||
v3s16 m_crack_pos_relative = v3s16(-1337,-1337,-1337);
|
||||
bool m_smooth_lighting = false;
|
||||
MeshGrid m_mesh_grid;
|
||||
u16 side_length;
|
||||
|
||||
Client *m_client;
|
||||
const NodeDefManager *nodedef;
|
||||
bool m_use_shaders;
|
||||
|
||||
MeshMakeData(Client *client, bool use_shaders);
|
||||
MeshMakeData(const NodeDefManager *ndef, u16 side_length, bool use_shaders);
|
||||
|
||||
/*
|
||||
Copy block data manually (to allow optimizations by the caller)
|
||||
|
@ -179,7 +179,7 @@ class MapBlockMesh
|
|||
{
|
||||
public:
|
||||
// Builds the mesh given
|
||||
MapBlockMesh(MeshMakeData *data, v3s16 camera_offset);
|
||||
MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offset);
|
||||
~MapBlockMesh();
|
||||
|
||||
// Main animation function, parameters:
|
||||
|
|
|
@ -189,16 +189,17 @@ void MeshUpdateQueue::done(v3s16 pos)
|
|||
|
||||
void MeshUpdateQueue::fillDataFromMapBlocks(QueuedMeshUpdate *q)
|
||||
{
|
||||
MeshMakeData *data = new MeshMakeData(m_client, m_cache_enable_shaders);
|
||||
auto mesh_grid = m_client->getMeshGrid();
|
||||
MeshMakeData *data = new MeshMakeData(m_client->ndef(), MAP_BLOCKSIZE * mesh_grid.cell_size, m_cache_enable_shaders);
|
||||
q->data = data;
|
||||
|
||||
data->fillBlockDataBegin(q->p);
|
||||
|
||||
v3s16 pos;
|
||||
int i = 0;
|
||||
for (pos.X = q->p.X - 1; pos.X <= q->p.X + data->m_mesh_grid.cell_size; pos.X++)
|
||||
for (pos.Z = q->p.Z - 1; pos.Z <= q->p.Z + data->m_mesh_grid.cell_size; pos.Z++)
|
||||
for (pos.Y = q->p.Y - 1; pos.Y <= q->p.Y + data->m_mesh_grid.cell_size; pos.Y++) {
|
||||
for (pos.X = q->p.X - 1; pos.X <= q->p.X + mesh_grid.cell_size; pos.X++)
|
||||
for (pos.Z = q->p.Z - 1; pos.Z <= q->p.Z + mesh_grid.cell_size; pos.Z++)
|
||||
for (pos.Y = q->p.Y - 1; pos.Y <= q->p.Y + mesh_grid.cell_size; pos.Y++) {
|
||||
MapBlock *block = q->map_blocks[i++];
|
||||
data->fillBlockData(pos, block ? block->getData() : block_placeholder.data);
|
||||
}
|
||||
|
@ -211,8 +212,8 @@ void MeshUpdateQueue::fillDataFromMapBlocks(QueuedMeshUpdate *q)
|
|||
MeshUpdateWorkerThread
|
||||
*/
|
||||
|
||||
MeshUpdateWorkerThread::MeshUpdateWorkerThread(MeshUpdateQueue *queue_in, MeshUpdateManager *manager, v3s16 *camera_offset) :
|
||||
UpdateThread("Mesh"), m_queue_in(queue_in), m_manager(manager), m_camera_offset(camera_offset)
|
||||
MeshUpdateWorkerThread::MeshUpdateWorkerThread(Client *client, MeshUpdateQueue *queue_in, MeshUpdateManager *manager, v3s16 *camera_offset) :
|
||||
UpdateThread("Mesh"), m_client(client), m_queue_in(queue_in), m_manager(manager), m_camera_offset(camera_offset)
|
||||
{
|
||||
m_generation_interval = g_settings->getU16("mesh_generation_interval");
|
||||
m_generation_interval = rangelim(m_generation_interval, 0, 50);
|
||||
|
@ -226,7 +227,7 @@ void MeshUpdateWorkerThread::doUpdate()
|
|||
sleep_ms(m_generation_interval);
|
||||
ScopeProfiler sp(g_profiler, "Client: Mesh making (sum)");
|
||||
|
||||
MapBlockMesh *mesh_new = new MapBlockMesh(q->data, *m_camera_offset);
|
||||
MapBlockMesh *mesh_new = new MapBlockMesh(m_client, q->data, *m_camera_offset);
|
||||
|
||||
|
||||
|
||||
|
@ -262,7 +263,7 @@ MeshUpdateManager::MeshUpdateManager(Client *client):
|
|||
infostream << "MeshUpdateManager: using " << number_of_threads << " threads" << std::endl;
|
||||
|
||||
for (int i = 0; i < number_of_threads; i++)
|
||||
m_workers.push_back(std::make_unique<MeshUpdateWorkerThread>(&m_queue_in, this, &m_camera_offset));
|
||||
m_workers.push_back(std::make_unique<MeshUpdateWorkerThread>(client, &m_queue_in, this, &m_camera_offset));
|
||||
}
|
||||
|
||||
void MeshUpdateManager::updateBlock(Map *map, v3s16 p, bool ack_block_to_server,
|
||||
|
|
|
@ -109,12 +109,13 @@ class MeshUpdateManager;
|
|||
class MeshUpdateWorkerThread : public UpdateThread
|
||||
{
|
||||
public:
|
||||
MeshUpdateWorkerThread(MeshUpdateQueue *queue_in, MeshUpdateManager *manager, v3s16 *camera_offset);
|
||||
MeshUpdateWorkerThread(Client *client, MeshUpdateQueue *queue_in, MeshUpdateManager *manager, v3s16 *camera_offset);
|
||||
|
||||
protected:
|
||||
virtual void doUpdate();
|
||||
|
||||
private:
|
||||
Client *m_client;
|
||||
MeshUpdateQueue *m_queue_in;
|
||||
MeshUpdateManager *m_manager;
|
||||
v3s16 *m_camera_offset;
|
||||
|
|
|
@ -171,8 +171,8 @@ f32 ShadowRenderer::getMaxShadowFar() const
|
|||
|
||||
void ShadowRenderer::setShadowIntensity(float shadow_intensity)
|
||||
{
|
||||
m_shadow_strength = pow(shadow_intensity, 1.0f / m_shadow_strength_gamma);
|
||||
if (m_shadow_strength > 1E-2)
|
||||
m_shadow_strength = std::pow(shadow_intensity, 1.0f / m_shadow_strength_gamma);
|
||||
if (m_shadow_strength > 1e-2f)
|
||||
enable();
|
||||
else
|
||||
disable();
|
||||
|
|
|
@ -680,7 +680,7 @@ void Sky::draw_stars(video::IVideoDriver * driver, float wicked_time_of_day)
|
|||
|
||||
float tod = wicked_time_of_day < 0.5f ? wicked_time_of_day : (1.0f - wicked_time_of_day);
|
||||
float day_opacity = clamp(m_star_params.day_opacity, 0.0f, 1.0f);
|
||||
float starbrightness = (0.25f - fabs(tod)) * 20.0f;
|
||||
float starbrightness = (0.25f - std::abs(tod)) * 20.0f;
|
||||
float alpha = clamp(starbrightness, day_opacity, 1.0f);
|
||||
|
||||
m_star_color = m_star_params.starcolor;
|
||||
|
|
|
@ -315,7 +315,7 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename,
|
|||
static scene::SMesh *createSpecialNodeMesh(Client *client, MapNode n,
|
||||
std::vector<ItemPartColor> *colors, const ContentFeatures &f)
|
||||
{
|
||||
MeshMakeData mesh_make_data(client, false);
|
||||
MeshMakeData mesh_make_data(client->ndef(), 1, false);
|
||||
MeshCollector collector(v3f(0.0f * BS), v3f());
|
||||
mesh_make_data.setSmoothLighting(false);
|
||||
MapblockMeshGenerator gen(&mesh_make_data, &collector,
|
||||
|
|
|
@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#ifndef SERVER
|
||||
#include "settings.h"
|
||||
#include "client/renderingengine.h"
|
||||
#include "gui/touchscreengui.h"
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -37,8 +38,8 @@ public:
|
|||
|
||||
bool equal(const ClientDynamicInfo &other) const {
|
||||
return render_target_size == other.render_target_size &&
|
||||
abs(real_gui_scaling - other.real_gui_scaling) < 0.001f &&
|
||||
abs(real_hud_scaling - other.real_hud_scaling) < 0.001f &&
|
||||
std::abs(real_gui_scaling - other.real_gui_scaling) < 0.001f &&
|
||||
std::abs(real_hud_scaling - other.real_hud_scaling) < 0.001f &&
|
||||
touch_controls == other.touch_controls;
|
||||
}
|
||||
|
||||
|
@ -50,11 +51,7 @@ public:
|
|||
f32 hud_scaling = g_settings->getFloat("hud_scaling", 0.5f, 20.0f);
|
||||
f32 real_gui_scaling = gui_scaling * density;
|
||||
f32 real_hud_scaling = hud_scaling * density;
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
bool touch_controls = true;
|
||||
#else
|
||||
bool touch_controls = false;
|
||||
#endif
|
||||
bool touch_controls = g_touchscreengui;
|
||||
|
||||
return {
|
||||
screen_size, real_gui_scaling, real_hud_scaling,
|
||||
|
@ -67,12 +64,7 @@ public:
|
|||
private:
|
||||
#ifndef SERVER
|
||||
static v2f32 calculateMaxFSSize(v2u32 render_target_size, f32 gui_scaling) {
|
||||
f32 factor =
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
10 / gui_scaling;
|
||||
#else
|
||||
15 / gui_scaling;
|
||||
#endif
|
||||
f32 factor = (g_settings->getBool("enable_touch") ? 10 : 15) / gui_scaling;
|
||||
f32 ratio = (f32)render_target_size.X / (f32)render_target_size.Y;
|
||||
if (ratio < 1)
|
||||
return { factor, factor / ratio };
|
||||
|
|
|
@ -24,68 +24,59 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "filesys.h"
|
||||
#include "settings.h"
|
||||
|
||||
enum ContentType
|
||||
ContentType getContentType(const std::string &path)
|
||||
{
|
||||
ECT_UNKNOWN,
|
||||
ECT_MOD,
|
||||
ECT_MODPACK,
|
||||
ECT_GAME,
|
||||
ECT_TXP
|
||||
};
|
||||
|
||||
ContentType getContentType(const ContentSpec &spec)
|
||||
{
|
||||
std::ifstream modpack_is((spec.path + DIR_DELIM + "modpack.txt").c_str());
|
||||
std::ifstream modpack_is((path + DIR_DELIM + "modpack.txt").c_str());
|
||||
if (modpack_is.good()) {
|
||||
modpack_is.close();
|
||||
return ECT_MODPACK;
|
||||
return ContentType::MODPACK;
|
||||
}
|
||||
|
||||
std::ifstream modpack2_is((spec.path + DIR_DELIM + "modpack.conf").c_str());
|
||||
std::ifstream modpack2_is((path + DIR_DELIM + "modpack.conf").c_str());
|
||||
if (modpack2_is.good()) {
|
||||
modpack2_is.close();
|
||||
return ECT_MODPACK;
|
||||
return ContentType::MODPACK;
|
||||
}
|
||||
|
||||
std::ifstream init_is((spec.path + DIR_DELIM + "init.lua").c_str());
|
||||
std::ifstream init_is((path + DIR_DELIM + "init.lua").c_str());
|
||||
if (init_is.good()) {
|
||||
init_is.close();
|
||||
return ECT_MOD;
|
||||
return ContentType::MOD;
|
||||
}
|
||||
|
||||
std::ifstream game_is((spec.path + DIR_DELIM + "game.conf").c_str());
|
||||
std::ifstream game_is((path + DIR_DELIM + "game.conf").c_str());
|
||||
if (game_is.good()) {
|
||||
game_is.close();
|
||||
return ECT_GAME;
|
||||
return ContentType::GAME;
|
||||
}
|
||||
|
||||
std::ifstream txp_is((spec.path + DIR_DELIM + "texture_pack.conf").c_str());
|
||||
std::ifstream txp_is((path + DIR_DELIM + "texture_pack.conf").c_str());
|
||||
if (txp_is.good()) {
|
||||
txp_is.close();
|
||||
return ECT_TXP;
|
||||
return ContentType::TXP;
|
||||
}
|
||||
|
||||
return ECT_UNKNOWN;
|
||||
return ContentType::UNKNOWN;
|
||||
}
|
||||
|
||||
void parseContentInfo(ContentSpec &spec)
|
||||
{
|
||||
std::string conf_path;
|
||||
|
||||
switch (getContentType(spec)) {
|
||||
case ECT_MOD:
|
||||
switch (getContentType(spec.path)) {
|
||||
case ContentType::MOD:
|
||||
spec.type = "mod";
|
||||
conf_path = spec.path + DIR_DELIM + "mod.conf";
|
||||
break;
|
||||
case ECT_MODPACK:
|
||||
case ContentType::MODPACK:
|
||||
spec.type = "modpack";
|
||||
conf_path = spec.path + DIR_DELIM + "modpack.conf";
|
||||
break;
|
||||
case ECT_GAME:
|
||||
case ContentType::GAME:
|
||||
spec.type = "game";
|
||||
conf_path = spec.path + DIR_DELIM + "game.conf";
|
||||
break;
|
||||
case ECT_TXP:
|
||||
case ContentType::TXP:
|
||||
spec.type = "txp";
|
||||
conf_path = spec.path + DIR_DELIM + "texture_pack.conf";
|
||||
break;
|
||||
|
@ -104,6 +95,15 @@ void parseContentInfo(ContentSpec &spec)
|
|||
if (spec.type != "game" && conf.exists("name"))
|
||||
spec.name = conf.get("name");
|
||||
|
||||
if (conf.exists("title"))
|
||||
spec.title = conf.get("title");
|
||||
|
||||
if (spec.type == "game") {
|
||||
if (spec.title.empty())
|
||||
spec.title = spec.name;
|
||||
spec.name = "";
|
||||
}
|
||||
|
||||
if (conf.exists("description"))
|
||||
spec.desc = conf.get("description");
|
||||
|
||||
|
@ -112,8 +112,17 @@ void parseContentInfo(ContentSpec &spec)
|
|||
|
||||
if (conf.exists("release"))
|
||||
spec.release = conf.getS32("release");
|
||||
|
||||
if (conf.exists("textdomain"))
|
||||
spec.textdomain = conf.get("textdomain");
|
||||
}
|
||||
|
||||
if (spec.name.empty())
|
||||
spec.name = fs::GetFilenameFromPath(spec.path.c_str());
|
||||
|
||||
if (spec.textdomain.empty())
|
||||
spec.textdomain = spec.name;
|
||||
|
||||
if (spec.desc.empty()) {
|
||||
std::ifstream is((spec.path + DIR_DELIM + "description.txt").c_str());
|
||||
spec.desc = std::string((std::istreambuf_iterator<char>(is)),
|
||||
|
|
|
@ -22,6 +22,16 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "convert_json.h"
|
||||
#include "irrlichttypes.h"
|
||||
|
||||
enum class ContentType
|
||||
{
|
||||
UNKNOWN,
|
||||
MOD,
|
||||
MODPACK,
|
||||
GAME,
|
||||
TXP
|
||||
};
|
||||
|
||||
|
||||
struct ContentSpec
|
||||
{
|
||||
std::string type;
|
||||
|
@ -37,6 +47,9 @@ struct ContentSpec
|
|||
/// Short description
|
||||
std::string desc;
|
||||
std::string path;
|
||||
std::string textdomain;
|
||||
};
|
||||
|
||||
|
||||
ContentType getContentType(const std::string &path);
|
||||
void parseContentInfo(ContentSpec &spec);
|
||||
|
|
|
@ -39,6 +39,11 @@ void set_default_settings()
|
|||
// Client
|
||||
settings->setDefault("address", "");
|
||||
settings->setDefault("enable_sound", "true");
|
||||
#if ENABLE_TOUCH
|
||||
settings->setDefault("enable_touch", "true");
|
||||
#else
|
||||
settings->setDefault("enable_touch", "false");
|
||||
#endif
|
||||
settings->setDefault("sound_volume", "0.8");
|
||||
settings->setDefault("sound_volume_unfocused", "0.3");
|
||||
settings->setDefault("mute_sound", "false");
|
||||
|
@ -90,7 +95,7 @@ void set_default_settings()
|
|||
settings->setDefault("keymap_cmd_local", ".");
|
||||
settings->setDefault("keymap_minimap", "KEY_KEY_V");
|
||||
settings->setDefault("keymap_console", "KEY_F10");
|
||||
#if HAVE_TOUCHSCREENGUI
|
||||
#if ENABLE_TOUCH
|
||||
// See https://github.com/minetest/minetest/issues/12792
|
||||
settings->setDefault("keymap_rangeselect", "KEY_KEY_R");
|
||||
#else
|
||||
|
@ -192,7 +197,11 @@ void set_default_settings()
|
|||
settings->setDefault("screen_h", "600");
|
||||
settings->setDefault("window_maximized", "false");
|
||||
settings->setDefault("autosave_screensize", "true");
|
||||
#ifdef ENABLE_TOUCH
|
||||
settings->setDefault("fullscreen", "true");
|
||||
#else
|
||||
settings->setDefault("fullscreen", "false");
|
||||
#endif
|
||||
settings->setDefault("vsync", "false");
|
||||
settings->setDefault("fov", "72");
|
||||
settings->setDefault("leaves_style", "fancy");
|
||||
|
@ -298,7 +307,7 @@ void set_default_settings()
|
|||
settings->setDefault("aux1_descends", "false");
|
||||
settings->setDefault("doubletap_jump", "false");
|
||||
settings->setDefault("always_fly_fast", "true");
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
#ifdef ENABLE_TOUCH
|
||||
settings->setDefault("autojump", "true");
|
||||
#else
|
||||
settings->setDefault("autojump", "false");
|
||||
|
@ -477,12 +486,12 @@ void set_default_settings()
|
|||
settings->setDefault("keymap_sneak", "KEY_SHIFT");
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
settings->setDefault("touchscreen_threshold", "20");
|
||||
settings->setDefault("touchscreen_sensitivity", "0.2");
|
||||
settings->setDefault("touch_use_crosshair", "false");
|
||||
settings->setDefault("fixed_virtual_joystick", "false");
|
||||
settings->setDefault("virtual_joystick_triggers_aux1", "false");
|
||||
#ifdef ENABLE_TOUCH
|
||||
settings->setDefault("clickable_chat_weblinks", "false");
|
||||
#else
|
||||
settings->setDefault("clickable_chat_weblinks", "true");
|
||||
|
@ -491,7 +500,6 @@ void set_default_settings()
|
|||
#ifdef __ANDROID__
|
||||
settings->setDefault("screen_w", "0");
|
||||
settings->setDefault("screen_h", "0");
|
||||
settings->setDefault("fullscreen", "true");
|
||||
settings->setDefault("performance_tradeoffs", "true");
|
||||
settings->setDefault("max_simultaneous_block_sends_per_client", "10");
|
||||
settings->setDefault("emergequeue_limit_diskonly", "16");
|
||||
|
|
|
@ -1,8 +1,3 @@
|
|||
set(extra_gui_SRCS "")
|
||||
if(ENABLE_TOUCH)
|
||||
set(extra_gui_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/touchscreengui.cpp)
|
||||
endif()
|
||||
|
||||
set(gui_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/guiAnimatedImage.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/guiBackgroundImage.cpp
|
||||
|
@ -29,6 +24,6 @@ set(gui_SRCS
|
|||
${CMAKE_CURRENT_SOURCE_DIR}/guiVolumeChange.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/modalMenu.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/profilergraph.cpp
|
||||
${extra_gui_SRCS}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/touchscreengui.cpp
|
||||
PARENT_SCOPE
|
||||
)
|
||||
|
|
|
@ -36,6 +36,8 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "client/guiscalingfilter.h"
|
||||
#include "irrlicht_changes/static_text.h"
|
||||
#include "client/tile.h"
|
||||
#include "content/content.h"
|
||||
#include "content/mods.h"
|
||||
|
||||
#if USE_SOUND
|
||||
#include "client/sound/sound_openal.h"
|
||||
|
@ -204,6 +206,57 @@ GUIEngine::GUIEngine(JoystickController *joystick,
|
|||
m_menu.reset();
|
||||
}
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
std::string findLocaleFileInMods(const std::string &path, const std::string &filename)
|
||||
{
|
||||
std::vector<ModSpec> mods = flattenMods(getModsInPath(path, "root", true));
|
||||
|
||||
for (const auto &mod : mods) {
|
||||
std::string ret = mod.path + DIR_DELIM "locale" DIR_DELIM + filename;
|
||||
if (fs::PathExists(ret)) {
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
Translations *GUIEngine::getContentTranslations(const std::string &path,
|
||||
const std::string &domain, const std::string &lang_code)
|
||||
{
|
||||
if (domain.empty() || lang_code.empty())
|
||||
return nullptr;
|
||||
|
||||
std::string filename = domain + "." + lang_code + ".tr";
|
||||
std::string key = path + DIR_DELIM "locale" DIR_DELIM + filename;
|
||||
|
||||
if (key == m_last_translations_key)
|
||||
return &m_last_translations;
|
||||
|
||||
std::string trans_path = key;
|
||||
ContentType type = getContentType(path);
|
||||
if (type == ContentType::GAME)
|
||||
trans_path = findLocaleFileInMods(path + DIR_DELIM "mods" DIR_DELIM, filename);
|
||||
else if (type == ContentType::MODPACK)
|
||||
trans_path = findLocaleFileInMods(path, filename);
|
||||
// We don't need to search for locale files in a mod, as there's only one `locale` folder.
|
||||
|
||||
if (trans_path.empty())
|
||||
return nullptr;
|
||||
|
||||
m_last_translations_key = key;
|
||||
m_last_translations = {};
|
||||
|
||||
std::string data;
|
||||
if (fs::ReadFile(trans_path, data)) {
|
||||
m_last_translations.loadTranslation(data);
|
||||
}
|
||||
|
||||
return &m_last_translations;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
bool GUIEngine::loadMainMenuScript()
|
||||
{
|
||||
|
|
|
@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "client/sound.h"
|
||||
#include "client/tile.h"
|
||||
#include "util/enriched_string.h"
|
||||
#include "translation.h"
|
||||
|
||||
/******************************************************************************/
|
||||
/* Structs and macros */
|
||||
|
@ -165,7 +166,22 @@ public:
|
|||
return m_scriptdir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get translations for content
|
||||
*
|
||||
* Only loads a single textdomain from the path, as specified by `domain`,
|
||||
* for performance reasons.
|
||||
*
|
||||
* WARNING: Do not store the returned pointer for long as the contents may
|
||||
* change with the next call to `getContentTranslations`.
|
||||
* */
|
||||
Translations *getContentTranslations(const std::string &path,
|
||||
const std::string &domain, const std::string &lang_code);
|
||||
|
||||
private:
|
||||
std::string m_last_translations_key;
|
||||
/** Only the most recently used translation set is kept loaded */
|
||||
Translations m_last_translations;
|
||||
|
||||
/** find and run the main menu script */
|
||||
bool loadMainMenuScript();
|
||||
|
|
|
@ -316,13 +316,11 @@ void GUIFormSpecMenu::parseSize(parserData* data, const std::string &element)
|
|||
data->invsize.Y = MYMAX(0, stof(parts[1]));
|
||||
|
||||
lockSize(false);
|
||||
#ifndef HAVE_TOUCHSCREENGUI
|
||||
if (parts.size() == 3) {
|
||||
if (!g_settings->getBool("enable_touch") && parts.size() == 3) {
|
||||
if (parts[2] == "true") {
|
||||
lockSize(true,v2u32(800,600));
|
||||
}
|
||||
}
|
||||
#endif
|
||||
data->explicit_size = true;
|
||||
return;
|
||||
}
|
||||
|
@ -3284,14 +3282,15 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize)
|
|||
|
||||
s32 min_screen_dim = std::min(padded_screensize.X, padded_screensize.Y);
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
// In Android, the preferred imgsize should be larger to accommodate the
|
||||
// smaller screensize.
|
||||
double prefer_imgsize = min_screen_dim / 10 * gui_scaling;
|
||||
#else
|
||||
// Desktop computers have more space, so try to fit 15 coordinates.
|
||||
double prefer_imgsize = min_screen_dim / 15 * gui_scaling;
|
||||
#endif
|
||||
double prefer_imgsize;
|
||||
if (g_settings->getBool("enable_touch")) {
|
||||
// The preferred imgsize should be larger to accommodate the
|
||||
// smaller screensize.
|
||||
prefer_imgsize = min_screen_dim / 10 * gui_scaling;
|
||||
} else {
|
||||
// Desktop computers have more space, so try to fit 15 coordinates.
|
||||
prefer_imgsize = min_screen_dim / 15 * gui_scaling;
|
||||
}
|
||||
// Try to use the preferred imgsize, but if that's bigger than the maximum
|
||||
// size, use the maximum size.
|
||||
use_imgsize = std::min(prefer_imgsize,
|
||||
|
|
|
@ -25,10 +25,6 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|||
#include <IGUIStaticText.h>
|
||||
#include <IGUIFont.h>
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
#include "client/renderingengine.h"
|
||||
#endif
|
||||
|
||||
#include "porting.h"
|
||||
#include "gettext.h"
|
||||
|
||||
|
@ -66,11 +62,8 @@ void GUIPasswordChange::regenerateGui(v2u32 screensize)
|
|||
/*
|
||||
Calculate new sizes and positions
|
||||
*/
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
const float s = m_gui_scale * RenderingEngine::getDisplayDensity() / 2;
|
||||
#else
|
||||
const float s = m_gui_scale;
|
||||
#endif
|
||||
|
||||
DesiredRect = core::rect<s32>(
|
||||
screensize.X / 2 - 580 * s / 2,
|
||||
screensize.Y / 2 - 300 * s / 2,
|
||||
|
|
|
@ -27,10 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "gettext.h"
|
||||
#include "porting.h"
|
||||
#include "settings.h"
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
#include "touchscreengui.h"
|
||||
#endif
|
||||
|
||||
GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
|
||||
s32 id, IMenuManager *menumgr, bool remap_dbl_click) :
|
||||
|
@ -44,11 +41,12 @@ GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent,
|
|||
{
|
||||
m_gui_scale = std::max(g_settings->getFloat("gui_scaling"), 0.5f);
|
||||
const float screen_dpi_scale = RenderingEngine::getDisplayDensity();
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
m_gui_scale *= 1.1f - 0.3f * screen_dpi_scale + 0.2f * screen_dpi_scale * screen_dpi_scale;
|
||||
#else
|
||||
m_gui_scale *= screen_dpi_scale;
|
||||
#endif
|
||||
|
||||
if (g_settings->getBool("enable_touch")) {
|
||||
m_gui_scale *= 1.1f - 0.3f * screen_dpi_scale + 0.2f * screen_dpi_scale * screen_dpi_scale;
|
||||
} else {
|
||||
m_gui_scale *= screen_dpi_scale;
|
||||
}
|
||||
|
||||
setVisible(true);
|
||||
m_menumgr->createdMenu(this);
|
||||
|
@ -102,10 +100,8 @@ void GUIModalMenu::quitMenu()
|
|||
Environment->removeFocus(this);
|
||||
m_menumgr->deletingMenu(this);
|
||||
this->remove();
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui)
|
||||
g_touchscreengui->show();
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent)
|
||||
|
|
|
@ -55,6 +55,7 @@ const std::string joystick_image_names[] = {
|
|||
|
||||
static EKEY_CODE id_to_keycode(touch_gui_button_id id)
|
||||
{
|
||||
EKEY_CODE code;
|
||||
// ESC isn't part of the keymap.
|
||||
if (id == exit_id)
|
||||
return KEY_ESCAPE;
|
||||
|
@ -110,7 +111,15 @@ static EKEY_CODE id_to_keycode(touch_gui_button_id id)
|
|||
break;
|
||||
}
|
||||
assert(!key.empty());
|
||||
return keyname_to_keycode(g_settings->get("keymap_" + key).c_str());
|
||||
std::string resolved = g_settings->get("keymap_" + key);
|
||||
try {
|
||||
code = keyname_to_keycode(resolved.c_str());
|
||||
} catch (UnknownKeycode &e) {
|
||||
code = KEY_UNKNOWN;
|
||||
warningstream << "TouchScreenGUI: Unknown key '" << resolved
|
||||
<< "' for '" << key << "', hiding button." << std::endl;
|
||||
}
|
||||
return code;
|
||||
}
|
||||
|
||||
static void load_button_texture(const button_info *btn, const std::string &path,
|
||||
|
@ -523,13 +532,23 @@ void TouchScreenGUI::init(ISimpleTextureSource *tsrc)
|
|||
+ 0.5f * button_size),
|
||||
AHBB_Dir_Right_Left, 3.0f);
|
||||
|
||||
m_settings_bar.addButton(fly_id, L"fly", "fly_btn.png");
|
||||
m_settings_bar.addButton(noclip_id, L"noclip", "noclip_btn.png");
|
||||
m_settings_bar.addButton(fast_id, L"fast", "fast_btn.png");
|
||||
m_settings_bar.addButton(debug_id, L"debug", "debug_btn.png");
|
||||
m_settings_bar.addButton(camera_id, L"camera", "camera_btn.png");
|
||||
m_settings_bar.addButton(range_id, L"rangeview", "rangeview_btn.png");
|
||||
m_settings_bar.addButton(minimap_id, L"minimap", "minimap_btn.png");
|
||||
const static std::map<touch_gui_button_id, std::string> settings_bar_buttons {
|
||||
{fly_id, "fly"},
|
||||
{noclip_id, "noclip"},
|
||||
{fast_id, "fast"},
|
||||
{debug_id, "debug"},
|
||||
{camera_id, "camera"},
|
||||
{range_id, "rangeview"},
|
||||
{minimap_id, "minimap"},
|
||||
};
|
||||
for (const auto &pair : settings_bar_buttons) {
|
||||
if (id_to_keycode(pair.first) == KEY_UNKNOWN)
|
||||
continue;
|
||||
|
||||
std::wstring wide = utf8_to_wide(pair.second);
|
||||
m_settings_bar.addButton(pair.first, wide.c_str(),
|
||||
pair.second + "_btn.png");
|
||||
}
|
||||
|
||||
// Chat is shown by default, so chat_hide_btn.png is shown first.
|
||||
m_settings_bar.addToggleButton(toggle_chat_id, L"togglechat",
|
||||
|
@ -545,10 +564,20 @@ void TouchScreenGUI::init(ISimpleTextureSource *tsrc)
|
|||
+ 0.5f * button_size),
|
||||
AHBB_Dir_Left_Right, 2.0f);
|
||||
|
||||
m_rare_controls_bar.addButton(chat_id, L"chat", "chat_btn.png");
|
||||
m_rare_controls_bar.addButton(inventory_id, L"inv", "inventory_btn.png");
|
||||
m_rare_controls_bar.addButton(drop_id, L"drop", "drop_btn.png");
|
||||
m_rare_controls_bar.addButton(exit_id, L"exit", "exit_btn.png");
|
||||
const static std::map<touch_gui_button_id, std::string> rare_controls_bar_buttons {
|
||||
{chat_id, "chat"},
|
||||
{inventory_id, "inventory"},
|
||||
{drop_id, "drop"},
|
||||
{exit_id, "exit"},
|
||||
};
|
||||
for (const auto &pair : rare_controls_bar_buttons) {
|
||||
if (id_to_keycode(pair.first) == KEY_UNKNOWN)
|
||||
continue;
|
||||
|
||||
std::wstring wide = utf8_to_wide(pair.second);
|
||||
m_rare_controls_bar.addButton(pair.first, wide.c_str(),
|
||||
pair.second + "_btn.png");
|
||||
}
|
||||
|
||||
m_initialized = true;
|
||||
}
|
||||
|
@ -687,12 +716,9 @@ void TouchScreenGUI::handleReleaseEvent(size_t evt_id)
|
|||
<< evt_id << std::endl;
|
||||
}
|
||||
|
||||
for (auto iter = m_known_ids.begin(); iter != m_known_ids.end(); ++iter) {
|
||||
if (iter->id == evt_id) {
|
||||
m_known_ids.erase(iter);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// By the way: Android reuses pointer IDs, so m_pointer_pos[evt_id]
|
||||
// will be overwritten soon anyway.
|
||||
m_pointer_pos.erase(evt_id);
|
||||
}
|
||||
|
||||
void TouchScreenGUI::translateEvent(const SEvent &event)
|
||||
|
@ -719,17 +745,6 @@ void TouchScreenGUI::translateEvent(const SEvent &event)
|
|||
const v2s32 dir_fixed = touch_pos - fixed_joystick_center;
|
||||
|
||||
if (event.TouchInput.Event == ETIE_PRESSED_DOWN) {
|
||||
/*
|
||||
* Add to own copy of event list...
|
||||
* android would provide this information but Irrlicht guys don't
|
||||
* wanna design an efficient interface
|
||||
*/
|
||||
id_status to_be_added{};
|
||||
to_be_added.id = event.TouchInput.ID;
|
||||
to_be_added.X = event.TouchInput.X;
|
||||
to_be_added.Y = event.TouchInput.Y;
|
||||
m_known_ids.push_back(to_be_added);
|
||||
|
||||
size_t eventID = event.TouchInput.ID;
|
||||
|
||||
touch_gui_button_id button = getButtonID(X, Y);
|
||||
|
@ -786,6 +801,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event)
|
|||
m_move_id = event.TouchInput.ID;
|
||||
m_move_has_really_moved = false;
|
||||
m_move_downtime = porting::getTimeMs();
|
||||
m_move_pos = touch_pos;
|
||||
// DON'T reset m_tap_state here, otherwise many short taps
|
||||
// will be ignored if you tap very fast.
|
||||
}
|
||||
|
@ -804,8 +820,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event)
|
|||
m_pointer_pos[event.TouchInput.ID] == touch_pos)
|
||||
return;
|
||||
|
||||
const v2s32 free_joystick_center = v2s32(m_pointer_pos[event.TouchInput.ID].X,
|
||||
m_pointer_pos[event.TouchInput.ID].Y);
|
||||
const v2s32 free_joystick_center = m_pointer_pos[event.TouchInput.ID];
|
||||
const v2s32 dir_free = touch_pos - free_joystick_center;
|
||||
|
||||
const double touch_threshold_sq = m_touchscreen_threshold * m_touchscreen_threshold;
|
||||
|
@ -814,6 +829,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event)
|
|||
if (dir_free.getLengthSQ() > touch_threshold_sq || m_move_has_really_moved) {
|
||||
m_move_has_really_moved = true;
|
||||
|
||||
m_move_pos = touch_pos;
|
||||
m_pointer_pos[event.TouchInput.ID] = touch_pos;
|
||||
|
||||
if (m_tap_state == TapState::None || m_draw_crosshair) {
|
||||
|
@ -994,7 +1010,9 @@ void TouchScreenGUI::step(float dtime)
|
|||
// thus the camera position can change, it doesn't suffice to update the
|
||||
// shootline when a touch event occurs.
|
||||
// Note that the shootline isn't used if touch_use_crosshair is enabled.
|
||||
if (!m_draw_crosshair) {
|
||||
// Only updating when m_has_move_id means that the shootline will stay at
|
||||
// it's last in-world position when the player doesn't need it.
|
||||
if (!m_draw_crosshair && m_has_move_id) {
|
||||
v2s32 pointer_pos = getPointerPos();
|
||||
m_shootline = m_device
|
||||
->getSceneManager()
|
||||
|
@ -1032,8 +1050,8 @@ void TouchScreenGUI::setVisible(bool visible)
|
|||
|
||||
// clear all active buttons
|
||||
if (!visible) {
|
||||
while (!m_known_ids.empty())
|
||||
handleReleaseEvent(m_known_ids.begin()->id);
|
||||
while (!m_pointer_pos.empty())
|
||||
handleReleaseEvent(m_pointer_pos.begin()->first);
|
||||
|
||||
m_settings_bar.hide();
|
||||
m_rare_controls_bar.hide();
|
||||
|
@ -1063,7 +1081,9 @@ v2s32 TouchScreenGUI::getPointerPos()
|
|||
{
|
||||
if (m_draw_crosshair)
|
||||
return v2s32(m_screensize.X / 2, m_screensize.Y / 2);
|
||||
return m_pointer_pos[m_move_id];
|
||||
// We can't just use m_pointer_pos[m_move_id] because applyContextControls
|
||||
// may emit release events after m_pointer_pos[m_move_id] is erased.
|
||||
return m_move_pos;
|
||||
}
|
||||
|
||||
void TouchScreenGUI::emitMouseEvent(EMOUSE_INPUT_EVENT type)
|
||||
|
|
|
@ -246,6 +246,8 @@ private:
|
|||
size_t m_move_id;
|
||||
bool m_move_has_really_moved = false;
|
||||
u64 m_move_downtime = 0;
|
||||
// m_move_pos stays valid even after m_move_id has been released.
|
||||
v2s32 m_move_pos;
|
||||
|
||||
bool m_has_joystick_id = false;
|
||||
size_t m_joystick_id;
|
||||
|
@ -281,16 +283,6 @@ private:
|
|||
const rect<s32> &button_rect, int texture_id,
|
||||
bool visible = true);
|
||||
|
||||
struct id_status
|
||||
{
|
||||
size_t id;
|
||||
int X;
|
||||
int Y;
|
||||
};
|
||||
|
||||
// vector to store known ids and their initial touch positions
|
||||
std::vector<id_status> m_known_ids;
|
||||
|
||||
// handle a button event
|
||||
void handleButtonEvent(touch_gui_button_id bID, size_t eventID, bool action);
|
||||
|
||||
|
@ -303,7 +295,7 @@ private:
|
|||
// apply joystick status
|
||||
void applyJoystickStatus();
|
||||
|
||||
// array for saving last known position of a pointer
|
||||
// map to store the IDs and positions of currently pressed pointers
|
||||
std::unordered_map<size_t, v2s32> m_pointer_pos;
|
||||
|
||||
// settings bar
|
||||
|
|
|
@ -50,9 +50,6 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "gui/guiEngine.h"
|
||||
#include "gui/mainmenumanager.h"
|
||||
#endif
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
#include "gui/touchscreengui.h"
|
||||
#endif
|
||||
|
||||
// for version information only
|
||||
extern "C" {
|
||||
|
|
15
src/map.cpp
15
src/map.cpp
|
@ -228,12 +228,12 @@ void Map::addNodeAndUpdate(v3s16 p, MapNode n,
|
|||
std::vector<std::pair<v3s16, MapNode> > oldnodes;
|
||||
oldnodes.emplace_back(p, oldnode);
|
||||
voxalgo::update_lighting_nodes(this, oldnodes, modified_blocks);
|
||||
|
||||
for (auto &modified_block : modified_blocks) {
|
||||
modified_block.second->expireDayNightDiff();
|
||||
}
|
||||
}
|
||||
|
||||
if (n.getContent() != oldnode.getContent() &&
|
||||
(oldnode.getContent() == CONTENT_AIR || n.getContent() == CONTENT_AIR))
|
||||
block->expireIsAirCache();
|
||||
|
||||
// Report for rollback
|
||||
if(m_gamedef->rollback())
|
||||
{
|
||||
|
@ -1462,14 +1462,14 @@ void ServerMap::finishBlockMake(BlockMakeData *data,
|
|||
if (!block)
|
||||
continue;
|
||||
/*
|
||||
Update day/night difference cache of the MapBlocks
|
||||
Update is air cache of the MapBlocks
|
||||
*/
|
||||
block->expireDayNightDiff();
|
||||
block->expireIsAirCache();
|
||||
/*
|
||||
Set block as modified
|
||||
*/
|
||||
block->raiseModified(MOD_STATE_WRITE_NEEDED,
|
||||
MOD_REASON_EXPIRE_DAYNIGHTDIFF);
|
||||
MOD_REASON_EXPIRE_IS_AIR);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -2064,6 +2064,7 @@ void MMVManip::blitBackAll(std::map<v3s16, MapBlock*> *modified_blocks,
|
|||
|
||||
block->copyFrom(*this);
|
||||
block->raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_VMANIP);
|
||||
block->expireIsAirCache();
|
||||
|
||||
if(modified_blocks)
|
||||
(*modified_blocks)[p] = block;
|
||||
|
|
|
@ -186,57 +186,27 @@ void MapBlock::copyFrom(VoxelManipulator &dst)
|
|||
getPosRelative(), data_size);
|
||||
}
|
||||
|
||||
void MapBlock::actuallyUpdateDayNightDiff()
|
||||
void MapBlock::actuallyUpdateIsAir()
|
||||
{
|
||||
const NodeDefManager *nodemgr = m_gamedef->ndef();
|
||||
// Running this function un-expires m_is_air
|
||||
m_is_air_expired = false;
|
||||
|
||||
// Running this function un-expires m_day_night_differs
|
||||
m_day_night_differs_expired = false;
|
||||
|
||||
bool differs = false;
|
||||
|
||||
/*
|
||||
Check if any lighting value differs
|
||||
*/
|
||||
|
||||
MapNode previous_n(CONTENT_IGNORE);
|
||||
bool only_air = true;
|
||||
for (u32 i = 0; i < nodecount; i++) {
|
||||
MapNode n = data[i];
|
||||
|
||||
// If node is identical to previous node, don't verify if it differs
|
||||
if (n == previous_n)
|
||||
continue;
|
||||
|
||||
differs = !n.isLightDayNightEq(nodemgr->getLightingFlags(n));
|
||||
if (differs)
|
||||
MapNode &n = data[i];
|
||||
if (n.getContent() != CONTENT_AIR) {
|
||||
only_air = false;
|
||||
break;
|
||||
previous_n = n;
|
||||
}
|
||||
|
||||
/*
|
||||
If some lighting values differ, check if the whole thing is
|
||||
just air. If it is just air, differs = false
|
||||
*/
|
||||
if (differs) {
|
||||
bool only_air = true;
|
||||
for (u32 i = 0; i < nodecount; i++) {
|
||||
MapNode &n = data[i];
|
||||
if (n.getContent() != CONTENT_AIR) {
|
||||
only_air = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (only_air)
|
||||
differs = false;
|
||||
}
|
||||
|
||||
// Set member variable
|
||||
m_day_night_differs = differs;
|
||||
m_is_air = only_air;
|
||||
}
|
||||
|
||||
void MapBlock::expireDayNightDiff()
|
||||
void MapBlock::expireIsAirCache()
|
||||
{
|
||||
m_day_night_differs_expired = true;
|
||||
m_is_air_expired = true;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -369,7 +339,12 @@ void MapBlock::serialize(std::ostream &os_compressed, u8 version, bool disk, int
|
|||
u8 flags = 0;
|
||||
if(is_underground)
|
||||
flags |= 0x01;
|
||||
if(getDayNightDiff())
|
||||
// This flag used to be day-night-differs, and it is no longer used.
|
||||
// We write it anyway so that old servers can still use this.
|
||||
// Above ground isAir implies !day-night-differs, !isAir is good enough for old servers
|
||||
// to check whether above ground blocks should be sent.
|
||||
// See RemoteClient::getNextBlocks(...)
|
||||
if(!isAir())
|
||||
flags |= 0x02;
|
||||
if (!m_generated)
|
||||
flags |= 0x08;
|
||||
|
@ -473,7 +448,7 @@ void MapBlock::deSerialize(std::istream &in_compressed, u8 version, bool disk)
|
|||
|
||||
TRACESTREAM(<<"MapBlock::deSerialize "<<getPos()<<std::endl);
|
||||
|
||||
m_day_night_differs_expired = false;
|
||||
m_is_air_expired = true;
|
||||
|
||||
if(version <= 21)
|
||||
{
|
||||
|
@ -489,7 +464,9 @@ void MapBlock::deSerialize(std::istream &in_compressed, u8 version, bool disk)
|
|||
|
||||
u8 flags = readU8(is);
|
||||
is_underground = (flags & 0x01) != 0;
|
||||
m_day_night_differs = (flags & 0x02) != 0;
|
||||
// IMPORTANT: when the version is bumped to 30 we can read m_is_air from here
|
||||
// m_is_air = (flags & 0x02) == 0;
|
||||
|
||||
if (version < 27)
|
||||
m_lighting_complete = 0xFFFF;
|
||||
else
|
||||
|
@ -599,6 +576,10 @@ void MapBlock::deSerialize(std::istream &in_compressed, u8 version, bool disk)
|
|||
<<": Node timers (ver>=25)"<<std::endl);
|
||||
m_node_timers.deSerialize(is, version);
|
||||
}
|
||||
|
||||
u16 dummy;
|
||||
m_is_air = nimap.size() == 1 && nimap.getId("air", dummy);
|
||||
m_is_air_expired = false;
|
||||
}
|
||||
|
||||
TRACESTREAM(<<"MapBlock::deSerialize "<<getPos()
|
||||
|
@ -647,7 +628,7 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
|
|||
{
|
||||
// Initialize default flags
|
||||
is_underground = false;
|
||||
m_day_night_differs = false;
|
||||
m_is_air = false;
|
||||
m_lighting_complete = 0xFFFF;
|
||||
m_generated = true;
|
||||
|
||||
|
@ -713,7 +694,6 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
|
|||
u8 flags;
|
||||
is.read((char*)&flags, 1);
|
||||
is_underground = (flags & 0x01) != 0;
|
||||
m_day_night_differs = (flags & 0x02) != 0;
|
||||
if (version >= 18)
|
||||
m_generated = (flags & 0x08) == 0;
|
||||
|
||||
|
@ -798,9 +778,13 @@ void MapBlock::deSerialize_pre22(std::istream &is, u8 version, bool disk)
|
|||
// If supported, read node definition id mapping
|
||||
if (version >= 21) {
|
||||
nimap.deSerialize(is);
|
||||
u16 dummy;
|
||||
m_is_air = nimap.size() == 1 && nimap.getId("air", dummy);
|
||||
// Else set the legacy mapping
|
||||
} else {
|
||||
content_mapnode_get_name_id_mapping(&nimap);
|
||||
m_is_air = false;
|
||||
m_is_air_expired = true;
|
||||
}
|
||||
correctBlockNodeIds(&nimap, data, m_gamedef);
|
||||
}
|
||||
|
|
|
@ -60,7 +60,7 @@ enum ModReason : u32 {
|
|||
MOD_REASON_STATIC_DATA_ADDED = 1 << 13,
|
||||
MOD_REASON_STATIC_DATA_REMOVED = 1 << 14,
|
||||
MOD_REASON_STATIC_DATA_CHANGED = 1 << 15,
|
||||
MOD_REASON_EXPIRE_DAYNIGHTDIFF = 1 << 16,
|
||||
MOD_REASON_EXPIRE_IS_AIR = 1 << 16,
|
||||
MOD_REASON_VMANIP = 1 << 17,
|
||||
MOD_REASON_UNKNOWN = 1 << 18,
|
||||
};
|
||||
|
@ -310,20 +310,19 @@ public:
|
|||
// Copies data from VoxelManipulator getPosRelative()
|
||||
void copyFrom(VoxelManipulator &dst);
|
||||
|
||||
// Update day-night lighting difference flag.
|
||||
// Sets m_day_night_differs to appropriate value.
|
||||
// These methods don't care about neighboring blocks.
|
||||
void actuallyUpdateDayNightDiff();
|
||||
// Update is air flag.
|
||||
// Sets m_is_air to appropriate value.
|
||||
void actuallyUpdateIsAir();
|
||||
|
||||
// Call this to schedule what the previous function does to be done
|
||||
// when the value is actually needed.
|
||||
void expireDayNightDiff();
|
||||
void expireIsAirCache();
|
||||
|
||||
inline bool getDayNightDiff()
|
||||
inline bool isAir()
|
||||
{
|
||||
if (m_day_night_differs_expired)
|
||||
actuallyUpdateDayNightDiff();
|
||||
return m_day_night_differs;
|
||||
if (m_is_air_expired)
|
||||
actuallyUpdateIsAir();
|
||||
return m_is_air;
|
||||
}
|
||||
|
||||
bool onObjectsActivation();
|
||||
|
@ -517,8 +516,8 @@ public:
|
|||
|
||||
private:
|
||||
// Whether day and night lighting differs
|
||||
bool m_day_night_differs = false;
|
||||
bool m_day_night_differs_expired = true;
|
||||
bool m_is_air = false;
|
||||
bool m_is_air_expired = true;
|
||||
|
||||
/*
|
||||
- On the server, this is used for telling whether the
|
||||
|
|
|
@ -471,6 +471,7 @@ void Client::handleCommand_ActiveObjectRemoveAdd(NetworkPacket* pkt)
|
|||
for (u16 i = 0; i < removed_count; i++) {
|
||||
*pkt >> id;
|
||||
m_env.removeActiveObject(id);
|
||||
removeActiveObjectSounds(id);
|
||||
}
|
||||
|
||||
// Read added objects
|
||||
|
|
|
@ -615,7 +615,7 @@ class Peer {
|
|||
u64 m_last_timeout_check;
|
||||
};
|
||||
|
||||
class UDPPeer : public Peer
|
||||
class UDPPeer final : public Peer
|
||||
{
|
||||
public:
|
||||
|
||||
|
@ -628,15 +628,15 @@ public:
|
|||
virtual ~UDPPeer() = default;
|
||||
|
||||
void PutReliableSendCommand(ConnectionCommandPtr &c,
|
||||
unsigned int max_packet_size);
|
||||
unsigned int max_packet_size) override;
|
||||
|
||||
bool getAddress(MTProtocols type, Address& toset);
|
||||
bool getAddress(MTProtocols type, Address& toset) override;
|
||||
|
||||
u16 getNextSplitSequenceNumber(u8 channel);
|
||||
void setNextSplitSequenceNumber(u8 channel, u16 seqnum);
|
||||
u16 getNextSplitSequenceNumber(u8 channel) override;
|
||||
void setNextSplitSequenceNumber(u8 channel, u16 seqnum) override;
|
||||
|
||||
SharedBuffer<u8> addSplitPacket(u8 channel, BufferedPacketPtr &toadd,
|
||||
bool reliable);
|
||||
bool reliable) override;
|
||||
|
||||
bool isTimedOut(float timeout, std::string &reason) override;
|
||||
|
||||
|
@ -645,7 +645,7 @@ protected:
|
|||
Calculates avg_rtt and resend_timeout.
|
||||
rtt=-1 only recalculates resend_timeout
|
||||
*/
|
||||
void reportRTT(float rtt);
|
||||
void reportRTT(float rtt) override;
|
||||
|
||||
void RunCommandQueues(
|
||||
unsigned int max_packet_size,
|
||||
|
@ -657,7 +657,7 @@ protected:
|
|||
void setResendTimeout(float timeout)
|
||||
{ MutexAutoLock lock(m_exclusive_access_mutex); resend_timeout = timeout; }
|
||||
|
||||
bool Ping(float dtime,SharedBuffer<u8>& data);
|
||||
bool Ping(float dtime, SharedBuffer<u8>& data) override;
|
||||
|
||||
Channel channels[CHANNEL_COUNT];
|
||||
bool m_pending_disconnect = false;
|
||||
|
|
|
@ -46,9 +46,9 @@ T RangedParameter<T>::pickWithin() const
|
|||
auto p = numericAbsolute(bias) + 1;
|
||||
for (size_t i = 0; i < sizeof(values) / sizeof(values[0]); ++i) {
|
||||
if (bias < 0)
|
||||
values[i] = 1.0f - pow(myrand_float(), p);
|
||||
values[i] = 1.0f - std::pow(myrand_float(), p);
|
||||
else
|
||||
values[i] = pow(myrand_float(), p);
|
||||
values[i] = std::pow(myrand_float(), p);
|
||||
}
|
||||
return T::pick(values, min, max);
|
||||
}
|
||||
|
|
|
@ -191,7 +191,7 @@ u32 PlayerControl::getKeysPressed() const
|
|||
float abs_d;
|
||||
|
||||
// (absolute value indicates forward / backward)
|
||||
abs_d = abs(movement_direction);
|
||||
abs_d = std::abs(movement_direction);
|
||||
if (abs_d < 3.0f / 8.0f * M_PI)
|
||||
keypress_bits |= (u32)1; // Forward
|
||||
if (abs_d > 5.0f / 8.0f * M_PI)
|
||||
|
@ -201,7 +201,7 @@ u32 PlayerControl::getKeysPressed() const
|
|||
abs_d = movement_direction + M_PI_2;
|
||||
if (abs_d >= M_PI)
|
||||
abs_d -= 2 * M_PI;
|
||||
abs_d = abs(abs_d);
|
||||
abs_d = std::abs(abs_d);
|
||||
// (value now indicates left / right)
|
||||
if (abs_d < 3.0f / 8.0f * M_PI)
|
||||
keypress_bits |= (u32)1 << 2; // Left
|
||||
|
|
|
@ -630,7 +630,7 @@ void script_dump_packed(const PackedValue *val)
|
|||
printf("table(%d, %d)", i.uidata1, i.uidata2);
|
||||
break;
|
||||
case LUA_TFUNCTION:
|
||||
printf("function(%lu byte)", i.sdata.size());
|
||||
printf("function(%d bytes)", (int)i.sdata.size());
|
||||
break;
|
||||
case LUA_TUSERDATA:
|
||||
printf("userdata %s %p", i.sdata.c_str(), i.ptrdata);
|
||||
|
|
|
@ -153,13 +153,6 @@ ScriptApiBase::ScriptApiBase(ScriptingType type):
|
|||
lua_pushstring(m_luastack, porting::getPlatformName());
|
||||
lua_setglobal(m_luastack, "PLATFORM");
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
lua_pushboolean(m_luastack, true);
|
||||
#else
|
||||
lua_pushboolean(m_luastack, false);
|
||||
#endif
|
||||
lua_setglobal(m_luastack, "TOUCHSCREEN_GUI");
|
||||
|
||||
// Make sure Lua uses the right locale
|
||||
setlocale(LC_NUMERIC, "C");
|
||||
}
|
||||
|
|
|
@ -363,6 +363,9 @@ int ModApiMainMenu::l_get_content_info(lua_State *L)
|
|||
lua_pushstring(L, spec.name.c_str());
|
||||
lua_setfield(L, -2, "name");
|
||||
|
||||
lua_pushstring(L, spec.title.c_str());
|
||||
lua_setfield(L, -2, "title");
|
||||
|
||||
lua_pushstring(L, spec.type.c_str());
|
||||
lua_setfield(L, -2, "type");
|
||||
|
||||
|
@ -383,6 +386,9 @@ int ModApiMainMenu::l_get_content_info(lua_State *L)
|
|||
lua_pushstring(L, spec.path.c_str());
|
||||
lua_setfield(L, -2, "path");
|
||||
|
||||
lua_pushstring(L, spec.textdomain.c_str());
|
||||
lua_setfield(L, -2, "textdomain");
|
||||
|
||||
if (spec.type == "mod") {
|
||||
ModSpec spec;
|
||||
spec.path = path;
|
||||
|
@ -432,8 +438,7 @@ int ModApiMainMenu::l_check_mod_configuration(lua_State *L)
|
|||
// Ignore non-string keys
|
||||
if (lua_type(L, -2) != LUA_TSTRING) {
|
||||
throw LuaError(
|
||||
"Unexpected non-string key in table passed to "
|
||||
"core.check_mod_configuration");
|
||||
"Unexpected non-string key in table passed to core.check_mod_configuration");
|
||||
}
|
||||
|
||||
std::string modpath = luaL_checkstring(L, -1);
|
||||
|
@ -472,7 +477,6 @@ int ModApiMainMenu::l_check_mod_configuration(lua_State *L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
|
||||
lua_newtable(L);
|
||||
|
||||
lua_pushboolean(L, modmgr.isConsistent());
|
||||
|
@ -500,7 +504,25 @@ int ModApiMainMenu::l_check_mod_configuration(lua_State *L)
|
|||
index++;
|
||||
}
|
||||
lua_setfield(L, -2, "satisfied_mods");
|
||||
return 1;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
int ModApiMainMenu::l_get_content_translation(lua_State *L)
|
||||
{
|
||||
GUIEngine* engine = getGuiEngine(L);
|
||||
sanity_check(engine != NULL);
|
||||
|
||||
std::string path = luaL_checkstring(L, 1);
|
||||
std::string domain = luaL_checkstring(L, 2);
|
||||
std::string string = luaL_checkstring(L, 3);
|
||||
std::string lang = gettext("LANG_CODE");
|
||||
if (lang == "LANG_CODE")
|
||||
lang = "";
|
||||
|
||||
auto *translations = engine->getContentTranslations(path, domain, lang);
|
||||
string = wide_to_utf8(translate_string(utf8_to_wide(string), translations));
|
||||
lua_pushstring(L, string.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -911,6 +933,17 @@ int ModApiMainMenu::l_get_video_drivers(lua_State *L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
int ModApiMainMenu::l_get_language(lua_State *L)
|
||||
{
|
||||
std::string lang = gettext("LANG_CODE");
|
||||
if (lang == "LANG_CODE")
|
||||
lang = "";
|
||||
|
||||
lua_pushstring(L, lang.c_str());
|
||||
return 1;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
int ModApiMainMenu::l_gettext(lua_State *L)
|
||||
{
|
||||
|
@ -1102,6 +1135,7 @@ void ModApiMainMenu::Initialize(lua_State *L, int top)
|
|||
API_FCT(get_games);
|
||||
API_FCT(get_content_info);
|
||||
API_FCT(check_mod_configuration);
|
||||
API_FCT(get_content_translation);
|
||||
API_FCT(start);
|
||||
API_FCT(close);
|
||||
API_FCT(show_keys_menu);
|
||||
|
@ -1128,6 +1162,7 @@ void ModApiMainMenu::Initialize(lua_State *L, int top)
|
|||
API_FCT(get_mainmenu_path);
|
||||
API_FCT(show_path_select_dialog);
|
||||
API_FCT(download_file);
|
||||
API_FCT(get_language);
|
||||
API_FCT(gettext);
|
||||
API_FCT(get_video_drivers);
|
||||
API_FCT(get_window_info);
|
||||
|
@ -1168,5 +1203,6 @@ void ModApiMainMenu::InitializeAsync(lua_State *L, int top)
|
|||
API_FCT(download_file);
|
||||
API_FCT(get_min_supp_proto);
|
||||
API_FCT(get_max_supp_proto);
|
||||
API_FCT(get_language);
|
||||
API_FCT(gettext);
|
||||
}
|
||||
|
|
|
@ -74,6 +74,8 @@ private:
|
|||
|
||||
static int l_get_mapgen_names(lua_State *L);
|
||||
|
||||
static int l_get_language(lua_State *L);
|
||||
|
||||
static int l_gettext(lua_State *L);
|
||||
|
||||
//packages
|
||||
|
@ -84,6 +86,8 @@ private:
|
|||
|
||||
static int l_check_mod_configuration(lua_State *L);
|
||||
|
||||
static int l_get_content_translation(lua_State *L);
|
||||
|
||||
//gui
|
||||
|
||||
static int l_show_keys_menu(lua_State *L);
|
||||
|
|
|
@ -115,7 +115,13 @@ int MetaDataRef::l_set_string(lua_State *L)
|
|||
|
||||
MetaDataRef *ref = checkAnyMetadata(L, 1);
|
||||
std::string name = luaL_checkstring(L, 2);
|
||||
auto str = readParam<std::string_view>(L, 3);
|
||||
std::string_view str;
|
||||
if (!lua_isnoneornil(L, 3)) {
|
||||
str = readParam<std::string_view>(L, 3);
|
||||
} else {
|
||||
log_deprecated(L, "Value passed to set_string is nil. This behaviour is"
|
||||
" undocumented and will result in an error in the future.", 1, true);
|
||||
}
|
||||
|
||||
IMetadata *meta = ref->getmeta(!str.empty());
|
||||
if (meta != NULL && meta->setString(name, str))
|
||||
|
|
|
@ -132,9 +132,6 @@ int LuaMinimap::l_show(lua_State *L)
|
|||
if (!g_settings->getBool("enable_minimap"))
|
||||
return 1;
|
||||
|
||||
Client *client = getClient(L);
|
||||
assert(client);
|
||||
|
||||
LuaMinimap *ref = checkObject<LuaMinimap>(L, 1);
|
||||
Minimap *m = getobject(ref);
|
||||
|
||||
|
@ -149,9 +146,6 @@ int LuaMinimap::l_show(lua_State *L)
|
|||
|
||||
int LuaMinimap::l_hide(lua_State *L)
|
||||
{
|
||||
Client *client = getClient(L);
|
||||
assert(client);
|
||||
|
||||
LuaMinimap *ref = checkObject<LuaMinimap>(L, 1);
|
||||
Minimap *m = getobject(ref);
|
||||
|
||||
|
|
|
@ -42,6 +42,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
|||
#include "version.h"
|
||||
#include "util/hex.h"
|
||||
#include "util/sha1.h"
|
||||
#include "util/sha256.h"
|
||||
#include "util/png.h"
|
||||
#include <cstdio>
|
||||
|
||||
|
@ -566,6 +567,27 @@ int ModApiUtil::l_sha1(lua_State *L)
|
|||
return 1;
|
||||
}
|
||||
|
||||
int ModApiUtil::l_sha256(lua_State *L)
|
||||
{
|
||||
NO_MAP_LOCK_REQUIRED;
|
||||
|
||||
auto data = readParam<std::string_view>(L, 1);
|
||||
bool hex = !lua_isboolean(L, 2) || !readParam<bool>(L, 2);
|
||||
|
||||
std::string data_sha256;
|
||||
data_sha256.resize(SHA256_DIGEST_LENGTH);
|
||||
SHA256(reinterpret_cast<const unsigned char*>(data.data()), data.size(),
|
||||
reinterpret_cast<unsigned char *>(data_sha256.data()));
|
||||
|
||||
if (hex) {
|
||||
lua_pushstring(L, hex_encode(data_sha256).c_str());
|
||||
} else {
|
||||
lua_pushlstring(L, data_sha256.data(), data_sha256.size());
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// colorspec_to_colorstring(colorspec)
|
||||
int ModApiUtil::l_colorspec_to_colorstring(lua_State *L)
|
||||
{
|
||||
|
@ -690,6 +712,7 @@ void ModApiUtil::Initialize(lua_State *L, int top)
|
|||
|
||||
API_FCT(get_version);
|
||||
API_FCT(sha1);
|
||||
API_FCT(sha256);
|
||||
API_FCT(colorspec_to_colorstring);
|
||||
API_FCT(colorspec_to_bytes);
|
||||
|
||||
|
@ -723,6 +746,7 @@ void ModApiUtil::InitializeClient(lua_State *L, int top)
|
|||
|
||||
API_FCT(get_version);
|
||||
API_FCT(sha1);
|
||||
API_FCT(sha256);
|
||||
API_FCT(colorspec_to_colorstring);
|
||||
API_FCT(colorspec_to_bytes);
|
||||
|
||||
|
@ -766,6 +790,7 @@ void ModApiUtil::InitializeAsync(lua_State *L, int top)
|
|||
|
||||
API_FCT(get_version);
|
||||
API_FCT(sha1);
|
||||
API_FCT(sha256);
|
||||
API_FCT(colorspec_to_colorstring);
|
||||
API_FCT(colorspec_to_bytes);
|
||||
|
||||
|
|
|
@ -113,6 +113,9 @@ private:
|
|||
// sha1(string, raw)
|
||||
static int l_sha1(lua_State *L);
|
||||
|
||||
// sha256(string, raw)
|
||||
static int l_sha256(lua_State *L);
|
||||
|
||||
// colorspec_to_colorstring(colorspec)
|
||||
static int l_colorspec_to_colorstring(lua_State *L);
|
||||
|
||||
|
|
|
@ -646,15 +646,29 @@ void Server::AsyncRunStep(float dtime, bool initial_step)
|
|||
|
||||
{
|
||||
MutexAutoLock lock(m_env_mutex);
|
||||
// Figure out and report maximum lag to environment
|
||||
float max_lag = m_env->getMaxLagEstimate();
|
||||
max_lag *= 0.9998; // Decrease slowly (about half per 5 minutes)
|
||||
if(dtime > max_lag){
|
||||
if(dtime > 0.1 && dtime > max_lag * 2.0)
|
||||
infostream<<"Server: Maximum lag peaked to "<<dtime
|
||||
<<" s"<<std::endl;
|
||||
max_lag = dtime;
|
||||
constexpr float lag_warn_threshold = 2.0f;
|
||||
|
||||
// Decrease value gradually, halve it every minute.
|
||||
if (m_max_lag_decrease.step(dtime, 0.5f)) {
|
||||
// To reproduce this number enter "solve (x)**(60/0.5) = 0.5"
|
||||
// into Wolfram Alpha.
|
||||
max_lag *= 0.99425f;
|
||||
}
|
||||
|
||||
// Report a 20% change to the log but only if we're >10% off target
|
||||
// also report if we crossed into the warning boundary
|
||||
if (dtime >= max_lag * 1.2f ||
|
||||
(max_lag < lag_warn_threshold && dtime >= lag_warn_threshold)) {
|
||||
const float steplen = getStepSettings().steplen;
|
||||
if (dtime > steplen * 1.1f) {
|
||||
auto &to = dtime >= lag_warn_threshold ? warningstream : infostream;
|
||||
to << "Server: Maximum lag peaked at " << dtime
|
||||
<< " (steplen=" << steplen << ")" << std::endl;
|
||||
}
|
||||
}
|
||||
max_lag = std::max(max_lag, dtime),
|
||||
|
||||
m_env->reportMaxLagEstimate(max_lag);
|
||||
|
||||
// Step environment
|
||||
|
@ -2264,6 +2278,22 @@ void Server::fadeSound(s32 handle, float step, float gain)
|
|||
m_playing_sounds.erase(it);
|
||||
}
|
||||
|
||||
void Server::stopAttachedSounds(u16 id)
|
||||
{
|
||||
assert(id);
|
||||
|
||||
for (auto it = m_playing_sounds.begin(); it != m_playing_sounds.end(); ) {
|
||||
const ServerPlayingSound &sound = it->second;
|
||||
|
||||
if (sound.object == id) {
|
||||
// Remove sound reference
|
||||
it = m_playing_sounds.erase(it);
|
||||
}
|
||||
else
|
||||
it++;
|
||||
}
|
||||
}
|
||||
|
||||
void Server::sendRemoveNode(v3s16 p, std::unordered_set<u16> *far_players,
|
||||
float far_d_nodes)
|
||||
{
|
||||
|
|
|
@ -239,6 +239,7 @@ public:
|
|||
s32 playSound(ServerPlayingSound ¶ms, bool ephemeral=false);
|
||||
void stopSound(s32 handle);
|
||||
void fadeSound(s32 handle, float step, float gain);
|
||||
void stopAttachedSounds(u16 id);
|
||||
|
||||
// Envlock
|
||||
std::set<std::string> getPlayerEffectivePrivs(const std::string &name);
|
||||
|
@ -608,12 +609,14 @@ private:
|
|||
MutexedVariable<std::string> m_async_fatal_error;
|
||||
|
||||
// Some timers
|
||||
float m_time_of_day_send_timer = 0.0f;
|
||||
float m_liquid_transform_timer = 0.0f;
|
||||
float m_liquid_transform_every = 1.0f;
|
||||
float m_masterserver_timer = 0.0f;
|
||||
float m_emergethread_trigger_timer = 0.0f;
|
||||
float m_savemap_timer = 0.0f;
|
||||
IntervalLimiter m_map_timer_and_unload_interval;
|
||||
IntervalLimiter m_max_lag_decrease;
|
||||
|
||||
// Environment
|
||||
ServerEnvironment *m_env = nullptr;
|
||||
|
@ -661,12 +664,6 @@ private:
|
|||
// The server mainly operates in this thread
|
||||
ServerThread *m_thread = nullptr;
|
||||
|
||||
/*
|
||||
Time related stuff
|
||||
*/
|
||||
// Timer for sending time of day over network
|
||||
float m_time_of_day_send_timer = 0.0f;
|
||||
|
||||
/*
|
||||
Client interface
|
||||
*/
|
||||
|
|
|
@ -354,18 +354,12 @@ void RemoteClient::GetNextBlocks (
|
|||
continue;
|
||||
|
||||
/*
|
||||
If block is not close, don't send it unless it is near
|
||||
ground level.
|
||||
|
||||
Block is near ground level if night-time mesh
|
||||
differs from day-time mesh.
|
||||
If block is not close, don't send it if it
|
||||
consists of air only.
|
||||
*/
|
||||
if (d >= d_opt) {
|
||||
if (!block->getIsUnderground() && !block->getDayNightDiff())
|
||||
if (d >= d_opt && block->isAir())
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Check occlusion cache first.
|
||||
*/
|
||||
|
|
|
@ -203,7 +203,7 @@ void LuaEntitySAO::step(float dtime, bool send_recommended)
|
|||
}
|
||||
}
|
||||
|
||||
if (fabs(m_prop.automatic_rotate) > 0.001f) {
|
||||
if (std::abs(m_prop.automatic_rotate) > 0.001f) {
|
||||
m_rotation_add_yaw = modulo360f(m_rotation_add_yaw + dtime * core::RADTODEG *
|
||||
m_prop.automatic_rotate);
|
||||
}
|
||||
|
|
|
@ -1253,10 +1253,7 @@ void ServerEnvironment::clearObjects(ClearObjectsMode mode)
|
|||
return false;
|
||||
}
|
||||
|
||||
// Tell the object about removal
|
||||
obj->removingFromEnvironment();
|
||||
// Deregister in scripting api
|
||||
m_script->removeObjectReference(obj);
|
||||
processActiveObjectRemove(obj, id);
|
||||
|
||||
// Delete active object
|
||||
return true;
|
||||
|
@ -1976,10 +1973,7 @@ void ServerEnvironment::removeRemovedObjects()
|
|||
}
|
||||
}
|
||||
|
||||
// Tell the object about removal
|
||||
obj->removingFromEnvironment();
|
||||
// Deregister in scripting api
|
||||
m_script->removeObjectReference(obj);
|
||||
processActiveObjectRemove(obj, id);
|
||||
|
||||
// Delete
|
||||
return true;
|
||||
|
@ -2216,10 +2210,7 @@ void ServerEnvironment::deactivateFarObjects(bool _force_delete)
|
|||
return false;
|
||||
}
|
||||
|
||||
// Tell the object about removal
|
||||
obj->removingFromEnvironment();
|
||||
// Deregister in scripting api
|
||||
m_script->removeObjectReference(obj);
|
||||
processActiveObjectRemove(obj, id);
|
||||
|
||||
// Delete active object
|
||||
return true;
|
||||
|
@ -2282,6 +2273,16 @@ bool ServerEnvironment::saveStaticToBlock(
|
|||
return true;
|
||||
}
|
||||
|
||||
void ServerEnvironment::processActiveObjectRemove(ServerActiveObject *obj, u16 id)
|
||||
{
|
||||
// Tell the object about removal
|
||||
obj->removingFromEnvironment();
|
||||
// Deregister in scripting api
|
||||
m_script->removeObjectReference(obj);
|
||||
// stop attached sounds
|
||||
m_server->stopAttachedSounds(id);
|
||||
}
|
||||
|
||||
PlayerDatabase *ServerEnvironment::openPlayerDatabase(const std::string &name,
|
||||
const std::string &savedir, const Settings &conf)
|
||||
{
|
||||
|
|
|
@ -367,7 +367,7 @@ public:
|
|||
u32 getGameTime() const { return m_game_time; }
|
||||
|
||||
void reportMaxLagEstimate(float f) { m_max_lag_estimate = f; }
|
||||
float getMaxLagEstimate() { return m_max_lag_estimate; }
|
||||
float getMaxLagEstimate() const { return m_max_lag_estimate; }
|
||||
|
||||
std::set<v3s16>* getForceloadedBlocks() { return &m_active_blocks.m_forceloaded_list; }
|
||||
|
||||
|
@ -454,6 +454,8 @@ private:
|
|||
bool saveStaticToBlock(v3s16 blockpos, u16 store_id,
|
||||
ServerActiveObject *obj, const StaticObject &s_obj, u32 mod_reason);
|
||||
|
||||
void processActiveObjectRemove(ServerActiveObject *obj, u16 id);
|
||||
|
||||
/*
|
||||
Member variables
|
||||
*/
|
||||
|
|
|
@ -31,11 +31,11 @@ void TileAnimationParams::serialize(std::ostream &os, u16 protocol_ver) const
|
|||
if (type == TAT_VERTICAL_FRAMES) {
|
||||
writeU16(os, vertical_frames.aspect_w);
|
||||
writeU16(os, vertical_frames.aspect_h);
|
||||
writeF32(os, need_abs ? fabs(vertical_frames.length) : vertical_frames.length);
|
||||
writeF32(os, need_abs ? std::abs(vertical_frames.length) : vertical_frames.length);
|
||||
} else if (type == TAT_SHEET_2D) {
|
||||
writeU8(os, sheet_2d.frames_w);
|
||||
writeU8(os, sheet_2d.frames_h);
|
||||
writeF32(os, need_abs ? fabs(sheet_2d.frame_length) : sheet_2d.frame_length);
|
||||
writeF32(os, need_abs ? std::abs(sheet_2d.frame_length) : sheet_2d.frame_length);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -54,6 +54,7 @@ const std::wstring &Translations::getTranslation(
|
|||
void Translations::loadTranslation(const std::string &data)
|
||||
{
|
||||
std::istringstream is(data);
|
||||
std::string textdomain_narrow;
|
||||
std::wstring textdomain;
|
||||
std::string line;
|
||||
|
||||
|
@ -70,7 +71,8 @@ void Translations::loadTranslation(const std::string &data)
|
|||
<< "\"" << std::endl;
|
||||
continue;
|
||||
}
|
||||
textdomain = utf8_to_wide(trim(parts[1]));
|
||||
textdomain_narrow = trim(parts[1]);
|
||||
textdomain = utf8_to_wide(textdomain_narrow);
|
||||
}
|
||||
if (line.empty() || line[0] == '#')
|
||||
continue;
|
||||
|
@ -116,7 +118,7 @@ void Translations::loadTranslation(const std::string &data)
|
|||
|
||||
if (i == wline.length()) {
|
||||
errorstream << "Malformed translation line \"" << line << "\""
|
||||
<< std::endl;
|
||||
<< " in text domain " << textdomain_narrow << std::endl;
|
||||
continue;
|
||||
}
|
||||
i++;
|
||||
|
|
|
@ -42,9 +42,12 @@ set (UNITTEST_SRCS
|
|||
PARENT_SCOPE)
|
||||
|
||||
set (UNITTEST_CLIENT_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/mesh_compare.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_clientactiveobjectmgr.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_content_mapblock.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_eventmanager.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_gameui.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_mesh_compare.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_keycode.cpp
|
||||
PARENT_SCOPE)
|
||||
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2023 Vitaliy Lobachevskiy
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "mesh_compare.h"
|
||||
#include <algorithm>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
|
||||
static std::vector<Triangle> expandMesh(const std::vector<video::S3DVertex> &vertices, const std::vector<u16> &indices)
|
||||
{
|
||||
const int n_indices = indices.size();
|
||||
const int n_triangles = n_indices / 3;
|
||||
if (n_indices % 3)
|
||||
throw std::invalid_argument("got fractional number of triangles");
|
||||
|
||||
std::vector<Triangle> ret(n_triangles);
|
||||
for (int i_triangle = 0; i_triangle < n_triangles; i_triangle++) {
|
||||
ret.at(i_triangle) = {
|
||||
vertices.at(indices.at(3 * i_triangle)),
|
||||
vertices.at(indices.at(3 * i_triangle + 1)),
|
||||
vertices.at(indices.at(3 * i_triangle + 2)),
|
||||
};
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/// Sorts triangle vertices, keeping winding order.
|
||||
static Triangle sortTriangle(Triangle t)
|
||||
{
|
||||
if (t[0] < t[1] && t[0] < t[2]) return {t[0], t[1], t[2]};
|
||||
if (t[1] < t[2] && t[1] < t[0]) return {t[1], t[2], t[0]};
|
||||
if (t[2] < t[0] && t[2] < t[1]) return {t[2], t[0], t[1]};
|
||||
throw std::invalid_argument("got bad triangle");
|
||||
}
|
||||
|
||||
static std::vector<Triangle> canonicalizeMesh(const std::vector<video::S3DVertex> &vertices, const std::vector<u16> &indices)
|
||||
{
|
||||
std::vector<Triangle> mesh = expandMesh(vertices, indices);
|
||||
for (auto &triangle: mesh)
|
||||
triangle = sortTriangle(triangle);
|
||||
std::sort(std::begin(mesh), std::end(mesh));
|
||||
return mesh;
|
||||
}
|
||||
|
||||
bool checkMeshEqual(const std::vector<video::S3DVertex> &vertices, const std::vector<u16> &indices, const std::vector<Triangle> &expected)
|
||||
{
|
||||
auto actual = canonicalizeMesh(vertices, indices);
|
||||
return actual == expected;
|
||||
}
|
||||
|
||||
bool checkMeshEqual(const std::vector<video::S3DVertex> &vertices, const std::vector<u16> &indices, const std::vector<Quad> &expected)
|
||||
{
|
||||
using QuadRefCount = std::array<int, 4>;
|
||||
struct QuadRef {
|
||||
unsigned quad_id;
|
||||
int quad_part;
|
||||
};
|
||||
|
||||
std::vector<QuadRefCount> refs(expected.size());
|
||||
std::map<Triangle, QuadRef> tris;
|
||||
for (unsigned k = 0; k < expected.size(); k++) {
|
||||
auto &&quad = expected[k];
|
||||
// There are 2 ways to split a quad into two triangles. So for each quad,
|
||||
// the mesh must contain either triangles 0 and 1, or triangles 2 and 3,
|
||||
// from the following list. No more, no less.
|
||||
tris.insert({sortTriangle({quad[0], quad[1], quad[2]}), {k, 0}});
|
||||
tris.insert({sortTriangle({quad[0], quad[2], quad[3]}), {k, 1}});
|
||||
tris.insert({sortTriangle({quad[0], quad[1], quad[3]}), {k, 2}});
|
||||
tris.insert({sortTriangle({quad[1], quad[2], quad[3]}), {k, 3}});
|
||||
}
|
||||
|
||||
auto actual = canonicalizeMesh(vertices, indices);
|
||||
for (auto &&tri: actual) {
|
||||
auto itri = tris.find(tri);
|
||||
if (itri == tris.end())
|
||||
return false;
|
||||
refs[itri->second.quad_id][itri->second.quad_part] += 1;
|
||||
}
|
||||
|
||||
for (unsigned k = 0; k < expected.size(); k++) {
|
||||
if (refs[k] != QuadRefCount{1, 1, 0, 0} && refs[k] != QuadRefCount{0, 0, 1, 1})
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2023 Vitaliy Lobachevskiy
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include <vector>
|
||||
#include <irrlichttypes.h>
|
||||
#include <S3DVertex.h>
|
||||
|
||||
/// Represents a triangle as three vertices.
|
||||
/// “Smallest” (according to <) vertex is expected to be first, others should follow in the counter-clockwise order.
|
||||
using Triangle = std::array<video::S3DVertex, 3>;
|
||||
|
||||
/// Represents a quad as four vertices.
|
||||
/// Vertices should be in the counter-clockwise order.
|
||||
using Quad = std::array<video::S3DVertex, 4>;
|
||||
|
||||
/// Compare two meshes for equality.
|
||||
/// @param vertices Vertices of the first mesh. Order doesn’t matter.
|
||||
/// @param indices Indices of the first mesh. Triangle order doesn’t matter. Vertex order in a triangle only matters for winding.
|
||||
/// @param expected The second mesh, in an expanded form. Must be sorted.
|
||||
/// @returns Whether the two meshes are equal.
|
||||
[[nodiscard]] bool checkMeshEqual(const std::vector<video::S3DVertex> &vertices, const std::vector<u16> &indices, const std::vector<Triangle> &expected);
|
||||
|
||||
/// Compare two meshes for equality.
|
||||
/// @param vertices Vertices of the first mesh. Order doesn’t matter.
|
||||
/// @param indices Indices of the first mesh. Triangle order doesn’t matter. Vertex order in a triangle only matters for winding.
|
||||
/// @param expected The second mesh, in a quad form.
|
||||
/// @returns Whether the two meshes are equal.
|
||||
/// @note There are two ways to split a quad into 2 triangles; either is allowed.
|
||||
[[nodiscard]] bool checkMeshEqual(const std::vector<video::S3DVertex> &vertices, const std::vector<u16> &indices, const std::vector<Quad> &expected);
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2023 Vitaliy Lobachevskiy
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "test.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <numeric>
|
||||
|
||||
#include "gamedef.h"
|
||||
#include "dummygamedef.h"
|
||||
#include "client/content_mapblock.h"
|
||||
#include "client/mapblock_mesh.h"
|
||||
#include "client/meshgen/collector.h"
|
||||
#include "mesh_compare.h"
|
||||
#include "util/directiontables.h"
|
||||
|
||||
namespace {
|
||||
|
||||
class MockGameDef : public DummyGameDef {
|
||||
public:
|
||||
IWritableItemDefManager *item_mgr() noexcept {
|
||||
return static_cast<IWritableItemDefManager *>(m_itemdef);
|
||||
}
|
||||
|
||||
NodeDefManager *node_mgr() noexcept {
|
||||
return const_cast<NodeDefManager *>(m_nodedef);
|
||||
}
|
||||
|
||||
content_t registerNode(ItemDefinition itemdef, ContentFeatures nodedef) {
|
||||
item_mgr()->registerItem(itemdef);
|
||||
return node_mgr()->set(nodedef.name, nodedef);
|
||||
}
|
||||
|
||||
void finalize() {
|
||||
node_mgr()->resolveCrossrefs();
|
||||
}
|
||||
|
||||
MeshMakeData makeSingleNodeMMD(bool smooth_lighting = true, bool for_shaders = true)
|
||||
{
|
||||
MeshMakeData data{ndef(), 1, for_shaders};
|
||||
data.setSmoothLighting(smooth_lighting);
|
||||
data.m_blockpos = {0, 0, 0};
|
||||
for (s16 x = -1; x <= 1; x++)
|
||||
for (s16 y = -1; y <= 1; y++)
|
||||
for (s16 z = -1; z <= 1; z++)
|
||||
data.m_vmanip.setNode({x, y, z}, {CONTENT_AIR, 0, 0});
|
||||
return data;
|
||||
}
|
||||
|
||||
content_t addSimpleNode(std::string name, u32 texture)
|
||||
{
|
||||
ItemDefinition itemdef;
|
||||
itemdef.type = ITEM_NODE;
|
||||
itemdef.name = "test:" + name;
|
||||
itemdef.description = name;
|
||||
|
||||
ContentFeatures f;
|
||||
f.name = itemdef.name;
|
||||
f.drawtype = NDT_NORMAL;
|
||||
f.solidness = 2;
|
||||
f.alpha = ALPHAMODE_OPAQUE;
|
||||
for (TileDef &tiledef : f.tiledef)
|
||||
tiledef.name = name + ".png";
|
||||
for (TileSpec &tile : f.tiles)
|
||||
tile.layers[0].texture_id = texture;
|
||||
|
||||
return registerNode(itemdef, f);
|
||||
}
|
||||
|
||||
content_t addLiquidSource(std::string name, u32 texture)
|
||||
{
|
||||
ItemDefinition itemdef;
|
||||
itemdef.type = ITEM_NODE;
|
||||
itemdef.name = "test:" + name + "_source";
|
||||
itemdef.description = name;
|
||||
|
||||
ContentFeatures f;
|
||||
f.name = itemdef.name;
|
||||
f.drawtype = NDT_LIQUID;
|
||||
f.solidness = 1;
|
||||
f.alpha = ALPHAMODE_BLEND;
|
||||
f.light_propagates = true;
|
||||
f.param_type = CPT_LIGHT;
|
||||
f.liquid_type = LIQUID_SOURCE;
|
||||
f.liquid_viscosity = 4;
|
||||
f.groups["liquids"] = 3;
|
||||
f.liquid_alternative_source = "test:" + name + "_source";
|
||||
f.liquid_alternative_flowing = "test:" + name + "_flowing";
|
||||
for (TileDef &tiledef : f.tiledef)
|
||||
tiledef.name = name + ".png";
|
||||
for (TileSpec &tile : f.tiles)
|
||||
tile.layers[0].texture_id = texture;
|
||||
|
||||
return registerNode(itemdef, f);
|
||||
}
|
||||
|
||||
content_t addLiquidFlowing(std::string name, u32 texture_top, u32 texture_side)
|
||||
{
|
||||
ItemDefinition itemdef;
|
||||
itemdef.type = ITEM_NODE;
|
||||
itemdef.name = "test:" + name + "_flowing";
|
||||
itemdef.description = name;
|
||||
|
||||
ContentFeatures f;
|
||||
f.name = itemdef.name;
|
||||
f.drawtype = NDT_FLOWINGLIQUID;
|
||||
f.solidness = 0;
|
||||
f.alpha = ALPHAMODE_BLEND;
|
||||
f.light_propagates = true;
|
||||
f.param_type = CPT_LIGHT;
|
||||
f.liquid_type = LIQUID_FLOWING;
|
||||
f.liquid_viscosity = 4;
|
||||
f.groups["liquids"] = 3;
|
||||
f.liquid_alternative_source = "test:" + name + "_source";
|
||||
f.liquid_alternative_flowing = "test:" + name + "_flowing";
|
||||
f.tiledef_special[0].name = name + "_top.png";
|
||||
f.tiledef_special[1].name = name + "_side.png";
|
||||
f.special_tiles[0].layers[0].texture_id = texture_top;
|
||||
f.special_tiles[1].layers[0].texture_id = texture_side;
|
||||
|
||||
return registerNode(itemdef, f);
|
||||
}
|
||||
};
|
||||
|
||||
void set_light_decode_table()
|
||||
{
|
||||
u8 table[LIGHT_SUN + 1] = {
|
||||
0x00, 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77,
|
||||
0x88, 0x99, 0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF,
|
||||
};
|
||||
memcpy(const_cast<u8 *>(light_decode_table), table, sizeof(table));
|
||||
}
|
||||
|
||||
class TestMapblockMeshGenerator : public TestBase {
|
||||
public:
|
||||
TestMapblockMeshGenerator() { TestManager::registerTestModule(this); }
|
||||
const char *getName() override { return "TestMapblockMeshGenerator"; }
|
||||
|
||||
void runTests(IGameDef *gamedef) override;
|
||||
void testSimpleNode();
|
||||
void testSurroundedNode();
|
||||
void testInterliquidSame();
|
||||
void testInterliquidDifferent();
|
||||
};
|
||||
|
||||
static TestMapblockMeshGenerator g_test_instance;
|
||||
|
||||
void TestMapblockMeshGenerator::runTests(IGameDef *gamedef)
|
||||
{
|
||||
set_light_decode_table();
|
||||
TEST(testSimpleNode);
|
||||
TEST(testSurroundedNode);
|
||||
TEST(testInterliquidSame);
|
||||
TEST(testInterliquidDifferent);
|
||||
}
|
||||
|
||||
namespace quad {
|
||||
constexpr float h = BS / 2.0f;
|
||||
const Quad zp{{{{-h, -h, h}, {0, 0, 1}, 0, {1, 1}}, {{h, -h, h}, {0, 0, 1}, 0, {0, 1}}, {{h, h, h}, {0, 0, 1}, 0, {0, 0}}, {{-h, h, h}, {0, 0, 1}, 0, {1, 0}}}};
|
||||
const Quad yp{{{{-h, h, -h}, {0, 1, 0}, 0, {0, 1}}, {{-h, h, h}, {0, 1, 0}, 0, {0, 0}}, {{h, h, h}, {0, 1, 0}, 0, {1, 0}}, {{h, h, -h}, {0, 1, 0}, 0, {1, 1}}}};
|
||||
const Quad xp{{{{h, -h, -h}, {1, 0, 0}, 0, {0, 1}}, {{h, h, -h}, {1, 0, 0}, 0, {0, 0}}, {{h, h, h}, {1, 0, 0}, 0, {1, 0}}, {{h, -h, h}, {1, 0, 0}, 0, {1, 1}}}};
|
||||
const Quad zn{{{{-h, -h, -h}, {0, 0, -1}, 0, {0, 1}}, {{-h, h, -h}, {0, 0, -1}, 0, {0, 0}}, {{h, h, -h}, {0, 0, -1}, 0, {1, 0}}, {{h, -h, -h}, {0, 0, -1}, 0, {1, 1}}}};
|
||||
const Quad yn{{{{-h, -h, -h}, {0, -1, 0}, 0, {0, 0}}, {{h, -h, -h}, {0, -1, 0}, 0, {1, 0}}, {{h, -h, h}, {0, -1, 0}, 0, {1, 1}}, {{-h, -h, h}, {0, -1, 0}, 0, {0, 1}}}};
|
||||
const Quad xn{{{{-h, -h, -h}, {-1, 0, 0}, 0, {1, 1}}, {{-h, -h, h}, {-1, 0, 0}, 0, {0, 1}}, {{-h, h, h}, {-1, 0, 0}, 0, {0, 0}}, {{-h, h, -h}, {-1, 0, 0}, 0, {1, 0}}}};
|
||||
}
|
||||
|
||||
void TestMapblockMeshGenerator::testSimpleNode()
|
||||
{
|
||||
MockGameDef gamedef;
|
||||
content_t stone = gamedef.addSimpleNode("stone", 42);
|
||||
gamedef.finalize();
|
||||
|
||||
MeshMakeData data = gamedef.makeSingleNodeMMD();
|
||||
data.m_vmanip.setNode({0, 0, 0}, {stone, 0, 0});
|
||||
|
||||
MeshCollector col{{}};
|
||||
MapblockMeshGenerator mg{&data, &col, nullptr};
|
||||
mg.generate();
|
||||
UASSERTEQ(std::size_t, col.prebuffers[0].size(), 1);
|
||||
UASSERTEQ(std::size_t, col.prebuffers[1].size(), 0);
|
||||
|
||||
auto &&buf = col.prebuffers[0][0];
|
||||
UASSERTEQ(u32, buf.layer.texture_id, 42);
|
||||
UASSERT(checkMeshEqual(buf.vertices, buf.indices, {quad::xn, quad::xp, quad::yn, quad::yp, quad::zn, quad::zp}));
|
||||
}
|
||||
|
||||
void TestMapblockMeshGenerator::testSurroundedNode()
|
||||
{
|
||||
MockGameDef gamedef;
|
||||
content_t stone = gamedef.addSimpleNode("stone", 42);
|
||||
content_t wood = gamedef.addSimpleNode("wood", 13);
|
||||
gamedef.finalize();
|
||||
|
||||
MeshMakeData data = gamedef.makeSingleNodeMMD();
|
||||
data.m_vmanip.setNode({0, 0, 0}, {stone, 0, 0});
|
||||
data.m_vmanip.setNode({1, 0, 0}, {wood, 0, 0});
|
||||
|
||||
MeshCollector col{{}};
|
||||
MapblockMeshGenerator mg{&data, &col, nullptr};
|
||||
mg.generate();
|
||||
UASSERTEQ(std::size_t, col.prebuffers[0].size(), 1);
|
||||
UASSERTEQ(std::size_t, col.prebuffers[1].size(), 0);
|
||||
|
||||
auto &&buf = col.prebuffers[0][0];
|
||||
UASSERTEQ(u32, buf.layer.texture_id, 42);
|
||||
UASSERT(checkMeshEqual(buf.vertices, buf.indices, {quad::xn, quad::yn, quad::yp, quad::zn, quad::zp}));
|
||||
}
|
||||
|
||||
void TestMapblockMeshGenerator::testInterliquidSame()
|
||||
{
|
||||
MockGameDef gamedef;
|
||||
auto water = gamedef.addLiquidSource("water", 42);
|
||||
gamedef.finalize();
|
||||
|
||||
MeshMakeData data = gamedef.makeSingleNodeMMD();
|
||||
data.m_vmanip.setNode({0, 0, 0}, {water, 0, 0});
|
||||
data.m_vmanip.setNode({1, 0, 0}, {water, 0, 0});
|
||||
|
||||
MeshCollector col{{}};
|
||||
MapblockMeshGenerator mg{&data, &col, nullptr};
|
||||
mg.generate();
|
||||
UASSERTEQ(std::size_t, col.prebuffers[0].size(), 1);
|
||||
UASSERTEQ(std::size_t, col.prebuffers[1].size(), 0);
|
||||
|
||||
auto &&buf = col.prebuffers[0][0];
|
||||
UASSERTEQ(u32, buf.layer.texture_id, 42);
|
||||
UASSERT(checkMeshEqual(buf.vertices, buf.indices, {quad::xn, quad::yn, quad::yp, quad::zn, quad::zp}));
|
||||
}
|
||||
|
||||
void TestMapblockMeshGenerator::testInterliquidDifferent()
|
||||
{
|
||||
MockGameDef gamedef;
|
||||
auto water = gamedef.addLiquidSource("water", 42);
|
||||
auto lava = gamedef.addLiquidSource("lava", 13);
|
||||
gamedef.finalize();
|
||||
|
||||
MeshMakeData data = gamedef.makeSingleNodeMMD();
|
||||
data.m_vmanip.setNode({0, 0, 0}, {water, 0, 0});
|
||||
data.m_vmanip.setNode({0, 0, 1}, {lava, 0, 0});
|
||||
|
||||
MeshCollector col{{}};
|
||||
MapblockMeshGenerator mg{&data, &col, nullptr};
|
||||
mg.generate();
|
||||
UASSERTEQ(std::size_t, col.prebuffers[0].size(), 1);
|
||||
UASSERTEQ(std::size_t, col.prebuffers[1].size(), 0);
|
||||
|
||||
auto &&buf = col.prebuffers[0][0];
|
||||
UASSERTEQ(u32, buf.layer.texture_id, 42);
|
||||
UASSERT(checkMeshEqual(buf.vertices, buf.indices, {quad::xn, quad::xp, quad::yn, quad::yp, quad::zn, quad::zp}));
|
||||
}
|
||||
|
||||
}
|
|
@ -104,7 +104,7 @@ void TestDataStructures::testMap1()
|
|||
UASSERT(t0.deleted);
|
||||
UASSERT(!t1.copied);
|
||||
UASSERT(!t1.deleted);
|
||||
if (once |= 1)
|
||||
if ((once |= 1))
|
||||
break;
|
||||
}
|
||||
UASSERT(once);
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
/*
|
||||
Minetest
|
||||
Copyright (C) 2023 Vitaliy Lobachevskiy
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "test.h"
|
||||
|
||||
#include "mesh_compare.h"
|
||||
|
||||
// This is a self-test to ensure proper functionality of the vertex
|
||||
// building functions (`Triangle`, `Quad`) and its validation function
|
||||
// `checkMeshEqual` in preparation for the tests in test_content_mapblock.cpp
|
||||
class TestMeshCompare : public TestBase {
|
||||
public:
|
||||
TestMeshCompare() { TestManager::registerTestModule(this); }
|
||||
const char *getName() override { return "TestMeshCompare"; }
|
||||
|
||||
void runTests(IGameDef *gamedef) override {
|
||||
TEST(testTriangle);
|
||||
TEST(testQuad);
|
||||
}
|
||||
|
||||
void testTriangle() {
|
||||
UASSERT(checkMeshEqual({
|
||||
{{1., 0., 0.}, {3., 0., 0.}, 1, {0., 0.}},
|
||||
{{0., 1., 0.}, {2., 0., 0.}, 2, {0., 0.}},
|
||||
{{0., 0., 1.}, {1., 0., 0.}, 3, {0., 0.}},
|
||||
}, {0, 1, 2}, {
|
||||
Triangle{{
|
||||
{{0., 0., 1.}, {1., 0., 0.}, 3, {0., 0.}},
|
||||
{{1., 0., 0.}, {3., 0., 0.}, 1, {0., 0.}},
|
||||
{{0., 1., 0.}, {2., 0., 0.}, 2, {0., 0.}},
|
||||
}},
|
||||
}));
|
||||
UASSERT(checkMeshEqual({
|
||||
{{1., 0., 0.}, {3., 0., 0.}, 1, {0., 0.}},
|
||||
{{0., 1., 0.}, {2., 0., 0.}, 2, {0., 0.}},
|
||||
{{0., 0., 1.}, {1., 0., 0.}, 3, {0., 0.}},
|
||||
}, {2, 0, 1}, {
|
||||
Triangle{{
|
||||
{{0., 0., 1.}, {1., 0., 0.}, 3, {0., 0.}},
|
||||
{{1., 0., 0.}, {3., 0., 0.}, 1, {0., 0.}},
|
||||
{{0., 1., 0.}, {2., 0., 0.}, 2, {0., 0.}},
|
||||
}},
|
||||
}));
|
||||
UASSERT(!checkMeshEqual({
|
||||
{{1., 0., 0.}, {3., 0., 0.}, 1, {0., 0.}},
|
||||
{{0., 1., 0.}, {2., 0., 0.}, 2, {0., 0.}},
|
||||
{{0., 0., 1.}, {1., 0., 0.}, 3, {0., 0.}},
|
||||
}, {0, 2, 1}, {
|
||||
Triangle{{
|
||||
{{0., 0., 1.}, {1., 0., 0.}, 3, {0., 0.}},
|
||||
{{1., 0., 0.}, {3., 0., 0.}, 1, {0., 0.}},
|
||||
{{0., 1., 0.}, {2., 0., 0.}, 2, {0., 0.}},
|
||||
}},
|
||||
}));
|
||||
|
||||
UASSERT(checkMeshEqual({
|
||||
{{0., 0., 1.}, {1., 0., 0.}, 3, {0., 0.}},
|
||||
{{0., 1., 0.}, {2., 0., 0.}, 2, {0., 0.}},
|
||||
{{1., 0., 0.}, {3., 0., 0.}, 1, {0., 0.}},
|
||||
}, {0, 1, 2}, {
|
||||
Triangle{{
|
||||
{{0., 0., 1.}, {1., 0., 0.}, 3, {0., 0.}},
|
||||
{{0., 1., 0.}, {2., 0., 0.}, 2, {0., 0.}},
|
||||
{{1., 0., 0.}, {3., 0., 0.}, 1, {0., 0.}},
|
||||
}},
|
||||
}));
|
||||
UASSERT(!checkMeshEqual({
|
||||
{{1., 0., 0.}, {3., 0., 0.}, 1, {0., 0.}},
|
||||
{{0., 1., 0.}, {2., 0., 0.}, 2, {0., 0.}},
|
||||
{{0., 0., 1.}, {1., 0., 0.}, 3, {0., 0.}},
|
||||
}, {0, 1, 2}, {
|
||||
Triangle{{
|
||||
{{0., 0., 1.}, {1., 0., 0.}, 3, {0., 0.}},
|
||||
{{0., 1., 0.}, {2., 0., 0.}, 2, {0., 0.}},
|
||||
{{1., 0., 0.}, {3., 0., 0.}, 1, {0., 0.}},
|
||||
}},
|
||||
}));
|
||||
}
|
||||
|
||||
void testQuad() {
|
||||
UASSERT(checkMeshEqual({
|
||||
{{1., 0., 0.}, {3., 0., 0.}, 1, {0., 0.}},
|
||||
{{0., 1., 0.}, {2., 0., 0.}, 2, {0., 0.}},
|
||||
{{0., 0., 1.}, {1., 0., 0.}, 3, {0., 0.}},
|
||||
{{1., -1., 1.}, {4., 0., 0.}, 4, {0., 0.}},
|
||||
}, {0, 1, 2, 0, 2, 3}, {
|
||||
Quad{{
|
||||
{{1., 0., 0.}, {3., 0., 0.}, 1, {0., 0.}},
|
||||
{{0., 1., 0.}, {2., 0., 0.}, 2, {0., 0.}},
|
||||
{{0., 0., 1.}, {1., 0., 0.}, 3, {0., 0.}},
|
||||
{{1., -1., 1.}, {4., 0., 0.}, 4, {0., 0.}},
|
||||
}},
|
||||
}));
|
||||
UASSERT(checkMeshEqual({
|
||||
{{1., 0., 0.}, {3., 0., 0.}, 1, {0., 0.}},
|
||||
{{0., 1., 0.}, {2., 0., 0.}, 2, {0., 0.}},
|
||||
{{0., 0., 1.}, {1., 0., 0.}, 3, {0., 0.}},
|
||||
{{1., -1., 1.}, {4., 0., 0.}, 4, {0., 0.}},
|
||||
}, {2, 3, 0, 1, 2, 0}, {
|
||||
Quad{{
|
||||
{{1., 0., 0.}, {3., 0., 0.}, 1, {0., 0.}},
|
||||
{{0., 1., 0.}, {2., 0., 0.}, 2, {0., 0.}},
|
||||
{{0., 0., 1.}, {1., 0., 0.}, 3, {0., 0.}},
|
||||
{{1., -1., 1.}, {4., 0., 0.}, 4, {0., 0.}},
|
||||
}},
|
||||
}));
|
||||
UASSERT(checkMeshEqual({
|
||||
{{1., 0., 0.}, {3., 0., 0.}, 1, {0., 0.}},
|
||||
{{0., 1., 0.}, {2., 0., 0.}, 2, {0., 0.}},
|
||||
{{0., 0., 1.}, {1., 0., 0.}, 3, {0., 0.}},
|
||||
{{1., -1., 1.}, {4., 0., 0.}, 4, {0., 0.}},
|
||||
}, {2, 3, 1, 0, 1, 3}, {
|
||||
Quad{{
|
||||
{{1., 0., 0.}, {3., 0., 0.}, 1, {0., 0.}},
|
||||
{{0., 1., 0.}, {2., 0., 0.}, 2, {0., 0.}},
|
||||
{{0., 0., 1.}, {1., 0., 0.}, 3, {0., 0.}},
|
||||
{{1., -1., 1.}, {4., 0., 0.}, 4, {0., 0.}},
|
||||
}},
|
||||
}));
|
||||
UASSERT(checkMeshEqual({
|
||||
{{1., 0., 0.}, {3., 0., 0.}, 1, {0., 0.}},
|
||||
{{0., 1., 0.}, {2., 0., 0.}, 2, {0., 0.}},
|
||||
{{0., 0., 1.}, {1., 0., 0.}, 3, {0., 0.}},
|
||||
{{1., -1., 1.}, {4., 0., 0.}, 4, {0., 0.}},
|
||||
}, {3, 0, 1, 1, 2, 3}, {
|
||||
Quad{{
|
||||
{{1., 0., 0.}, {3., 0., 0.}, 1, {0., 0.}},
|
||||
{{0., 1., 0.}, {2., 0., 0.}, 2, {0., 0.}},
|
||||
{{0., 0., 1.}, {1., 0., 0.}, 3, {0., 0.}},
|
||||
{{1., -1., 1.}, {4., 0., 0.}, 4, {0., 0.}},
|
||||
}},
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
static TestMeshCompare mesh_compare_test;
|
|
@ -350,12 +350,10 @@ class IntervalLimiter
|
|||
public:
|
||||
IntervalLimiter() = default;
|
||||
|
||||
/*
|
||||
dtime: time from last call to this method
|
||||
wanted_interval: interval wanted
|
||||
return value:
|
||||
true: action should be skipped
|
||||
false: action should be done
|
||||
/**
|
||||
@param dtime time from last call to this method
|
||||
@param wanted_interval interval wanted
|
||||
@return true if action should be done
|
||||
*/
|
||||
bool step(float dtime, float wanted_interval)
|
||||
{
|
||||
|
|
|
@ -43,13 +43,19 @@ class Translations;
|
|||
( (unsigned int)(x) <= 0x7e))
|
||||
|
||||
// Checks whether a value is in a Unicode private use area
|
||||
#define IS_PRIVATE_USE_CHAR(x) \
|
||||
(((wchar_t)(x) >= 0xE000 && \
|
||||
(wchar_t)(x) <= 0xF8FF) || \
|
||||
((wchar_t)(x) >= 0xF0000 && \
|
||||
#define IS_PRIVATE_USE_CHAR16(x) \
|
||||
((wchar_t)(x) >= 0xE000 && \
|
||||
(wchar_t)(x) <= 0xF8FF)
|
||||
#define IS_PRIVATE_USE_CHAR32(x) \
|
||||
(((wchar_t)(x) >= 0xF0000 && \
|
||||
(wchar_t)(x) <= 0xFFFFD) || \
|
||||
((wchar_t)(x) >= 0x100000 && \
|
||||
(wchar_t)(x) <= 0x10FFFD)) \
|
||||
(wchar_t)(x) <= 0x10FFFD))
|
||||
#if WCHAR_MAX > 0xFFFF
|
||||
#define IS_PRIVATE_USE_CHAR(x) (IS_PRIVATE_USE_CHAR16(x) || IS_PRIVATE_USE_CHAR32(x))
|
||||
#else
|
||||
#define IS_PRIVATE_USE_CHAR(x) IS_PRIVATE_USE_CHAR16(x)
|
||||
#endif
|
||||
|
||||
// Checks whether a byte is an inner byte for an utf-8 multibyte sequence
|
||||
#define IS_UTF8_MULTB_INNER(x) \
|
||||
|
|
|
@ -4,8 +4,8 @@ aa86abc3eb054d74d5fe15996f281cf84230a61b4ab7b3a702ab7dbb71e1203f curl-8.5.0-win
|
|||
acf901e93aedbcfa92eb3aab1def252676af845b1747ca5c3e7c5866576168cc freetype-2.13.2-win64.zip
|
||||
41b10766de2773f0f0851fde16b363024685e0397f4bb2e5cd2a7be196960a01 gettext-0.20.2-win32.zip
|
||||
1ceed167ff16fea944f76ab6ea2969160c71a67419259b17c9c523e7a01eb883 gettext-0.20.2-win64.zip
|
||||
6b37e3f734c48caa7ba8756abed63c5171bf54f4f679a1dc3869ec2bd94be48d irrlicht-1.9.0mt14-win32.zip
|
||||
5aca6d711881d3d6a4d5da4b53b2cf0e73a0c113b8776730bbfa3f3bc4aebea7 irrlicht-1.9.0mt14-win64.zip
|
||||
14bb60cbf9dd93e906d9c9117a99d19a500cda6bcfd6fc125e04f3c75778acc2 irrlicht-1.9.0mt15-win32.zip
|
||||
31edd6a0af033b9ab455c5e019748cfa7e0cf167c9cbc5957227e72e971c2565 irrlicht-1.9.0mt15-win64.zip
|
||||
f54e9a577e2db47ed28f4a01e74181d2c607627c551d30f48263e01b59e84f67 libleveldb-1.23-win32.zip
|
||||
2f039848a4e6c05a2347fe5a7fa63c430dd08d1bc88235645a863c859e14f5f8 libleveldb-1.23-win64.zip
|
||||
0df94afb8efa361cceb132ecf9491720afbc45ba844a7b1c94607295829b53ca libogg-1.3.5-win32.zip
|
||||
|
|
|
@ -425,6 +425,7 @@ def generate_template(folder, mod_name):
|
|||
sources = sorted(list(sources), key=str.lower)
|
||||
newSources = []
|
||||
for i in sources:
|
||||
i = "/".join(os.path.split(i)).lstrip("/")
|
||||
newSources.append(f"{symbol_source_prefix} {i} {symbol_source_suffix}")
|
||||
dOut[d] = newSources
|
||||
|
||||
|
|
Loading…
Reference in New Issue