diff --git a/.github/workflows/lua.yml b/.github/workflows/lua.yml index 0073eca2e..1f83c7688 100644 --- a/.github/workflows/lua.yml +++ b/.github/workflows/lua.yml @@ -35,7 +35,7 @@ jobs: - name: Integration test + devtest run: | - ./util/test_multiplayer.sh + serverconf="profiler.load=true" ./util/test_multiplayer.sh luacheck: name: "Builtin Luacheck and Unit Tests" diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 34556ce8c..e193c828d 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -29,8 +29,8 @@ on: jobs: build: - # use macOS 13 since it's the last one that still runs on x86 - runs-on: macos-13 + # use lowest possible macOS running on x86_64 to support more users + runs-on: macos-12 steps: - uses: actions/checkout@v4 - name: Install deps diff --git a/CMakeLists.txt b/CMakeLists.txt index 54a830089..a9a0ef000 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -56,6 +56,11 @@ if((WIN32 AND CMAKE_CXX_COMPILER_ID STREQUAL "GNU") OR APPLE) endif() set(ENABLE_LTO ${DEFAULT_ENABLE_LTO} CACHE BOOL "Use Link Time Optimization") +set(BUILD_WITH_TRACY FALSE CACHE BOOL + "Fetch and build with the Tracy profiler client") +set(FETCH_TRACY_GIT_TAG "master" CACHE STRING + "Git tag for fetching Tracy client. Match with your server (gui) version") + set(DEFAULT_RUN_IN_PLACE FALSE) if(WIN32) set(DEFAULT_RUN_IN_PLACE TRUE) @@ -370,3 +375,19 @@ if(BUILD_DOCUMENTATION) ) endif() endif() + +# Fetch Tracy +if(BUILD_WITH_TRACY) + include(FetchContent) + + message(STATUS "Fetching Tracy (${FETCH_TRACY_GIT_TAG})...") + FetchContent_Declare( + tracy + GIT_REPOSITORY https://github.com/wolfpld/tracy.git + GIT_TAG ${FETCH_TRACY_GIT_TAG} + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE + ) + FetchContent_MakeAvailable(tracy) + message(STATUS "Fetching Tracy - done") +endif() diff --git a/README.md b/README.md index 5724359d6..919cb144c 100644 --- a/README.md +++ b/README.md @@ -119,6 +119,7 @@ Command-line options Compiling --------- +- [Compiling - common information](doc/compiling/README.md) - [Compiling on GNU/Linux](doc/compiling/linux.md) - [Compiling on Windows](doc/compiling/windows.md) - [Compiling on MacOS](doc/compiling/macos.md) diff --git a/builtin/client/death_formspec.lua b/builtin/client/death_formspec.lua deleted file mode 100644 index c25c799ab..000000000 --- a/builtin/client/death_formspec.lua +++ /dev/null @@ -1,15 +0,0 @@ --- CSM death formspec. Only used when clientside modding is enabled, otherwise --- handled by the engine. - -core.register_on_death(function() - local formspec = "size[11,5.5]bgcolor[#320000b4;true]" .. - "label[4.85,1.35;" .. fgettext("You died") .. - "]button_exit[4,3;3,0.5;btn_respawn;".. fgettext("Respawn") .."]" - core.show_formspec("bultin:death", formspec) -end) - -core.register_on_formspec_input(function(formname, fields) - if formname == "bultin:death" then - core.send_respawn() - end -end) diff --git a/builtin/client/init.lua b/builtin/client/init.lua index 301a8050c..8d01c99a4 100644 --- a/builtin/client/init.lua +++ b/builtin/client/init.lua @@ -9,6 +9,5 @@ dofile(commonpath .. "mod_storage.lua") dofile(commonpath .. "chatcommands.lua") dofile(commonpath .. "information_formspecs.lua") dofile(clientpath .. "chatcommands.lua") -dofile(clientpath .. "death_formspec.lua") dofile(clientpath .. "misc.lua") assert(loadfile(commonpath .. "item_s.lua"))({}) -- Just for push/read node functions diff --git a/builtin/emerge/env.lua b/builtin/emerge/env.lua index 2b32a0339..5beb6285c 100644 --- a/builtin/emerge/env.lua +++ b/builtin/emerge/env.lua @@ -23,6 +23,8 @@ core.add_node = core.set_node -- we don't deal with metadata currently core.swap_node = core.set_node +core.bulk_swap_node = core.bulk_set_node + function core.remove_node(pos) return core.vmanip:set_node_at(pos, {name="air"}) end diff --git a/builtin/fstk/buttonbar.lua b/builtin/fstk/buttonbar.lua index 64ac37f03..e53814751 100644 --- a/builtin/fstk/buttonbar.lua +++ b/builtin/fstk/buttonbar.lua @@ -28,10 +28,8 @@ local function buttonbar_formspec(self) end local formspec = { - "style_type[box;noclip=true]", string.format("box[%f,%f;%f,%f;%s]", self.pos.x, self.pos.y, self.size.x, self.size.y, self.bgcolor), - "style_type[box;noclip=false]", } local btn_size = self.size.y - 2*BASE_SPACING @@ -71,7 +69,7 @@ local function buttonbar_formspec(self) y = self.pos.y + BASE_SPACING, } - table.insert(formspec, string.format("image_button[%f,%f;%f,%f;%s;%s;%s;true;false]tooltip[%s;%s]", + table.insert(formspec, string.format("image_button[%f,%f;%f,%f;%s;%s;%s;false;false]tooltip[%s;%s]", btn_pos.x, btn_pos.y, btn_size, btn_size, btn.image, btn.name, btn.caption, btn.name, btn.tooltip)) end @@ -86,9 +84,6 @@ local function buttonbar_formspec(self) y = self.pos.y + BASE_SPACING, } - table.insert(formspec, string.format("style[%s,%s;noclip=true]", - 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, get_scroll_btn_width(), btn_size, self.btn_prev_name)) diff --git a/builtin/fstk/tabview.lua b/builtin/fstk/tabview.lua index f09c4df2d..9f8889143 100644 --- a/builtin/fstk/tabview.lua +++ b/builtin/fstk/tabview.lua @@ -66,11 +66,22 @@ local function get_formspec(self) local content, prepend = tab.get_formspec(self, tab.name, tab.tabdata, tab.tabsize) - local tsize = tab.tabsize or { width = self.width, height = self.height } + local ENABLE_TOUCH = core.settings:get_bool("enable_touch") + + local orig_tsize = tab.tabsize or { width = self.width, height = self.height } + local tsize = { width = orig_tsize.width, height = orig_tsize.height } + tsize.height = tsize.height + + TABHEADER_H -- tabheader included in formspec size + + (ENABLE_TOUCH and GAMEBAR_OFFSET_TOUCH or GAMEBAR_OFFSET_DESKTOP) + + GAMEBAR_H -- gamebar included in formspec size + if self.parent == nil and not prepend then prepend = string.format("size[%f,%f,%s]", tsize.width, tsize.height, dump(self.fixed_size)) + local anchor_pos = TABHEADER_H + orig_tsize.height / 2 + prepend = prepend .. ("anchor[0.5,%f]"):format(anchor_pos / tsize.height) + if tab.formspec_version then prepend = ("formspec_version[%d]"):format(tab.formspec_version) .. prepend end @@ -78,12 +89,15 @@ local function get_formspec(self) local end_button_size = 0.75 - local tab_header_size = { width = tsize.width, height = 0.85 } + local tab_header_size = { width = tsize.width, height = TABHEADER_H } if self.end_button then tab_header_size.width = tab_header_size.width - end_button_size - 0.1 end - local formspec = (prepend or "") .. self:tab_header(tab_header_size) .. content + local formspec = (prepend or "") + formspec = formspec .. ("bgcolor[;neither]container[0,%f]box[0,0;%f,%f;#0000008C]"):format( + TABHEADER_H, orig_tsize.width, orig_tsize.height) + formspec = formspec .. self:tab_header(tab_header_size) .. content if self.end_button then formspec = formspec .. @@ -98,6 +112,8 @@ local function get_formspec(self) self.end_button.name) end + formspec = formspec .. "container_end[]" + return formspec end diff --git a/builtin/game/death_screen.lua b/builtin/game/death_screen.lua new file mode 100644 index 000000000..77f56aaff --- /dev/null +++ b/builtin/game/death_screen.lua @@ -0,0 +1,31 @@ +local F = core.formspec_escape +local S = core.get_translator("__builtin") + +function core.show_death_screen(player, _reason) + local fs = { + "formspec_version[1]", + "size[11,5.5,true]", + "bgcolor[#320000b4;true]", + "label[4.85,1.35;", F(S("You died")), "]", + "button_exit[4,3;3,0.5;btn_respawn;", F(S("Respawn")), "]", + } + core.show_formspec(player:get_player_name(), "__builtin:death", table.concat(fs, "")) +end + +core.register_on_dieplayer(function(player, reason) + core.show_death_screen(player, reason) +end) + +core.register_on_joinplayer(function(player) + if player:get_hp() == 0 then + core.show_death_screen(player, nil) + end +end) + +core.register_on_player_receive_fields(function(player, formname, fields) + if formname == "__builtin:death" and fields.quit and player:get_hp() == 0 then + player:respawn() + core.log("action", player:get_player_name() .. " respawns at " .. + player:get_pos():to_string()) + end +end) diff --git a/builtin/game/features.lua b/builtin/game/features.lua index 358ee8ff0..10884497c 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -43,6 +43,8 @@ core.features = { moveresult_new_pos = true, override_item_remove_fields = true, hotbar_hud_element = true, + bulk_lbms = true, + abm_without_neighbors = true, } function core.has_feature(arg) diff --git a/builtin/game/init.lua b/builtin/game/init.lua index 4e16c686d..b3c64e729 100644 --- a/builtin/game/init.lua +++ b/builtin/game/init.lua @@ -38,6 +38,7 @@ dofile(gamepath .. "forceloading.lua") dofile(gamepath .. "hud.lua") dofile(gamepath .. "knockback.lua") dofile(gamepath .. "async.lua") +dofile(gamepath .. "death_screen.lua") core.after(0, builtin_shared.cache_content_ids) diff --git a/builtin/game/misc.lua b/builtin/game/misc.lua index a8a6700f9..25e19ddb0 100644 --- a/builtin/game/misc.lua +++ b/builtin/game/misc.lua @@ -6,14 +6,14 @@ local S = core.get_translator("__builtin") -- Misc. API functions -- --- @spec core.kick_player(String, String) :: Boolean -function core.kick_player(player_name, reason) +-- @spec core.kick_player(String, String, Boolean) :: Boolean +function core.kick_player(player_name, reason, reconnect) if type(reason) == "string" then reason = "Kicked: " .. reason else reason = "Kicked." end - return core.disconnect_player(player_name, reason) + return core.disconnect_player(player_name, reason, reconnect) end function core.check_player_privs(name, ...) @@ -298,3 +298,28 @@ do return valid_object_iterator(core.get_objects_in_area(min_pos, max_pos)) end end + +-- +-- Helper for LBM execution, called from C++ +-- + +function core.run_lbm(id, pos_list, dtime_s) + local lbm = core.registered_lbms[id] + assert(lbm, "Entry with given id not found in registered_lbms table") + core.set_last_run_mod(lbm.mod_origin) + if lbm.bulk_action then + return lbm.bulk_action(pos_list, dtime_s) + end + -- emulate non-bulk LBMs + local expect = core.get_node(pos_list[1]).name + -- engine guarantees that + -- 1) all nodes are the same content type + -- 2) the list is up-to-date when we're called + assert(expect ~= "ignore") + for _, pos in ipairs(pos_list) do + local n = core.get_node(pos) + if n.name == expect then -- might have been changed by previous call + lbm.action(pos, n, dtime_s) + end + end +end diff --git a/builtin/game/register.lua b/builtin/game/register.lua index 76cb72f87..3d99a6925 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -105,7 +105,12 @@ function core.register_lbm(spec) -- Add to core.registered_lbms check_modname_prefix(spec.name) check_node_list(spec.nodenames, "nodenames") - assert(type(spec.action) == "function", "Required field 'action' of type function") + local have = spec.action ~= nil + local have_bulk = spec.bulk_action ~= nil + assert(not have or type(spec.action) == "function", "Field 'action' must be a function") + assert(not have_bulk or type(spec.bulk_action) == "function", "Field 'bulk_action' must be a function") + assert(have ~= have_bulk, "Either 'action' or 'bulk_action' must be present") + core.registered_lbms[#core.registered_lbms + 1] = spec spec.mod_origin = core.get_current_modname() or "??" end diff --git a/builtin/locale/__builtin.be.tr b/builtin/locale/__builtin.be.tr new file mode 100644 index 000000000..bd8ea999e --- /dev/null +++ b/builtin/locale/__builtin.be.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Вы загінулі +Respawn=Адрадзіцца diff --git a/builtin/locale/__builtin.bg.tr b/builtin/locale/__builtin.bg.tr new file mode 100644 index 000000000..43fe8f31e --- /dev/null +++ b/builtin/locale/__builtin.bg.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Умряхте +Respawn=Прераждане diff --git a/builtin/locale/__builtin.ca.tr b/builtin/locale/__builtin.ca.tr new file mode 100644 index 000000000..5cde155f9 --- /dev/null +++ b/builtin/locale/__builtin.ca.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Has mort +Respawn=Reaparèixer diff --git a/builtin/locale/__builtin.cs.tr b/builtin/locale/__builtin.cs.tr new file mode 100644 index 000000000..4f4b592e4 --- /dev/null +++ b/builtin/locale/__builtin.cs.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Zemřel jsi +Respawn=Oživit diff --git a/builtin/locale/__builtin.cy.tr b/builtin/locale/__builtin.cy.tr new file mode 100644 index 000000000..372da1a89 --- /dev/null +++ b/builtin/locale/__builtin.cy.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Buest ti farw +Respawn=Atgyfodi diff --git a/builtin/locale/__builtin.da.tr b/builtin/locale/__builtin.da.tr new file mode 100644 index 000000000..c34eceeb9 --- /dev/null +++ b/builtin/locale/__builtin.da.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Du døde +Respawn=Genopstå diff --git a/builtin/locale/__builtin.de.tr b/builtin/locale/__builtin.de.tr index 0552ef88b..3665a3f54 100644 --- a/builtin/locale/__builtin.de.tr +++ b/builtin/locale/__builtin.de.tr @@ -244,3 +244,5 @@ A total of @1 sample(s) were taken.=Es wurden insgesamt @1 Datenpunkt(e) aufgeze The output is limited to '@1'.=Die Ausgabe ist beschränkt auf „@1“. Saving of profile failed: @1=Speichern des Profils fehlgeschlagen: @1 Profile saved to @1=Profil abgespeichert nach @1 +You died=Sie sind gestorben +Respawn=Wiederbeleben diff --git a/builtin/locale/__builtin.el.tr b/builtin/locale/__builtin.el.tr new file mode 100644 index 000000000..c14180a62 --- /dev/null +++ b/builtin/locale/__builtin.el.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Πέθανες +Respawn=Επανεμφάνηση diff --git a/builtin/locale/__builtin.eo.tr b/builtin/locale/__builtin.eo.tr index c3fec6c37..f1fe5333b 100644 --- a/builtin/locale/__builtin.eo.tr +++ b/builtin/locale/__builtin.eo.tr @@ -244,3 +244,5 @@ A total of @1 sample(s) were taken.=Sume @1 ekzemplero(j) konserviĝis. The output is limited to '@1'.=La eligo estas limigita al «@1». Saving of profile failed: @1=Konservado de profilo malsukcesis: @1 Profile saved to @1=Profilo konservita al @1 +You died=Vi mortis +Respawn=Renaskiĝi diff --git a/builtin/locale/__builtin.es.tr b/builtin/locale/__builtin.es.tr new file mode 100644 index 000000000..3c4a66933 --- /dev/null +++ b/builtin/locale/__builtin.es.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Has muerto +Respawn=Reaparecer diff --git a/builtin/locale/__builtin.et.tr b/builtin/locale/__builtin.et.tr new file mode 100644 index 000000000..e0745c24a --- /dev/null +++ b/builtin/locale/__builtin.et.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Said surma +Respawn=Ärka ellu diff --git a/builtin/locale/__builtin.eu.tr b/builtin/locale/__builtin.eu.tr new file mode 100644 index 000000000..77f510855 --- /dev/null +++ b/builtin/locale/__builtin.eu.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Hil zara +Respawn=Birsortu diff --git a/builtin/locale/__builtin.fi.tr b/builtin/locale/__builtin.fi.tr new file mode 100644 index 000000000..f7c0ee038 --- /dev/null +++ b/builtin/locale/__builtin.fi.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Kuolit +Respawn=Synny uudelleen diff --git a/builtin/locale/__builtin.fil.tr b/builtin/locale/__builtin.fil.tr new file mode 100644 index 000000000..0755234a1 --- /dev/null +++ b/builtin/locale/__builtin.fil.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Namatay ka +Respawn=Mag-respawn diff --git a/builtin/locale/__builtin.fr.tr b/builtin/locale/__builtin.fr.tr index 6c3a244dc..d36574afd 100644 --- a/builtin/locale/__builtin.fr.tr +++ b/builtin/locale/__builtin.fr.tr @@ -244,3 +244,5 @@ A total of @1 sample(s) were taken.=@1 échantillons ont été collectés. The output is limited to '@1'.=La sortie est limitée à '@1'. Saving of profile failed: @1=La sauvegarde du profil a échoué : @1 Profile saved to @1=Le profil a été sauvegardé dans @1 +You died=Vous êtes mort +Respawn=Réapparaître diff --git a/builtin/locale/__builtin.ga.tr b/builtin/locale/__builtin.ga.tr new file mode 100644 index 000000000..e19959b10 --- /dev/null +++ b/builtin/locale/__builtin.ga.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Fuair tú bás +Respawn=Athsceith diff --git a/builtin/locale/__builtin.gl.tr b/builtin/locale/__builtin.gl.tr new file mode 100644 index 000000000..7f692f633 --- /dev/null +++ b/builtin/locale/__builtin.gl.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Morreches +Respawn=Reaparecer diff --git a/builtin/locale/__builtin.hu.tr b/builtin/locale/__builtin.hu.tr new file mode 100644 index 000000000..d7eebf7a0 --- /dev/null +++ b/builtin/locale/__builtin.hu.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Meghaltál +Respawn=Újraéledés diff --git a/builtin/locale/__builtin.id.tr b/builtin/locale/__builtin.id.tr index a541b0f8c..a7eba8c1c 100644 --- a/builtin/locale/__builtin.id.tr +++ b/builtin/locale/__builtin.id.tr @@ -244,3 +244,5 @@ A total of @1 sample(s) were taken.=Total @1 sampel yang diambil. The output is limited to '@1'.=Keluaran dibatasi ke '@1'. Saving of profile failed: @1=Penyimpanan profil gagal: @1 Profile saved to @1=Profil disimpan ke @1 +You died=Anda mati +Respawn=Bangkit kembali diff --git a/builtin/locale/__builtin.it.tr b/builtin/locale/__builtin.it.tr index aecb80a9a..24cf38104 100644 --- a/builtin/locale/__builtin.it.tr +++ b/builtin/locale/__builtin.it.tr @@ -245,3 +245,5 @@ A total of @1 sample(s) were taken.=Son stati ottenuti campioni per un totale di The output is limited to '@1'.=L'output è limitato a '@1'. Saving of profile failed: @1=Errore nel salvare il profilo: @1 Profile saved to @1=Profilo salvato in @1 +You died=Sei morto +Respawn=Rinasci diff --git a/builtin/locale/__builtin.ja.tr b/builtin/locale/__builtin.ja.tr new file mode 100644 index 000000000..1138edddd --- /dev/null +++ b/builtin/locale/__builtin.ja.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=死んでしまった +Respawn=リスポーン diff --git a/builtin/locale/__builtin.jbo.tr b/builtin/locale/__builtin.jbo.tr new file mode 100644 index 000000000..fe492bcdb --- /dev/null +++ b/builtin/locale/__builtin.jbo.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=.i do morsi +Respawn=tolcanci diff --git a/builtin/locale/__builtin.jv.tr b/builtin/locale/__builtin.jv.tr new file mode 100644 index 000000000..282cc5476 --- /dev/null +++ b/builtin/locale/__builtin.jv.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Panjenengan pejah +Respawn=Bangkit Malilh diff --git a/builtin/locale/__builtin.ko.tr b/builtin/locale/__builtin.ko.tr new file mode 100644 index 000000000..0d5d35a90 --- /dev/null +++ b/builtin/locale/__builtin.ko.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=사망했습니다 +Respawn=리스폰 diff --git a/builtin/locale/__builtin.kv.tr b/builtin/locale/__builtin.kv.tr new file mode 100644 index 000000000..f985d2ac4 --- /dev/null +++ b/builtin/locale/__builtin.kv.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Кулінныд +Respawn=Ловзьыны diff --git a/builtin/locale/__builtin.ky.tr b/builtin/locale/__builtin.ky.tr new file mode 100644 index 000000000..9ad468aa0 --- /dev/null +++ b/builtin/locale/__builtin.ky.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Сиз өлдүңүз. +Respawn=Кайтадан жаралуу diff --git a/builtin/locale/__builtin.lt.tr b/builtin/locale/__builtin.lt.tr new file mode 100644 index 000000000..cc613162c --- /dev/null +++ b/builtin/locale/__builtin.lt.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Jūs numirėte +Respawn=Prisikelti diff --git a/builtin/locale/__builtin.lv.tr b/builtin/locale/__builtin.lv.tr new file mode 100644 index 000000000..1da1fe3d8 --- /dev/null +++ b/builtin/locale/__builtin.lv.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Jūs nomirāt +Respawn=Atdzīvoties diff --git a/builtin/locale/__builtin.lzh.tr b/builtin/locale/__builtin.lzh.tr new file mode 100644 index 000000000..1af6aba98 --- /dev/null +++ b/builtin/locale/__builtin.lzh.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=尔死矣 +Respawn=复生 diff --git a/builtin/locale/__builtin.mn.tr b/builtin/locale/__builtin.mn.tr new file mode 100644 index 000000000..dd9a3465b --- /dev/null +++ b/builtin/locale/__builtin.mn.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Та үхсэн +Respawn=Дахин төрөх diff --git a/builtin/locale/__builtin.mr.tr b/builtin/locale/__builtin.mr.tr new file mode 100644 index 000000000..e4fcfb9b1 --- /dev/null +++ b/builtin/locale/__builtin.mr.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=तू मेलास +Respawn=पुनर्जन्म diff --git a/builtin/locale/__builtin.ms.tr b/builtin/locale/__builtin.ms.tr index ebf794e97..65ac557bf 100644 --- a/builtin/locale/__builtin.ms.tr +++ b/builtin/locale/__builtin.ms.tr @@ -244,3 +244,5 @@ A total of @1 sample(s) were taken.=Sebanyak @1 sampel telah diambil secara kese The output is limited to '@1'.=Output dihadkan kepada '@1'. Saving of profile failed: @1=Penyimpanan profil telah gagal: @1 Profile saved to @1=Profil telah disimpan ke @1 +You died=Anda telah meninggal +Respawn=Jelma semula diff --git a/builtin/locale/__builtin.nb.tr b/builtin/locale/__builtin.nb.tr new file mode 100644 index 000000000..b02a2d282 --- /dev/null +++ b/builtin/locale/__builtin.nb.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Du døde +Respawn=Gjenoppstå diff --git a/builtin/locale/__builtin.nl.tr b/builtin/locale/__builtin.nl.tr new file mode 100644 index 000000000..bd23c04b0 --- /dev/null +++ b/builtin/locale/__builtin.nl.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Je bent gestorven +Respawn=Herboren worden diff --git a/builtin/locale/__builtin.nn.tr b/builtin/locale/__builtin.nn.tr new file mode 100644 index 000000000..240191f73 --- /dev/null +++ b/builtin/locale/__builtin.nn.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Du døydde +Respawn=Kom opp att diff --git a/builtin/locale/__builtin.oc.tr b/builtin/locale/__builtin.oc.tr new file mode 100644 index 000000000..34b6577ad --- /dev/null +++ b/builtin/locale/__builtin.oc.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Setz mòrt·a +Respawn=Tornar diff --git a/builtin/locale/__builtin.pl.tr b/builtin/locale/__builtin.pl.tr new file mode 100644 index 000000000..ec0a9b7c1 --- /dev/null +++ b/builtin/locale/__builtin.pl.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Nie żyjesz +Respawn=Wróć do gry diff --git a/builtin/locale/__builtin.pt.tr b/builtin/locale/__builtin.pt.tr new file mode 100644 index 000000000..fbdb461e5 --- /dev/null +++ b/builtin/locale/__builtin.pt.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Você morreu +Respawn=Renascer diff --git a/builtin/locale/__builtin.pt_BR.tr b/builtin/locale/__builtin.pt_BR.tr index e12e9fae8..e48425bda 100644 --- a/builtin/locale/__builtin.pt_BR.tr +++ b/builtin/locale/__builtin.pt_BR.tr @@ -244,3 +244,5 @@ A total of @1 sample(s) were taken.=Um total de @1 amostra(s) foi coletada. The output is limited to '@1'.=A saída é limitada a '@1'. Saving of profile failed: @1=Falha ao salvar o perfil: @1 Profile saved to @1=Perfil salvo em @1 +You died=Você morreu +Respawn=Reviver diff --git a/builtin/locale/__builtin.ro.tr b/builtin/locale/__builtin.ro.tr new file mode 100644 index 000000000..7ff5e3c38 --- /dev/null +++ b/builtin/locale/__builtin.ro.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Ai murit +Respawn=Reînviere diff --git a/builtin/locale/__builtin.ru.tr b/builtin/locale/__builtin.ru.tr index d43fbc589..863cdd638 100644 --- a/builtin/locale/__builtin.ru.tr +++ b/builtin/locale/__builtin.ru.tr @@ -244,3 +244,5 @@ A total of @1 sample(s) were taken.=Всего было взято @1 образ The output is limited to '@1'.=Вывод ограничен значением '@1'. Saving of profile failed: @1=Не удалось сохранить данные профилирования: @1 Profile saved to @1=Данные профилирования сохранены в @1 +You died=Вы умерли +Respawn=Возродиться diff --git a/builtin/locale/__builtin.sk.tr b/builtin/locale/__builtin.sk.tr new file mode 100644 index 000000000..e9e93f6a3 --- /dev/null +++ b/builtin/locale/__builtin.sk.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Zomrel si +Respawn=Oživiť diff --git a/builtin/locale/__builtin.sl.tr b/builtin/locale/__builtin.sl.tr new file mode 100644 index 000000000..6dc77c3b2 --- /dev/null +++ b/builtin/locale/__builtin.sl.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Umrl si +Respawn=Ponovno oživi diff --git a/builtin/locale/__builtin.sr_Cyrl.tr b/builtin/locale/__builtin.sr_Cyrl.tr new file mode 100644 index 000000000..68551e77a --- /dev/null +++ b/builtin/locale/__builtin.sr_Cyrl.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Умро си +Respawn=Врати се у живот diff --git a/builtin/locale/__builtin.sr_Latn.tr b/builtin/locale/__builtin.sr_Latn.tr new file mode 100644 index 000000000..4adc496b4 --- /dev/null +++ b/builtin/locale/__builtin.sr_Latn.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Umro/la si. +Respawn=Vrati se u zivot diff --git a/builtin/locale/__builtin.sv.tr b/builtin/locale/__builtin.sv.tr new file mode 100644 index 000000000..115014506 --- /dev/null +++ b/builtin/locale/__builtin.sv.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Du dog +Respawn=Återuppstå diff --git a/builtin/locale/__builtin.sw.tr b/builtin/locale/__builtin.sw.tr new file mode 100644 index 000000000..92aa1f3e1 --- /dev/null +++ b/builtin/locale/__builtin.sw.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Umekufa. +Respawn=Respawn diff --git a/builtin/locale/__builtin.tok.tr b/builtin/locale/__builtin.tok.tr new file mode 100644 index 000000000..14d131d6a --- /dev/null +++ b/builtin/locale/__builtin.tok.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=sina moli +Respawn=o kama sin diff --git a/builtin/locale/__builtin.tr.tr b/builtin/locale/__builtin.tr.tr new file mode 100644 index 000000000..dafec5630 --- /dev/null +++ b/builtin/locale/__builtin.tr.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Öldün +Respawn=Yeniden Canlan diff --git a/builtin/locale/__builtin.tt.tr b/builtin/locale/__builtin.tt.tr new file mode 100644 index 000000000..a162cd9b7 --- /dev/null +++ b/builtin/locale/__builtin.tt.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Сез үлдегез +Respawn=Тергезелергә diff --git a/builtin/locale/__builtin.uk.tr b/builtin/locale/__builtin.uk.tr new file mode 100644 index 000000000..e82a10bde --- /dev/null +++ b/builtin/locale/__builtin.uk.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Ви загинули +Respawn=Відродитися diff --git a/builtin/locale/__builtin.vi.tr b/builtin/locale/__builtin.vi.tr new file mode 100644 index 000000000..53caac93c --- /dev/null +++ b/builtin/locale/__builtin.vi.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=Bạn đã bị chết +Respawn=Hồi sinh diff --git a/builtin/locale/__builtin.zh_CN.tr b/builtin/locale/__builtin.zh_CN.tr new file mode 100644 index 000000000..5b1077429 --- /dev/null +++ b/builtin/locale/__builtin.zh_CN.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=您已死亡 +Respawn=重生 diff --git a/builtin/locale/__builtin.zh_TW.tr b/builtin/locale/__builtin.zh_TW.tr new file mode 100644 index 000000000..5b1077429 --- /dev/null +++ b/builtin/locale/__builtin.zh_TW.tr @@ -0,0 +1,3 @@ +# textdomain: __builtin +You died=您已死亡 +Respawn=重生 diff --git a/builtin/mainmenu/init.lua b/builtin/mainmenu/init.lua index 41885e298..dd35334c2 100644 --- a/builtin/mainmenu/init.lua +++ b/builtin/mainmenu/init.lua @@ -23,6 +23,13 @@ mt_color_dark_green = "#25C191" mt_color_orange = "#FF8800" mt_color_red = "#FF3300" +MAIN_TAB_W = 15.5 +MAIN_TAB_H = 7.1 +TABHEADER_H = 0.85 +GAMEBAR_H = 1.25 +GAMEBAR_OFFSET_DESKTOP = 0.375 +GAMEBAR_OFFSET_TOUCH = 0.15 + local menupath = core.get_mainmenu_path() local basepath = core.get_builtin_path() defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" .. @@ -89,7 +96,7 @@ local function init_globals() mm_game_theme.set_engine() -- This is just a fallback. -- Create main tabview - local tv_main = tabview_create("maintab", {x = 15.5, y = 7.1}, {x = 0, y = 0}) + local tv_main = tabview_create("maintab", {x = MAIN_TAB_W, y = MAIN_TAB_H}, {x = 0, y = 0}) tv_main:set_autosave_tab(true) tv_main:add(tabs.local_game) diff --git a/builtin/mainmenu/tab_local.lua b/builtin/mainmenu/tab_local.lua index 7f46be213..f0a7255d7 100644 --- a/builtin/mainmenu/tab_local.lua +++ b/builtin/mainmenu/tab_local.lua @@ -92,10 +92,16 @@ function singleplayer_refresh_gamebar() end end + local ENABLE_TOUCH = core.settings:get_bool("enable_touch") + + local gamebar_pos_y = MAIN_TAB_H + + TABHEADER_H -- tabheader included in formspec size + + (ENABLE_TOUCH and GAMEBAR_OFFSET_TOUCH or GAMEBAR_OFFSET_DESKTOP) + local btnbar = buttonbar_create( "game_button_bar", - core.settings:get_bool("touch_gui") and {x = 0, y = 7.25} or {x = 0, y = 7.475}, - {x = 15.5, y = 1.25}, + {x = 0, y = gamebar_pos_y}, + {x = MAIN_TAB_W, y = GAMEBAR_H}, "#000000", game_buttonbar_button_handler) diff --git a/builtin/profiler/instrumentation.lua b/builtin/profiler/instrumentation.lua index 0ffd9e6a9..c4feda7b4 100644 --- a/builtin/profiler/instrumentation.lua +++ b/builtin/profiler/instrumentation.lua @@ -217,8 +217,9 @@ local function init() -- Wrap register_lbm() to automatically instrument lbms. local orig_register_lbm = core.register_lbm core.register_lbm = function(spec) - spec.action = instrument { - func = spec.action, + local k = spec.bulk_action ~= nil and "bulk_action" or "action" + spec[k] = instrument { + func = spec[k], class = "LBM", label = spec.label or spec.name, } diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 0203e807e..8ff6a48ea 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -403,6 +403,11 @@ enable_clouds (Clouds) bool true # Requires: enable_clouds enable_3d_clouds (3D clouds) bool true +# Use smooth cloud shading. +# +# Requires: enable_3d_clouds, enable_clouds +soft_clouds (Soft clouds) bool false + [**Filtering and Antialiasing] # Use mipmaps when scaling textures. May slightly increase performance, @@ -505,6 +510,11 @@ water_wave_length (Waving liquids wavelength) float 20.0 0.1 # Requires: shaders, enable_waving_water water_wave_speed (Waving liquids wave speed) float 5.0 +# When enabled, liquid reflections are simulated. +# +# Requires: shaders, enable_waving_water, enable_dynamic_shadows +enable_water_reflections (Liquid reflections) bool false + [**Dynamic shadows] # Set to true to enable Shadow Mapping. @@ -661,6 +671,18 @@ bloom_radius (Bloom Radius) float 1 0.1 8 # Requires: shaders, enable_post_processing, enable_bloom enable_volumetric_lighting (Volumetric lighting) bool false +[**Other Effects] + +# Simulate translucency when looking at foliage in the sunlight. +# +# Requires: shaders, enable_dynamic_shadows +enable_translucent_foliage (Translucent foliage) bool false + +# Apply specular shading to nodes. +# +# Requires: shaders, enable_dynamic_shadows +enable_node_specular (Node specular) bool false + [*Audio] # Volume of all sounds. @@ -1030,7 +1052,7 @@ mapgen_limit (Map generation limit) int 31007 0 31007 # Global map generation attributes. # In Mapgen v6 the 'decorations' flag controls all decorations except trees # and jungle grass, in all other mapgens this flag controls all decorations. -mg_flags (Mapgen flags) flags caves,dungeons,light,decorations,biomes,ores caves,dungeons,light,decorations,biomes,ores,nocaves,nodungeons,nolight,nodecorations,nobiomes,noores +mg_flags (Mapgen flags) flags caves,dungeons,light,decorations,biomes,ores caves,dungeons,light,decorations,biomes,ores [*Biome API] @@ -1049,7 +1071,7 @@ mg_biome_np_humidity_blend (Humidity blend noise) noise_params_2d 0, 1.5, (8, 8, [*Mapgen V5] # Map generation attributes specific to Mapgen v5. -mgv5_spflags (Mapgen V5 specific flags) flags caverns caverns,nocaverns +mgv5_spflags (Mapgen V5 specific flags) flags caverns caverns # Controls width of tunnels, a smaller value creates wider tunnels. # Value >= 10.0 completely disables generation of tunnels and avoids the @@ -1123,7 +1145,7 @@ mgv5_np_dungeons (Dungeon noise) noise_params_3d 0.9, 0.5, (500, 500, 500), 0, 2 # When the 'snowbiomes' flag is enabled jungles are automatically enabled and # the 'jungles' flag is ignored. # The 'temples' flag disables generation of desert temples. Normal dungeons will appear instead. -mgv6_spflags (Mapgen V6 specific flags) flags jungles,biomeblend,mudflow,snowbiomes,noflat,trees,temples jungles,biomeblend,mudflow,snowbiomes,flat,trees,temples,nojungles,nobiomeblend,nomudflow,nosnowbiomes,noflat,notrees,notemples +mgv6_spflags (Mapgen V6 specific flags) flags jungles,biomeblend,mudflow,snowbiomes,noflat,trees,temples jungles,biomeblend,mudflow,snowbiomes,flat,trees,temples # Deserts occur when np_biome exceeds this value. # When the 'snowbiomes' flag is enabled, this is ignored. @@ -1179,7 +1201,7 @@ mgv6_np_apple_trees (Apple trees noise) noise_params_2d 0, 1, (100, 100, 100), 3 # 'ridges': Rivers. # 'floatlands': Floating land masses in the atmosphere. # 'caverns': Giant caves deep underground. -mgv7_spflags (Mapgen V7 specific flags) flags mountains,ridges,nofloatlands,caverns mountains,ridges,floatlands,caverns,nomountains,noridges,nofloatlands,nocaverns +mgv7_spflags (Mapgen V7 specific flags) flags mountains,ridges,nofloatlands,caverns mountains,ridges,floatlands,caverns # Y of mountain density gradient zero level. Used to shift mountains vertically. mgv7_mount_zero_level (Mountain zero level) int 0 -31000 31000 @@ -1313,7 +1335,7 @@ mgv7_np_dungeons (Dungeon noise) noise_params_3d 0.9, 0.5, (500, 500, 500), 0, 2 [*Mapgen Carpathian] # Map generation attributes specific to Mapgen Carpathian. -mgcarpathian_spflags (Mapgen Carpathian specific flags) flags caverns,norivers caverns,rivers,nocaverns,norivers +mgcarpathian_spflags (Mapgen Carpathian specific flags) flags caverns,norivers caverns,rivers # Defines the base ground level. mgcarpathian_base_level (Base ground level) float 12.0 @@ -1422,7 +1444,7 @@ mgcarpathian_np_dungeons (Dungeon noise) noise_params_3d 0.9, 0.5, (500, 500, 50 # Map generation attributes specific to Mapgen Flat. # Occasional lakes and hills can be added to the flat world. -mgflat_spflags (Mapgen Flat specific flags) flags nolakes,nohills,nocaverns lakes,hills,caverns,nolakes,nohills,nocaverns +mgflat_spflags (Mapgen Flat specific flags) flags nolakes,nohills,nocaverns lakes,hills,caverns # Y of flat ground. mgflat_ground_level (Ground level) int 8 -31000 31000 @@ -1506,7 +1528,7 @@ mgflat_np_dungeons (Dungeon noise) noise_params_3d 0.9, 0.5, (500, 500, 500), 0, # Map generation attributes specific to Mapgen Fractal. # 'terrain' enables the generation of non-fractal terrain: # ocean, islands and underground. -mgfractal_spflags (Mapgen Fractal specific flags) flags terrain terrain,noterrain +mgfractal_spflags (Mapgen Fractal specific flags) flags terrain terrain # Controls width of tunnels, a smaller value creates wider tunnels. # Value >= 10.0 completely disables generation of tunnels and avoids the @@ -1640,7 +1662,7 @@ mgfractal_np_dungeons (Dungeon noise) noise_params_3d 0.9, 0.5, (500, 500, 500), # 'vary_river_depth': If enabled, low humidity and high heat causes rivers # to become shallower and occasionally dry. # 'altitude_dry': Reduces humidity with altitude. -mgvalleys_spflags (Mapgen Valleys specific flags) flags altitude_chill,humid_rivers,vary_river_depth,altitude_dry altitude_chill,humid_rivers,vary_river_depth,altitude_dry,noaltitude_chill,nohumid_rivers,novary_river_depth,noaltitude_dry +mgvalleys_spflags (Mapgen Valleys specific flags) flags altitude_chill,humid_rivers,vary_river_depth,altitude_dry altitude_chill,humid_rivers,vary_river_depth,altitude_dry # The vertical distance over which heat drops by 20 if 'altitude_chill' is # enabled. Also, the vertical distance over which humidity drops by 10 if diff --git a/client/shaders/Irrlicht/Solid.vsh b/client/shaders/Irrlicht/Solid.vsh index 7379e5bb4..fd7467f5c 100644 --- a/client/shaders/Irrlicht/Solid.vsh +++ b/client/shaders/Irrlicht/Solid.vsh @@ -11,7 +11,6 @@ attribute vec2 inTexCoord0; uniform mat4 uWVPMatrix; uniform mat4 uWVMatrix; -uniform mat4 uNMatrix; uniform mat4 uTMatrix0; uniform float uThickness; diff --git a/client/shaders/cloud_shader/opengl_vertex.glsl b/client/shaders/cloud_shader/opengl_vertex.glsl index 3f2e7d9b3..ebf4aae49 100644 --- a/client/shaders/cloud_shader/opengl_vertex.glsl +++ b/client/shaders/cloud_shader/opengl_vertex.glsl @@ -1,4 +1,4 @@ -uniform lowp vec4 emissiveColor; +uniform lowp vec4 materialColor; varying lowp vec4 varColor; @@ -14,7 +14,7 @@ void main(void) vec4 color = inVertexColor; #endif - color *= emissiveColor; + color *= materialColor; varColor = color; eyeVec = -(mWorldView * inVertexPosition).xyz; diff --git a/client/shaders/nodes_shader/opengl_fragment.glsl b/client/shaders/nodes_shader/opengl_fragment.glsl index 46977b147..c560a8505 100644 --- a/client/shaders/nodes_shader/opengl_fragment.glsl +++ b/client/shaders/nodes_shader/opengl_fragment.glsl @@ -1,3 +1,7 @@ +#if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_TRANSPARENT || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_OPAQUE || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LIQUID_BASIC || MATERIAL_TYPE == TILE_MATERIAL_LIQUID_TRANSPARENT) +#define MATERIAL_WAVING_LIQUID 1 +#endif + uniform sampler2D baseTexture; uniform vec3 dayLight; @@ -7,6 +11,7 @@ uniform float fogShadingParameter; // The cameraOffset is the current center of the visible world. uniform highp vec3 cameraOffset; +uniform vec3 cameraPosition; uniform float animationTimer; #ifdef ENABLE_DYNAMIC_SHADOWS // shadow texture @@ -20,6 +25,7 @@ uniform float animationTimer; uniform vec4 CameraPos; uniform float xyPerspectiveBias0; uniform float xyPerspectiveBias1; + uniform vec3 shadow_tint; varying float adj_shadow_strength; varying float cosLight; @@ -47,6 +53,49 @@ varying highp vec3 eyeVec; varying float nightRatio; #ifdef ENABLE_DYNAMIC_SHADOWS +#if (defined(MATERIAL_WAVING_LIQUID) && defined(ENABLE_WATER_REFLECTIONS) && ENABLE_WAVING_WATER) +vec4 perm(vec4 x) +{ + return mod(((x * 34.0) + 1.0) * x, 289.0); +} + +// Corresponding gradient of snoise +vec3 gnoise(vec3 p){ + vec3 a = floor(p); + vec3 d = p - a; + vec3 dd = 6.0 * d * (1.0 - d); + d = d * d * (3.0 - 2.0 * d); + + vec4 b = a.xxyy + vec4(0.0, 1.0, 0.0, 1.0); + vec4 k1 = perm(b.xyxy); + vec4 k2 = perm(k1.xyxy + b.zzww); + + vec4 c = k2 + a.zzzz; + vec4 k3 = perm(c); + vec4 k4 = perm(c + 1.0); + + vec4 o1 = fract(k3 * (1.0 / 41.0)); + vec4 o2 = fract(k4 * (1.0 / 41.0)); + + vec4 o3 = o2 * d.z + o1 * (1.0 - d.z); + vec2 o4 = o3.yw * d.x + o3.xz * (1.0 - d.x); + + vec4 dz1 = (o2 - o1) * dd.z; + vec2 dz2 = dz1.yw * d.x + dz1.xz * (1.0 - d.x); + + vec2 dx = (o3.yw - o3.xz) * dd.x; + + return vec3( + dx.y * d.y + dx.x * (1. - d.y), + (o4.y - o4.x) * dd.y, + dz2.y * d.y + dz2.x * (1. - d.y) + ); +} + +vec2 wave_noise(vec3 p, float off) { + return (gnoise(p + vec3(0.0, 0.0, off)) * 0.4 + gnoise(2.0 * p + vec3(0.0, off, off)) * 0.2 + gnoise(3.0 * p + vec3(0.0, off, off)) * 0.225 + gnoise(4.0 * p + vec3(-off, off, 0.0)) * 0.2).xz; +} +#endif // assuming near is always 1.0 float getLinearDepth() @@ -66,6 +115,14 @@ float mtsmoothstep(in float edge0, in float edge1, in float x) return t * t * (3.0 - 2.0 * t); } +float shadowCutoff(float x) { + #if defined(ENABLE_TRANSLUCENT_FOLIAGE) && MATERIAL_TYPE == TILE_MATERIAL_WAVING_LEAVES + return mtsmoothstep(0.0, 0.002, x); + #else + return step(0.0, x); + #endif +} + #ifdef COLORED_SHADOWS // c_precision of 128 fits within 7 base-10 digits @@ -92,10 +149,10 @@ vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDist { vec4 texDepth = texture2D(shadowsampler, smTexCoord.xy).rgba; - float visibility = step(0.0, realDistance - texDepth.r); + float visibility = shadowCutoff(realDistance - texDepth.r); vec4 result = vec4(visibility, vec3(0.0,0.0,0.0));//unpackColor(texDepth.g)); if (visibility < 0.1) { - visibility = step(0.0, realDistance - texDepth.b); + visibility = shadowCutoff(realDistance - texDepth.b); result = vec4(visibility, unpackColor(texDepth.a)); } return result; @@ -106,7 +163,7 @@ vec4 getHardShadowColor(sampler2D shadowsampler, vec2 smTexCoord, float realDist float getHardShadow(sampler2D shadowsampler, vec2 smTexCoord, float realDistance) { float texDepth = texture2D(shadowsampler, smTexCoord.xy).r; - float visibility = step(0.0, realDistance - texDepth); + float visibility = shadowCutoff(realDistance - texDepth); return visibility; } @@ -378,6 +435,9 @@ void main(void) vec4 col = vec4(color.rgb * varColor.rgb, 1.0); #ifdef ENABLE_DYNAMIC_SHADOWS + // Fragment normal, can differ from vNormal which is derived from vertex normals. + vec3 fNormal = vNormal; + if (f_shadow_strength > 0.0) { float shadow_int = 0.0; vec3 shadow_color = vec3(0.0, 0.0, 0.0); @@ -414,12 +474,19 @@ void main(void) // Power ratio was measured on torches in MTG (brightness = 14). float adjusted_night_ratio = pow(max(0.0, nightRatio), 0.6); + float shadow_uncorrected = shadow_int; + // Apply self-shadowing when light falls at a narrow angle to the surface // Cosine of the cut-off angle. const float self_shadow_cutoff_cosine = 0.035; if (f_normal_length != 0 && cosLight < self_shadow_cutoff_cosine) { shadow_int = max(shadow_int, 1 - clamp(cosLight, 0.0, self_shadow_cutoff_cosine)/self_shadow_cutoff_cosine); shadow_color = mix(vec3(0.0), shadow_color, min(cosLight, self_shadow_cutoff_cosine)/self_shadow_cutoff_cosine); + +#if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_LEAVES || MATERIAL_TYPE == TILE_MATERIAL_WAVING_PLANTS) + // Prevents foliage from becoming insanely bright outside the shadow map. + shadow_uncorrected = mix(shadow_int, shadow_uncorrected, clamp(distance_rate * 4.0 - 3.0, 0.0, 1.0)); +#endif } shadow_int *= f_adj_shadow_strength; @@ -428,8 +495,60 @@ void main(void) col.rgb = adjusted_night_ratio * col.rgb + // artificial light (1.0 - adjusted_night_ratio) * ( // natural light - col.rgb * (1.0 - shadow_int * (1.0 - shadow_color)) + // filtered texture color + col.rgb * (1.0 - shadow_int * (1.0 - shadow_color) * (1.0 - shadow_tint)) + // filtered texture color dayLight * shadow_color * shadow_int); // reflected filtered sunlight/moonlight + + + vec3 reflect_ray = -normalize(v_LightDirection - fNormal * dot(v_LightDirection, fNormal) * 2.0); + + vec3 viewVec = normalize(worldPosition + cameraOffset - cameraPosition); + + // Water reflections +#if (defined(MATERIAL_WAVING_LIQUID) && defined(ENABLE_WATER_REFLECTIONS) && ENABLE_WAVING_WATER) + vec3 wavePos = worldPosition * vec3(2.0, 0.0, 2.0); + float off = animationTimer * WATER_WAVE_SPEED * 10.0; + wavePos.x /= WATER_WAVE_LENGTH * 3.0; + wavePos.z /= WATER_WAVE_LENGTH * 2.0; + + // This is an analogous method to the bumpmap, except we get the gradient information directly from gnoise. + vec2 gradient = wave_noise(wavePos, off); + fNormal = normalize(normalize(fNormal) + vec3(gradient.x, 0., gradient.y) * WATER_WAVE_HEIGHT * abs(fNormal.y) * 0.25); + reflect_ray = -normalize(v_LightDirection - fNormal * dot(v_LightDirection, fNormal) * 2.0); + float fresnel_factor = dot(fNormal, viewVec); + + float brightness_factor = 1.0 - adjusted_night_ratio; + + // A little trig hack. We go from the dot product of viewVec and normal to the dot product of viewVec and tangent to apply a fresnel effect. + fresnel_factor = clamp(pow(1.0 - fresnel_factor * fresnel_factor, 8.0), 0.0, 1.0) * 0.8 + 0.2; + col.rgb *= 0.5; + vec3 reflection_color = mix(vec3(max(fogColor.r, max(fogColor.g, fogColor.b))), fogColor.rgb, f_shadow_strength); + + // Sky reflection + col.rgb += reflection_color * pow(fresnel_factor, 2.0) * 0.5 * brightness_factor; + vec3 water_reflect_color = 12.0 * dayLight * fresnel_factor * mtsmoothstep(0.85, 0.9, pow(clamp(dot(reflect_ray, viewVec), 0.0, 1.0), 32.0)) * max(1.0 - shadow_uncorrected, 0.0); + + // This line exists to prevent ridiculously bright reflection colors. + water_reflect_color /= clamp(max(water_reflect_color.r, max(water_reflect_color.g, water_reflect_color.b)) * 0.375, 1.0, 400.0); + col.rgb += water_reflect_color * f_adj_shadow_strength * brightness_factor; +#endif + +#if (defined(ENABLE_NODE_SPECULAR) && !defined(MATERIAL_WAVING_LIQUID)) + // Apply specular to blocks. + if (dot(v_LightDirection, vNormal) < 0.0) { + float intensity = 2.0 * (1.0 - (base.r * varColor.r)); + const float specular_exponent = 5.0; + const float fresnel_exponent = 4.0; + + col.rgb += + intensity * dayLight * (1.0 - nightRatio) * (1.0 - shadow_uncorrected) * f_adj_shadow_strength * + pow(max(dot(reflect_ray, viewVec), 0.0), fresnel_exponent) * pow(1.0 - abs(dot(viewVec, fNormal)), specular_exponent); + } +#endif + +#if (MATERIAL_TYPE == TILE_MATERIAL_WAVING_PLANTS || MATERIAL_TYPE == TILE_MATERIAL_WAVING_LEAVES) && defined(ENABLE_TRANSLUCENT_FOLIAGE) + // Simulate translucent foliage. + col.rgb += 4.0 * dayLight * base.rgb * normalize(base.rgb * varColor.rgb * varColor.rgb) * f_adj_shadow_strength * pow(max(-dot(v_LightDirection, viewVec), 0.0), 4.0) * max(1.0 - shadow_uncorrected, 0.0); +#endif } #endif @@ -444,7 +563,13 @@ void main(void) // Note: clarity = (1 - fogginess) float clarity = clamp(fogShadingParameter - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0); - col = mix(fogColor, col, clarity); + float fogColorMax = max(max(fogColor.r, fogColor.g), fogColor.b); + // Prevent zero division. + if (fogColorMax < 0.0000001) fogColorMax = 1.0; + // For high clarity (light fog) we tint the fog color. + // For this to not make the fog color artificially dark we need to normalize using the + // fog color's brightest value. We then blend our base color with this to make the fog. + col = mix(fogColor * pow(fogColor / fogColorMax, vec4(2.0 * clarity)), col, clarity); col = vec4(col.rgb, base.a); gl_FragData[0] = col; diff --git a/client/shaders/nodes_shader/opengl_vertex.glsl b/client/shaders/nodes_shader/opengl_vertex.glsl index d96164d76..ba48189a5 100644 --- a/client/shaders/nodes_shader/opengl_vertex.glsl +++ b/client/shaders/nodes_shader/opengl_vertex.glsl @@ -256,7 +256,9 @@ void main(void) z_bias *= pFactor * pFactor / f_textureresolution / f_shadowfar; shadow_position = applyPerspectiveDistortion(m_ShadowViewProj * mWorld * (shadow_pos + vec4(normalOffsetScale * nNormal, 0.0))).xyz; +#if !defined(ENABLE_TRANSLUCENT_FOLIAGE) || MATERIAL_TYPE != TILE_MATERIAL_WAVING_LEAVES shadow_position.z -= z_bias; +#endif perspective_factor = pFactor; if (f_timeofday < 0.2) { diff --git a/client/shaders/object_shader/opengl_fragment.glsl b/client/shaders/object_shader/opengl_fragment.glsl index 2b8af3fa9..db72d7f7d 100644 --- a/client/shaders/object_shader/opengl_fragment.glsl +++ b/client/shaders/object_shader/opengl_fragment.glsl @@ -20,6 +20,7 @@ uniform float animationTimer; uniform vec4 CameraPos; uniform float xyPerspectiveBias0; uniform float xyPerspectiveBias1; + uniform vec3 shadow_tint; varying float adj_shadow_strength; varying float cosLight; @@ -432,7 +433,7 @@ void main(void) col.rgb = adjusted_night_ratio * col.rgb + // artificial light (1.0 - adjusted_night_ratio) * ( // natural light - col.rgb * (1.0 - shadow_int * (1.0 - shadow_color)) + // filtered texture color + col.rgb * (1.0 - shadow_int * (1.0 - shadow_color) * (1.0 - shadow_tint)) + // filtered texture color dayLight * shadow_color * shadow_int); // reflected filtered sunlight/moonlight } #endif @@ -448,7 +449,13 @@ void main(void) // Note: clarity = (1 - fogginess) float clarity = clamp(fogShadingParameter - fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0); - col = mix(fogColor, col, clarity); + float fogColorMax = max(max(fogColor.r, fogColor.g), fogColor.b); + // Prevent zero division. + if (fogColorMax < 0.0000001) fogColorMax = 1.0; + // For high clarity (light fog) we tint the fog color. + // For this to not make the fog color artificially dark we need to normalize using the + // fog color's brightest value. We then blend our base color with this to make the fog. + col = mix(fogColor * pow(fogColor / fogColorMax, vec4(2.0 * clarity)), col, clarity); col = vec4(col.rgb, base.a); gl_FragData[0] = col; diff --git a/client/shaders/object_shader/opengl_vertex.glsl b/client/shaders/object_shader/opengl_vertex.glsl index d5a434da5..05134a5f6 100644 --- a/client/shaders/object_shader/opengl_vertex.glsl +++ b/client/shaders/object_shader/opengl_vertex.glsl @@ -1,7 +1,7 @@ uniform mat4 mWorld; uniform vec3 dayLight; uniform float animationTimer; -uniform lowp vec4 emissiveColor; +uniform lowp vec4 materialColor; varying vec3 vNormal; varying vec3 vPosition; @@ -91,7 +91,7 @@ float directional_ambient(vec3 normal) void main(void) { - varTexCoord = (mTexture * inTexCoord0).st; + varTexCoord = (mTexture * vec4(inTexCoord0.xy, 1.0, 1.0)).st; gl_Position = mWorldViewProj * inVertexPosition; vPosition = gl_Position.xyz; @@ -115,7 +115,7 @@ void main(void) vec4 color = inVertexColor; #endif - color *= emissiveColor; + color *= materialColor; // The alpha gives the ratio of sunlight in the incoming light. nightRatio = 1.0 - color.a; diff --git a/client/shaders/stars_shader/opengl_fragment.glsl b/client/shaders/stars_shader/opengl_fragment.glsl index 224032fa3..e991e4f94 100644 --- a/client/shaders/stars_shader/opengl_fragment.glsl +++ b/client/shaders/stars_shader/opengl_fragment.glsl @@ -1,6 +1,6 @@ -uniform lowp vec4 emissiveColor; +uniform lowp vec4 materialColor; void main(void) { - gl_FragColor = emissiveColor; + gl_FragColor = materialColor; } diff --git a/client/shaders/volumetric_light/opengl_fragment.glsl b/client/shaders/volumetric_light/opengl_fragment.glsl index 9ed5fa9ba..001f59465 100644 --- a/client/shaders/volumetric_light/opengl_fragment.glsl +++ b/client/shaders/volumetric_light/opengl_fragment.glsl @@ -46,7 +46,9 @@ float sampleVolumetricLight(vec2 uv, vec3 lightVec, float rawDepth) if (min(samplepos.x, samplepos.y) > 0. && max(samplepos.x, samplepos.y) < 1.) result += texture2D(depthmap, samplepos).r < 1. ? 0.0 : 1.0; } - return result / samples; + // We use the depth map to approximate the effect of depth on the light intensity. + // The exponent was chosen based on aesthetic preference. + return result / samples * pow(texture2D(depthmap, uv).r, 128.0); } vec3 getDirectLightScatteringAtGround(vec3 v_LightDirection) diff --git a/doc/client_lua_api.md b/doc/client_lua_api.md index 08d0317ab..cd651f1b3 100644 --- a/doc/client_lua_api.md +++ b/doc/client_lua_api.md @@ -338,8 +338,6 @@ Call these functions only at load time! is checked to see if the command exists, but after the input is parsed. * Return `true` to mark the command as handled, which means that the default handlers will be prevented. -* `minetest.register_on_death(function())` - * Called when the local player dies * `minetest.register_on_hp_modification(function(hp))` * Called when server modified player's HP * `minetest.register_on_damage_taken(function(hp))` @@ -487,8 +485,6 @@ Call these functions only at load time! * Returns `false` if the client is already disconnecting otherwise returns `true`. * `minetest.get_server_info()` * Returns [server info](#server-info). -* `minetest.send_respawn()` - * Sends a respawn request to the server. ### Storage API * `minetest.get_mod_storage()`: diff --git a/doc/compiling/README.md b/doc/compiling/README.md index a1ab1ebbd..bfe91950f 100644 --- a/doc/compiling/README.md +++ b/doc/compiling/README.md @@ -22,6 +22,7 @@ General options and their default values: MinSizeRel - Release build with -Os passed to compiler to make executable as small as possible PRECOMPILE_HEADERS=FALSE - Precompile some headers (experimental; requires CMake 3.16 or later) PRECOMPILED_HEADERS_PATH= - Path to a file listing all headers to precompile (default points to src/precompiled_headers.txt) + USE_SDL2=TRUE - Build with SDL2; Enables IrrlichtMt device SDL2 ENABLE_CURL=ON - Build with cURL; Enables use of online mod repo, public serverlist and remote media fetching via http ENABLE_CURSES=ON - Build with (n)curses; Enables a server side terminal (command line option: --terminal) ENABLE_GETTEXT=ON - Build with Gettext; Allows using translations @@ -39,10 +40,15 @@ General options and their default values: ENABLE_UPDATE_CHECKER=TRUE - Whether to enable update checks by default INSTALL_DEVTEST=FALSE - Whether the Development Test game should be installed alongside Minetest USE_GPROF=FALSE - Enable profiling using GProf + BUILD_WITH_TRACY=FALSE - Fetch and build with the Tracy profiler client + FETCH_TRACY_GIT_TAG=master - Git tag for fetching Tracy client. Match with your server (gui) version VERSION_EXTRA= - Text to append to version (e.g. VERSION_EXTRA=foobar -> Minetest 0.4.9-foobar) Library specific options: + SDL2_DLL - Only if building with SDL2 on Windows; path to libSDL2.dll + SDL2_INCLUDE_DIRS - Only if building with SDL2; directory where SDL.h is located + SDL2_LIBRARIES - Only if building with SDL2; path to libSDL2.a/libSDL2.so/libSDL2.lib CURL_DLL - Only if building with cURL on Windows; path to libcurl.dll CURL_INCLUDE_DIR - Only if building with cURL; directory where curl.h is located CURL_LIBRARY - Only if building with cURL; path to libcurl.a/libcurl.so/libcurl.lib diff --git a/doc/developing/misc.md b/doc/developing/misc.md index 1d3d8c941..2ac843caf 100644 --- a/doc/developing/misc.md +++ b/doc/developing/misc.md @@ -1,6 +1,6 @@ # Miscellaneous -## Profiling Minetest on Linux +## Profiling Minetest on Linux with perf We will be using a tool called "perf", which you can get by installing `perf` or `linux-perf` or `linux-tools-common`. @@ -36,3 +36,54 @@ Give both files to the developer and also provide: * commit the source was built from and/or modified source code (if applicable) Hotspot will resolve symbols correctly when pointing the sysroot option at the collected libs. + + +## Profiling with Tracy + +[Tracy](https://github.com/wolfpld/tracy) is +> A real time, nanosecond resolution, remote telemetry, hybrid frame and sampling +> profiler for games and other applications. + +It allows one to annotate important functions and generate traces, where one can +see when each individual function call happened, and how long it took. + +Tracy can also record when frames, e.g. server step, start and end, and inspect +frames that took longer than usual. Minetest already contains annotations for +its frames. + +See also [Tracy's official documentation](https://github.com/wolfpld/tracy/releases/latest/download/tracy.pdf). + +### Installing + +Tracy consists of a client (Minetest) and a server (the gui). + +Install the server, e.g. using your package manager. + +### Building + +Build Minetest with `-DDBUILD_WITH_TRACY=1`, this will fetch Tracy for building +the Tracy client. And use `FETCH_TRACY_GIT_TAG` to get a version matching your +Tracy server, e.g. `-DFETCH_TRACY_GIT_TAG=v0.11.0` if it's `0.11.0`. + +To actually use Tracy, you also have to enable it with Tracy's build options: +``` +-DTRACY_ENABLE=1 -DTRACY_ONLY_LOCALHOST=1 +``` + +See Tracy's documentation for more build options. + +### Using in C++ + +Start the Tracy server and Minetest. You should see Minetest in the menu. + +To actually get useful traces, you have to annotate functions with `ZoneScoped` +macros and recompile. Please refer to Tracy's official documentation. + +### Using in Lua + +Tracy also supports Lua. +If built with Tracy, Minetest loads its API in the global `tracy` table. +See Tracy's official documentation for more information. + +Note: The whole Tracy Lua API is accessible to all mods. And we don't check if it +is or becomes insecure. Run untrusted mods at your own risk. diff --git a/doc/lua_api.md b/doc/lua_api.md index 6989f3483..9c888ff13 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -1470,7 +1470,8 @@ Look for examples in `games/devtest` or `games/minetest_game`. 'Connected Glass'. * `allfaces` * Often used for partially-transparent nodes. - * External and internal sides of textures are visible. + * External sides of textures, and unlike other drawtypes, the external sides + of other blocks, are visible from the inside. * `allfaces_optional` * Often used for leaves nodes. * This switches between `normal`, `glasslike` and `allfaces` according to @@ -5522,6 +5523,10 @@ Utilities override_item_remove_fields = true, -- The predefined hotbar is a Lua HUD element of type `hotbar` (5.10.0) hotbar_hud_element = true, + -- Bulk LBM support (5.10.0) + bulk_lbms = true, + -- ABM supports field without_neighbors (5.10.0) + abm_without_neighbors = true, } ``` @@ -5580,8 +5585,8 @@ Utilities }, -- Estimated maximum formspec size before Minetest will start shrinking the - -- formspec to fit. For a fullscreen formspec, use a size 10-20% larger than - -- this and `padding[-0.01,-0.01]`. + -- formspec to fit. For a fullscreen formspec, use this formspec size and + -- `padding[0,0]`. `bgcolor[;true]` is also recommended. max_formspec_size = { x = 20, y = 11.25 @@ -5655,6 +5660,13 @@ Utilities * `minetest.colorspec_to_bytes(colorspec)`: Converts a ColorSpec to a raw string of four bytes in an RGBA layout, returned as a string. * `colorspec`: The ColorSpec to convert +* `minetest.colorspec_to_table(colorspec)`: Converts a ColorSpec into RGBA table + form. If the ColorSpec is invalid, returns `nil`. You can use this to parse + ColorStrings. + * `colorspec`: The ColorSpec to convert +* `minetest.time_to_day_night_ratio(time_of_day)`: Returns a "day-night ratio" value + (as accepted by `ObjectRef:override_day_night_ratio`) that is equivalent to + the given "time of day" value (as returned by `minetest.get_timeofday`). * `minetest.encode_png(width, height, data, [compression])`: Encode a PNG image and return it in string form. * `width`: Width of the image @@ -5838,8 +5850,13 @@ Call these functions only at load time! * `clicker`: ObjectRef - Object that acted upon `player`, may or may not be a player * `minetest.register_on_player_hpchange(function(player, hp_change, reason), modifier)` * Called when the player gets damaged or healed + * When `hp == 0`, damage doesn't trigger this callback. + * When `hp == hp_max`, healing does still trigger this callback. * `player`: ObjectRef of the player * `hp_change`: the amount of change. Negative when it is damage. + * Historically, the new HP value was clamped to [0, 65535] before + calculating the HP change. This clamping has been removed as of + Minetest 5.10.0 * `reason`: a PlayerHPChangeReason table. * The `type` field will have one of the following values: * `set_hp`: A mod or the engine called `set_hp` without @@ -5860,6 +5877,7 @@ Call these functions only at load time! * `minetest.register_on_dieplayer(function(ObjectRef, reason))` * Called when a player dies * `reason`: a PlayerHPChangeReason table, see register_on_player_hpchange + * For customizing the death screen, see `minetest.show_death_screen`. * `minetest.register_on_respawnplayer(function(ObjectRef))` * Called when player is to be respawned * Called _before_ repositioning of player occurs @@ -6136,6 +6154,8 @@ Environment access * `minetest.swap_node(pos, node)` * Swap node at position with another. * This keeps the metadata intact and will not run con-/destructor callbacks. +* `minetest.bulk_swap_node({pos1, pos2, pos3, ...}, node)` + * Equivalent to `minetest.swap_node` but in bulk. * `minetest.remove_node(pos)`: Remove a node * Equivalent to `minetest.set_node(pos, {name="air"})`, but a bit faster. * `minetest.get_node(pos)` @@ -6562,6 +6582,13 @@ Formspec * `"INV"`: something failed * `"CHG"`: has been changed * `"VAL"`: not changed +* `minetest.show_death_screen(player, reason)` + * Called when the death screen should be shown. + * `player` is an ObjectRef, `reason` is a PlayerHPChangeReason table or nil. + * By default, this shows a simple formspec with the option to respawn. + Respawning is done via `ObjectRef:respawn`. + * You can override this to show a custom death screen. + * For general death handling, use `minetest.register_on_dieplayer` instead. Item handling ------------- @@ -6999,10 +7026,11 @@ Bans * Returns boolean indicating success * `minetest.unban_player_or_ip(ip_or_name)`: remove ban record matching IP address or name -* `minetest.kick_player(name, [reason])`: disconnect a player with an optional +* `minetest.kick_player(name[, reason[, reconnect]])`: disconnect a player with an optional reason. * Returns boolean indicating success (false if player nonexistent) -* `minetest.disconnect_player(name, [reason])`: disconnect a player with an + * If `reconnect` is true, allow the user to reconnect. +* `minetest.disconnect_player(name[, reason[, reconnect]])`: disconnect a player with an optional reason, this will not prefix with 'Kicked: ' like kick_player. If no reason is given, it will default to 'Disconnected.' * Returns boolean indicating success (false if player nonexistent) @@ -8508,12 +8536,15 @@ child will follow movement and rotation of that bone. if set to zero the clouds are rendered flat. * `speed`: 2D cloud speed + direction in nodes per second (default `{x=0, z=-2}`). + * `shadow`: shadow color, applied to the base of the cloud + (default `#cccccc`). * `get_clouds()`: returns a table with the current cloud parameters as in `set_clouds`. * `override_day_night_ratio(ratio or nil)` * `0`...`1`: Overrides day-night ratio, controlling sunlight to a specific amount. * Passing no arguments disables override, defaulting to sunlight based on day-night cycle + * See also `minetest.time_to_day_night_ratio`, * `get_day_night_ratio()`: returns the ratio or nil if it isn't overridden * `set_local_animation(idle, walk, dig, walk_while_dig, frame_speed)`: set animation for player model in third person view. @@ -8540,11 +8571,23 @@ child will follow movement and rotation of that bone. * Passing no arguments resets lighting to its default values. * `light_definition` is a table with the following optional fields: * `saturation` sets the saturation (vividness; default: `1.0`). - * values > 1 increase the saturation - * values in [0,1] decrease the saturation + * It is applied according to the function `result = b*(1-s) + c*s`, where: + * `c` is the original color + * `b` is the greyscale version of the color with the same luma + * `s` is the saturation set here + * The resulting color always has the same luma (perceived brightness) as the original. + * This means that: + * values > 1 oversaturate + * values < 1 down to 0 desaturate, 0 being entirely greyscale + * values < 0 cause an effect similar to inversion, + but keeping original luma and being symmetrical in terms of saturation + (eg. -1 and 1 is the same saturation and luma, but different hues) * `shadows` is a table that controls ambient shadows * `intensity` sets the intensity of the shadows from 0 (no shadows, default) to 1 (blackness) * This value has no effect on clients who have the "Dynamic Shadows" shader disabled. + * `tint` tints the shadows with the provided color, with RGB values ranging from 0 to 255. + (default `{r=0, g=0, b=0}`) + * This value has no effect on clients who have the "Dynamic Shadows" shader disabled. * `exposure` is a table that controls automatic exposure. The basic exposure factor equation is `e = 2^exposure_correction / clamp(luminance, 2^luminance_min, 2^luminance_max)` * `luminance_min` set the lower luminance boundary to use in the calculation (default: `-3.0`) @@ -9071,6 +9114,11 @@ Used by `minetest.register_abm`. -- If left out or empty, any neighbor will do. -- `group:groupname` can also be used here. + without_neighbors = {"default:lava_source", "default:lava_flowing"}, + -- Only apply `action` to nodes that have no one of these neighbors. + -- If left out or empty, it has no effect. + -- `group:groupname` can also be used here. + interval = 10.0, -- Operation interval in seconds @@ -9106,7 +9154,12 @@ Used by `minetest.register_lbm`. A loading block modifier (LBM) is used to define a function that is called for specific nodes (defined by `nodenames`) when a mapblock which contains such nodes -gets activated (not loaded!) +gets activated (not loaded!). + +Note: LBMs operate on a "snapshot" of node positions taken once before they are triggered. +That means if an LBM callback adds a node, it won't be taken into account. +However the engine guarantees that when the callback is called that all given position(s) +contain a matching node. ```lua { @@ -9130,7 +9183,13 @@ gets activated (not loaded!) action = function(pos, node, dtime_s) end, -- Function triggered for each qualifying node. -- `dtime_s` is the in-game time (in seconds) elapsed since the block - -- was last active + -- was last active. + + bulk_action = function(pos_list, dtime_s) end, + -- Function triggered with a list of all applicable node positions at once. + -- This can be provided as an alternative to `action` (not both). + -- Available since `minetest.features.bulk_lbms` (5.10.0) + -- `dtime_s`: as above } ``` @@ -9337,9 +9396,17 @@ Used by `minetest.register_node`, `minetest.register_craftitem`, and -- If specified as a table, the field to be used is selected according to -- the current `pointed_thing`. -- There are three possible TouchInteractionMode values: - -- * "user" (meaning depends on client-side settings) -- * "long_dig_short_place" (long tap = dig, short tap = place) -- * "short_dig_long_place" (short tap = dig, long tap = place) + -- * "user": + -- * For `pointed_object`: Equivalent to "short_dig_long_place" if the + -- client-side setting "touch_punch_gesture" is "short_tap" (the + -- default value) and the item is able to punch (i.e. has no on_use + -- callback defined). + -- Equivalent to "long_dig_short_place" otherwise. + -- * For `pointed_node` and `pointed_nothing`: + -- Equivalent to "long_dig_short_place". + -- * The behavior of "user" may change in the future. -- The default value is "user". sound = { @@ -11368,6 +11435,16 @@ Functions: bit.tobit, bit.tohex, bit.bnot, bit.band, bit.bor, bit.bxor, bit.lshi See http://bitop.luajit.org/ for advanced information. +Tracy Profiler +-------------- + +Minetest can be built with support for the Tracy profiler, which can also be +useful for profiling mods and is exposed to Lua as the global `tracy`. + +See doc/developing/misc.md for details. + +Note: This is a development feature and not covered by compatibility promises. + Error Handling -------------- diff --git a/doc/menu_lua_api.md b/doc/menu_lua_api.md index df14b859d..be63af904 100644 --- a/doc/menu_lua_api.md +++ b/doc/menu_lua_api.md @@ -253,8 +253,8 @@ GUI }, -- Estimated maximum formspec size before Minetest will start shrinking the - -- formspec to fit. For a fullscreen formspec, use a size 10-20% larger than - -- this and `padding[-0.01,-0.01]`. + -- formspec to fit. For a fullscreen formspec, use this formspec size and + -- `padding[0,0]`. `bgcolor[;true]` is also recommended. max_formspec_size = { x = 20, y = 11.25 diff --git a/games/devtest/mods/benchmarks/init.lua b/games/devtest/mods/benchmarks/init.lua index 1f5001c69..e3a4409a5 100644 --- a/games/devtest/mods/benchmarks/init.lua +++ b/games/devtest/mods/benchmarks/init.lua @@ -154,3 +154,36 @@ minetest.register_chatcommand("bench_bulk_get_node", { return true, msg end, }) + +minetest.register_chatcommand("bench_bulk_swap_node", { + params = "", + description = "Benchmark: Bulk-swap 99×99×99 stone nodes", + func = function(name, param) + local player = minetest.get_player_by_name(name) + if not player then + return false, "No player." + end + local pos_list = get_positions_cube(player:get_pos()) + + minetest.chat_send_player(name, "Benchmarking minetest.bulk_swap_node. Warming up ...") + + -- warm up because first execution otherwise becomes + -- significantly slower + minetest.bulk_swap_node(pos_list, {name = "mapgen_stone"}) + + minetest.chat_send_player(name, "Warming up finished, now benchmarking ...") + + local start_time = minetest.get_us_time() + for i=1,#pos_list do + minetest.swap_node(pos_list[i], {name = "mapgen_stone"}) + end + local middle_time = minetest.get_us_time() + minetest.bulk_swap_node(pos_list, {name = "mapgen_stone"}) + local end_time = minetest.get_us_time() + local msg = string.format("Benchmark results: minetest.swap_node loop: %.2f ms; minetest.bulk_swap_node: %.2f ms", + ((middle_time - start_time)) / 1000, + ((end_time - middle_time)) / 1000 + ) + return true, msg + end, +}) diff --git a/games/devtest/mods/testabms/README.md b/games/devtest/mods/testabms/README.md new file mode 100644 index 000000000..60fa6d656 --- /dev/null +++ b/games/devtest/mods/testabms/README.md @@ -0,0 +1,6 @@ +# Test ABMs + +This mod contains a nodes and related ABM actions. +By placing these nodes, you can test basic ABM behaviours. + +There are separate tests for ABM `chance`, `interval`, `min_y`, `max_y`, `neighbor` and `without_neighbor` fields. diff --git a/games/devtest/mods/testabms/after_node.lua b/games/devtest/mods/testabms/after_node.lua new file mode 100644 index 000000000..64cdfb484 --- /dev/null +++ b/games/devtest/mods/testabms/after_node.lua @@ -0,0 +1,12 @@ + +local S = minetest.get_translator("testnodes") + +-- After ABM node +minetest.register_node("testabms:after_abm", { + description = S("After ABM processed node."), + drawtype = "normal", + tiles = { "testabms_after_node.png" }, + + groups = { dig_immediate = 3 }, +}) + diff --git a/games/devtest/mods/testabms/chances.lua b/games/devtest/mods/testabms/chances.lua new file mode 100644 index 000000000..95f416b45 --- /dev/null +++ b/games/devtest/mods/testabms/chances.lua @@ -0,0 +1,56 @@ +-- test ABMs with different chances + +local S = minetest.get_translator("testnodes") + +-- ABM chance 5 node +minetest.register_node("testabms:chance_5", { + description = S("Node for test ABM chance_5"), + drawtype = "normal", + tiles = { "testabms_wait_node.png" }, + + groups = { dig_immediate = 3 }, + + on_construct = function (pos) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", "Waiting for ABM testabms:chance_5") + end, +}) + +minetest.register_abm({ + label = "testabms:chance_5", + nodenames = "testabms:chance_5", + interval = 10, + chance = 5, + action = function (pos) + minetest.swap_node(pos, {name="testabms:after_abm"}) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", "ABM testabsm:chance_5 changed this node.") + end +}) + +-- ABM chance 20 node +minetest.register_node("testabms:chance_20", { + description = S("Node for test ABM chance_20"), + drawtype = "normal", + tiles = { "testabms_wait_node.png" }, + + groups = { dig_immediate = 3 }, + + on_construct = function (pos) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", "Waiting for ABM testabms:chance_20") + end, +}) + +minetest.register_abm({ + label = "testabms:chance_20", + nodenames = "testabms:chance_20", + interval = 10, + chance = 20, + action = function (pos) + minetest.swap_node(pos, {name="testabms:after_abm"}) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", "ABM testabsm:chance_20 changed this node.") + end +}) + diff --git a/games/devtest/mods/testabms/init.lua b/games/devtest/mods/testabms/init.lua new file mode 100644 index 000000000..7830d8436 --- /dev/null +++ b/games/devtest/mods/testabms/init.lua @@ -0,0 +1,7 @@ +local path = minetest.get_modpath(minetest.get_current_modname()) + +dofile(path.."/after_node.lua") +dofile(path.."/chances.lua") +dofile(path.."/intervals.lua") +dofile(path.."/min_max.lua") +dofile(path.."/neighbors.lua") diff --git a/games/devtest/mods/testabms/intervals.lua b/games/devtest/mods/testabms/intervals.lua new file mode 100644 index 000000000..57b58faa5 --- /dev/null +++ b/games/devtest/mods/testabms/intervals.lua @@ -0,0 +1,56 @@ +-- test ABMs with different interval + +local S = minetest.get_translator("testnodes") + +-- ABM inteval 1 node +minetest.register_node("testabms:interval_1", { + description = S("Node for test ABM interval_1"), + drawtype = "normal", + tiles = { "testabms_wait_node.png" }, + + groups = { dig_immediate = 3 }, + + on_construct = function (pos) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", "Waiting for ABM testabms:interval_1") + end, +}) + +minetest.register_abm({ + label = "testabms:interval_1", + nodenames = "testabms:interval_1", + interval = 1, + chance = 1, + action = function (pos) + minetest.swap_node(pos, {name="testabms:after_abm"}) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", "ABM testabsm:interval_1 changed this node.") + end +}) + +-- ABM interval 60 node +minetest.register_node("testabms:interval_60", { + description = S("Node for test ABM interval_60"), + drawtype = "normal", + tiles = { "testabms_wait_node.png" }, + + groups = { dig_immediate = 3 }, + + on_construct = function (pos) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", "Waiting for ABM testabms:interval_60") + end, +}) + +minetest.register_abm({ + label = "testabms:interval_60", + nodenames = "testabms:interval_60", + interval = 60, + chance = 1, + action = function (pos) + minetest.swap_node(pos, {name="testabms:after_abm"}) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", "ABM testabsm:interval_60 changed this node.") + end +}) + diff --git a/games/devtest/mods/testabms/min_max.lua b/games/devtest/mods/testabms/min_max.lua new file mode 100644 index 000000000..62f1ccd53 --- /dev/null +++ b/games/devtest/mods/testabms/min_max.lua @@ -0,0 +1,58 @@ +-- test ABMs with min_y and max_y + +local S = minetest.get_translator("testnodes") + +-- ABM min_y node +minetest.register_node("testabms:min_y", { + description = S("Node for test ABM min_y."), + drawtype = "normal", + tiles = { "testabms_wait_node.png" }, + + groups = { dig_immediate = 3 }, + + on_construct = function (pos) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", "Waiting for ABM testabms:min_y at y "..pos.y.." with min_y = 0") + end, +}) + +minetest.register_abm({ + label = "testabms:min_y", + nodenames = "testabms:min_y", + interval = 10, + chance = 1, + min_y = 0, + action = function (pos) + minetest.swap_node(pos, {name="testabms:after_abm"}) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", "ABM testabsm:min_y changed this node.") + end +}) + +-- ABM max_y node +minetest.register_node("testabms:max_y", { + description = S("Node for test ABM max_y."), + drawtype = "normal", + tiles = { "testabms_wait_node.png" }, + + groups = { dig_immediate = 3 }, + + on_construct = function (pos) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", "Waiting for ABM testabms:max_y at y "..pos.y.." with max_y = 0") + end, +}) + +minetest.register_abm({ + label = "testabms:max_y", + nodenames = "testabms:max_y", + interval = 10, + chance = 1, + max_y = 0, + action = function (pos) + minetest.swap_node(pos, {name="testabms:after_abm"}) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", "ABM testabsm:max_y changed this node.") + end +}) + diff --git a/games/devtest/mods/testabms/mod.conf b/games/devtest/mods/testabms/mod.conf new file mode 100644 index 000000000..ad74cd2ed --- /dev/null +++ b/games/devtest/mods/testabms/mod.conf @@ -0,0 +1,2 @@ +name = testabms +description = Contains some nodes for test ABMs. diff --git a/games/devtest/mods/testabms/neighbors.lua b/games/devtest/mods/testabms/neighbors.lua new file mode 100644 index 000000000..42ce47dff --- /dev/null +++ b/games/devtest/mods/testabms/neighbors.lua @@ -0,0 +1,99 @@ +-- test ABMs with neighbor and without_neighbor + +local S = minetest.get_translator("testnodes") + +-- ABM required neighbor +minetest.register_node("testabms:required_neighbor", { + description = S("Node for test ABM required_neighbor.") .. "\n" + .. S("Sensitive neighbor node is testnodes:normal."), + drawtype = "normal", + tiles = { "testabms_wait_node.png" }, + + groups = { dig_immediate = 3 }, + + on_construct = function (pos) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", + "Waiting for ABM testabms:required_neighbor " + .. "(normal drawtype testnode sensitive)") + end, +}) + +minetest.register_abm({ + label = "testabms:required_neighbor", + nodenames = "testabms:required_neighbor", + neighbors = {"testnodes:normal"}, + interval = 1, + chance = 1, + action = function (pos) + minetest.swap_node(pos, {name="testabms:after_abm"}) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", + "ABM testabsm:required_neighbor changed this node.") + end +}) + +-- ABM missing neighbor node +minetest.register_node("testabms:missing_neighbor", { + description = S("Node for test ABM missing_neighbor.") .. "\n" + .. S("Sensitive neighbor node is testnodes:normal."), + drawtype = "normal", + tiles = { "testabms_wait_node.png" }, + + groups = { dig_immediate = 3 }, + + on_construct = function (pos) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", + "Waiting for ABM testabms:missing_neighbor" + .. " (normal drawtype testnode sensitive)") + end, +}) + +minetest.register_abm({ + label = "testabms:missing_neighbor", + nodenames = "testabms:missing_neighbor", + without_neighbors = {"testnodes:normal"}, + interval = 1, + chance = 1, + action = function (pos) + minetest.swap_node(pos, {name="testabms:after_abm"}) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", + "ABM testabsm:missing_neighbor changed this node.") + end +}) + +-- ABM required and missing neighbor node +minetest.register_node("testabms:required_missing_neighbor", { + description = S("Node for test ABM required_missing_neighbor.") .. "\n" + .. S("Sensitive neighbor nodes are testnodes:normal and testnodes:glasslike."), + drawtype = "normal", + tiles = { "testabms_wait_node.png" }, + + groups = { dig_immediate = 3 }, + + on_construct = function (pos) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", + "Waiting for ABM testabms:required_missing_neighbor" + .. " (wint normal drawtype testnode and no glasslike" + .. " drawtype testnode sensitive)") + end, +}) + +minetest.register_abm({ + label = "testabms:required_missing_neighbor", + nodenames = "testabms:required_missing_neighbor", + neighbors = {"testnodes:normal"}, + without_neighbors = {"testnodes:glasslike"}, + interval = 1, + chance = 1, + action = function (pos) + minetest.swap_node(pos, {name="testabms:after_abm"}) + local meta = minetest.get_meta(pos) + meta:set_string("infotext", + "ABM testabsm:required_missing_neighbor changed this node.") + end +}) + diff --git a/games/devtest/mods/testabms/textures/testabms_after_node.png b/games/devtest/mods/testabms/textures/testabms_after_node.png new file mode 100644 index 000000000..dab87594b Binary files /dev/null and b/games/devtest/mods/testabms/textures/testabms_after_node.png differ diff --git a/games/devtest/mods/testabms/textures/testabms_wait_node.png b/games/devtest/mods/testabms/textures/testabms_wait_node.png new file mode 100644 index 000000000..a9bd9a36f Binary files /dev/null and b/games/devtest/mods/testabms/textures/testabms_wait_node.png differ diff --git a/games/devtest/mods/testformspec/formspec.lua b/games/devtest/mods/testformspec/formspec.lua index 99ee691f1..8d0b759f5 100644 --- a/games/devtest/mods/testformspec/formspec.lua +++ b/games/devtest/mods/testformspec/formspec.lua @@ -66,7 +66,7 @@ local inv_style_fs = [[ -- Some textures from textures/base/pack and Devtest, with many different sizes -- and aspect ratios. -local image_column = "image,0=logo.png,1=rare_controls.png,2=checkbox_16.png," .. +local image_column = "image,0=logo.png,1=crack_anylength.png^[invert:rgb,2=checkbox_16.png," .. "3=checkbox_32.png,4=checkbox_64.png,5=default_lava.png," .. "6=progress_bar.png,7=progress_bar_bg.png" local words = { diff --git a/games/devtest/mods/testfullscreenfs/init.lua b/games/devtest/mods/testfullscreenfs/init.lua index e1af3ae33..7abc7f817 100644 --- a/games/devtest/mods/testfullscreenfs/init.lua +++ b/games/devtest/mods/testfullscreenfs/init.lua @@ -1,18 +1,30 @@ -local function show_fullscreen_fs(name) - local window = minetest.get_player_window_information(name) - if not window then - return false, "Unable to get window info" - end +local function window_info_equal(a, b) + return + -- size + a.size.x == b.size.x and a.size.y == b.size.y and + -- real_gui_scaling, real_hud_scaling + a.real_gui_scaling == b.real_gui_scaling and + a.real_hud_scaling == b.real_hud_scaling and + -- max_formspec_size + a.max_formspec_size.x == b.max_formspec_size.x and + a.max_formspec_size.y == b.max_formspec_size.y and + -- touch_controls + a.touch_controls == b.touch_controls +end +local last_window_info = {} + +local function show_fullscreen_fs(name, window) print(dump(window)) - local size = { x = window.max_formspec_size.x * 1.1, y = window.max_formspec_size.y * 1.1 } + local size = window.max_formspec_size local touch_text = window.touch_controls and "Touch controls enabled" or "Touch controls disabled" local fs = { "formspec_version[4]", ("size[%f,%f]"):format(size.x, size.y), - "padding[-0.01,-0.01]", + "padding[0,0]", + "bgcolor[;true]", ("button[%f,%f;1,1;%s;%s]"):format(0, 0, "tl", "TL"), ("button[%f,%f;1,1;%s;%s]"):format(size.x - 1, 0, "tr", "TR"), ("button[%f,%f;1,1;%s;%s]"):format(size.x - 1, size.y - 1, "br", "BR"), @@ -23,10 +35,37 @@ local function show_fullscreen_fs(name) } minetest.show_formspec(name, "testfullscreenfs:fs", table.concat(fs)) - return true, ("Calculated size of %f, %f"):format(size.x, size.y) + minetest.chat_send_player(name, ("Calculated size of %f, %f"):format(size.x, size.y)) + last_window_info[name] = window end - minetest.register_chatcommand("testfullscreenfs", { - func = show_fullscreen_fs, + func = function(name) + local window = minetest.get_player_window_information(name) + if not window then + return false, "Unable to get window info" + end + + show_fullscreen_fs(name, window) + return true + end, }) + +minetest.register_globalstep(function() + for name, last_window in pairs(last_window_info) do + local window = minetest.get_player_window_information(name) + if window and not window_info_equal(last_window, window) then + show_fullscreen_fs(name, window) + end + end +end) + +minetest.register_on_player_receive_fields(function(player, formname, fields) + if formname == "testfullscreenfs:fs" and fields.quit then + last_window_info[player:get_player_name()] = nil + end +end) + +minetest.register_on_leaveplayer(function(player) + last_window_info[player:get_player_name()] = nil +end) diff --git a/games/devtest/mods/testnodes/drawtypes.lua b/games/devtest/mods/testnodes/drawtypes.lua index 087d09eff..4a657b739 100644 --- a/games/devtest/mods/testnodes/drawtypes.lua +++ b/games/devtest/mods/testnodes/drawtypes.lua @@ -98,6 +98,23 @@ minetest.register_node("testnodes:allfaces", { groups = { dig_immediate = 3 }, }) +minetest.register_node("testnodes:allfaces_6", { + description = S("\"allfaces 6 Textures\" Drawtype Test Node").."\n".. + S("Transparent node with visible internal backfaces"), + drawtype = "allfaces", + paramtype = "light", + tiles = { + "testnodes_allfaces.png^[colorize:red", + "testnodes_allfaces.png^[colorize:orange", + "testnodes_allfaces.png^[colorize:yellow", + "testnodes_allfaces.png^[colorize:green", + "testnodes_allfaces.png^[colorize:blue", + "testnodes_allfaces.png^[colorize:purple" + }, + + groups = { dig_immediate = 3 }, +}) + local allfaces_optional_tooltip = "".. S("Rendering depends on 'leaves_style' setting:").."\n".. S("* 'fancy': transparent with visible internal backfaces").."\n".. diff --git a/games/devtest/mods/unittests/color.lua b/games/devtest/mods/unittests/color.lua new file mode 100644 index 000000000..86154445c --- /dev/null +++ b/games/devtest/mods/unittests/color.lua @@ -0,0 +1,17 @@ +local function assert_colors_equal(c1, c2) + if type(c1) == "table" and type(c2) == "table" then + assert(c1.r == c2.r and c1.g == c2.g and c1.b == c2.b and c1.a == c2.a) + else + assert(c1 == c2) + end +end + +local function test_color_conversion() + assert_colors_equal(core.colorspec_to_table("#fff"), {r = 255, g = 255, b = 255, a = 255}) + assert_colors_equal(core.colorspec_to_table(0xFF00FF00), {r = 0, g = 255, b = 0, a = 255}) + assert_colors_equal(core.colorspec_to_table("#00000000"), {r = 0, g = 0, b = 0, a = 0}) + assert_colors_equal(core.colorspec_to_table("green"), {r = 0, g = 128, b = 0, a = 255}) + assert_colors_equal(core.colorspec_to_table("gren"), nil) +end + +unittests.register("test_color_conversion", test_color_conversion) diff --git a/games/devtest/mods/unittests/init.lua b/games/devtest/mods/unittests/init.lua index eae003a2a..a967a986f 100644 --- a/games/devtest/mods/unittests/init.lua +++ b/games/devtest/mods/unittests/init.lua @@ -187,6 +187,7 @@ dofile(modpath .. "/raycast.lua") dofile(modpath .. "/inventory.lua") dofile(modpath .. "/load_time.lua") dofile(modpath .. "/on_shutdown.lua") +dofile(modpath .. "/color.lua") -------------- diff --git a/games/devtest/mods/unittests/player.lua b/games/devtest/mods/unittests/player.lua index 7650d5f57..f8945f320 100644 --- a/games/devtest/mods/unittests/player.lua +++ b/games/devtest/mods/unittests/player.lua @@ -42,41 +42,97 @@ unittests.register("test_hpchangereason", run_hpchangereason_tests, {player=true -- local expected_diff = nil +local hpchange_counter = 0 +local die_counter = 0 core.register_on_player_hpchange(function(player, hp_change, reason) if expected_diff then assert(hp_change == expected_diff) + hpchange_counter = hpchange_counter + 1 end end) +core.register_on_dieplayer(function() + die_counter = die_counter + 1 +end) + +local function hp_diference_test(player, hp_max) + assert(hp_max >= 22) -local function run_hp_difference_tests(player) local old_hp = player:get_hp() local old_hp_max = player:get_properties().hp_max - expected_diff = nil - player:set_properties({hp_max = 30}) - player:set_hp(22) + hpchange_counter = 0 + die_counter = 0 - -- final HP value is clamped to >= 0 before difference calculation - expected_diff = -22 + expected_diff = nil + player:set_properties({hp_max = hp_max}) + player:set_hp(22) + assert(player:get_hp() == 22) + assert(hpchange_counter == 0) + assert(die_counter == 0) + + -- HP difference is not clamped + expected_diff = -25 player:set_hp(-3) - -- and actual final HP value is clamped to >= 0 too + -- actual final HP value is clamped to >= 0 assert(player:get_hp() == 0) + assert(hpchange_counter == 1) + assert(die_counter == 1) expected_diff = 22 player:set_hp(22) assert(player:get_hp() == 22) + assert(hpchange_counter == 2) + assert(die_counter == 1) - -- final HP value is clamped to <= U16_MAX before difference calculation - expected_diff = 65535 - 22 + -- Integer overflow is prevented + -- so result is S32_MIN, not S32_MIN - 22 + expected_diff = -2147483648 + player:set_hp(-2147483648) + -- actual final HP value is clamped to >= 0 + assert(player:get_hp() == 0) + assert(hpchange_counter == 3) + assert(die_counter == 2) + + -- Damage is ignored if player is already dead (hp == 0) + expected_diff = "never equal" + player:set_hp(-11) + assert(player:get_hp() == 0) + -- no on_player_hpchange or on_dieplayer call expected + assert(hpchange_counter == 3) + assert(die_counter == 2) + + expected_diff = 11 + player:set_hp(11) + assert(player:get_hp() == 11) + assert(hpchange_counter == 4) + assert(die_counter == 2) + + -- HP difference is not clamped + expected_diff = 1000000 - 11 player:set_hp(1000000) - -- and actual final HP value is clamped to <= hp_max - assert(player:get_hp() == 30) + -- actual final HP value is clamped to <= hp_max + assert(player:get_hp() == hp_max) + assert(hpchange_counter == 5) + assert(die_counter == 2) + + -- "Healing" is not ignored when hp == hp_max + expected_diff = 80000 - hp_max + player:set_hp(80000) + assert(player:get_hp() == hp_max) + -- on_player_hpchange_call expected + assert(hpchange_counter == 6) + assert(die_counter == 2) expected_diff = nil player:set_properties({hp_max = old_hp_max}) player:set_hp(old_hp) core.close_formspec(player:get_player_name(), "") -- hide death screen end +local function run_hp_difference_tests(player) + hp_diference_test(player, 22) + hp_diference_test(player, 30) + hp_diference_test(player, 65535) -- U16_MAX +end unittests.register("test_hp_difference", run_hp_difference_tests, {player=true}) -- diff --git a/irr/include/CIndexBuffer.h b/irr/include/CIndexBuffer.h index 4318ddaab..904b0ab9a 100644 --- a/irr/include/CIndexBuffer.h +++ b/irr/include/CIndexBuffer.h @@ -7,6 +7,13 @@ #include #include "IIndexBuffer.h" +// Define to receive warnings when violating the hw mapping hints +//#define INDEXBUFFER_HINT_DEBUG + +#ifdef INDEXBUFFER_HINT_DEBUG +#include "../src/os.h" +#endif + namespace irr { namespace scene @@ -58,6 +65,13 @@ public: void setDirty() override { ++ChangedID; +#ifdef INDEXBUFFER_HINT_DEBUG + if (MappingHint == EHM_STATIC && HWBuffer) { + char buf[100]; + snprintf_irr(buf, sizeof(buf), "CIndexBuffer @ %p modified, but it has a static hint", this); + os::Printer::log(buf, ELL_WARNING); + } +#endif } u32 getChangedID() const override { return ChangedID; } diff --git a/irr/include/CVertexBuffer.h b/irr/include/CVertexBuffer.h index 3559bbddb..4b3f33688 100644 --- a/irr/include/CVertexBuffer.h +++ b/irr/include/CVertexBuffer.h @@ -7,6 +7,13 @@ #include #include "IVertexBuffer.h" +// Define to receive warnings when violating the hw mapping hints +//#define VERTEXBUFFER_HINT_DEBUG + +#ifdef VERTEXBUFFER_HINT_DEBUG +#include "../src/os.h" +#endif + namespace irr { namespace scene @@ -87,6 +94,13 @@ public: void setDirty() override { ++ChangedID; +#ifdef VERTEXBUFFER_HINT_DEBUG + if (MappingHint == EHM_STATIC && HWBuffer) { + char buf[100]; + snprintf_irr(buf, sizeof(buf), "CVertexBuffer @ %p modified, but it has a static hint", this); + os::Printer::log(buf, ELL_WARNING); + } +#endif } u32 getChangedID() const override { return ChangedID; } diff --git a/irr/include/EMaterialProps.h b/irr/include/EMaterialProps.h index 6f37c95b8..765084340 100644 --- a/irr/include/EMaterialProps.h +++ b/irr/include/EMaterialProps.h @@ -18,12 +18,6 @@ enum E_MATERIAL_PROP //! Corresponds to SMaterial::PointCloud. EMP_POINTCLOUD = 0x2, - //! Corresponds to SMaterial::GouraudShading. - EMP_GOURAUD_SHADING = 0x4, - - //! Corresponds to SMaterial::Lighting. - EMP_LIGHTING = 0x8, - //! Corresponds to SMaterial::ZBuffer. EMP_ZBUFFER = 0x10, @@ -48,9 +42,6 @@ enum E_MATERIAL_PROP //! Corresponds to SMaterial::FogEnable. EMP_FOG_ENABLE = 0x800, - //! Corresponds to SMaterial::NormalizeNormals. - EMP_NORMALIZE_NORMALS = 0x1000, - //! Corresponds to SMaterialLayer::TextureWrapU, TextureWrapV and //! TextureWrapW. EMP_TEXTURE_WRAP = 0x2000, @@ -61,9 +52,6 @@ enum E_MATERIAL_PROP //! Corresponds to SMaterial::ColorMask. EMP_COLOR_MASK = 0x8000, - //! Corresponds to SMaterial::ColorMaterial. - EMP_COLOR_MATERIAL = 0x10000, - //! Corresponds to SMaterial::UseMipMaps. EMP_USE_MIP_MAPS = 0x20000, diff --git a/irr/include/IGUISkin.h b/irr/include/IGUISkin.h index 36b510606..b323983ae 100644 --- a/irr/include/IGUISkin.h +++ b/irr/include/IGUISkin.h @@ -437,6 +437,10 @@ public: virtual void draw3DButtonPaneStandard(IGUIElement *element, const core::rect &rect, const core::rect *clip = 0) = 0; + virtual void drawColored3DButtonPaneStandard(IGUIElement* element, + const core::rect& rect, + const core::rect* clip=0, + const video::SColor* colors=0) = 0; //! draws a pressed 3d button pane /** Used for drawing for example buttons in pressed state. @@ -450,6 +454,10 @@ public: virtual void draw3DButtonPanePressed(IGUIElement *element, const core::rect &rect, const core::rect *clip = 0) = 0; + virtual void drawColored3DButtonPanePressed(IGUIElement* element, + const core::rect& rect, + const core::rect* clip=0, + const video::SColor* colors=0) = 0; //! draws a sunken 3d pane /** Used for drawing the background of edit, combo or check boxes. diff --git a/irr/include/IReferenceCounted.h b/irr/include/IReferenceCounted.h index e1000d389..68aa20fb6 100644 --- a/irr/include/IReferenceCounted.h +++ b/irr/include/IReferenceCounted.h @@ -42,7 +42,7 @@ class IReferenceCounted public: //! Constructor. IReferenceCounted() : - DebugName(0), ReferenceCounter(1) + ReferenceCounter(1) { } @@ -136,6 +136,7 @@ public: return ReferenceCounter; } +#ifdef _DEBUG //! Returns the debug name of the object. /** The Debugname may only be set and changed by the object itself. This method should only be used in Debug mode. @@ -157,7 +158,10 @@ protected: private: //! The debug name. - const c8 *DebugName; + const c8 *DebugName = nullptr; +#endif + +private: //! The reference counter. Mutable to do reference counting on const objects. mutable s32 ReferenceCounter; diff --git a/irr/include/IVideoDriver.h b/irr/include/IVideoDriver.h index b3312160c..af8d97fef 100644 --- a/irr/include/IVideoDriver.h +++ b/irr/include/IVideoDriver.h @@ -54,6 +54,8 @@ const c8 *const FogTypeNames[] = { }; struct SFrameStats { + //! Number of draw calls + u32 Drawcalls = 0; //! Count of primitives drawn u32 PrimitivesDrawn = 0; //! Number of hardware buffers uploaded (new or updated) diff --git a/irr/include/SMaterial.h b/irr/include/SMaterial.h index 8f24b9984..cceccad78 100644 --- a/irr/include/SMaterial.h +++ b/irr/include/SMaterial.h @@ -194,29 +194,6 @@ enum E_ANTI_ALIASING_MODE EAAM_ALPHA_TO_COVERAGE = 4 }; -//! These flags allow to define the interpretation of vertex color when lighting is enabled -/** Without lighting being enabled the vertex color is the only value defining the fragment color. -Once lighting is enabled, the four values for diffuse, ambient, emissive, and specular take over. -With these flags it is possible to define which lighting factor shall be defined by the vertex color -instead of the lighting factor which is the same for all faces of that material. -The default is to use vertex color for the diffuse value, another pretty common value is to use -vertex color for both diffuse and ambient factor. */ -enum E_COLOR_MATERIAL -{ - //! Don't use vertex color for lighting - ECM_NONE = 0, - //! Use vertex color for diffuse light, this is default - ECM_DIFFUSE, - //! Use vertex color for ambient light - ECM_AMBIENT, - //! Use vertex color for emissive light - ECM_EMISSIVE, - //! Use vertex color for specular light - ECM_SPECULAR, - //! Use vertex color for both diffuse and ambient light - ECM_DIFFUSE_AND_AMBIENT -}; - //! Names for polygon offset direction const c8 *const PolygonOffsetDirectionNames[] = { "Back", @@ -262,16 +239,14 @@ class SMaterial public: //! Default constructor. Creates a solid, lit material with white colors SMaterial() : - MaterialType(EMT_SOLID), AmbientColor(255, 255, 255, 255), - DiffuseColor(255, 255, 255, 255), EmissiveColor(0, 0, 0, 0), - SpecularColor(255, 255, 255, 255), Shininess(0.0f), + MaterialType(EMT_SOLID), ColorParam(0, 0, 0, 0), MaterialTypeParam(0.0f), Thickness(1.0f), ZBuffer(ECFN_LESSEQUAL), - AntiAliasing(EAAM_SIMPLE), ColorMask(ECP_ALL), ColorMaterial(ECM_DIFFUSE), + AntiAliasing(EAAM_SIMPLE), ColorMask(ECP_ALL), BlendOperation(EBO_NONE), BlendFactor(0.0f), PolygonOffsetDepthBias(0.f), PolygonOffsetSlopeScale(0.f), Wireframe(false), PointCloud(false), - GouraudShading(true), Lighting(true), ZWriteEnable(EZW_AUTO), + ZWriteEnable(EZW_AUTO), BackfaceCulling(true), FrontfaceCulling(false), FogEnable(false), - NormalizeNormals(false), UseMipMaps(true) + UseMipMaps(true) { } @@ -281,42 +256,9 @@ public: //! Type of the material. Specifies how everything is blended together E_MATERIAL_TYPE MaterialType; - //! How much ambient light (a global light) is reflected by this material. - /** The default is full white, meaning objects are completely - globally illuminated. Reduce this if you want to see diffuse - or specular light effects. */ - SColor AmbientColor; - - //! How much diffuse light coming from a light source is reflected by this material. - /** The default is full white. */ - SColor DiffuseColor; - - //! Light emitted by this material. Default is to emit no light. - SColor EmissiveColor; - - //! How much specular light (highlights from a light) is reflected. - /** The default is to reflect white specular light. See - SMaterial::Shininess on how to enable specular lights. */ - SColor SpecularColor; - - //! Value affecting the size of specular highlights. - /** A value of 20 is common. If set to 0, no specular - highlights are being used. To activate, simply set the - shininess of a material to a value in the range [0.5;128]: - \code - sceneNode->getMaterial(0).Shininess = 20.0f; - \endcode - - You can change the color of the highlights using - \code - sceneNode->getMaterial(0).SpecularColor.set(255,255,255,255); - \endcode - - The specular color of the dynamic lights - (SLight::SpecularColor) will influence the the highlight color - too, but they are set to a useful value by default when - creating the light scene node.*/ - f32 Shininess; + //! Custom color parameter, can be used by custom shader materials. + // See MainShaderConstantSetter in Minetest. + SColor ColorParam; //! Free parameter, dependent on the material type. /** Mostly ignored, used for example in @@ -344,14 +286,6 @@ public: depth or stencil buffer, or using Red and Green for Stereo rendering. */ u8 ColorMask : 4; - //! Defines the interpretation of vertex color in the lighting equation - /** Values should be chosen from E_COLOR_MATERIAL. - When lighting is enabled, vertex color can be used instead of the - material values for light modulation. This allows to easily change e.g. the - diffuse light behavior of each face. The default, ECM_DIFFUSE, will result in - a very similar rendering as with lighting turned off, just with light shading. */ - u8 ColorMaterial : 3; - //! Store the blend operation of choice /** Values to be chosen from E_BLEND_OPERATION. */ E_BLEND_OPERATION BlendOperation : 4; @@ -392,12 +326,6 @@ public: //! Draw as point cloud or filled triangles? Default: false bool PointCloud : 1; - //! Flat or Gouraud shading? Default: true - bool GouraudShading : 1; - - //! Will this material be lighted? Default: true - bool Lighting : 1; - //! Is the zbuffer writable or is it read-only. Default: EZW_AUTO. /** If this parameter is not EZW_OFF, you probably also want to set ZBuffer to values other than ECFN_DISABLED */ @@ -412,10 +340,6 @@ public: //! Is fog enabled? Default: false bool FogEnable : 1; - //! Should normals be normalized? - /** Always use this if the mesh lit and scaled. Default: false */ - bool NormalizeNormals : 1; - //! Shall mipmaps be used if available /** Sometimes, disabling mipmap usage can be useful. Default: true */ bool UseMipMaps : 1; @@ -486,26 +410,17 @@ public: { bool different = MaterialType != b.MaterialType || - AmbientColor != b.AmbientColor || - DiffuseColor != b.DiffuseColor || - EmissiveColor != b.EmissiveColor || - SpecularColor != b.SpecularColor || - Shininess != b.Shininess || MaterialTypeParam != b.MaterialTypeParam || Thickness != b.Thickness || Wireframe != b.Wireframe || PointCloud != b.PointCloud || - GouraudShading != b.GouraudShading || - Lighting != b.Lighting || ZBuffer != b.ZBuffer || ZWriteEnable != b.ZWriteEnable || BackfaceCulling != b.BackfaceCulling || FrontfaceCulling != b.FrontfaceCulling || FogEnable != b.FogEnable || - NormalizeNormals != b.NormalizeNormals || AntiAliasing != b.AntiAliasing || ColorMask != b.ColorMask || - ColorMaterial != b.ColorMaterial || BlendOperation != b.BlendOperation || BlendFactor != b.BlendFactor || PolygonOffsetDepthBias != b.PolygonOffsetDepthBias || diff --git a/irr/include/SOverrideMaterial.h b/irr/include/SOverrideMaterial.h index c52a55b55..1ae324211 100644 --- a/irr/include/SOverrideMaterial.h +++ b/irr/include/SOverrideMaterial.h @@ -98,12 +98,6 @@ struct SOverrideMaterial case EMP_POINTCLOUD: material.PointCloud = Material.PointCloud; break; - case EMP_GOURAUD_SHADING: - material.GouraudShading = Material.GouraudShading; - break; - case EMP_LIGHTING: - material.Lighting = Material.Lighting; - break; case EMP_ZBUFFER: material.ZBuffer = Material.ZBuffer; break; @@ -140,9 +134,6 @@ struct SOverrideMaterial case EMP_FOG_ENABLE: material.FogEnable = Material.FogEnable; break; - case EMP_NORMALIZE_NORMALS: - material.NormalizeNormals = Material.NormalizeNormals; - break; case EMP_TEXTURE_WRAP: for (u32 i = 0; i < MATERIAL_MAX_TEXTURES; ++i) { if (EnableLayerProps[i]) { @@ -158,9 +149,6 @@ struct SOverrideMaterial case EMP_COLOR_MASK: material.ColorMask = Material.ColorMask; break; - case EMP_COLOR_MATERIAL: - material.ColorMaterial = Material.ColorMaterial; - break; case EMP_USE_MIP_MAPS: material.UseMipMaps = Material.UseMipMaps; break; diff --git a/src/irr_ptr.h b/irr/include/irr_ptr.h similarity index 87% rename from src/irr_ptr.h rename to irr/include/irr_ptr.h index fc4a0f558..48717976b 100644 --- a/src/irr_ptr.h +++ b/irr/include/irr_ptr.h @@ -20,8 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include #include -#include "irrlichttypes.h" -#include "IReferenceCounted.h" +namespace irr { class IReferenceCounted; } /** Shared pointer for IrrLicht objects. * @@ -37,15 +36,13 @@ with this program; if not, write to the Free Software Foundation, Inc., * from such object is a bug and may lead to a crash. Indirect construction * is possible though; see the @c grab free function for details and use cases. */ -template ::value>::type> +template class irr_ptr { ReferenceCounted *value = nullptr; public: - irr_ptr() {} + irr_ptr() noexcept = default; irr_ptr(std::nullptr_t) noexcept {} @@ -53,15 +50,15 @@ public: irr_ptr(irr_ptr &&b) noexcept { reset(b.release()); } - template ::value>::type> + template , bool> = true> irr_ptr(const irr_ptr &b) noexcept { grab(b.get()); } - template ::value>::type> + template , bool> = true> irr_ptr(irr_ptr &&b) noexcept { reset(b.release()); @@ -88,16 +85,16 @@ public: return *this; } - template ::value>::type> + template , bool> = true> irr_ptr &operator=(const irr_ptr &b) noexcept { grab(b.get()); return *this; } - template ::value>::type> + template , bool> = true> irr_ptr &operator=(irr_ptr &&b) noexcept { reset(b.release()); @@ -128,6 +125,8 @@ public: */ void reset(ReferenceCounted *object = nullptr) noexcept { + static_assert(std::is_base_of_v, + "Class is not an IReferenceCounted"); if (value) value->drop(); value = object; @@ -138,6 +137,8 @@ public: */ void grab(ReferenceCounted *object) noexcept { + static_assert(std::is_base_of_v, + "Class is not an IReferenceCounted"); if (object) object->grab(); reset(object); @@ -152,6 +153,7 @@ public: * in this function and decreased when the returned pointer is destroyed. */ template +[[nodiscard]] irr_ptr grab(ReferenceCounted *object) noexcept { irr_ptr ptr; diff --git a/irr/src/CAnimatedMeshSceneNode.cpp b/irr/src/CAnimatedMeshSceneNode.cpp index c3a71f943..295d408f3 100644 --- a/irr/src/CAnimatedMeshSceneNode.cpp +++ b/irr/src/CAnimatedMeshSceneNode.cpp @@ -258,7 +258,6 @@ void CAnimatedMeshSceneNode::render() // for debug purposes only: if (DebugDataVisible && PassCount == 1) { video::SMaterial debug_mat; - debug_mat.Lighting = false; debug_mat.AntiAliasing = 0; driver->setMaterial(debug_mat); // show normals @@ -280,7 +279,6 @@ void CAnimatedMeshSceneNode::render() } debug_mat.ZBuffer = video::ECFN_DISABLED; - debug_mat.Lighting = false; driver->setMaterial(debug_mat); if (DebugDataVisible & scene::EDS_BBOX) @@ -316,7 +314,6 @@ void CAnimatedMeshSceneNode::render() // show mesh if (DebugDataVisible & scene::EDS_MESH_WIRE_OVERLAY) { - debug_mat.Lighting = false; debug_mat.Wireframe = true; debug_mat.ZBuffer = video::ECFN_DISABLED; driver->setMaterial(debug_mat); diff --git a/irr/src/CB3DMeshFileLoader.cpp b/irr/src/CB3DMeshFileLoader.cpp index 4cd7b1d82..4d78860b2 100644 --- a/irr/src/CB3DMeshFileLoader.cpp +++ b/irr/src/CB3DMeshFileLoader.cpp @@ -485,7 +485,8 @@ bool CB3DMeshFileLoader::readChunkTRIS(scene::SSkinMeshBuffer *meshBuffer, u32 m video::S3DVertex *Vertex = meshBuffer->getVertex(meshBuffer->getVertexCount() - 1); if (!HasVertexColors) - Vertex->Color = B3dMaterial->Material.DiffuseColor; + Vertex->Color = video::SColorf(B3dMaterial->red, B3dMaterial->green, + B3dMaterial->blue, B3dMaterial->alpha).toSColor(); else if (Vertex->Color.getAlpha() == 255) Vertex->Color.setAlpha((s32)(B3dMaterial->alpha * 255.0f)); @@ -890,23 +891,8 @@ bool CB3DMeshFileLoader::readChunkBRUS() } } - B3dMaterial.Material.DiffuseColor = video::SColorf(B3dMaterial.red, B3dMaterial.green, B3dMaterial.blue, B3dMaterial.alpha).toSColor(); - B3dMaterial.Material.ColorMaterial = video::ECM_NONE; - //------ Material fx ------ - if (B3dMaterial.fx & 1) { // full-bright - B3dMaterial.Material.AmbientColor = video::SColor(255, 255, 255, 255); - B3dMaterial.Material.Lighting = false; - } else - B3dMaterial.Material.AmbientColor = B3dMaterial.Material.DiffuseColor; - - if (B3dMaterial.fx & 2) // use vertex colors instead of brush color - B3dMaterial.Material.ColorMaterial = video::ECM_DIFFUSE_AND_AMBIENT; - - if (B3dMaterial.fx & 4) // flatshaded - B3dMaterial.Material.GouraudShading = false; - if (B3dMaterial.fx & 16) // disable backface culling B3dMaterial.Material.BackfaceCulling = false; @@ -914,8 +900,6 @@ bool CB3DMeshFileLoader::readChunkBRUS() B3dMaterial.Material.MaterialType = video::EMT_TRANSPARENT_VERTEX_ALPHA; B3dMaterial.Material.ZWriteEnable = video::EZW_OFF; } - - B3dMaterial.Material.Shininess = B3dMaterial.shininess; } B3dStack.erase(B3dStack.size() - 1); diff --git a/irr/src/CBillboardSceneNode.cpp b/irr/src/CBillboardSceneNode.cpp index db75a2af4..1c6d88dae 100644 --- a/irr/src/CBillboardSceneNode.cpp +++ b/irr/src/CBillboardSceneNode.cpp @@ -85,7 +85,6 @@ void CBillboardSceneNode::render() if (DebugDataVisible & scene::EDS_BBOX) { driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); video::SMaterial m; - m.Lighting = false; driver->setMaterial(m); driver->draw3DBox(BBoxSafe, video::SColor(0, 208, 195, 152)); } diff --git a/irr/src/CGUISkin.cpp b/irr/src/CGUISkin.cpp index 84ceaeabf..e9721a5fa 100644 --- a/irr/src/CGUISkin.cpp +++ b/irr/src/CGUISkin.cpp @@ -1,4 +1,6 @@ // Copyright (C) 2002-2012 Nikolaus Gebhardt +// Copyright (C) 2019 Irrlick +// // This file is part of the "Irrlicht Engine". // For conditions of distribution and use, see copyright notice in irrlicht.h @@ -15,39 +17,41 @@ namespace irr namespace gui { -CGUISkin::CGUISkin(EGUI_SKIN_TYPE type, video::IVideoDriver *driver) : - SpriteBank(0), Driver(driver), Type(type) +CGUISkin::CGUISkin(EGUI_SKIN_TYPE type, video::IVideoDriver* driver) +: SpriteBank(0), Driver(driver), Type(type) { -#ifdef _DEBUG + #ifdef _DEBUG setDebugName("CGUISkin"); -#endif + #endif + + if ((Type == EGST_WINDOWS_CLASSIC) || (Type == EGST_WINDOWS_METALLIC)) + { + Colors[EGDC_3D_DARK_SHADOW] = video::SColor(101,50,50,50); + Colors[EGDC_3D_SHADOW] = video::SColor(101,130,130,130); + Colors[EGDC_3D_FACE] = video::SColor(220,100,100,100); + Colors[EGDC_3D_HIGH_LIGHT] = video::SColor(101,255,255,255); + Colors[EGDC_3D_LIGHT] = video::SColor(101,210,210,210); + Colors[EGDC_ACTIVE_BORDER] = video::SColor(101,16,14,115); + Colors[EGDC_ACTIVE_CAPTION] = video::SColor(255,255,255,255); + Colors[EGDC_APP_WORKSPACE] = video::SColor(101,100,100,100); + Colors[EGDC_BUTTON_TEXT] = video::SColor(240,10,10,10); + Colors[EGDC_GRAY_TEXT] = video::SColor(240,130,130,130); + Colors[EGDC_HIGH_LIGHT] = video::SColor(101,8,36,107); + Colors[EGDC_HIGH_LIGHT_TEXT] = video::SColor(240,255,255,255); + Colors[EGDC_INACTIVE_BORDER] = video::SColor(101,165,165,165); + Colors[EGDC_INACTIVE_CAPTION] = video::SColor(255,30,30,30); + Colors[EGDC_TOOLTIP] = video::SColor(200,0,0,0); + Colors[EGDC_TOOLTIP_BACKGROUND] = video::SColor(200,255,255,225); + Colors[EGDC_SCROLLBAR] = video::SColor(101,230,230,230); + Colors[EGDC_WINDOW] = video::SColor(101,255,255,255); + Colors[EGDC_WINDOW_SYMBOL] = video::SColor(200,10,10,10); + Colors[EGDC_ICON] = video::SColor(200,255,255,255); + Colors[EGDC_ICON_HIGH_LIGHT] = video::SColor(200,8,36,107); + Colors[EGDC_GRAY_WINDOW_SYMBOL] = video::SColor(240,100,100,100); + Colors[EGDC_EDITABLE] = video::SColor(255,255,255,255); + Colors[EGDC_GRAY_EDITABLE] = video::SColor(255,120,120,120); + Colors[EGDC_FOCUSED_EDITABLE] = video::SColor(255,240,240,255); - if ((Type == EGST_WINDOWS_CLASSIC) || (Type == EGST_WINDOWS_METALLIC)) { - Colors[EGDC_3D_DARK_SHADOW] = video::SColor(101, 50, 50, 50); - Colors[EGDC_3D_SHADOW] = video::SColor(101, 130, 130, 130); - Colors[EGDC_3D_FACE] = video::SColor(101, 210, 210, 210); - Colors[EGDC_3D_HIGH_LIGHT] = video::SColor(101, 255, 255, 255); - Colors[EGDC_3D_LIGHT] = video::SColor(101, 210, 210, 210); - Colors[EGDC_ACTIVE_BORDER] = video::SColor(101, 16, 14, 115); - Colors[EGDC_ACTIVE_CAPTION] = video::SColor(255, 255, 255, 255); - Colors[EGDC_APP_WORKSPACE] = video::SColor(101, 100, 100, 100); - Colors[EGDC_BUTTON_TEXT] = video::SColor(240, 10, 10, 10); - Colors[EGDC_GRAY_TEXT] = video::SColor(240, 130, 130, 130); - Colors[EGDC_HIGH_LIGHT] = video::SColor(101, 8, 36, 107); - Colors[EGDC_HIGH_LIGHT_TEXT] = video::SColor(240, 255, 255, 255); - Colors[EGDC_INACTIVE_BORDER] = video::SColor(101, 165, 165, 165); - Colors[EGDC_INACTIVE_CAPTION] = video::SColor(255, 30, 30, 30); - Colors[EGDC_TOOLTIP] = video::SColor(200, 0, 0, 0); - Colors[EGDC_TOOLTIP_BACKGROUND] = video::SColor(200, 255, 255, 225); - Colors[EGDC_SCROLLBAR] = video::SColor(101, 230, 230, 230); - Colors[EGDC_WINDOW] = video::SColor(101, 255, 255, 255); - Colors[EGDC_WINDOW_SYMBOL] = video::SColor(200, 10, 10, 10); - Colors[EGDC_ICON] = video::SColor(200, 255, 255, 255); - Colors[EGDC_ICON_HIGH_LIGHT] = video::SColor(200, 8, 36, 107); - Colors[EGDC_GRAY_WINDOW_SYMBOL] = video::SColor(240, 100, 100, 100); - Colors[EGDC_EDITABLE] = video::SColor(255, 255, 255, 255); - Colors[EGDC_GRAY_EDITABLE] = video::SColor(255, 120, 120, 120); - Colors[EGDC_FOCUSED_EDITABLE] = video::SColor(255, 240, 240, 255); Sizes[EGDS_SCROLLBAR_SIZE] = 14; Sizes[EGDS_MENU_HEIGHT] = 30; @@ -63,34 +67,36 @@ CGUISkin::CGUISkin(EGUI_SKIN_TYPE type, video::IVideoDriver *driver) : Sizes[EGDS_TITLEBARTEXT_DISTANCE_X] = 2; Sizes[EGDS_TITLEBARTEXT_DISTANCE_Y] = 0; - } else { - // 0x80a6a8af - Colors[EGDC_3D_DARK_SHADOW] = 0x60767982; - // Colors[EGDC_3D_FACE] = 0xc0c9ccd4; // tab background - Colors[EGDC_3D_FACE] = 0xc0cbd2d9; // tab background - Colors[EGDC_3D_SHADOW] = 0x50e4e8f1; // tab background, and left-top highlight - Colors[EGDC_3D_HIGH_LIGHT] = 0x40c7ccdc; - Colors[EGDC_3D_LIGHT] = 0x802e313a; - Colors[EGDC_ACTIVE_BORDER] = 0x80404040; // window title - Colors[EGDC_ACTIVE_CAPTION] = 0xffd0d0d0; - Colors[EGDC_APP_WORKSPACE] = 0xc0646464; // unused - Colors[EGDC_BUTTON_TEXT] = 0xd0161616; - Colors[EGDC_GRAY_TEXT] = 0x3c141414; - Colors[EGDC_HIGH_LIGHT] = 0x6c606060; - Colors[EGDC_HIGH_LIGHT_TEXT] = 0xd0e0e0e0; - Colors[EGDC_INACTIVE_BORDER] = 0xf0a5a5a5; - Colors[EGDC_INACTIVE_CAPTION] = 0xffd2d2d2; - Colors[EGDC_TOOLTIP] = 0xf00f2033; - Colors[EGDC_TOOLTIP_BACKGROUND] = 0xc0cbd2d9; - Colors[EGDC_SCROLLBAR] = 0xf0e0e0e0; - Colors[EGDC_WINDOW] = 0xf0f0f0f0; - Colors[EGDC_WINDOW_SYMBOL] = 0xd0161616; - Colors[EGDC_ICON] = 0xd0161616; - Colors[EGDC_ICON_HIGH_LIGHT] = 0xd0606060; - Colors[EGDC_GRAY_WINDOW_SYMBOL] = 0x3c101010; - Colors[EGDC_EDITABLE] = 0xf0ffffff; - Colors[EGDC_GRAY_EDITABLE] = 0xf0cccccc; - Colors[EGDC_FOCUSED_EDITABLE] = 0xf0fffff0; + } + else + { + //0x80a6a8af + Colors[EGDC_3D_DARK_SHADOW] = 0x60767982; + //Colors[EGDC_3D_FACE] = 0xc0c9ccd4; // tab background + Colors[EGDC_3D_FACE] = 0xc0cbd2d9; // tab background + Colors[EGDC_3D_SHADOW] = 0x50e4e8f1; // tab background, and left-top highlight + Colors[EGDC_3D_HIGH_LIGHT] = 0x40c7ccdc; + Colors[EGDC_3D_LIGHT] = 0x802e313a; + Colors[EGDC_ACTIVE_BORDER] = 0x80404040; // window title + Colors[EGDC_ACTIVE_CAPTION] = 0xffd0d0d0; + Colors[EGDC_APP_WORKSPACE] = 0xc0646464; // unused + Colors[EGDC_BUTTON_TEXT] = 0xd0161616; + Colors[EGDC_GRAY_TEXT] = 0x3c141414; + Colors[EGDC_HIGH_LIGHT] = 0x6c606060; + Colors[EGDC_HIGH_LIGHT_TEXT] = 0xd0e0e0e0; + Colors[EGDC_INACTIVE_BORDER] = 0xf0a5a5a5; + Colors[EGDC_INACTIVE_CAPTION] = 0xffd2d2d2; + Colors[EGDC_TOOLTIP] = 0xf00f2033; + Colors[EGDC_TOOLTIP_BACKGROUND] = 0xc0cbd2d9; + Colors[EGDC_SCROLLBAR] = 0xf0e0e0e0; + Colors[EGDC_WINDOW] = 0xf0f0f0f0; + Colors[EGDC_WINDOW_SYMBOL] = 0xd0161616; + Colors[EGDC_ICON] = 0xd0161616; + Colors[EGDC_ICON_HIGH_LIGHT] = 0xd0606060; + Colors[EGDC_GRAY_WINDOW_SYMBOL] = 0x3c101010; + Colors[EGDC_EDITABLE] = 0xf0ffffff; + Colors[EGDC_GRAY_EDITABLE] = 0xf0cccccc; + Colors[EGDC_FOCUSED_EDITABLE] = 0xf0fffff0; Sizes[EGDS_SCROLLBAR_SIZE] = 14; Sizes[EGDS_MENU_HEIGHT] = 48; @@ -118,8 +124,6 @@ CGUISkin::CGUISkin(EGUI_SKIN_TYPE type, video::IVideoDriver *driver) : Sizes[EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y] = 1; Sizes[EGDS_BUTTON_PRESSED_TEXT_OFFSET_X] = 0; Sizes[EGDS_BUTTON_PRESSED_TEXT_OFFSET_Y] = 2; - Sizes[EGDS_BUTTON_PRESSED_SPRITE_OFFSET_X] = 0; - Sizes[EGDS_BUTTON_PRESSED_SPRITE_OFFSET_Y] = 0; Texts[EGDT_MSG_BOX_OK] = L"OK"; Texts[EGDT_MSG_BOX_CANCEL] = L"Cancel"; @@ -155,16 +159,18 @@ CGUISkin::CGUISkin(EGUI_SKIN_TYPE type, video::IVideoDriver *driver) : Icons[EGDI_FILE] = 245; Icons[EGDI_DIRECTORY] = 246; - for (u32 i = 0; i < EGDF_COUNT; ++i) + for (u32 i=0; idrop(); } @@ -173,6 +179,7 @@ CGUISkin::~CGUISkin() SpriteBank->drop(); } + //! returns default color video::SColor CGUISkin::getColor(EGUI_DEFAULT_COLOR color) const { @@ -182,6 +189,7 @@ video::SColor CGUISkin::getColor(EGUI_DEFAULT_COLOR color) const return video::SColor(); } + //! sets a default color void CGUISkin::setColor(EGUI_DEFAULT_COLOR which, video::SColor newColor) { @@ -189,6 +197,7 @@ void CGUISkin::setColor(EGUI_DEFAULT_COLOR which, video::SColor newColor) Colors[which] = newColor; } + //! returns size for the given size type s32 CGUISkin::getSize(EGUI_DEFAULT_SIZE size) const { @@ -198,6 +207,7 @@ s32 CGUISkin::getSize(EGUI_DEFAULT_SIZE size) const return 0; } + //! sets a default size void CGUISkin::setSize(EGUI_DEFAULT_SIZE which, s32 size) { @@ -205,8 +215,9 @@ void CGUISkin::setSize(EGUI_DEFAULT_SIZE which, s32 size) Sizes[which] = size; } + //! returns the default font -IGUIFont *CGUISkin::getFont(EGUI_DEFAULT_FONT which) const +IGUIFont* CGUISkin::getFont(EGUI_DEFAULT_FONT which) const { if (((u32)which < EGDF_COUNT) && Fonts[which]) return Fonts[which]; @@ -214,13 +225,15 @@ IGUIFont *CGUISkin::getFont(EGUI_DEFAULT_FONT which) const return Fonts[EGDF_DEFAULT]; } + //! sets a default font -void CGUISkin::setFont(IGUIFont *font, EGUI_DEFAULT_FONT which) +void CGUISkin::setFont(IGUIFont* font, EGUI_DEFAULT_FONT which) { if ((u32)which >= EGDF_COUNT) return; - if (font) { + if (font) + { font->grab(); if (Fonts[which]) Fonts[which]->drop(); @@ -229,14 +242,16 @@ void CGUISkin::setFont(IGUIFont *font, EGUI_DEFAULT_FONT which) } } + //! gets the sprite bank stored -IGUISpriteBank *CGUISkin::getSpriteBank() const +IGUISpriteBank* CGUISkin::getSpriteBank() const { return SpriteBank; } + //! set a new sprite bank or remove one by passing 0 -void CGUISkin::setSpriteBank(IGUISpriteBank *bank) +void CGUISkin::setSpriteBank(IGUISpriteBank* bank) { if (bank) bank->grab(); @@ -247,6 +262,7 @@ void CGUISkin::setSpriteBank(IGUISpriteBank *bank) SpriteBank = bank; } + //! Returns a default icon u32 CGUISkin::getIcon(EGUI_DEFAULT_ICON icon) const { @@ -256,6 +272,7 @@ u32 CGUISkin::getIcon(EGUI_DEFAULT_ICON icon) const return 0; } + //! Sets a default icon void CGUISkin::setIcon(EGUI_DEFAULT_ICON icon, u32 index) { @@ -263,9 +280,10 @@ void CGUISkin::setIcon(EGUI_DEFAULT_ICON icon, u32 index) Icons[icon] = index; } + //! Returns a default text. For example for Message box button captions: //! "OK", "Cancel", "Yes", "No" and so on. -const wchar_t *CGUISkin::getDefaultText(EGUI_DEFAULT_TEXT text) const +const wchar_t* CGUISkin::getDefaultText(EGUI_DEFAULT_TEXT text) const { if ((u32)text < EGDT_COUNT) return Texts[text].c_str(); @@ -273,14 +291,16 @@ const wchar_t *CGUISkin::getDefaultText(EGUI_DEFAULT_TEXT text) const return Texts[0].c_str(); } + //! Sets a default text. For example for Message box button captions: //! "OK", "Cancel", "Yes", "No" and so on. -void CGUISkin::setDefaultText(EGUI_DEFAULT_TEXT which, const wchar_t *newText) +void CGUISkin::setDefaultText(EGUI_DEFAULT_TEXT which, const wchar_t* newText) { if ((u32)which < EGDT_COUNT) Texts[which] = newText; } + //! draws a standard 3d button pane /** Used for drawing for example buttons in normal state. It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and @@ -290,46 +310,58 @@ EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. \param element: Pointer to the element which wishes to draw this. This parameter is usually not used by ISkin, but can be used for example by more complex implementations to find out how to draw the part exactly. */ -void CGUISkin::draw3DButtonPaneStandard(IGUIElement *element, - const core::rect &r, - const core::rect *clip) +// PATCH +void CGUISkin::drawColored3DButtonPaneStandard(IGUIElement* element, + const core::rect& r, + const core::rect* clip, + const video::SColor* colors) { if (!Driver) return; + if (!colors) + colors = Colors; + core::rect rect = r; - if (Type == EGST_BURNING_SKIN) { + if ( Type == EGST_BURNING_SKIN ) + { rect.UpperLeftCorner.X -= 1; rect.UpperLeftCorner.Y -= 1; rect.LowerRightCorner.X += 1; rect.LowerRightCorner.Y += 1; draw3DSunkenPane(element, - getColor(EGDC_WINDOW).getInterpolated(0xFFFFFFFF, 0.9f), false, true, rect, clip); + colors[ EGDC_WINDOW ].getInterpolated( 0xFFFFFFFF, 0.9f ) + ,false, true, rect, clip); return; } - Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), rect, clip); + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); rect.LowerRightCorner.X -= 1; rect.LowerRightCorner.Y -= 1; - Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip); + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); rect.UpperLeftCorner.X += 1; rect.UpperLeftCorner.Y += 1; - Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip); + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); rect.LowerRightCorner.X -= 1; rect.LowerRightCorner.Y -= 1; - if (!UseGradient) { - Driver->draw2DRectangle(getColor(EGDC_3D_FACE), rect, clip); - } else { - const video::SColor c1 = getColor(EGDC_3D_FACE); - const video::SColor c2 = c1.getInterpolated(getColor(EGDC_3D_DARK_SHADOW), 0.4f); + if (!UseGradient) + { + Driver->draw2DRectangle(colors[EGDC_3D_FACE], rect, clip); + } + else + { + const video::SColor c1 = colors[EGDC_3D_FACE]; + const video::SColor c2 = c1.getInterpolated(colors[EGDC_3D_DARK_SHADOW], 0.4f); Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip); } } +// END PATCH + //! draws a pressed 3d button pane /** Used for drawing for example buttons in pressed state. @@ -340,35 +372,45 @@ EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. \param element: Pointer to the element which wishes to draw this. This parameter is usually not used by ISkin, but can be used for example by more complex implementations to find out how to draw the part exactly. */ -void CGUISkin::draw3DButtonPanePressed(IGUIElement *element, - const core::rect &r, - const core::rect *clip) +// PATCH +void CGUISkin::drawColored3DButtonPanePressed(IGUIElement* element, + const core::rect& r, + const core::rect* clip, + const video::SColor* colors) { if (!Driver) return; + if (!colors) + colors = Colors; + core::rect rect = r; - Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip); + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); rect.LowerRightCorner.X -= 1; rect.LowerRightCorner.Y -= 1; - Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), rect, clip); + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); rect.UpperLeftCorner.X += 1; rect.UpperLeftCorner.Y += 1; - Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip); + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); rect.UpperLeftCorner.X += 1; rect.UpperLeftCorner.Y += 1; - if (!UseGradient) { - Driver->draw2DRectangle(getColor(EGDC_3D_FACE), rect, clip); - } else { - const video::SColor c1 = getColor(EGDC_3D_FACE); - const video::SColor c2 = c1.getInterpolated(getColor(EGDC_3D_DARK_SHADOW), 0.4f); + if (!UseGradient) + { + Driver->draw2DRectangle(colors[EGDC_3D_FACE], rect, clip); + } + else + { + const video::SColor c1 = colors[EGDC_3D_FACE]; + const video::SColor c2 = c1.getInterpolated(colors[EGDC_3D_DARK_SHADOW], 0.4f); Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip); } } +// END PATCH + //! draws a sunken 3d pane /** Used for drawing the background of edit, combo or check boxes. @@ -380,112 +422,130 @@ implementations to find out how to draw the part exactly. deep into the ground. \param rect: Defining area where to draw. \param clip: Clip area. */ -void CGUISkin::draw3DSunkenPane(IGUIElement *element, video::SColor bgcolor, - bool flat, bool fillBackGround, - const core::rect &r, - const core::rect *clip) +// PATCH +void CGUISkin::drawColored3DSunkenPane(IGUIElement* element, video::SColor bgcolor, + bool flat, bool fillBackGround, + const core::rect& r, + const core::rect* clip, + const video::SColor* colors) { if (!Driver) return; + if (!colors) + colors = Colors; + core::rect rect = r; if (fillBackGround) Driver->draw2DRectangle(bgcolor, rect, clip); - if (flat) { + if (flat) + { // draw flat sunken pane rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 1; - Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip); // top + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); // top ++rect.UpperLeftCorner.Y; rect.LowerRightCorner.Y = r.LowerRightCorner.Y; rect.LowerRightCorner.X = rect.UpperLeftCorner.X + 1; - Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip); // left + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); // left rect = r; ++rect.UpperLeftCorner.Y; rect.UpperLeftCorner.X = rect.LowerRightCorner.X - 1; - Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip); // right + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); // right rect = r; ++rect.UpperLeftCorner.X; rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1; --rect.LowerRightCorner.X; - Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip); // bottom - } else { + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); // bottom + } + else + { // draw deep sunken pane rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 1; - Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip); // top + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); // top ++rect.UpperLeftCorner.X; ++rect.UpperLeftCorner.Y; --rect.LowerRightCorner.X; ++rect.LowerRightCorner.Y; - Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), rect, clip); + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); rect.UpperLeftCorner.X = r.UpperLeftCorner.X; - rect.UpperLeftCorner.Y = r.UpperLeftCorner.Y + 1; + rect.UpperLeftCorner.Y = r.UpperLeftCorner.Y+1; rect.LowerRightCorner.X = rect.UpperLeftCorner.X + 1; rect.LowerRightCorner.Y = r.LowerRightCorner.Y; - Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip); // left + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); // left ++rect.UpperLeftCorner.X; ++rect.UpperLeftCorner.Y; ++rect.LowerRightCorner.X; --rect.LowerRightCorner.Y; - Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), rect, clip); + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); rect = r; rect.UpperLeftCorner.X = rect.LowerRightCorner.X - 1; ++rect.UpperLeftCorner.Y; - Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip); // right + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); // right --rect.UpperLeftCorner.X; ++rect.UpperLeftCorner.Y; --rect.LowerRightCorner.X; --rect.LowerRightCorner.Y; - Driver->draw2DRectangle(getColor(EGDC_3D_LIGHT), rect, clip); + Driver->draw2DRectangle(colors[EGDC_3D_LIGHT], rect, clip); rect = r; ++rect.UpperLeftCorner.X; rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1; --rect.LowerRightCorner.X; - Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip); // bottom + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); // bottom ++rect.UpperLeftCorner.X; --rect.UpperLeftCorner.Y; --rect.LowerRightCorner.X; --rect.LowerRightCorner.Y; - Driver->draw2DRectangle(getColor(EGDC_3D_LIGHT), rect, clip); + Driver->draw2DRectangle(colors[EGDC_3D_LIGHT], rect, clip); } } +// END PATCH //! draws a window background // return where to draw title bar text. -core::rect CGUISkin::draw3DWindowBackground(IGUIElement *element, - bool drawTitleBar, video::SColor titleBarColor, - const core::rect &r, - const core::rect *clip, - core::rect *checkClientArea) +// PATCH +core::rect CGUISkin::drawColored3DWindowBackground(IGUIElement* element, + bool drawTitleBar, video::SColor titleBarColor, + const core::rect& r, + const core::rect* clip, + core::rect* checkClientArea, + const video::SColor* colors) { - if (!Driver) { - if (checkClientArea) { + if (!Driver) + { + if ( checkClientArea ) + { *checkClientArea = r; } return r; } + if (!colors) + colors = Colors; + core::rect rect = r; // top border rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 1; - if (!checkClientArea) { - Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip); + if ( !checkClientArea ) + { + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); } // left border rect.LowerRightCorner.Y = r.LowerRightCorner.Y; rect.LowerRightCorner.X = rect.UpperLeftCorner.X + 1; - if (!checkClientArea) { - Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip); + if ( !checkClientArea ) + { + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); } // right border dark outer line @@ -493,8 +553,9 @@ core::rect CGUISkin::draw3DWindowBackground(IGUIElement *element, rect.LowerRightCorner.X = r.LowerRightCorner.X; rect.UpperLeftCorner.Y = r.UpperLeftCorner.Y; rect.LowerRightCorner.Y = r.LowerRightCorner.Y; - if (!checkClientArea) { - Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), rect, clip); + if ( !checkClientArea ) + { + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); } // right border bright innner line @@ -502,8 +563,9 @@ core::rect CGUISkin::draw3DWindowBackground(IGUIElement *element, rect.LowerRightCorner.X -= 1; rect.UpperLeftCorner.Y += 1; rect.LowerRightCorner.Y -= 1; - if (!checkClientArea) { - Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip); + if ( !checkClientArea ) + { + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); } // bottom border dark outer line @@ -511,8 +573,9 @@ core::rect CGUISkin::draw3DWindowBackground(IGUIElement *element, rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1; rect.LowerRightCorner.Y = r.LowerRightCorner.Y; rect.LowerRightCorner.X = r.LowerRightCorner.X; - if (!checkClientArea) { - Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), rect, clip); + if ( !checkClientArea ) + { + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); } // bottom border bright inner line @@ -520,31 +583,39 @@ core::rect CGUISkin::draw3DWindowBackground(IGUIElement *element, rect.LowerRightCorner.X -= 1; rect.UpperLeftCorner.Y -= 1; rect.LowerRightCorner.Y -= 1; - if (!checkClientArea) { - Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip); + if ( !checkClientArea ) + { + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); } // client area for background rect = r; - rect.UpperLeftCorner.X += 1; - rect.UpperLeftCorner.Y += 1; + rect.UpperLeftCorner.X +=1; + rect.UpperLeftCorner.Y +=1; rect.LowerRightCorner.X -= 2; rect.LowerRightCorner.Y -= 2; - if (checkClientArea) { + if (checkClientArea) + { *checkClientArea = rect; } - if (!checkClientArea) { - if (!UseGradient) { - Driver->draw2DRectangle(getColor(EGDC_3D_FACE), rect, clip); - } else if (Type == EGST_BURNING_SKIN) { - const video::SColor c1 = getColor(EGDC_WINDOW).getInterpolated(0xFFFFFFFF, 0.9f); - const video::SColor c2 = getColor(EGDC_WINDOW).getInterpolated(0xFFFFFFFF, 0.8f); + if ( !checkClientArea ) + { + if (!UseGradient) + { + Driver->draw2DRectangle(colors[EGDC_3D_FACE], rect, clip); + } + else if ( Type == EGST_BURNING_SKIN ) + { + const video::SColor c1 = colors[EGDC_WINDOW].getInterpolated ( 0xFFFFFFFF, 0.9f ); + const video::SColor c2 = colors[EGDC_WINDOW].getInterpolated ( 0xFFFFFFFF, 0.8f ); Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip); - } else { - const video::SColor c2 = getColor(EGDC_3D_SHADOW); - const video::SColor c1 = getColor(EGDC_3D_FACE); + } + else + { + const video::SColor c2 = colors[EGDC_3D_SHADOW]; + const video::SColor c1 = colors[EGDC_3D_FACE]; Driver->draw2DRectangle(rect, c1, c1, c1, c2, clip); } } @@ -556,19 +627,26 @@ core::rect CGUISkin::draw3DWindowBackground(IGUIElement *element, rect.LowerRightCorner.X -= 2; rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + getSize(EGDS_WINDOW_BUTTON_WIDTH) + 2; - if (drawTitleBar) { - if (checkClientArea) { + if (drawTitleBar ) + { + if (checkClientArea) + { (*checkClientArea).UpperLeftCorner.Y = rect.LowerRightCorner.Y; - } else { + } + else + { // draw title bar - // if (!UseGradient) + //if (!UseGradient) // Driver->draw2DRectangle(titleBarColor, rect, clip); - // else - if (Type == EGST_BURNING_SKIN) { - const video::SColor c = titleBarColor.getInterpolated(video::SColor(titleBarColor.getAlpha(), 255, 255, 255), 0.8f); + //else + if ( Type == EGST_BURNING_SKIN ) + { + const video::SColor c = titleBarColor.getInterpolated( video::SColor(titleBarColor.getAlpha(),255,255,255), 0.8f); Driver->draw2DRectangle(rect, titleBarColor, titleBarColor, c, c, clip); - } else { - const video::SColor c = titleBarColor.getInterpolated(video::SColor(titleBarColor.getAlpha(), 0, 0, 0), 0.2f); + } + else + { + const video::SColor c = titleBarColor.getInterpolated(video::SColor(titleBarColor.getAlpha(),0,0,0), 0.2f); Driver->draw2DRectangle(rect, titleBarColor, c, titleBarColor, c, clip); } } @@ -576,6 +654,8 @@ core::rect CGUISkin::draw3DWindowBackground(IGUIElement *element, return rect; } +// END PATCH + //! draws a standard 3d menu pane /** Used for drawing for menus and context menus. @@ -586,15 +666,21 @@ is usually not used by ISkin, but can be used for example by more complex implementations to find out how to draw the part exactly. \param rect: Defining area where to draw. \param clip: Clip area. */ -void CGUISkin::draw3DMenuPane(IGUIElement *element, - const core::rect &r, const core::rect *clip) +// PATCH +void CGUISkin::drawColored3DMenuPane(IGUIElement* element, + const core::rect& r, const core::rect* clip, + const video::SColor* colors) { if (!Driver) return; + if (!colors) + colors = Colors; + core::rect rect = r; - if (Type == EGST_BURNING_SKIN) { + if ( Type == EGST_BURNING_SKIN ) + { rect.UpperLeftCorner.Y -= 3; draw3DButtonPaneStandard(element, rect, clip); return; @@ -609,50 +695,53 @@ void CGUISkin::draw3DMenuPane(IGUIElement *element, // but there aren't that much menus visible anyway. rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 1; - Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip); + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); rect.LowerRightCorner.Y = r.LowerRightCorner.Y; rect.LowerRightCorner.X = rect.UpperLeftCorner.X + 1; - Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), rect, clip); + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); rect.UpperLeftCorner.X = r.LowerRightCorner.X - 1; rect.LowerRightCorner.X = r.LowerRightCorner.X; rect.UpperLeftCorner.Y = r.UpperLeftCorner.Y; rect.LowerRightCorner.Y = r.LowerRightCorner.Y; - Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), rect, clip); + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); rect.UpperLeftCorner.X -= 1; rect.LowerRightCorner.X -= 1; rect.UpperLeftCorner.Y += 1; rect.LowerRightCorner.Y -= 1; - Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip); + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); rect.UpperLeftCorner.X = r.UpperLeftCorner.X; rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1; rect.LowerRightCorner.Y = r.LowerRightCorner.Y; rect.LowerRightCorner.X = r.LowerRightCorner.X; - Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), rect, clip); + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); rect.UpperLeftCorner.X += 1; rect.LowerRightCorner.X -= 1; rect.UpperLeftCorner.Y -= 1; rect.LowerRightCorner.Y -= 1; - Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip); + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); rect = r; - rect.UpperLeftCorner.X += 1; - rect.UpperLeftCorner.Y += 1; + rect.UpperLeftCorner.X +=1; + rect.UpperLeftCorner.Y +=1; rect.LowerRightCorner.X -= 2; rect.LowerRightCorner.Y -= 2; if (!UseGradient) - Driver->draw2DRectangle(getColor(EGDC_3D_FACE), rect, clip); - else { - const video::SColor c1 = getColor(EGDC_3D_FACE); - const video::SColor c2 = getColor(EGDC_3D_SHADOW); + Driver->draw2DRectangle(colors[EGDC_3D_FACE], rect, clip); + else + { + const video::SColor c1 = colors[EGDC_3D_FACE]; + const video::SColor c2 = colors[EGDC_3D_SHADOW]; Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip); } } +// END PATCH + //! draws a standard 3d tool bar /** Used for drawing for toolbars and menus. @@ -661,38 +750,50 @@ is usually not used by ISkin, but can be used for example by more complex implementations to find out how to draw the part exactly. \param rect: Defining area where to draw. \param clip: Clip area. */ -void CGUISkin::draw3DToolBar(IGUIElement *element, - const core::rect &r, - const core::rect *clip) +// PATCH +void CGUISkin::drawColored3DToolBar(IGUIElement* element, + const core::rect& r, + const core::rect* clip, + const video::SColor* colors) { if (!Driver) return; + if (!colors) + colors = Colors; + core::rect rect = r; rect.UpperLeftCorner.X = r.UpperLeftCorner.X; rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1; rect.LowerRightCorner.Y = r.LowerRightCorner.Y; rect.LowerRightCorner.X = r.LowerRightCorner.X; - Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), rect, clip); + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); rect = r; rect.LowerRightCorner.Y -= 1; - if (!UseGradient) { - Driver->draw2DRectangle(getColor(EGDC_3D_FACE), rect, clip); - } else if (Type == EGST_BURNING_SKIN) { - const video::SColor c1 = 0xF0000000 | getColor(EGDC_3D_FACE).color; - const video::SColor c2 = 0xF0000000 | getColor(EGDC_3D_SHADOW).color; + if (!UseGradient) + { + Driver->draw2DRectangle(colors[EGDC_3D_FACE], rect, clip); + } + else + if ( Type == EGST_BURNING_SKIN ) + { + const video::SColor c1 = 0xF0000000 | colors[EGDC_3D_FACE].color; + const video::SColor c2 = 0xF0000000 | colors[EGDC_3D_SHADOW].color; rect.LowerRightCorner.Y += 1; Driver->draw2DRectangle(rect, c1, c2, c1, c2, clip); - } else { - const video::SColor c1 = getColor(EGDC_3D_FACE); - const video::SColor c2 = getColor(EGDC_3D_SHADOW); + } + else + { + const video::SColor c1 = colors[EGDC_3D_FACE]; + const video::SColor c2 = colors[EGDC_3D_SHADOW]; Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip); } } +// END PATCH //! draws a tab button /** Used for drawing for tab buttons on top of tabs. @@ -702,53 +803,61 @@ implementations to find out how to draw the part exactly. \param active: Specifies if the tab is currently active. \param rect: Defining area where to draw. \param clip: Clip area. */ -void CGUISkin::draw3DTabButton(IGUIElement *element, bool active, - const core::rect &frameRect, const core::rect *clip, EGUI_ALIGNMENT alignment) +// PATCH +void CGUISkin::drawColored3DTabButton(IGUIElement* element, bool active, + const core::rect& frameRect, const core::rect* clip, EGUI_ALIGNMENT alignment, + const video::SColor* colors) { if (!Driver) return; + if (!colors) + colors = Colors; + core::rect tr = frameRect; - if (alignment == EGUIA_UPPERLEFT) { + if ( alignment == EGUIA_UPPERLEFT ) + { tr.LowerRightCorner.X -= 2; tr.LowerRightCorner.Y = tr.UpperLeftCorner.Y + 1; tr.UpperLeftCorner.X += 1; - Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), tr, clip); + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); // draw left highlight tr = frameRect; tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1; tr.UpperLeftCorner.Y += 1; - Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), tr, clip); + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); // draw grey background tr = frameRect; tr.UpperLeftCorner.X += 1; tr.UpperLeftCorner.Y += 1; tr.LowerRightCorner.X -= 2; - Driver->draw2DRectangle(getColor(EGDC_3D_FACE), tr, clip); + Driver->draw2DRectangle(colors[EGDC_3D_FACE], tr, clip); // draw right middle gray shadow tr.LowerRightCorner.X += 1; tr.UpperLeftCorner.X = tr.LowerRightCorner.X - 1; - Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), tr, clip); + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], tr, clip); tr.LowerRightCorner.X += 1; tr.UpperLeftCorner.X += 1; tr.UpperLeftCorner.Y += 1; - Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), tr, clip); - } else { + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], tr, clip); + } + else + { tr.LowerRightCorner.X -= 2; tr.UpperLeftCorner.Y = tr.LowerRightCorner.Y - 1; tr.UpperLeftCorner.X += 1; - Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), tr, clip); + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); // draw left highlight tr = frameRect; tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1; tr.LowerRightCorner.Y -= 1; - Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), tr, clip); + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); // draw grey background tr = frameRect; @@ -756,20 +865,22 @@ void CGUISkin::draw3DTabButton(IGUIElement *element, bool active, tr.UpperLeftCorner.Y -= 1; tr.LowerRightCorner.X -= 2; tr.LowerRightCorner.Y -= 1; - Driver->draw2DRectangle(getColor(EGDC_3D_FACE), tr, clip); + Driver->draw2DRectangle(colors[EGDC_3D_FACE], tr, clip); // draw right middle gray shadow tr.LowerRightCorner.X += 1; tr.UpperLeftCorner.X = tr.LowerRightCorner.X - 1; - // tr.LowerRightCorner.Y -= 1; - Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), tr, clip); + //tr.LowerRightCorner.Y -= 1; + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], tr, clip); tr.LowerRightCorner.X += 1; tr.UpperLeftCorner.X += 1; tr.LowerRightCorner.Y -= 1; - Driver->draw2DRectangle(getColor(EGDC_3D_DARK_SHADOW), tr, clip); + Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], tr, clip); } } +// END PATCH + //! draws a tab control body /** \param element: Pointer to the element which wishes to draw this. This parameter @@ -779,77 +890,93 @@ implementations to find out how to draw the part exactly. \param background: Specifies if the background should be drawn. \param rect: Defining area where to draw. \param clip: Clip area. */ -void CGUISkin::draw3DTabBody(IGUIElement *element, bool border, bool background, - const core::rect &rect, const core::rect *clip, s32 tabHeight, EGUI_ALIGNMENT alignment) +// PATCH +void CGUISkin::drawColored3DTabBody(IGUIElement* element, bool border, bool background, + const core::rect& rect, const core::rect* clip, s32 tabHeight, EGUI_ALIGNMENT alignment, + const video::SColor* colors) { if (!Driver) return; + if (!colors) + colors = Colors; + core::rect tr = rect; - if (tabHeight == -1) + if ( tabHeight == -1 ) tabHeight = getSize(gui::EGDS_BUTTON_HEIGHT); // draw border. - if (border) { - if (alignment == EGUIA_UPPERLEFT) { + if (border) + { + if ( alignment == EGUIA_UPPERLEFT ) + { // draw left hightlight tr.UpperLeftCorner.Y += tabHeight + 2; tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1; - Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), tr, clip); + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); // draw right shadow tr.UpperLeftCorner.X = rect.LowerRightCorner.X - 1; tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1; - Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), tr, clip); + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], tr, clip); // draw lower shadow tr = rect; tr.UpperLeftCorner.Y = tr.LowerRightCorner.Y - 1; - Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), tr, clip); - } else { + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], tr, clip); + } + else + { // draw left hightlight tr.LowerRightCorner.Y -= tabHeight + 2; tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1; - Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), tr, clip); + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); // draw right shadow tr.UpperLeftCorner.X = rect.LowerRightCorner.X - 1; tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1; - Driver->draw2DRectangle(getColor(EGDC_3D_SHADOW), tr, clip); + Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], tr, clip); // draw lower shadow tr = rect; tr.LowerRightCorner.Y = tr.UpperLeftCorner.Y + 1; - Driver->draw2DRectangle(getColor(EGDC_3D_HIGH_LIGHT), tr, clip); + Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); } } - if (background) { - if (alignment == EGUIA_UPPERLEFT) { + if (background) + { + if ( alignment == EGUIA_UPPERLEFT ) + { tr = rect; tr.UpperLeftCorner.Y += tabHeight + 2; tr.LowerRightCorner.X -= 1; tr.UpperLeftCorner.X += 1; tr.LowerRightCorner.Y -= 1; - } else { + } + else + { tr = rect; tr.UpperLeftCorner.X += 1; tr.UpperLeftCorner.Y -= 1; tr.LowerRightCorner.X -= 1; tr.LowerRightCorner.Y -= tabHeight + 2; - // tr.UpperLeftCorner.X += 1; + //tr.UpperLeftCorner.X += 1; } if (!UseGradient) - Driver->draw2DRectangle(getColor(EGDC_3D_FACE), tr, clip); - else { - video::SColor c1 = getColor(EGDC_3D_FACE); - video::SColor c2 = getColor(EGDC_3D_SHADOW); + Driver->draw2DRectangle(colors[EGDC_3D_FACE], tr, clip); + else + { + video::SColor c1 = colors[EGDC_3D_FACE]; + video::SColor c2 = colors[EGDC_3D_SHADOW]; Driver->draw2DRectangle(tr, c1, c1, c2, c2, clip); } } } +// END PATCH + //! draws an icon, usually from the skin's sprite bank /** \param parent: Pointer to the element which wishes to draw this icon. @@ -861,31 +988,50 @@ by more complex implementations to find out how to draw the part exactly. \param currenttime: The present time, used to calculate the frame number \param loop: Whether the animation should loop or not \param clip: Clip area. */ -void CGUISkin::drawIcon(IGUIElement *element, EGUI_DEFAULT_ICON icon, - const core::position2di position, - u32 starttime, u32 currenttime, - bool loop, const core::rect *clip) +// PATCH +void CGUISkin::drawColoredIcon(IGUIElement* element, EGUI_DEFAULT_ICON icon, + const core::position2di position, + u32 starttime, u32 currenttime, + bool loop, const core::rect* clip, + const video::SColor* colors) { if (!SpriteBank) return; + if (!colors) + colors = Colors; + bool gray = element && !element->isEnabled(); SpriteBank->draw2DSprite(Icons[icon], position, clip, - Colors[gray ? EGDC_GRAY_WINDOW_SYMBOL : EGDC_WINDOW_SYMBOL], starttime, currenttime, loop, true); + colors[gray? EGDC_GRAY_WINDOW_SYMBOL : EGDC_WINDOW_SYMBOL], starttime, currenttime, loop, true); } +// END PATCH + EGUI_SKIN_TYPE CGUISkin::getType() const { return Type; } + //! draws a 2d rectangle. -void CGUISkin::draw2DRectangle(IGUIElement *element, - const video::SColor &color, const core::rect &pos, - const core::rect *clip) +void CGUISkin::draw2DRectangle(IGUIElement* element, + const video::SColor &color, const core::rect& pos, + const core::rect* clip) { Driver->draw2DRectangle(color, pos, clip); } + +//! gets the colors +// PATCH +void CGUISkin::getColors(video::SColor* colors) +{ + u32 i; + for (i=0; i +#include "ITexture.h" namespace irr { namespace video { -class IVideoDriver; + class IVideoDriver; } namespace gui { + class CGUISkin : public IGUISkin + { + public: -class CGUISkin : public IGUISkin -{ -public: - CGUISkin(EGUI_SKIN_TYPE type, video::IVideoDriver *driver); + CGUISkin(EGUI_SKIN_TYPE type, video::IVideoDriver* driver); - //! destructor - virtual ~CGUISkin(); + //! destructor + virtual ~CGUISkin(); - //! returns display density scaling factor - virtual float getScale() const override { return Scale; } + //! returns display density scaling factor + virtual float getScale() const { return Scale; } - //! sets display density scaling factor - virtual void setScale(float scale) override { Scale = scale; } + //! sets display density scaling factor + virtual void setScale(float scale) { Scale = scale; } - //! returns default color - video::SColor getColor(EGUI_DEFAULT_COLOR color) const override; + //! returns default color + virtual video::SColor getColor(EGUI_DEFAULT_COLOR color) const; - //! sets a default color - void setColor(EGUI_DEFAULT_COLOR which, video::SColor newColor) override; + //! sets a default color + virtual void setColor(EGUI_DEFAULT_COLOR which, video::SColor newColor); - //! returns size for the given size type - s32 getSize(EGUI_DEFAULT_SIZE size) const override; + //! returns size for the given size type + virtual s32 getSize(EGUI_DEFAULT_SIZE size) const; - //! sets a default size - void setSize(EGUI_DEFAULT_SIZE which, s32 size) override; + //! sets a default size + virtual void setSize(EGUI_DEFAULT_SIZE which, s32 size); - //! returns the default font - IGUIFont *getFont(EGUI_DEFAULT_FONT which = EGDF_DEFAULT) const override; + //! returns the default font + virtual IGUIFont* getFont(EGUI_DEFAULT_FONT which=EGDF_DEFAULT) const; - //! sets a default font - void setFont(IGUIFont *font, EGUI_DEFAULT_FONT which = EGDF_DEFAULT) override; + //! sets a default font + virtual void setFont(IGUIFont* font, EGUI_DEFAULT_FONT which=EGDF_DEFAULT); - //! sets the sprite bank used for drawing icons - void setSpriteBank(IGUISpriteBank *bank) override; + //! sets the sprite bank used for drawing icons + virtual void setSpriteBank(IGUISpriteBank* bank); - //! gets the sprite bank used for drawing icons - IGUISpriteBank *getSpriteBank() const override; + //! gets the sprite bank used for drawing icons + virtual IGUISpriteBank* getSpriteBank() const; - //! Returns a default icon - /** Returns the sprite index within the sprite bank */ - u32 getIcon(EGUI_DEFAULT_ICON icon) const override; + //! Returns a default icon + /** Returns the sprite index within the sprite bank */ + virtual u32 getIcon(EGUI_DEFAULT_ICON icon) const; - //! Sets a default icon - /** Sets the sprite index used for drawing icons like arrows, - close buttons and ticks in checkboxes - \param icon: Enum specifying which icon to change - \param index: The sprite index used to draw this icon */ - void setIcon(EGUI_DEFAULT_ICON icon, u32 index) override; + //! Sets a default icon + /** Sets the sprite index used for drawing icons like arrows, + close buttons and ticks in checkboxes + \param icon: Enum specifying which icon to change + \param index: The sprite index used to draw this icon */ + virtual void setIcon(EGUI_DEFAULT_ICON icon, u32 index); - //! Returns a default text. - /** For example for Message box button captions: - "OK", "Cancel", "Yes", "No" and so on. */ - const wchar_t *getDefaultText(EGUI_DEFAULT_TEXT text) const override; + //! Returns a default text. + /** For example for Message box button captions: + "OK", "Cancel", "Yes", "No" and so on. */ + virtual const wchar_t* getDefaultText(EGUI_DEFAULT_TEXT text) const; - //! Sets a default text. - /** For example for Message box button captions: - "OK", "Cancel", "Yes", "No" and so on. */ - void setDefaultText(EGUI_DEFAULT_TEXT which, const wchar_t *newText) override; + //! Sets a default text. + /** For example for Message box button captions: + "OK", "Cancel", "Yes", "No" and so on. */ + virtual void setDefaultText(EGUI_DEFAULT_TEXT which, const wchar_t* newText); - //! draws a standard 3d button pane - /** Used for drawing for example buttons in normal state. - It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and - EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. - \param rect: Defining area where to draw. - \param clip: Clip area. - \param element: Pointer to the element which wishes to draw this. This parameter - is usually not used by ISkin, but can be used for example by more complex - implementations to find out how to draw the part exactly. */ - virtual void draw3DButtonPaneStandard(IGUIElement *element, - const core::rect &rect, - const core::rect *clip = 0) override; + //! draws a standard 3d button pane + /** Used for drawing for example buttons in normal state. + It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and + EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. + \param rect: Defining area where to draw. + \param clip: Clip area. + \param element: Pointer to the element which wishes to draw this. This parameter + is usually not used by ISkin, but can be used for example by more complex + implementations to find out how to draw the part exactly. */ + virtual void draw3DButtonPaneStandard(IGUIElement* element, + const core::rect& rect, + const core::rect* clip=0) + { + drawColored3DButtonPaneStandard(element, rect,clip); + } - //! draws a pressed 3d button pane - /** Used for drawing for example buttons in pressed state. - It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and - EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. - \param rect: Defining area where to draw. - \param clip: Clip area. - \param element: Pointer to the element which wishes to draw this. This parameter - is usually not used by ISkin, but can be used for example by more complex - implementations to find out how to draw the part exactly. */ - virtual void draw3DButtonPanePressed(IGUIElement *element, - const core::rect &rect, - const core::rect *clip = 0) override; + virtual void drawColored3DButtonPaneStandard(IGUIElement* element, + const core::rect& rect, + const core::rect* clip=0, + const video::SColor* colors=0); - //! draws a sunken 3d pane - /** Used for drawing the background of edit, combo or check boxes. - \param element: Pointer to the element which wishes to draw this. This parameter - is usually not used by ISkin, but can be used for example by more complex - implementations to find out how to draw the part exactly. - \param bgcolor: Background color. - \param flat: Specifies if the sunken pane should be flat or displayed as sunken - deep into the ground. - \param rect: Defining area where to draw. - \param clip: Clip area. */ - virtual void draw3DSunkenPane(IGUIElement *element, - video::SColor bgcolor, bool flat, - bool fillBackGround, - const core::rect &rect, - const core::rect *clip = 0) override; + //! draws a pressed 3d button pane + /** Used for drawing for example buttons in pressed state. + It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and + EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. + \param rect: Defining area where to draw. + \param clip: Clip area. + \param element: Pointer to the element which wishes to draw this. This parameter + is usually not used by ISkin, but can be used for example by more complex + implementations to find out how to draw the part exactly. */ + virtual void draw3DButtonPanePressed(IGUIElement* element, + const core::rect& rect, + const core::rect* clip=0) + { + drawColored3DButtonPanePressed(element, rect, clip); + } - //! draws a window background - /** Used for drawing the background of dialogs and windows. - \param element: Pointer to the element which wishes to draw this. This parameter - is usually not used by ISkin, but can be used for example by more complex - implementations to find out how to draw the part exactly. - \param titleBarColor: Title color. - \param drawTitleBar: True to enable title drawing. - \param rect: Defining area where to draw. - \param clip: Clip area. - \param checkClientArea: When set to non-null the function will not draw anything, - but will instead return the clientArea which can be used for drawing by the calling window. - That is the area without borders and without titlebar. - \return Returns rect where it would be good to draw title bar text. This will - work even when checkClientArea is set to a non-null value.*/ - virtual core::rect draw3DWindowBackground(IGUIElement *element, - bool drawTitleBar, video::SColor titleBarColor, - const core::rect &rect, - const core::rect *clip, - core::rect *checkClientArea) override; + virtual void drawColored3DButtonPanePressed(IGUIElement* element, + const core::rect& rect, + const core::rect* clip=0, + const video::SColor* colors=0); - //! draws a standard 3d menu pane - /** Used for drawing for menus and context menus. - It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and - EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. - \param element: Pointer to the element which wishes to draw this. This parameter - is usually not used by ISkin, but can be used for example by more complex - implementations to find out how to draw the part exactly. - \param rect: Defining area where to draw. - \param clip: Clip area. */ - virtual void draw3DMenuPane(IGUIElement *element, - const core::rect &rect, - const core::rect *clip = 0) override; + //! draws a sunken 3d pane + /** Used for drawing the background of edit, combo or check boxes. + \param element: Pointer to the element which wishes to draw this. This parameter + is usually not used by ISkin, but can be used for example by more complex + implementations to find out how to draw the part exactly. + \param bgcolor: Background color. + \param flat: Specifies if the sunken pane should be flat or displayed as sunken + deep into the ground. + \param rect: Defining area where to draw. + \param clip: Clip area. */ + virtual void draw3DSunkenPane(IGUIElement* element, + video::SColor bgcolor, bool flat, + bool fillBackGround, + const core::rect& rect, + const core::rect* clip=0) + { + drawColored3DSunkenPane(element, bgcolor, flat, fillBackGround, rect, clip); + } - //! draws a standard 3d tool bar - /** Used for drawing for toolbars and menus. - \param element: Pointer to the element which wishes to draw this. This parameter - is usually not used by ISkin, but can be used for example by more complex - implementations to find out how to draw the part exactly. - \param rect: Defining area where to draw. - \param clip: Clip area. */ - virtual void draw3DToolBar(IGUIElement *element, - const core::rect &rect, - const core::rect *clip = 0) override; + virtual void drawColored3DSunkenPane(IGUIElement* element, + video::SColor bgcolor, bool flat, + bool fillBackGround, + const core::rect& rect, + const core::rect* clip=0, + const video::SColor* colors=0); - //! draws a tab button - /** Used for drawing for tab buttons on top of tabs. - \param element: Pointer to the element which wishes to draw this. This parameter - is usually not used by ISkin, but can be used for example by more complex - implementations to find out how to draw the part exactly. - \param active: Specifies if the tab is currently active. - \param rect: Defining area where to draw. - \param clip: Clip area. */ - virtual void draw3DTabButton(IGUIElement *element, bool active, - const core::rect &rect, const core::rect *clip = 0, - EGUI_ALIGNMENT alignment = EGUIA_UPPERLEFT) override; + //! draws a window background + /** Used for drawing the background of dialogs and windows. + \param element: Pointer to the element which wishes to draw this. This parameter + is usually not used by ISkin, but can be used for example by more complex + implementations to find out how to draw the part exactly. + \param titleBarColor: Title color. + \param drawTitleBar: True to enable title drawing. + \param rect: Defining area where to draw. + \param clip: Clip area. + \param checkClientArea: When set to non-null the function will not draw anything, + but will instead return the clientArea which can be used for drawing by the calling window. + That is the area without borders and without titlebar. + \return Returns rect where it would be good to draw title bar text. This will + work even when checkClientArea is set to a non-null value.*/ + virtual core::rect draw3DWindowBackground(IGUIElement* element, + bool drawTitleBar, video::SColor titleBarColor, + const core::rect& rect, + const core::rect* clip, + core::rect* checkClientArea) + { + return drawColored3DWindowBackground(element, drawTitleBar, titleBarColor, + rect, clip, checkClientArea); + } - //! draws a tab control body - /** \param element: Pointer to the element which wishes to draw this. This parameter - is usually not used by ISkin, but can be used for example by more complex - implementations to find out how to draw the part exactly. - \param border: Specifies if the border should be drawn. - \param background: Specifies if the background should be drawn. - \param rect: Defining area where to draw. - \param clip: Clip area. */ - virtual void draw3DTabBody(IGUIElement *element, bool border, bool background, - const core::rect &rect, const core::rect *clip = 0, s32 tabHeight = -1, - EGUI_ALIGNMENT alignment = EGUIA_UPPERLEFT) override; + virtual core::rect drawColored3DWindowBackground(IGUIElement* element, + bool drawTitleBar, video::SColor titleBarColor, + const core::rect& rect, + const core::rect* clip, + core::rect* checkClientArea, + const video::SColor* colors=0); - //! draws an icon, usually from the skin's sprite bank - /** \param element: Pointer to the element which wishes to draw this icon. - This parameter is usually not used by IGUISkin, but can be used for example - by more complex implementations to find out how to draw the part exactly. - \param icon: Specifies the icon to be drawn. - \param position: The position to draw the icon - \param starttime: The time at the start of the animation - \param currenttime: The present time, used to calculate the frame number - \param loop: Whether the animation should loop or not - \param clip: Clip area. */ - virtual void drawIcon(IGUIElement *element, EGUI_DEFAULT_ICON icon, - const core::position2di position, - u32 starttime = 0, u32 currenttime = 0, - bool loop = false, const core::rect *clip = 0) override; + //! draws a standard 3d menu pane + /** Used for drawing for menus and context menus. + It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and + EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. + \param element: Pointer to the element which wishes to draw this. This parameter + is usually not used by ISkin, but can be used for example by more complex + implementations to find out how to draw the part exactly. + \param rect: Defining area where to draw. + \param clip: Clip area. */ + virtual void draw3DMenuPane(IGUIElement* element, + const core::rect& rect, + const core::rect* clip=0) + { + drawColored3DMenuPane(element, rect, clip); + } - //! draws a 2d rectangle. - /** \param element: Pointer to the element which wishes to draw this icon. - This parameter is usually not used by IGUISkin, but can be used for example - by more complex implementations to find out how to draw the part exactly. - \param color: Color of the rectangle to draw. The alpha component specifies how - transparent the rectangle will be. - \param pos: Position of the rectangle. - \param clip: Pointer to rectangle against which the rectangle will be clipped. - If the pointer is null, no clipping will be performed. */ - virtual void draw2DRectangle(IGUIElement *element, const video::SColor &color, - const core::rect &pos, const core::rect *clip = 0) override; + virtual void drawColored3DMenuPane(IGUIElement* element, + const core::rect& rect, + const core::rect* clip=0, + const video::SColor* colors=0); - //! get the type of this skin - EGUI_SKIN_TYPE getType() const override; + //! draws a standard 3d tool bar + /** Used for drawing for toolbars and menus. + \param element: Pointer to the element which wishes to draw this. This parameter + is usually not used by ISkin, but can be used for example by more complex + implementations to find out how to draw the part exactly. + \param rect: Defining area where to draw. + \param clip: Clip area. */ + virtual void draw3DToolBar(IGUIElement* element, + const core::rect& rect, + const core::rect* clip=0) + { + drawColored3DToolBar(element, rect, clip); + } -private: - float Scale = 1.0f; - video::SColor Colors[EGDC_COUNT]; - s32 Sizes[EGDS_COUNT]; - u32 Icons[EGDI_COUNT]; - IGUIFont *Fonts[EGDF_COUNT]; - IGUISpriteBank *SpriteBank; - core::stringw Texts[EGDT_COUNT]; - video::IVideoDriver *Driver; - bool UseGradient; + virtual void drawColored3DToolBar(IGUIElement* element, + const core::rect& rect, + const core::rect* clip=0, + const video::SColor* colors=0); - EGUI_SKIN_TYPE Type; -}; + //! draws a tab button + /** Used for drawing for tab buttons on top of tabs. + \param element: Pointer to the element which wishes to draw this. This parameter + is usually not used by ISkin, but can be used for example by more complex + implementations to find out how to draw the part exactly. + \param active: Specifies if the tab is currently active. + \param rect: Defining area where to draw. + \param clip: Clip area. */ + virtual void draw3DTabButton(IGUIElement* element, bool active, + const core::rect& rect, const core::rect* clip=0, EGUI_ALIGNMENT alignment=EGUIA_UPPERLEFT) + { + drawColored3DTabButton(element, active, rect, clip, alignment); + } + + virtual void drawColored3DTabButton(IGUIElement* element, bool active, + const core::rect& rect, const core::rect* clip=0, EGUI_ALIGNMENT alignment=EGUIA_UPPERLEFT, + const video::SColor* colors=0); + + //! draws a tab control body + /** \param element: Pointer to the element which wishes to draw this. This parameter + is usually not used by ISkin, but can be used for example by more complex + implementations to find out how to draw the part exactly. + \param border: Specifies if the border should be drawn. + \param background: Specifies if the background should be drawn. + \param rect: Defining area where to draw. + \param clip: Clip area. */ + virtual void draw3DTabBody(IGUIElement* element, bool border, bool background, + const core::rect& rect, const core::rect* clip=0, s32 tabHeight=-1, EGUI_ALIGNMENT alignment=EGUIA_UPPERLEFT) + { + drawColored3DTabBody(element, border, background, rect, clip, tabHeight, alignment); + } + + virtual void drawColored3DTabBody(IGUIElement* element, bool border, bool background, + const core::rect& rect, const core::rect* clip=0, s32 tabHeight=-1, EGUI_ALIGNMENT alignment=EGUIA_UPPERLEFT, + const video::SColor* colors=0); + + //! draws an icon, usually from the skin's sprite bank + /** \param element: Pointer to the element which wishes to draw this icon. + This parameter is usually not used by IGUISkin, but can be used for example + by more complex implementations to find out how to draw the part exactly. + \param icon: Specifies the icon to be drawn. + \param position: The position to draw the icon + \param starttime: The time at the start of the animation + \param currenttime: The present time, used to calculate the frame number + \param loop: Whether the animation should loop or not + \param clip: Clip area. */ + virtual void drawIcon(IGUIElement* element, EGUI_DEFAULT_ICON icon, + const core::position2di position, + u32 starttime=0, u32 currenttime=0, + bool loop=false, const core::rect* clip=0) + { + drawColoredIcon(element, icon, position, starttime, currenttime, loop, clip); + } + + virtual void drawColoredIcon(IGUIElement* element, EGUI_DEFAULT_ICON icon, + const core::position2di position, + u32 starttime=0, u32 currenttime=0, + bool loop=false, const core::rect* clip=0, + const video::SColor* colors=0); + + //! draws a 2d rectangle. + /** \param element: Pointer to the element which wishes to draw this icon. + This parameter is usually not used by IGUISkin, but can be used for example + by more complex implementations to find out how to draw the part exactly. + \param color: Color of the rectangle to draw. The alpha component specifies how + transparent the rectangle will be. + \param pos: Position of the rectangle. + \param clip: Pointer to rectangle against which the rectangle will be clipped. + If the pointer is null, no clipping will be performed. */ + virtual void draw2DRectangle(IGUIElement* element, const video::SColor &color, + const core::rect& pos, const core::rect* clip = 0); + + + //! get the type of this skin + virtual EGUI_SKIN_TYPE getType() const; + + //! gets the colors + virtual void getColors(video::SColor* colors); // ::PATCH: + + private: + + float Scale = 1.0f; + video::SColor Colors[EGDC_COUNT]; + s32 Sizes[EGDS_COUNT]; + u32 Icons[EGDI_COUNT]; + IGUIFont* Fonts[EGDF_COUNT]; + IGUISpriteBank* SpriteBank; + core::stringw Texts[EGDT_COUNT]; + video::IVideoDriver* Driver; + bool UseGradient; + + EGUI_SKIN_TYPE Type; + }; } // end namespace gui } // end namespace irr + diff --git a/irr/src/CMakeLists.txt b/irr/src/CMakeLists.txt index f5bc675e4..22a0d0093 100644 --- a/irr/src/CMakeLists.txt +++ b/irr/src/CMakeLists.txt @@ -468,6 +468,10 @@ foreach(object_lib target_include_directories(${object_lib} PRIVATE ${link_includes}) # Add objects from object library to main library target_sources(IrrlichtMt PRIVATE $) + + if(BUILD_WITH_TRACY) + target_link_libraries(${object_lib} PRIVATE Tracy::TracyClient) + endif() endforeach() # Alias target provides add_submodule compatibility @@ -506,6 +510,9 @@ target_link_libraries(IrrlichtMt PRIVATE if(WIN32) target_compile_definitions(IrrlichtMt INTERFACE _IRR_WINDOWS_API_) # used in _IRR_DEBUG_BREAK_IF definition in a public header endif() +if(CMAKE_BUILD_TYPE STREQUAL "Debug") + target_compile_definitions(IrrlichtMt INTERFACE _DEBUG) # same +endif() if(APPLE OR ANDROID OR EMSCRIPTEN) target_compile_definitions(IrrlichtMt PUBLIC IRR_MOBILE_PATHS) endif() diff --git a/irr/src/CMeshSceneNode.cpp b/irr/src/CMeshSceneNode.cpp index 7ff6cad55..030e1fd15 100644 --- a/irr/src/CMeshSceneNode.cpp +++ b/irr/src/CMeshSceneNode.cpp @@ -115,7 +115,6 @@ void CMeshSceneNode::render() // for debug purposes only: if (DebugDataVisible && PassCount == 1) { video::SMaterial m; - m.Lighting = false; m.AntiAliasing = 0; driver->setMaterial(m); diff --git a/irr/src/CNullDriver.cpp b/irr/src/CNullDriver.cpp index 6f261cef1..c7b296b57 100644 --- a/irr/src/CNullDriver.cpp +++ b/irr/src/CNullDriver.cpp @@ -104,7 +104,6 @@ CNullDriver::CNullDriver(io::IFileSystem *io, const core::dimension2d &scre FeatureEnabled[i] = true; InitMaterial2D.AntiAliasing = video::EAAM_OFF; - InitMaterial2D.Lighting = false; InitMaterial2D.ZWriteEnable = video::EZW_OFF; InitMaterial2D.ZBuffer = video::ECFN_DISABLED; InitMaterial2D.UseMipMaps = false; @@ -605,6 +604,7 @@ void CNullDriver::drawVertexPrimitiveList(const void *vertices, u32 vertexCount, { if ((iType == EIT_16BIT) && (vertexCount > 65536)) os::Printer::log("Too many vertices for 16bit index type, render artifacts may occur."); + FrameStats.Drawcalls++; FrameStats.PrimitivesDrawn += primitiveCount; } @@ -613,6 +613,7 @@ void CNullDriver::draw2DVertexPrimitiveList(const void *vertices, u32 vertexCoun { if ((iType == EIT_16BIT) && (vertexCount > 65536)) os::Printer::log("Too many vertices for 16bit index type, render artifacts may occur."); + FrameStats.Drawcalls++; FrameStats.PrimitivesDrawn += primitiveCount; } @@ -1129,15 +1130,10 @@ void CNullDriver::drawBuffers(const scene::IVertexBuffer *vb, void CNullDriver::drawMeshBufferNormals(const scene::IMeshBuffer *mb, f32 length, SColor color) { const u32 count = mb->getVertexCount(); - const bool normalize = mb->getMaterial().NormalizeNormals; - for (u32 i = 0; i < count; ++i) { - core::vector3df normalizedNormal = mb->getNormal(i); - if (normalize) - normalizedNormal.normalize(); - + core::vector3df normal = mb->getNormal(i); const core::vector3df &pos = mb->getPosition(i); - draw3DLine(pos, pos + (normalizedNormal * length), color); + draw3DLine(pos, pos + (normal * length), color); } } @@ -1304,10 +1300,8 @@ void CNullDriver::runOcclusionQuery(scene::ISceneNode *node, bool visible) OcclusionQueries[index].Run = 0; if (!visible) { SMaterial mat; - mat.Lighting = false; mat.AntiAliasing = 0; mat.ColorMask = ECP_NONE; - mat.GouraudShading = false; mat.ZWriteEnable = EZW_OFF; setMaterial(mat); } diff --git a/irr/src/COBJMeshFileLoader.cpp b/irr/src/COBJMeshFileLoader.cpp index bceba6a90..97e90c322 100644 --- a/irr/src/COBJMeshFileLoader.cpp +++ b/irr/src/COBJMeshFileLoader.cpp @@ -182,7 +182,7 @@ IAnimatedMesh *COBJMeshFileLoader::createMesh(io::IReadFile *file) mtlChanged = false; } if (currMtl) - v.Color = currMtl->Meshbuffer->Material.DiffuseColor; + v.Color = video::SColorf(0.8f, 0.8f, 0.8f, 1.0f).toSColor(); // get all vertices data in this face (current line of obj file) const core::stringc wordBuffer = copyLine(bufPtr, bufEnd); diff --git a/irr/src/COBJMeshFileLoader.h b/irr/src/COBJMeshFileLoader.h index 63e768e4f..467392a3e 100644 --- a/irr/src/COBJMeshFileLoader.h +++ b/irr/src/COBJMeshFileLoader.h @@ -43,10 +43,6 @@ private: RecalculateNormals(false) { Meshbuffer = new SMeshBuffer(); - Meshbuffer->Material.Shininess = 0.0f; - Meshbuffer->Material.AmbientColor = video::SColorf(0.2f, 0.2f, 0.2f, 1.0f).toSColor(); - Meshbuffer->Material.DiffuseColor = video::SColorf(0.8f, 0.8f, 0.8f, 1.0f).toSColor(); - Meshbuffer->Material.SpecularColor = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f).toSColor(); } SObjMtl(const SObjMtl &o) : diff --git a/irr/src/COpenGLDriver.cpp b/irr/src/COpenGLDriver.cpp index 837dc05d3..e5f070f8e 100644 --- a/irr/src/COpenGLDriver.cpp +++ b/irr/src/COpenGLDriver.cpp @@ -1840,112 +1840,11 @@ void COpenGLDriver::setBasicRenderStates(const SMaterial &material, const SMater E_OPENGL_FIXED_PIPELINE_STATE tempState = FixedPipelineState; if (resetAllRenderStates || tempState == EOFPS_ENABLE || tempState == EOFPS_DISABLE_TO_ENABLE) { - // material colors - if (resetAllRenderStates || tempState == EOFPS_DISABLE_TO_ENABLE || - lastmaterial.ColorMaterial != material.ColorMaterial) { - switch (material.ColorMaterial) { - case ECM_NONE: - glDisable(GL_COLOR_MATERIAL); - break; - case ECM_DIFFUSE: - glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE); - break; - case ECM_AMBIENT: - glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT); - break; - case ECM_EMISSIVE: - glColorMaterial(GL_FRONT_AND_BACK, GL_EMISSION); - break; - case ECM_SPECULAR: - glColorMaterial(GL_FRONT_AND_BACK, GL_SPECULAR); - break; - case ECM_DIFFUSE_AND_AMBIENT: - glColorMaterial(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE); - break; - } - if (material.ColorMaterial != ECM_NONE) - glEnable(GL_COLOR_MATERIAL); - } - - if (resetAllRenderStates || tempState == EOFPS_DISABLE_TO_ENABLE || - lastmaterial.AmbientColor != material.AmbientColor || - lastmaterial.DiffuseColor != material.DiffuseColor || - lastmaterial.EmissiveColor != material.EmissiveColor || - lastmaterial.ColorMaterial != material.ColorMaterial) { - GLfloat color[4]; - - const f32 inv = 1.0f / 255.0f; - - if ((material.ColorMaterial != video::ECM_AMBIENT) && - (material.ColorMaterial != video::ECM_DIFFUSE_AND_AMBIENT)) { - color[0] = material.AmbientColor.getRed() * inv; - color[1] = material.AmbientColor.getGreen() * inv; - color[2] = material.AmbientColor.getBlue() * inv; - color[3] = material.AmbientColor.getAlpha() * inv; - glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, color); - } - - if ((material.ColorMaterial != video::ECM_DIFFUSE) && - (material.ColorMaterial != video::ECM_DIFFUSE_AND_AMBIENT)) { - color[0] = material.DiffuseColor.getRed() * inv; - color[1] = material.DiffuseColor.getGreen() * inv; - color[2] = material.DiffuseColor.getBlue() * inv; - color[3] = material.DiffuseColor.getAlpha() * inv; - glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, color); - } - - if (material.ColorMaterial != video::ECM_EMISSIVE) { - color[0] = material.EmissiveColor.getRed() * inv; - color[1] = material.EmissiveColor.getGreen() * inv; - color[2] = material.EmissiveColor.getBlue() * inv; - color[3] = material.EmissiveColor.getAlpha() * inv; - glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, color); - } - } - - if (resetAllRenderStates || tempState == EOFPS_DISABLE_TO_ENABLE || - lastmaterial.SpecularColor != material.SpecularColor || - lastmaterial.Shininess != material.Shininess || - lastmaterial.ColorMaterial != material.ColorMaterial) { - GLfloat color[4] = {0.f, 0.f, 0.f, 1.f}; - const f32 inv = 1.0f / 255.0f; - - glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, material.Shininess); - // disable Specular colors if no shininess is set - if ((material.Shininess != 0.0f) && - (material.ColorMaterial != video::ECM_SPECULAR)) { -#ifdef GL_EXT_separate_specular_color - if (FeatureAvailable[IRR_EXT_separate_specular_color]) - glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR); -#endif - color[0] = material.SpecularColor.getRed() * inv; - color[1] = material.SpecularColor.getGreen() * inv; - color[2] = material.SpecularColor.getBlue() * inv; - color[3] = material.SpecularColor.getAlpha() * inv; - } -#ifdef GL_EXT_separate_specular_color - else if (FeatureAvailable[IRR_EXT_separate_specular_color]) - glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SINGLE_COLOR); -#endif - glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, color); - } - - // shademode - if (resetAllRenderStates || tempState == EOFPS_DISABLE_TO_ENABLE || - lastmaterial.GouraudShading != material.GouraudShading) { - if (material.GouraudShading) - glShadeModel(GL_SMOOTH); - else - glShadeModel(GL_FLAT); - } - - // lighting - if (resetAllRenderStates || tempState == EOFPS_DISABLE_TO_ENABLE || - lastmaterial.Lighting != material.Lighting) { - if (material.Lighting) - glEnable(GL_LIGHTING); - else - glDisable(GL_LIGHTING); + if (resetAllRenderStates || tempState == EOFPS_DISABLE_TO_ENABLE) { + glDisable(GL_COLOR_MATERIAL); + glDisable(GL_LIGHTING); + glShadeModel(GL_SMOOTH); + glDisable(GL_NORMALIZE); } // fog @@ -1957,15 +1856,6 @@ void COpenGLDriver::setBasicRenderStates(const SMaterial &material, const SMater glDisable(GL_FOG); } - // normalization - if (resetAllRenderStates || tempState == EOFPS_DISABLE_TO_ENABLE || - lastmaterial.NormalizeNormals != material.NormalizeNormals) { - if (material.NormalizeNormals) - glEnable(GL_NORMALIZE); - else - glDisable(GL_NORMALIZE); - } - // Set fixed pipeline as active. tempState = EOFPS_ENABLE; } else if (tempState == EOFPS_ENABLE_TO_DISABLE) { @@ -2405,7 +2295,6 @@ void COpenGLDriver::setRenderStates2DMode(bool alpha, bool texture, bool alphaCh } SMaterial currentMaterial = (!OverrideMaterial2DEnabled) ? InitMaterial2D : OverrideMaterial2D; - currentMaterial.Lighting = false; if (texture) { setTransform(ETS_TEXTURE_0, core::IdentityMatrix); diff --git a/irr/src/CXMeshFileLoader.cpp b/irr/src/CXMeshFileLoader.cpp index 483955805..fc0e6e237 100644 --- a/irr/src/CXMeshFileLoader.cpp +++ b/irr/src/CXMeshFileLoader.cpp @@ -109,10 +109,6 @@ bool CXMeshFileLoader::load(io::IReadFile *file) // default material if nothing loaded if (!mesh->Materials.size()) { mesh->Materials.push_back(video::SMaterial()); - mesh->Materials[0].DiffuseColor.set(0xff777777); - mesh->Materials[0].Shininess = 0.f; - mesh->Materials[0].SpecularColor.set(0xff777777); - mesh->Materials[0].EmissiveColor.set(0xff000000); } u32 i; @@ -142,7 +138,7 @@ bool CXMeshFileLoader::load(io::IReadFile *file) if (!mesh->HasVertexColors) { for (u32 j = 0; j < mesh->FaceMaterialIndices.size(); ++j) { for (u32 id = j * 3 + 0; id <= j * 3 + 2; ++id) { - mesh->Vertices[mesh->Indices[id]].Color = mesh->Buffers[mesh->FaceMaterialIndices[j]]->Material.DiffuseColor; + mesh->Vertices[mesh->Indices[id]].Color = 0xff777777; } } } diff --git a/irr/src/OpenGL/Driver.cpp b/irr/src/OpenGL/Driver.cpp index 46aa36d5c..fe9a24758 100644 --- a/irr/src/OpenGL/Driver.cpp +++ b/irr/src/OpenGL/Driver.cpp @@ -1096,8 +1096,11 @@ void COpenGL3DriverBase::setMaterial(const SMaterial &material) OverrideMaterial.apply(Material); for (u32 i = 0; i < Feature.MaxTextureUnits; ++i) { - CacheHandler->getTextureCache().set(i, material.getTexture(i)); - setTransform((E_TRANSFORMATION_STATE)(ETS_TEXTURE_0 + i), material.getTextureMatrix(i)); + auto *texture = material.getTexture(i); + CacheHandler->getTextureCache().set(i, texture); + if (texture) { + setTransform((E_TRANSFORMATION_STATE)(ETS_TEXTURE_0 + i), material.getTextureMatrix(i)); + } } } @@ -1475,10 +1478,8 @@ void COpenGL3DriverBase::chooseMaterial2D() Material = InitMaterial2D; if (OverrideMaterial2DEnabled) { - OverrideMaterial2D.Lighting = false; OverrideMaterial2D.ZWriteEnable = EZW_OFF; OverrideMaterial2D.ZBuffer = ECFN_DISABLED; // it will be ECFN_DISABLED after merge - OverrideMaterial2D.Lighting = false; Material = OverrideMaterial2D; } diff --git a/irr/src/OpenGL/FixedPipelineRenderer.cpp b/irr/src/OpenGL/FixedPipelineRenderer.cpp index 7c4adf719..cef446587 100644 --- a/irr/src/OpenGL/FixedPipelineRenderer.cpp +++ b/irr/src/OpenGL/FixedPipelineRenderer.cpp @@ -4,6 +4,7 @@ // For conditions of distribution and use, see copyright notice in Irrlicht.h #include "FixedPipelineRenderer.h" +#include "os.h" #include "IVideoDriver.h" @@ -15,24 +16,15 @@ namespace video // Base callback COpenGL3MaterialBaseCB::COpenGL3MaterialBaseCB() : - FirstUpdateBase(true), WVPMatrixID(-1), WVMatrixID(-1), NMatrixID(-1), + FirstUpdateBase(true), WVPMatrixID(-1), WVMatrixID(-1), FogEnableID(-1), FogTypeID(-1), FogColorID(-1), FogStartID(-1), - FogEndID(-1), FogDensityID(-1), ThicknessID(-1), LightEnable(false), MaterialAmbient(SColorf(0.f, 0.f, 0.f)), MaterialDiffuse(SColorf(0.f, 0.f, 0.f)), MaterialEmissive(SColorf(0.f, 0.f, 0.f)), MaterialSpecular(SColorf(0.f, 0.f, 0.f)), - MaterialShininess(0.f), FogEnable(0), FogType(1), FogColor(SColorf(0.f, 0.f, 0.f, 1.f)), FogStart(0.f), FogEnd(0.f), FogDensity(0.f), Thickness(1.f) + FogEndID(-1), FogDensityID(-1), ThicknessID(-1), Thickness(1.f), FogEnable(false) { } void COpenGL3MaterialBaseCB::OnSetMaterial(const SMaterial &material) { - LightEnable = material.Lighting; - MaterialAmbient = SColorf(material.AmbientColor); - MaterialDiffuse = SColorf(material.DiffuseColor); - MaterialEmissive = SColorf(material.EmissiveColor); - MaterialSpecular = SColorf(material.SpecularColor); - MaterialShininess = material.Shininess; - - FogEnable = material.FogEnable ? 1 : 0; - + FogEnable = material.FogEnable; Thickness = (material.Thickness > 0.f) ? material.Thickness : 1.f; } @@ -43,7 +35,6 @@ void COpenGL3MaterialBaseCB::OnSetConstants(IMaterialRendererServices *services, if (FirstUpdateBase) { WVPMatrixID = services->getVertexShaderConstantID("uWVPMatrix"); WVMatrixID = services->getVertexShaderConstantID("uWVMatrix"); - NMatrixID = services->getVertexShaderConstantID("uNMatrix"); FogEnableID = services->getVertexShaderConstantID("uFogEnable"); FogTypeID = services->getVertexShaderConstantID("uFogType"); @@ -56,31 +47,29 @@ void COpenGL3MaterialBaseCB::OnSetConstants(IMaterialRendererServices *services, FirstUpdateBase = false; } - const core::matrix4 W = driver->getTransform(ETS_WORLD); - const core::matrix4 V = driver->getTransform(ETS_VIEW); - const core::matrix4 P = driver->getTransform(ETS_PROJECTION); + const core::matrix4 &W = driver->getTransform(ETS_WORLD); + const core::matrix4 &V = driver->getTransform(ETS_VIEW); + const core::matrix4 &P = driver->getTransform(ETS_PROJECTION); - core::matrix4 Matrix = P * V * W; - services->setPixelShaderConstant(WVPMatrixID, Matrix.pointer(), 16); - - Matrix = V * W; + core::matrix4 Matrix = V * W; services->setPixelShaderConstant(WVMatrixID, Matrix.pointer(), 16); - Matrix.makeInverse(); - services->setPixelShaderConstant(NMatrixID, Matrix.getTransposed().pointer(), 16); + Matrix = P * Matrix; + services->setPixelShaderConstant(WVPMatrixID, Matrix.pointer(), 16); - services->setPixelShaderConstant(FogEnableID, &FogEnable, 1); + s32 TempEnable = FogEnable ? 1 : 0; + services->setPixelShaderConstant(FogEnableID, &TempEnable, 1); if (FogEnable) { SColor TempColor(0); E_FOG_TYPE TempType = EFT_FOG_LINEAR; - bool TempPerFragment = false; - bool TempRange = false; + f32 FogStart, FogEnd, FogDensity; + bool unused = false; - driver->getFog(TempColor, TempType, FogStart, FogEnd, FogDensity, TempPerFragment, TempRange); + driver->getFog(TempColor, TempType, FogStart, FogEnd, FogDensity, unused, unused); - FogType = (s32)TempType; - FogColor = SColorf(TempColor); + s32 FogType = (s32)TempType; + SColorf FogColor(TempColor); services->setPixelShaderConstant(FogTypeID, &FogType, 1); services->setPixelShaderConstant(FogColorID, reinterpret_cast(&FogColor), 4); diff --git a/irr/src/OpenGL/FixedPipelineRenderer.h b/irr/src/OpenGL/FixedPipelineRenderer.h index a07af1df9..89e251a35 100644 --- a/irr/src/OpenGL/FixedPipelineRenderer.h +++ b/irr/src/OpenGL/FixedPipelineRenderer.h @@ -26,7 +26,6 @@ protected: s32 WVPMatrixID; s32 WVMatrixID; - s32 NMatrixID; s32 FogEnableID; s32 FogTypeID; @@ -37,22 +36,8 @@ protected: s32 ThicknessID; - bool LightEnable; - SColorf GlobalAmbient; - SColorf MaterialAmbient; - SColorf MaterialDiffuse; - SColorf MaterialEmissive; - SColorf MaterialSpecular; - f32 MaterialShininess; - - s32 FogEnable; - s32 FogType; - SColorf FogColor; - f32 FogStart; - f32 FogEnd; - f32 FogDensity; - f32 Thickness; + bool FogEnable; }; class COpenGL3MaterialSolidCB : public COpenGL3MaterialBaseCB diff --git a/irr/src/os.h b/irr/src/os.h index 58699ab3e..bcc096f95 100644 --- a/irr/src/os.h +++ b/irr/src/os.h @@ -52,7 +52,6 @@ public: // prints out a string to the console out stdout or debug log or whatever static void print(const c8 *message, ELOG_LEVEL ll = ELL_INFORMATION); static void log(const c8 *message, ELOG_LEVEL ll = ELL_INFORMATION); - static void log(const wchar_t *message, ELOG_LEVEL ll = ELL_INFORMATION); // The string ": " is added between message and hint static void log(const c8 *message, const c8 *hint, ELOG_LEVEL ll = ELL_INFORMATION); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0ea8a40a9..cad22ca6f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -648,6 +648,9 @@ if(BUILD_CLIENT) if(BUILD_UNITTESTS OR BUILD_BENCHMARKS) target_link_libraries(${PROJECT_NAME} Catch2::Catch2) endif() + if(BUILD_WITH_TRACY) + target_link_libraries(${PROJECT_NAME} Tracy::TracyClient) + endif() if(PRECOMPILE_HEADERS) target_precompile_headers(${PROJECT_NAME} PRIVATE ${PRECOMPILED_HEADERS_LIST}) @@ -715,6 +718,9 @@ if(BUILD_SERVER) if(BUILD_UNITTESTS OR BUILD_BENCHMARKS) target_link_libraries(${PROJECT_NAME}server Catch2::Catch2) endif() + if(BUILD_WITH_TRACY) + target_link_libraries(${PROJECT_NAME}server Tracy::TracyClient) + endif() if(PRECOMPILE_HEADERS) target_precompile_headers(${PROJECT_NAME}server PRIVATE ${PRECOMPILED_HEADERS_LIST}) diff --git a/src/client/camera.cpp b/src/client/camera.cpp index f13046848..bf9ec0bd5 100644 --- a/src/client/camera.cpp +++ b/src/client/camera.cpp @@ -63,7 +63,7 @@ Camera::Camera(MapDrawControl &draw_control, Client *client, RenderingEngine *re // all other 3D scene nodes and before the GUI. m_wieldmgr = smgr->createNewSceneManager(); m_wieldmgr->addCameraSceneNode(); - m_wieldnode = new WieldMeshSceneNode(m_wieldmgr, -1, false); + m_wieldnode = new WieldMeshSceneNode(m_wieldmgr, -1); m_wieldnode->setItem(ItemStack(), m_client); m_wieldnode->drop(); // m_wieldmgr grabbed it diff --git a/src/client/client.cpp b/src/client/client.cpp index dedbebbb4..1a2f51db9 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -120,7 +120,7 @@ Client::Client( m_rendering_engine(rendering_engine), m_mesh_update_manager(std::make_unique(this)), m_env( - new ClientMap(this, rendering_engine, control, 666), + make_irr(this, rendering_engine, control, 666), tsrc, this ), m_particle_manager(std::make_unique(&m_env)), @@ -1359,9 +1359,9 @@ void Client::sendDamage(u16 damage) Send(&pkt); } -void Client::sendRespawn() +void Client::sendRespawnLegacy() { - NetworkPacket pkt(TOSERVER_RESPAWN, 0); + NetworkPacket pkt(TOSERVER_RESPAWN_LEGACY, 0); Send(&pkt); } diff --git a/src/client/client.h b/src/client/client.h index adbe7a70d..f9f77ede4 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -194,7 +194,7 @@ public: void handleCommand_Breath(NetworkPacket* pkt); void handleCommand_MovePlayer(NetworkPacket* pkt); void handleCommand_MovePlayerRel(NetworkPacket* pkt); - void handleCommand_DeathScreen(NetworkPacket* pkt); + void handleCommand_DeathScreenLegacy(NetworkPacket* pkt); void handleCommand_AnnounceMedia(NetworkPacket* pkt); void handleCommand_Media(NetworkPacket* pkt); void handleCommand_NodeDef(NetworkPacket* pkt); @@ -249,7 +249,7 @@ public: void sendChangePassword(const std::string &oldpassword, const std::string &newpassword); void sendDamage(u16 damage); - void sendRespawn(); + void sendRespawnLegacy(); void sendReady(); void sendHaveMedia(const std::vector &tokens); void sendUpdateClientInfo(const ClientDynamicInfo &info); diff --git a/src/client/clientenvironment.cpp b/src/client/clientenvironment.cpp index f1ec14d3b..08c5586ea 100644 --- a/src/client/clientenvironment.cpp +++ b/src/client/clientenvironment.cpp @@ -43,10 +43,10 @@ with this program; if not, write to the Free Software Foundation, Inc., ClientEnvironment */ -ClientEnvironment::ClientEnvironment(ClientMap *map, +ClientEnvironment::ClientEnvironment(irr_ptr map, ITextureSource *texturesource, Client *client): Environment(client), - m_map(map), + m_map(std::move(map)), m_texturesource(texturesource), m_client(client) { @@ -60,18 +60,17 @@ ClientEnvironment::~ClientEnvironment() delete simple_object; } - // Drop/delete map - m_map->drop(); + m_map.reset(); delete m_local_player; } -Map & ClientEnvironment::getMap() +Map &ClientEnvironment::getMap() { return *m_map; } -ClientMap & ClientEnvironment::getClientMap() +ClientMap &ClientEnvironment::getClientMap() { return *m_map; } diff --git a/src/client/clientenvironment.h b/src/client/clientenvironment.h index bdb8b9726..db31e69f2 100644 --- a/src/client/clientenvironment.h +++ b/src/client/clientenvironment.h @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "environment.h" #include "util/numeric.h" // IntervalLimiter #include "activeobjectmgr.h" // client::ActiveObjectMgr +#include "irr_ptr.h" #include #ifdef SERVER @@ -66,7 +67,7 @@ typedef std::unordered_map ClientActiveObjectMap; class ClientEnvironment : public Environment { public: - ClientEnvironment(ClientMap *map, ITextureSource *texturesource, Client *client); + ClientEnvironment(irr_ptr map, ITextureSource *texturesource, Client *client); ~ClientEnvironment(); Map & getMap(); @@ -151,7 +152,7 @@ public: u64 getFrameTimeDelta() const { return m_frame_dtime; } private: - ClientMap *m_map; + irr_ptr m_map; LocalPlayer *m_local_player = nullptr; ITextureSource *m_texturesource; Client *m_client; diff --git a/src/client/clientevent.h b/src/client/clientevent.h index a53c007dd..8c505786b 100644 --- a/src/client/clientevent.h +++ b/src/client/clientevent.h @@ -35,7 +35,7 @@ enum ClientEventType : u8 CE_NONE, CE_PLAYER_DAMAGE, CE_PLAYER_FORCE_MOVE, - CE_DEATHSCREEN, + CE_DEATHSCREEN_LEGACY, CE_SHOW_FORMSPEC, CE_SHOW_LOCAL_FORMSPEC, CE_SPAWN_PARTICLE, @@ -96,13 +96,6 @@ struct ClientEvent f32 yaw; } player_force_move; struct - { - bool set_camera_point_target; - f32 camera_point_target_x; - f32 camera_point_target_y; - f32 camera_point_target_z; - } deathscreen; - struct { std::string *formspec; std::string *formname; @@ -137,6 +130,7 @@ struct ClientEvent f32 density; u32 color_bright; u32 color_ambient; + u32 color_shadow; f32 height; f32 thickness; f32 speed_x; diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index d30a9d89d..03d3b5ea6 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -35,6 +35,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "version.h" #include "renderingengine.h" #include "network/networkexceptions.h" +#include "util/tracy_wrapper.h" #include #include #include @@ -140,8 +141,10 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args) // Create the menu clouds // This is only global so it can be used by RenderingEngine::draw_load_screen(). assert(!g_menucloudsmgr && !g_menuclouds); + std::unique_ptr ssrc(createShaderSource()); + ssrc->addShaderConstantSetterFactory(new FogShaderConstantSetterFactory()); g_menucloudsmgr = m_rendering_engine->get_scene_manager()->createNewSceneManager(); - g_menuclouds = new Clouds(g_menucloudsmgr, nullptr, -1, rand()); + g_menuclouds = new Clouds(g_menucloudsmgr, ssrc.get(), -1, rand()); g_menuclouds->setHeight(100.0f); g_menuclouds->update(v3f(0, 0, 0), video::SColor(255, 240, 240, 255)); scene::ICameraSceneNode* camera; @@ -546,15 +549,19 @@ void ClientLauncher::main_menu(MainMenuData *menudata) video::IVideoDriver *driver = m_rendering_engine->get_video_driver(); infostream << "Waiting for other menus" << std::endl; + auto framemarker = FrameMarker("ClientLauncher::main_menu()-wait-frame").started(); while (m_rendering_engine->run() && !*kill) { if (!isMenuActive()) break; driver->beginScene(true, true, video::SColor(255, 128, 128, 128)); m_rendering_engine->get_gui_env()->drawAll(); driver->endScene(); + framemarker.end(); // On some computers framerate doesn't seem to be automatically limited sleep_ms(25); + framemarker.start(); } + framemarker.end(); infostream << "Waited for other menus" << std::endl; auto *cur_control = m_rendering_engine->get_raw_device()->getCursorControl(); diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index b4356dc27..d608ae2f6 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "camera.h" // CameraModes #include "util/basic_macros.h" +#include "util/tracy_wrapper.h" #include "client/renderingengine.h" #include @@ -714,6 +715,8 @@ void ClientMap::touchMapBlocks() void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) { + ZoneScoped; + bool is_transparent_pass = pass == scene::ESNRP_TRANSPARENT; std::string prefix; @@ -1206,11 +1209,22 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver, // Render all mesh buffers in order drawcall_count += draw_order.size(); + bool translucent_foliage = g_settings->getBool("enable_translucent_foliage"); + + video::E_MATERIAL_TYPE leaves_material = video::EMT_SOLID; + + // For translucent leaves, we want to use backface culling instead of frontface. + if (translucent_foliage) { + // this is the material leaves would use, compare to nodedef.cpp + auto* shdsrc = m_client->getShaderSource(); + const u32 leaves_shader = shdsrc->getShader("nodes_shader", TILE_MATERIAL_WAVING_LEAVES, NDT_ALLFACES); + leaves_material = shdsrc->getShaderInfo(leaves_shader).material; + } + for (auto &descriptor : draw_order) { if (!descriptor.m_reuse_material) { // override some material properties video::SMaterial local_material = descriptor.getMaterial(); - local_material.MaterialType = material.MaterialType; // do not override culling if the original material renders both back // and front faces in solid mode (e.g. plantlike) // Transparent plants would still render shadows only from one side, @@ -1219,8 +1233,12 @@ void ClientMap::renderMapShadows(video::IVideoDriver *driver, local_material.BackfaceCulling = material.BackfaceCulling; local_material.FrontfaceCulling = material.FrontfaceCulling; } + if (local_material.MaterialType == leaves_material && translucent_foliage) { + local_material.BackfaceCulling = true; + local_material.FrontfaceCulling = false; + } + local_material.MaterialType = material.MaterialType; local_material.BlendOperation = material.BlendOperation; - local_material.Lighting = false; driver->setMaterial(local_material); ++material_swaps; } diff --git a/src/client/clouds.cpp b/src/client/clouds.cpp index 64e4b775a..96926f3e0 100644 --- a/src/client/clouds.cpp +++ b/src/client/clouds.cpp @@ -28,11 +28,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include - -// Menu clouds are created later class Clouds; -Clouds *g_menuclouds = NULL; -scene::ISceneManager *g_menucloudsmgr = NULL; +scene::ISceneManager *g_menucloudsmgr = nullptr; +Clouds *g_menuclouds = nullptr; // Constant for now static constexpr const float cloud_size = BS * 64.0f; @@ -49,11 +47,9 @@ Clouds::Clouds(scene::ISceneManager* mgr, IShaderSource *ssrc, scene::ISceneNode(mgr->getRootSceneNode(), mgr, id), m_seed(seed) { + assert(ssrc); m_enable_shaders = g_settings->getBool("enable_shaders"); - // menu clouds use shader-less clouds for simplicity (ssrc == NULL) - m_enable_shaders = m_enable_shaders && ssrc; - m_material.Lighting = false; m_material.BackfaceCulling = true; m_material.FogEnable = true; m_material.AntiAliasing = video::EAAM_SIMPLE; @@ -69,6 +65,8 @@ Clouds::Clouds(scene::ISceneManager* mgr, IShaderSource *ssrc, readSettings(); g_settings->registerChangedCallback("enable_3d_clouds", &cloud_3d_setting_changed, this); + g_settings->registerChangedCallback("soft_clouds", + &cloud_3d_setting_changed, this); updateBox(); @@ -80,6 +78,8 @@ Clouds::~Clouds() { g_settings->deregisterChangedCallback("enable_3d_clouds", &cloud_3d_setting_changed, this); + g_settings->deregisterChangedCallback("soft_clouds", + &cloud_3d_setting_changed, this); } void Clouds::OnRegisterSceneNode() @@ -142,18 +142,21 @@ void Clouds::updateMesh() video::SColorf c_side_2_f(m_color); video::SColorf c_bottom_f(m_color); if (m_enable_shaders) { - // shader mixes the base color, set via EmissiveColor + // shader mixes the base color, set via ColorParam c_top_f = c_side_1_f = c_side_2_f = c_bottom_f = video::SColorf(1.0f, 1.0f, 1.0f, 1.0f); } - c_side_1_f.r *= 0.95f; - c_side_1_f.g *= 0.95f; - c_side_1_f.b *= 0.95f; - c_side_2_f.r *= 0.90f; - c_side_2_f.g *= 0.90f; - c_side_2_f.b *= 0.90f; - c_bottom_f.r *= 0.80f; - c_bottom_f.g *= 0.80f; - c_bottom_f.b *= 0.80f; + video::SColorf shadow = m_params.color_shadow; + + c_side_1_f.r *= shadow.r * 0.25f + 0.75f; + c_side_1_f.g *= shadow.g * 0.25f + 0.75f; + c_side_1_f.b *= shadow.b * 0.25f + 0.75f; + c_side_2_f.r *= shadow.r * 0.5f + 0.5f; + c_side_2_f.g *= shadow.g * 0.5f + 0.5f; + c_side_2_f.b *= shadow.b * 0.5f + 0.5f; + c_bottom_f.r *= shadow.r; + c_bottom_f.g *= shadow.g; + c_bottom_f.b *= shadow.b; + video::SColor c_top = c_top_f.toSColor(); video::SColor c_side_1 = c_side_1_f.toSColor(); video::SColor c_side_2 = c_side_2_f.toSColor(); @@ -225,13 +228,14 @@ void Clouds::updateMesh() const f32 ry = is3D() ? m_params.thickness * BS : 0.0f; const f32 rz = cloud_size / 2; - for(u32 i = 0; i < num_faces_to_draw; i++) + bool soft_clouds_enabled = g_settings->getBool("soft_clouds"); + for (u32 i = 0; i < num_faces_to_draw; i++) { - switch(i) + switch (i) { case 0: // top - for (video::S3DVertex &vertex : v) { - vertex.Normal.set(0,1,0); + for (video::S3DVertex& vertex : v) { + vertex.Normal.set(0, 1, 0); } v[0].Pos.set(-rx, ry,-rz); v[1].Pos.set(-rx, ry, rz); @@ -241,12 +245,20 @@ void Clouds::updateMesh() case 1: // back if (INAREA(xi, zi - 1, m_cloud_radius_i)) { u32 j = GETINDEX(xi, zi - 1, m_cloud_radius_i); - if(grid[j]) + if (grid[j]) continue; } - for (video::S3DVertex &vertex : v) { - vertex.Color = c_side_1; - vertex.Normal.set(0,0,-1); + if (soft_clouds_enabled) { + for (video::S3DVertex& vertex : v) { + vertex.Normal.set(0, 0, -1); + } + v[2].Color = c_bottom; + v[3].Color = c_bottom; + } else { + for (video::S3DVertex& vertex : v) { + vertex.Color = c_side_1; + vertex.Normal.set(0, 0, -1); + } } v[0].Pos.set(-rx, ry,-rz); v[1].Pos.set( rx, ry,-rz); @@ -255,28 +267,45 @@ void Clouds::updateMesh() break; case 2: //right if (INAREA(xi + 1, zi, m_cloud_radius_i)) { - u32 j = GETINDEX(xi+1, zi, m_cloud_radius_i); - if(grid[j]) + u32 j = GETINDEX(xi + 1, zi, m_cloud_radius_i); + if (grid[j]) continue; } - for (video::S3DVertex &vertex : v) { - vertex.Color = c_side_2; - vertex.Normal.set(1,0,0); + if (soft_clouds_enabled) { + for (video::S3DVertex& vertex : v) { + vertex.Normal.set(1, 0, 0); + } + v[2].Color = c_bottom; + v[3].Color = c_bottom; } - v[0].Pos.set( rx, ry,-rz); - v[1].Pos.set( rx, ry, rz); - v[2].Pos.set( rx, 0, rz); - v[3].Pos.set( rx, 0,-rz); + else { + for (video::S3DVertex& vertex : v) { + vertex.Color = c_side_2; + vertex.Normal.set(1, 0, 0); + } + } + v[0].Pos.set(rx, ry,-rz); + v[1].Pos.set(rx, ry, rz); + v[2].Pos.set(rx, 0, rz); + v[3].Pos.set(rx, 0,-rz); break; case 3: // front if (INAREA(xi, zi + 1, m_cloud_radius_i)) { u32 j = GETINDEX(xi, zi + 1, m_cloud_radius_i); - if(grid[j]) + if (grid[j]) continue; } - for (video::S3DVertex &vertex : v) { - vertex.Color = c_side_1; - vertex.Normal.set(0,0,-1); + if (soft_clouds_enabled) { + for (video::S3DVertex& vertex : v) { + vertex.Normal.set(0, 0, -1); + } + v[2].Color = c_bottom; + v[3].Color = c_bottom; + } else { + for (video::S3DVertex& vertex : v) { + vertex.Color = c_side_1; + vertex.Normal.set(0, 0, -1); + } } v[0].Pos.set( rx, ry, rz); v[1].Pos.set(-rx, ry, rz); @@ -284,14 +313,22 @@ void Clouds::updateMesh() v[3].Pos.set( rx, 0, rz); break; case 4: // left - if (INAREA(xi-1, zi, m_cloud_radius_i)) { - u32 j = GETINDEX(xi-1, zi, m_cloud_radius_i); - if(grid[j]) + if (INAREA(xi - 1, zi, m_cloud_radius_i)) { + u32 j = GETINDEX(xi - 1, zi, m_cloud_radius_i); + if (grid[j]) continue; } - for (video::S3DVertex &vertex : v) { - vertex.Color = c_side_2; - vertex.Normal.set(-1,0,0); + if (soft_clouds_enabled) { + for (video::S3DVertex& vertex : v) { + vertex.Normal.set(-1, 0, 0); + } + v[2].Color = c_bottom; + v[3].Color = c_bottom; + } else { + for (video::S3DVertex& vertex : v) { + vertex.Color = c_side_2; + vertex.Normal.set(-1, 0, 0); + } } v[0].Pos.set(-rx, ry, rz); v[1].Pos.set(-rx, ry,-rz); @@ -299,9 +336,9 @@ void Clouds::updateMesh() v[3].Pos.set(-rx, 0, rz); break; case 5: // bottom - for (video::S3DVertex &vertex : v) { + for (video::S3DVertex& vertex : v) { vertex.Color = c_bottom; - vertex.Normal.set(0,-1,0); + vertex.Normal.set(0, -1, 0); } v[0].Pos.set( rx, 0, rz); v[1].Pos.set(-rx, 0, rz); @@ -367,7 +404,7 @@ void Clouds::render() m_material.BackfaceCulling = is3D(); if (m_enable_shaders) - m_material.EmissiveColor = m_color.toSColor(); + m_material.ColorParam = m_color.toSColor(); driver->setTransform(video::ETS_WORLD, AbsoluteTransformation); driver->setMaterial(m_material); diff --git a/src/client/clouds.h b/src/client/clouds.h index 332aa81e9..7193c03f9 100644 --- a/src/client/clouds.h +++ b/src/client/clouds.h @@ -36,11 +36,11 @@ namespace irr::scene } // Menu clouds +// The mainmenu and the loading screen use the same Clouds object so that the +// clouds don't jump when switching between the two. class Clouds; -extern Clouds *g_menuclouds; - -// Scene manager used for menu clouds extern scene::ISceneManager *g_menucloudsmgr; +extern Clouds *g_menuclouds; class Clouds : public scene::ISceneNode { @@ -109,6 +109,14 @@ public: m_params.color_ambient = color_ambient; } + void setColorShadow(video::SColor color_shadow) + { + if (m_params.color_shadow == color_shadow) + return; + m_params.color_shadow = color_shadow; + invalidateMesh(); + } + void setHeight(float height) { if (m_params.height == height) diff --git a/src/client/content_cao.cpp b/src/client/content_cao.cpp index 563fe0abd..adec70983 100644 --- a/src/client/content_cao.cpp +++ b/src/client/content_cao.cpp @@ -186,6 +186,12 @@ static bool logOnce(const std::ostringstream &from, std::ostream &log_to) return true; } +static void setColorParam(scene::ISceneNode *node, video::SColor color) +{ + for (u32 i = 0; i < node->getMaterialCount(); ++i) + node->getMaterial(i).ColorParam = color; +} + /* TestCAO */ @@ -255,7 +261,6 @@ void TestCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr) u16 indices[] = {0,1,2,2,3,0}; buf->append(vertices, 4, indices, 6); // Set material - buf->getMaterial().Lighting = false; buf->getMaterial().BackfaceCulling = false; buf->getMaterial().TextureLayers[0].Texture = tsrc->getTextureForMesh("rat.png"); buf->getMaterial().TextureLayers[0].MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST; @@ -642,12 +647,7 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr) auto setMaterial = [this] (video::SMaterial &mat) { mat.MaterialType = m_material_type; - mat.Lighting = false; mat.FogEnable = true; - if (m_enable_shaders) { - mat.GouraudShading = false; - mat.NormalizeNormals = true; - } mat.forEachTexture([] (auto &tex) { tex.MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST; tex.MagFilter = video::ETMAGF_NEAREST; @@ -704,7 +704,7 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr) // Set material setMaterial(buf->getMaterial()); if (m_enable_shaders) { - buf->getMaterial().EmissiveColor = c; + buf->getMaterial().ColorParam = c; } // Add to mesh @@ -730,7 +730,7 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr) // Set material setMaterial(buf->getMaterial()); if (m_enable_shaders) { - buf->getMaterial().EmissiveColor = c; + buf->getMaterial().ColorParam = c; } // Add to mesh @@ -774,8 +774,6 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr) // set vertex colors to ensure alpha is set setMeshColor(m_animated_meshnode->getMesh(), video::SColor(0xFFFFFFFF)); - setAnimatedMeshColor(m_animated_meshnode, video::SColor(0xFFFFFFFF)); - setSceneNodeMaterials(m_animated_meshnode); m_animated_meshnode->forEachMaterial([this] (auto &mat) { @@ -810,15 +808,21 @@ void GenericCAO::addToScene(ITextureSource *tsrc, scene::ISceneManager *smgr) } /* Set VBO hint */ - // - if shaders are disabled we modify the mesh often - // - sprites are also modified often - // - the wieldmesh sets its own hint - // - bone transformations do not need to modify the vertex data + // wieldmesh sets its own hint, no need to handle it if (m_enable_shaders && (m_meshnode || m_animated_meshnode)) { - if (m_meshnode) + // sprite uses vertex animation + if (m_meshnode && m_prop.visual != "upright_sprite") m_meshnode->getMesh()->setHardwareMappingHint(scene::EHM_STATIC); - if (m_animated_meshnode) - m_animated_meshnode->getMesh()->setHardwareMappingHint(scene::EHM_STATIC); + + if (m_animated_meshnode) { + auto *mesh = m_animated_meshnode->getMesh(); + // skinning happens on the CPU + if (m_animated_meshnode->getJointCount() > 0) + mesh->setHardwareMappingHint(scene::EHM_STREAM, scene::EBT_VERTEX); + else + mesh->setHardwareMappingHint(scene::EHM_STATIC, scene::EBT_VERTEX); + mesh->setHardwareMappingHint(scene::EHM_STATIC, scene::EBT_INDEX); + } } /* don't update while punch texture modifier is active */ @@ -923,26 +927,15 @@ void GenericCAO::setNodeLight(const video::SColor &light_color) } if (m_enable_shaders) { - if (m_prop.visual == "upright_sprite") { - if (!m_meshnode) - return; - for (u32 i = 0; i < m_meshnode->getMaterialCount(); ++i) - m_meshnode->getMaterial(i).EmissiveColor = light_color; - } else { - scene::ISceneNode *node = getSceneNode(); - if (!node) - return; - - for (u32 i = 0; i < node->getMaterialCount(); ++i) { - video::SMaterial &material = node->getMaterial(i); - material.EmissiveColor = light_color; - } - } + auto *node = getSceneNode(); + if (!node) + return; + setColorParam(node, light_color); } else { if (m_meshnode) { setMeshColor(m_meshnode->getMesh(), light_color); } else if (m_animated_meshnode) { - setAnimatedMeshColor(m_animated_meshnode, light_color); + setMeshColor(m_animated_meshnode->getMesh(), light_color); } else if (m_spritenode) { m_spritenode->setColor(light_color); } @@ -1264,6 +1257,16 @@ void GenericCAO::step(float dtime, ClientEnvironment *env) } } +static void setMeshBufferTextureCoords(scene::IMeshBuffer *buf, const v2f *uv, u32 count) +{ + assert(buf->getVertexType() == video::EVT_STANDARD); + assert(buf->getVertexCount() == count); + auto *vertices = static_cast(buf->getVertices()); + for (u32 i = 0; i < count; i++) + vertices[i].TCoords = uv[i]; + buf->setDirty(scene::EBT_VERTEX); +} + void GenericCAO::updateTexturePos() { if(m_spritenode) @@ -1357,15 +1360,6 @@ void GenericCAO::updateTextures(std::string mod) material.MaterialTypeParam = m_material_type_param; material.setTexture(0, tsrc->getTextureForMesh(texturestring)); - // This allows setting per-material colors. However, until a real lighting - // system is added, the code below will have no effect. Once MineTest - // has directional lighting, it should work automatically. - if (!m_prop.colors.empty()) { - material.AmbientColor = m_prop.colors[0]; - material.DiffuseColor = m_prop.colors[0]; - material.SpecularColor = m_prop.colors[0]; - } - material.forEachTexture([=] (auto &tex) { setMaterialFilters(tex, use_bilinear_filter, use_trilinear_filter, use_anisotropic_filter); @@ -1394,7 +1388,6 @@ void GenericCAO::updateTextures(std::string mod) material.MaterialType = m_material_type; material.MaterialTypeParam = m_material_type_param; material.TextureLayers[0].Texture = texture; - material.Lighting = true; material.BackfaceCulling = m_prop.backface_culling; // don't filter low-res textures, makes them look blurry @@ -1409,17 +1402,6 @@ void GenericCAO::updateTextures(std::string mod) use_anisotropic_filter); }); } - for (u32 i = 0; i < m_prop.colors.size() && - i < m_animated_meshnode->getMaterialCount(); ++i) - { - video::SMaterial &material = m_animated_meshnode->getMaterial(i); - // This allows setting per-material colors. However, until a real lighting - // system is added, the code below will have no effect. Once MineTest - // has directional lighting, it should work automatically. - material.AmbientColor = m_prop.colors[i]; - material.DiffuseColor = m_prop.colors[i]; - material.SpecularColor = m_prop.colors[i]; - } } } @@ -1437,20 +1419,9 @@ void GenericCAO::updateTextures(std::string mod) video::SMaterial &material = m_meshnode->getMaterial(i); material.MaterialType = m_material_type; material.MaterialTypeParam = m_material_type_param; - material.Lighting = false; material.setTexture(0, tsrc->getTextureForMesh(texturestring)); material.getTextureMatrix(0).makeIdentity(); - // This allows setting per-material colors. However, until a real lighting - // system is added, the code below will have no effect. Once MineTest - // has directional lighting, it should work automatically. - if(m_prop.colors.size() > i) - { - material.AmbientColor = m_prop.colors[i]; - material.DiffuseColor = m_prop.colors[i]; - material.SpecularColor = m_prop.colors[i]; - } - material.forEachTexture([=] (auto &tex) { setMaterialFilters(tex, use_bilinear_filter, use_trilinear_filter, use_anisotropic_filter); @@ -1467,15 +1438,6 @@ void GenericCAO::updateTextures(std::string mod) auto &material = m_meshnode->getMaterial(0); material.setTexture(0, tsrc->getTextureForMesh(tname)); - // This allows setting per-material colors. However, until a real lighting - // system is added, the code below will have no effect. Once MineTest - // has directional lighting, it should work automatically. - if(!m_prop.colors.empty()) { - material.AmbientColor = m_prop.colors[0]; - material.DiffuseColor = m_prop.colors[0]; - material.SpecularColor = m_prop.colors[0]; - } - material.forEachTexture([=] (auto &tex) { setMaterialFilters(tex, use_bilinear_filter, use_trilinear_filter, use_anisotropic_filter); @@ -1492,19 +1454,6 @@ void GenericCAO::updateTextures(std::string mod) auto &material = m_meshnode->getMaterial(1); material.setTexture(0, tsrc->getTextureForMesh(tname)); - // This allows setting per-material colors. However, until a real lighting - // system is added, the code below will have no effect. Once MineTest - // has directional lighting, it should work automatically. - if (m_prop.colors.size() >= 2) { - material.AmbientColor = m_prop.colors[1]; - material.DiffuseColor = m_prop.colors[1]; - material.SpecularColor = m_prop.colors[1]; - } else if (!m_prop.colors.empty()) { - material.AmbientColor = m_prop.colors[0]; - material.DiffuseColor = m_prop.colors[0]; - material.SpecularColor = m_prop.colors[0]; - } - material.forEachTexture([=] (auto &tex) { setMaterialFilters(tex, use_bilinear_filter, use_trilinear_filter, use_anisotropic_filter); diff --git a/src/client/content_cso.cpp b/src/client/content_cso.cpp index 821c2507d..733044b23 100644 --- a/src/client/content_cso.cpp +++ b/src/client/content_cso.cpp @@ -40,7 +40,6 @@ public: video::ITexture *tex = env->getGameDef()->tsrc()->getTextureForMesh("smoke_puff.png"); m_spritenode->forEachMaterial([tex] (auto &mat) { mat.TextureLayers[0].Texture = tex; - mat.Lighting = false; mat.TextureLayers[0].MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST; mat.TextureLayers[0].MagFilter = video::ETMAGF_NEAREST; mat.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; diff --git a/src/client/content_mapblock.cpp b/src/client/content_mapblock.cpp index 4f4056668..6ce3ca0f4 100644 --- a/src/client/content_mapblock.cpp +++ b/src/client/content_mapblock.cpp @@ -22,6 +22,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/basic_macros.h" #include "util/numeric.h" #include "util/directiontables.h" +#include "util/tracy_wrapper.h" #include "mapblock_mesh.h" #include "settings.h" #include "nodedef.h" @@ -82,7 +83,8 @@ MapblockMeshGenerator::MapblockMeshGenerator(MeshMakeData *input, MeshCollector meshmanip(mm), blockpos_nodes(data->m_blockpos * MAP_BLOCKSIZE), enable_mesh_cache(g_settings->getBool("enable_mesh_cache") && - !data->m_smooth_lighting) // Mesh cache is not supported with smooth lighting + !data->m_smooth_lighting), // Mesh cache is not supported with smooth lighting + smooth_liquids(g_settings->getBool("enable_water_reflections")) { } @@ -716,7 +718,7 @@ void MapblockMeshGenerator::drawLiquidSides() if (data->m_smooth_lighting) cur_node.color = blendLightColor(pos); pos += cur_node.origin; - vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, 0, 0, 0, cur_node.color, vertex.u, v); + vertices[j] = video::S3DVertex(pos.X, pos.Y, pos.Z, face.dir.X, face.dir.Y, face.dir.Z, cur_node.color, vertex.u, v); }; collector->append(cur_liquid.tile, vertices, 4, quad_indices, 6); } @@ -739,6 +741,19 @@ void MapblockMeshGenerator::drawLiquidTop() for (int i = 0; i < 4; i++) { int u = corner_resolve[i][0]; int w = corner_resolve[i][1]; + + if (smooth_liquids) { + int x = vertices[i].Pos.X > 0; + int z = vertices[i].Pos.Z > 0; + + f32 dx = 0.5f * (cur_liquid.neighbors[z][x].level - cur_liquid.neighbors[z][x + 1].level + + cur_liquid.neighbors[z + 1][x].level - cur_liquid.neighbors[z + 1][x + 1].level); + f32 dz = 0.5f * (cur_liquid.neighbors[z][x].level - cur_liquid.neighbors[z + 1][x].level + + cur_liquid.neighbors[z][x + 1].level - cur_liquid.neighbors[z + 1][x + 1].level); + + vertices[i].Normal = v3f(dx, 1., dz).normalize(); + } + vertices[i].Pos.Y += cur_liquid.corner_levels[w][u] * BS; if (data->m_smooth_lighting) vertices[i].Color = blendLightColor(vertices[i].Pos); @@ -778,6 +793,10 @@ void MapblockMeshGenerator::drawLiquidTop() vertex.TCoords += tcoord_center; vertex.TCoords += tcoord_translate; + + if (!smooth_liquids) { + vertex.Normal = v3f(dx, 1., dz).normalize(); + } } std::swap(vertices[0].TCoords, vertices[2].TCoords); @@ -997,13 +1016,6 @@ void MapblockMeshGenerator::drawGlasslikeFramedNode() } } -void MapblockMeshGenerator::drawAllfacesNode() -{ - static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2); - useTile(0, 0, 0); - drawAutoLightedCuboid(box); -} - void MapblockMeshGenerator::drawTorchlikeNode() { u8 wall = cur_node.n.getWallMounted(nodedef); @@ -1526,6 +1538,17 @@ namespace { }; } +void MapblockMeshGenerator::drawAllfacesNode() +{ + static const aabb3f box(-BS / 2, -BS / 2, -BS / 2, BS / 2, BS / 2, BS / 2); + TileSpec tiles[6]; + for (int face = 0; face < 6; face++) + getTile(nodebox_tile_dirs[face], &tiles[face]); + if (data->m_smooth_lighting) + getSmoothLightFrame(); + drawAutoLightedCuboid(box, nullptr, tiles, 6); +} + void MapblockMeshGenerator::drawNodeboxNode() { TileSpec tiles[6]; @@ -1750,6 +1773,8 @@ void MapblockMeshGenerator::drawNode() void MapblockMeshGenerator::generate() { + ZoneScoped; + for (cur_node.p.Z = 0; cur_node.p.Z < data->side_length; cur_node.p.Z++) for (cur_node.p.Y = 0; cur_node.p.Y < data->side_length; cur_node.p.Y++) for (cur_node.p.X = 0; cur_node.p.X < data->side_length; cur_node.p.X++) { diff --git a/src/client/content_mapblock.h b/src/client/content_mapblock.h index 730330a03..1c3060c83 100644 --- a/src/client/content_mapblock.h +++ b/src/client/content_mapblock.h @@ -134,6 +134,7 @@ private: f32 corner_levels[2][2]; }; LiquidData cur_liquid; + bool smooth_liquids = false; void prepareLiquidNodeDrawing(); void getLiquidNeighborhood(); diff --git a/src/client/game.cpp b/src/client/game.cpp index 0a9b4ec58..6cbf54f5a 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -79,6 +79,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "hud.h" #include "clientdynamicinfo.h" #include +#include "util/tracy_wrapper.h" #if USE_SOUND #include "client/sound/sound_openal.h" @@ -185,7 +186,7 @@ struct LocalFormspecHandler : public TextDest assert(m_client != nullptr); if (fields.find("quit") != fields.end()) - m_client->sendRespawn(); + m_client->sendRespawnLegacy(); return; } @@ -385,6 +386,8 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter CachedPixelShaderSetting m_minimap_yaw{"yawVec"}; CachedPixelShaderSetting m_camera_offset_pixel{"cameraOffset"}; CachedVertexShaderSetting m_camera_offset_vertex{"cameraOffset"}; + CachedPixelShaderSetting m_camera_position_pixel{ "cameraPosition" }; + CachedVertexShaderSetting m_camera_position_vertex{ "cameraPosition" }; CachedPixelShaderSetting m_texture0{"texture0"}; CachedPixelShaderSetting m_texture1{"texture1"}; CachedPixelShaderSetting m_texture2{"texture2"}; @@ -491,6 +494,10 @@ public: m_camera_offset_pixel.set(offset, services); m_camera_offset_vertex.set(offset, services); + v3f camera_position = m_client->getCamera()->getPosition(); + m_camera_position_pixel.set(camera_position, services); + m_camera_position_pixel.set(camera_position, services); + SamplerLayer_t tex_id; tex_id = 0; m_texture0.set(&tex_id, services); @@ -594,7 +601,8 @@ public: m_client(client) {} - void setSky(Sky *sky) { + void setSky(Sky *sky) + { m_sky = sky; for (GameGlobalShaderConstantSetter *ggscs : created_nosky) { ggscs->setSky(m_sky); @@ -829,7 +837,7 @@ private: bool disable_camera_update = false; }; - void showDeathFormspec(); + void showDeathFormspecLegacy(); void showPauseMenu(); void pauseAnimation(); @@ -839,7 +847,7 @@ private: void handleClientEvent_None(ClientEvent *event, CameraOrientation *cam); void handleClientEvent_PlayerDamage(ClientEvent *event, CameraOrientation *cam); void handleClientEvent_PlayerForceMove(ClientEvent *event, CameraOrientation *cam); - void handleClientEvent_Deathscreen(ClientEvent *event, CameraOrientation *cam); + void handleClientEvent_DeathscreenLegacy(ClientEvent *event, CameraOrientation *cam); void handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation *cam); void handleClientEvent_ShowLocalFormSpec(ClientEvent *event, CameraOrientation *cam); void handleClientEvent_HandleParticleEvent(ClientEvent *event, @@ -889,11 +897,11 @@ private: QuicktuneShortcutter *quicktune = nullptr; std::unique_ptr m_game_ui; - GUIChatConsole *gui_chat_console = nullptr; // Free using ->Drop() + irr_ptr gui_chat_console; MapDrawControl *draw_control = nullptr; Camera *camera = nullptr; - Clouds *clouds = nullptr; // Free using ->Drop() - Sky *sky = nullptr; // Free using ->Drop() + irr_ptr clouds; + irr_ptr sky; Hud *hud = nullptr; Minimap *mapper = nullptr; @@ -1140,6 +1148,8 @@ bool Game::startup(bool *kill, void Game::run() { + ZoneScoped; + ProfilerGraph graph; RunStats stats = {}; CameraOrientation cam_view_target = {}; @@ -1167,15 +1177,21 @@ void Game::run() const bool initial_window_maximized = !g_settings->getBool("fullscreen") && g_settings->getBool("window_maximized"); + auto framemarker = FrameMarker("Game::run()-frame").started(); + while (m_rendering_engine->run() && !(*kill || g_gamecallback->shutdown_requested || (server && server->isShutdownRequested()))) { + framemarker.end(); + // Calculate dtime = // m_rendering_engine->run() from this iteration // + Sleep time until the wanted FPS are reached draw_times.limit(device, &dtime, g_menumgr.pausesGame()); + framemarker.start(); + const auto current_dynamic_info = ClientDynamicInfo::getCurrent(); if (!current_dynamic_info.equal(client_display_info)) { client_display_info = current_dynamic_info; @@ -1232,6 +1248,8 @@ void Game::run() } } + framemarker.end(); + RenderingEngine::autosaveScreensizeAndCo(initial_screen_size, initial_window_maximized); } @@ -1252,14 +1270,11 @@ void Game::shutdown() if (m_shutdown_progress == 0.0f) showOverlayMessage(N_("Shutting down..."), 0, 0); - if (clouds) - clouds->drop(); + clouds.reset(); - if (gui_chat_console) - gui_chat_console->drop(); + gui_chat_console.reset(); - if (sky) - sky->drop(); + sky.reset(); /* cleanup menus */ while (g_menumgr.menuCount() > 0) { @@ -1510,19 +1525,15 @@ bool Game::createClient(const GameStartData &start_data) client->getScript()->on_camera_ready(camera); client->setCamera(camera); - if (g_touchcontrols) { - g_touchcontrols->setUseCrosshair(!isTouchCrosshairDisabled()); - } - /* Clouds */ if (m_cache_enable_clouds) - clouds = new Clouds(smgr, shader_src, -1, rand()); + clouds = make_irr(smgr, shader_src, -1, rand()); /* Skybox */ - sky = new Sky(-1, m_rendering_engine, texture_src, shader_src); - scsf->setSky(sky); + sky = make_irr(-1, m_rendering_engine, texture_src, shader_src); + scsf->setSky(sky.get()); /* Pre-calculated values */ @@ -1575,11 +1586,13 @@ bool Game::initGui() chat_backend->applySettings(); // Chat backend and console - gui_chat_console = new GUIChatConsole(guienv, guienv->getRootGUIElement(), + gui_chat_console = make_irr(guienv, guienv->getRootGUIElement(), -1, chat_backend, client, &g_menumgr); - if (g_settings->getBool("touch_controls")) + if (g_settings->getBool("touch_controls")) { g_touchcontrols = new TouchControls(device, texture_src); + g_touchcontrols->setUseCrosshair(!isTouchCrosshairDisabled()); + } return true; } @@ -1673,9 +1686,13 @@ bool Game::connectToServer(const GameStartData &start_data, fps_control.reset(); + auto framemarker = FrameMarker("Game::connectToServer()-frame").started(); + while (m_rendering_engine->run()) { + framemarker.end(); fps_control.limit(device, &dtime); + framemarker.start(); // Update client and server step(dtime); @@ -1721,6 +1738,7 @@ bool Game::connectToServer(const GameStartData &start_data, // Update status showOverlayMessage(N_("Connecting to server..."), dtime, 20); } + framemarker.end(); } catch (con::PeerNotFoundException &e) { warningstream << "This should not happen. Please report a bug." << std::endl; return false; @@ -1738,9 +1756,11 @@ bool Game::getServerContent(bool *aborted) fps_control.reset(); + auto framemarker = FrameMarker("Game::getServerContent()-frame").started(); while (m_rendering_engine->run()) { - + framemarker.end(); fps_control.limit(device, &dtime); + framemarker.start(); // Update client and server step(dtime); @@ -1806,6 +1826,7 @@ bool Game::getServerContent(bool *aborted) texture_src, dtime, progress); } } + framemarker.end(); *aborted = true; infostream << "Connect aborted [device]" << std::endl; @@ -1856,26 +1877,26 @@ inline bool Game::handleCallbacks() } if (g_gamecallback->changepassword_requested) { - (new GUIPasswordChange(guienv, guiroot, -1, - &g_menumgr, client, texture_src))->drop(); + (void)make_irr(guienv, guiroot, -1, + &g_menumgr, client, texture_src); g_gamecallback->changepassword_requested = false; } if (g_gamecallback->changevolume_requested) { - (new GUIVolumeChange(guienv, guiroot, -1, - &g_menumgr, texture_src))->drop(); + (void)make_irr(guienv, guiroot, -1, + &g_menumgr, texture_src); g_gamecallback->changevolume_requested = false; } if (g_gamecallback->keyconfig_requested) { - (new GUIKeyChangeMenu(guienv, guiroot, -1, - &g_menumgr, texture_src))->drop(); + (void)make_irr(guienv, guiroot, -1, + &g_menumgr, texture_src); g_gamecallback->keyconfig_requested = false; } if (!g_gamecallback->show_open_url_dialog.empty()) { - (new GUIOpenURLMenu(guienv, guiroot, -1, - &g_menumgr, texture_src, g_gamecallback->show_open_url_dialog))->drop(); + (void)make_irr(guienv, guiroot, -1, + &g_menumgr, texture_src, g_gamecallback->show_open_url_dialog); g_gamecallback->show_open_url_dialog.clear(); } @@ -1955,7 +1976,10 @@ void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times, g_profiler->graphSet("FPS", 1.0f / dtime); auto stats2 = driver->getFrameStats(); - g_profiler->avg("Irr: primitives drawn", stats2.PrimitivesDrawn); + g_profiler->avg("Irr: drawcalls", stats2.Drawcalls); + if (stats2.Drawcalls > 0) + g_profiler->avg("Irr: primitives per drawcall", + stats2.PrimitivesDrawn / float(stats2.Drawcalls)); g_profiler->avg("Irr: buffers uploaded", stats2.HWBuffersUploaded); g_profiler->avg("Irr: buffers uploaded (bytes)", stats2.HWBuffersUploadedSize); } @@ -2018,7 +2042,7 @@ void Game::updateStats(RunStats *stats, const FpsControl &draw_times, void Game::processUserInput(f32 dtime) { // Reset input if window not active or some menu is active - if (!device->isWindowActive() || isMenuActive() || guienv->hasFocus(gui_chat_console)) { + if (!device->isWindowActive() || isMenuActive() || guienv->hasFocus(gui_chat_console.get())) { if (m_game_focused) { m_game_focused = false; infostream << "Game lost focus" << std::endl; @@ -2041,7 +2065,7 @@ void Game::processUserInput(f32 dtime) m_game_focused = true; } - if (!guienv->hasFocus(gui_chat_console) && gui_chat_console->isOpen()) { + if (!guienv->hasFocus(gui_chat_console.get()) && gui_chat_console->isOpen()) { gui_chat_console->closeConsoleAtOnce(); } @@ -2772,6 +2796,8 @@ void Game::updatePauseState() inline void Game::step(f32 dtime) { + ZoneScoped; + if (server) { float fps_max = (!device->isWindowFocused() || g_menumgr.pausesGame()) ? g_settings->getFloat("fps_max_unfocused") : @@ -2828,7 +2854,7 @@ const ClientEventHandler Game::clientEventHandler[CLIENTEVENT_MAX] = { {&Game::handleClientEvent_None}, {&Game::handleClientEvent_PlayerDamage}, {&Game::handleClientEvent_PlayerForceMove}, - {&Game::handleClientEvent_Deathscreen}, + {&Game::handleClientEvent_DeathscreenLegacy}, {&Game::handleClientEvent_ShowFormSpec}, {&Game::handleClientEvent_ShowLocalFormSpec}, {&Game::handleClientEvent_HandleParticleEvent}, @@ -2884,20 +2910,9 @@ void Game::handleClientEvent_PlayerForceMove(ClientEvent *event, CameraOrientati cam->camera_pitch = event->player_force_move.pitch; } -void Game::handleClientEvent_Deathscreen(ClientEvent *event, CameraOrientation *cam) +void Game::handleClientEvent_DeathscreenLegacy(ClientEvent *event, CameraOrientation *cam) { - // If client scripting is enabled, deathscreen is handled by CSM code in - // builtin/client/init.lua - if (client->modsLoaded()) - client->getScript()->on_death(); - else - showDeathFormspec(); - - /* Handle visualization */ - LocalPlayer *player = client->getEnv().getLocalPlayer(); - runData.damage_flash = 0; - player->hurt_tilt_timer = 0; - player->hurt_tilt_strength = 0; + showDeathFormspecLegacy(); } void Game::handleClientEvent_ShowFormSpec(ClientEvent *event, CameraOrientation *cam) @@ -3164,6 +3179,7 @@ void Game::handleClientEvent_CloudParams(ClientEvent *event, CameraOrientation * clouds->setDensity(event->cloud_params.density); clouds->setColorBright(video::SColor(event->cloud_params.color_bright)); clouds->setColorAmbient(video::SColor(event->cloud_params.color_ambient)); + clouds->setColorShadow(video::SColor(event->cloud_params.color_shadow)); clouds->setHeight(event->cloud_params.height); clouds->setThickness(event->cloud_params.thickness); clouds->setSpeed(v2f(event->cloud_params.speed_x, event->cloud_params.speed_y)); @@ -3356,7 +3372,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud) infostream << "Pointing at " << pointed.dump() << std::endl; if (g_touchcontrols) { - auto mode = selected_def.touch_interaction.getMode(pointed.type); + auto mode = selected_def.touch_interaction.getMode(selected_def, pointed.type); g_touchcontrols->applyContextControls(mode); // applyContextControls may change dig/place input. // Update again so that TOSERVER_INTERACT packets have the correct controls set. @@ -4051,6 +4067,7 @@ void Game::handleDigging(const PointedThing &pointed, const v3s16 &nodepos, void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, const CameraOrientation &cam) { + ZoneScoped; TimeTaker tt_update("Game::updateFrame()"); LocalPlayer *player = client->getEnv().getLocalPlayer(); @@ -4195,7 +4212,8 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime, updateShadows(); } - m_game_ui->update(*stats, client, draw_control, cam, runData.pointed_old, gui_chat_console, dtime); + m_game_ui->update(*stats, client, draw_control, cam, runData.pointed_old, + gui_chat_console.get(), dtime); /* make sure menu is on top @@ -4293,7 +4311,9 @@ void Game::updateShadows() 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); + const auto &lighting = client->getEnv().getLocalPlayer()->getLighting(); + shadow->setShadowIntensity(is_shadow_visible ? lighting.shadow_intensity : 0.0f); + shadow->setShadowTint(lighting.shadow_tint); timeoftheday = std::fmod(timeoftheday + 0.75f, 0.5f) + 0.25f; const float offset_constant = 10000.0f; @@ -4310,6 +4330,8 @@ void Game::updateShadows() void Game::drawScene(ProfilerGraph *graph, RunStats *stats) { + ZoneScoped; + const video::SColor fog_color = this->sky->getFogColor(); const video::SColor sky_color = this->sky->getSkyColor(); @@ -4435,7 +4457,7 @@ void Game::readSettings() ****************************************************************************/ /****************************************************************************/ -void Game::showDeathFormspec() +void Game::showDeathFormspecLegacy() { static std::string formspec_str = std::string("formspec_version[1]") + diff --git a/src/client/gameui.cpp b/src/client/gameui.cpp index 7631f9c78..41071ef66 100644 --- a/src/client/gameui.cpp +++ b/src/client/gameui.cpp @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gui/mainmenumanager.h" #include "gui/guiChatConsole.h" #include "gui/guiFormSpecMenu.h" +#include "gui/touchcontrols.h" #include "util/enriched_string.h" #include "util/pointedthing.h" #include "client.h" @@ -191,16 +192,27 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_ } } - setStaticText(m_guitext_status, m_statustext.c_str()); - m_guitext_status->setVisible(!m_statustext.empty()); + IGUIStaticText *guitext_status; + bool overriden = g_touchcontrols && g_touchcontrols->isStatusTextOverriden(); + if (overriden) { + guitext_status = g_touchcontrols->getStatusText(); + m_guitext_status->setVisible(false); + } else { + guitext_status = m_guitext_status; + if (g_touchcontrols) + g_touchcontrols->getStatusText()->setVisible(false); + } + + setStaticText(guitext_status, m_statustext.c_str()); + guitext_status->setVisible(!m_statustext.empty()); if (!m_statustext.empty()) { - s32 status_width = m_guitext_status->getTextWidth(); - s32 status_height = m_guitext_status->getTextHeight(); - s32 status_y = screensize.Y - 150; + s32 status_width = guitext_status->getTextWidth(); + s32 status_height = guitext_status->getTextHeight(); + s32 status_y = screensize.Y - (overriden ? 15 : 150); s32 status_x = (screensize.X - status_width) / 2; - m_guitext_status->setRelativePosition(core::rect(status_x , + guitext_status->setRelativePosition(core::rect(status_x , status_y - status_height, status_x + status_width, status_y)); // Fade out @@ -208,8 +220,8 @@ void GameUI::update(const RunStats &stats, Client *client, MapDrawControl *draw_ final_color.setAlpha(0); video::SColor fade_color = m_statustext_initial_color.getInterpolated_quadratic( m_statustext_initial_color, final_color, m_statustext_time / statustext_time_max); - m_guitext_status->setOverrideColor(fade_color); - m_guitext_status->enableOverrideColor(true); + guitext_status->setOverrideColor(fade_color); + guitext_status->enableOverrideColor(true); } // Hide chat when disabled by server or when console is visible diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 3ff83bdae..e4c06b542 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -99,7 +99,6 @@ Hud::Hud(Client *client, LocalPlayer *player, // Initialize m_selection_material - m_selection_material.Lighting = false; if (g_settings->getBool("enable_shaders")) { IShaderSource *shdrsrc = client->getShaderSource(); @@ -121,7 +120,6 @@ Hud::Hud(Client *client, LocalPlayer *player, } // Initialize m_block_bounds_material - m_block_bounds_material.Lighting = false; if (g_settings->getBool("enable_shaders")) { IShaderSource *shdrsrc = client->getShaderSource(); auto shader_id = shdrsrc->getShader("default_shader", TILE_MATERIAL_ALPHA); @@ -155,7 +153,6 @@ Hud::Hud(Client *client, LocalPlayer *player, indices[4] = 3; indices[5] = 0; - b->getMaterial().Lighting = false; b->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; b->setHardwareMappingHint(scene::EHM_STATIC); } @@ -383,7 +380,7 @@ void Hud::drawLuaElements(const v3s16 &camera_offset) elems.push_back(&minimap); } if (client->getProtoVersion() < 46 && player->hud_flags & HUD_FLAG_HOTBAR_VISIBLE) { - hotbar = {HUD_ELEM_HOTBAR, v2f(0.5, 1), "", v2f(), "", 0 , 0, 0, v2f(-0.5, -1), + hotbar = {HUD_ELEM_HOTBAR, v2f(0.5, 1), "", v2f(), "", 0 , 0, 0, v2f(0, -1), v2f(0, -4), v3f(), v2s32(), 0, "", 0}; elems.push_back(&hotbar); } @@ -1205,7 +1202,6 @@ void drawItemStack( video::SMaterial &material = buf->getMaterial(); material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; - material.Lighting = false; driver->setMaterial(material); driver->drawMeshBuffer(buf); } diff --git a/src/client/joystick_controller.h b/src/client/joystick_controller.h index 98486df43..d74cc3db6 100644 --- a/src/client/joystick_controller.h +++ b/src/client/joystick_controller.h @@ -155,6 +155,11 @@ public: float getMovementDirection(); float getMovementSpeed(); + u8 getJoystickId() const + { + return m_joystick_id; + } + f32 doubling_dtime; private: diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index 5b47a32ff..071b03132 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "minimap.h" #include "content_mapblock.h" #include "util/directiontables.h" +#include "util/tracy_wrapper.h" #include "client/meshgen/collector.h" #include "client/renderingengine.h" #include @@ -611,8 +612,10 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs m_last_crack(-1), m_last_daynight_ratio((u32) -1) { + ZoneScoped; + for (auto &m : m_mesh) - m = new scene::SMesh(); + m = make_irr(); m_enable_shaders = data->m_use_shaders; auto mesh_grid = client->getMeshGrid(); @@ -660,7 +663,7 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs m_bounding_radius = std::sqrt(collector.m_bounding_radius_sq); for (int layer = 0; layer < MAX_TILE_LAYERS; layer++) { - scene::SMesh *mesh = (scene::SMesh *)m_mesh[layer]; + scene::SMesh *mesh = static_cast(m_mesh[layer].get()); for(u32 i = 0; i < collector.prebuffers[layer].size(); i++) { @@ -733,7 +736,6 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs // Create material video::SMaterial material; - material.Lighting = false; material.BackfaceCulling = true; material.FogEnable = true; material.setTexture(0, p.layer.texture); @@ -746,9 +748,6 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs material.MaterialType = m_shdrsrc->getShaderInfo( p.layer.shader_id).material; p.layer.applyMaterialOptionsWithShaders(material); - if (p.layer.normal_texture) - material.setTexture(1, p.layer.normal_texture); - material.setTexture(2, p.layer.flags_texture); } else { p.layer.applyMaterialOptions(material); } @@ -794,10 +793,10 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs MapBlockMesh::~MapBlockMesh() { size_t sz = 0; - for (scene::IMesh *m : m_mesh) { + for (auto &&m : m_mesh) { for (u32 i = 0; i < m->getMeshBufferCount(); i++) sz += m->getMeshBuffer(i)->getSize(); - m->drop(); + m.reset(); } for (MinimapMapblock *block : m_minimap_mapblocks) delete block; @@ -858,11 +857,6 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, const FrameSpec &frame = (*tile.frames)[frameno]; buf->getMaterial().setTexture(0, frame.texture); - if (m_enable_shaders) { - if (frame.normal_texture) - buf->getMaterial().setTexture(1, frame.normal_texture); - buf->getMaterial().setTexture(2, frame.flags_texture); - } } // Day-night transition @@ -871,7 +865,7 @@ bool MapBlockMesh::animate(bool faraway, float time, int crack, get_sunlight_color(&day_color, daynight_ratio); for (auto &daynight_diff : m_daynight_diffs) { - auto *mesh = m_mesh[daynight_diff.first.first]; + auto *mesh = m_mesh[daynight_diff.first.first].get(); mesh->setDirty(scene::EBT_VERTEX); // force reload to VBO scene::IMeshBuffer *buf = mesh-> getMeshBuffer(daynight_diff.first.second); diff --git a/src/client/mapblock_mesh.h b/src/client/mapblock_mesh.h index d2c651525..c52df5ed3 100644 --- a/src/client/mapblock_mesh.h +++ b/src/client/mapblock_mesh.h @@ -193,12 +193,12 @@ public: scene::IMesh *getMesh() { - return m_mesh[0]; + return m_mesh[0].get(); } scene::IMesh *getMesh(u8 layer) { - return m_mesh[layer]; + return m_mesh[layer].get(); } std::vector moveMinimapMapblocks() @@ -242,7 +242,7 @@ private: TileLayer tile; }; - scene::IMesh *m_mesh[MAX_TILE_LAYERS]; + irr_ptr m_mesh[MAX_TILE_LAYERS]; std::vector m_minimap_mapblocks; ITextureSource *m_tsrc; IShaderSource *m_shdrsrc; diff --git a/src/client/mesh.cpp b/src/client/mesh.cpp index 711712c33..9e66404e3 100644 --- a/src/client/mesh.cpp +++ b/src/client/mesh.cpp @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include "mesh.h" +#include "S3DVertex.h" #include "debug.h" #include "log.h" #include @@ -33,7 +34,7 @@ inline static void applyShadeFactor(video::SColor& color, float factor) color.setBlue(core::clamp(core::round32(color.getBlue()*factor), 0, 255)); } -void applyFacesShading(video::SColor &color, const v3f &normal) +void applyFacesShading(video::SColor &color, const v3f normal) { /* Some drawtypes have normals set to (0, 0, 0), this must result in @@ -98,7 +99,6 @@ scene::IAnimatedMesh* createCubeMesh(v3f scale) scene::IMeshBuffer *buf = new scene::SMeshBuffer(); buf->append(vertices + 4 * i, 4, indices, 6); // Set default material - buf->getMaterial().Lighting = false; buf->getMaterial().MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF; buf->getMaterial().forEachTexture([] (auto &tex) { tex.MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST; @@ -132,6 +132,7 @@ void scaleMesh(scene::IMesh *mesh, v3f scale) for (u32 i = 0; i < vertex_count; i++) ((video::S3DVertex *)(vertices + i * stride))->Pos *= scale; + buf->setDirty(scene::EBT_VERTEX); buf->recalculateBoundingBox(); // calculate total bounding box @@ -160,6 +161,7 @@ void translateMesh(scene::IMesh *mesh, v3f vec) for (u32 i = 0; i < vertex_count; i++) ((video::S3DVertex *)(vertices + i * stride))->Pos += vec; + buf->setDirty(scene::EBT_VERTEX); buf->recalculateBoundingBox(); // calculate total bounding box @@ -171,23 +173,17 @@ void translateMesh(scene::IMesh *mesh, v3f vec) mesh->setBoundingBox(bbox); } -void setMeshBufferColor(scene::IMeshBuffer *buf, const video::SColor &color) +void setMeshBufferColor(scene::IMeshBuffer *buf, const video::SColor color) { const u32 stride = getVertexPitchFromType(buf->getVertexType()); u32 vertex_count = buf->getVertexCount(); u8 *vertices = (u8 *) buf->getVertices(); for (u32 i = 0; i < vertex_count; i++) ((video::S3DVertex *) (vertices + i * stride))->Color = color; + buf->setDirty(scene::EBT_VERTEX); } -void setAnimatedMeshColor(scene::IAnimatedMeshSceneNode *node, const video::SColor &color) -{ - for (u32 i = 0; i < node->getMaterialCount(); ++i) { - node->getMaterial(i).EmissiveColor = color; - } -} - -void setMeshColor(scene::IMesh *mesh, const video::SColor &color) +void setMeshColor(scene::IMesh *mesh, const video::SColor color) { if (mesh == NULL) return; @@ -197,15 +193,6 @@ void setMeshColor(scene::IMesh *mesh, const video::SColor &color) setMeshBufferColor(mesh->getMeshBuffer(j), color); } -void setMeshBufferTextureCoords(scene::IMeshBuffer *buf, const v2f *uv, u32 count) -{ - const u32 stride = getVertexPitchFromType(buf->getVertexType()); - assert(buf->getVertexCount() >= count); - u8 *vertices = (u8 *) buf->getVertices(); - for (u32 i = 0; i < count; i++) - ((video::S3DVertex*) (vertices + i * stride))->TCoords = uv[i]; -} - template static void applyToMesh(scene::IMesh *mesh, const F &fn) { @@ -217,6 +204,7 @@ static void applyToMesh(scene::IMesh *mesh, const F &fn) char *vertices = reinterpret_cast(buf->getVertices()); for (u32 i = 0; i < vertex_count; i++) fn(reinterpret_cast(vertices + i * stride)); + buf->setDirty(scene::EBT_VERTEX); } } @@ -233,6 +221,7 @@ void colorizeMeshBuffer(scene::IMeshBuffer *buf, const video::SColor *buffercolo // Apply shading applyFacesShading(*vc, vertex->Normal); } + buf->setDirty(scene::EBT_VERTEX); } void setMeshColorByNormalXYZ(scene::IMesh *mesh, @@ -411,7 +400,6 @@ scene::IMesh* convertNodeboxesToMesh(const std::vector &boxes, for (u16 j = 0; j < 6; j++) { scene::IMeshBuffer *buf = new scene::SMeshBuffer(); - buf->getMaterial().Lighting = false; buf->getMaterial().forEachTexture([] (auto &tex) { tex.MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST; tex.MagFilter = video::ETMAGF_NEAREST; diff --git a/src/client/mesh.h b/src/client/mesh.h index 106787af3..c04cfb0af 100644 --- a/src/client/mesh.h +++ b/src/client/mesh.h @@ -27,7 +27,7 @@ with this program; if not, write to the Free Software Foundation, Inc., * Applies shading to a color based on the surface's * normal vector. */ -void applyFacesShading(video::SColor &color, const v3f &normal); +void applyFacesShading(video::SColor &color, const v3f normal); /* Create a new cube mesh. @@ -52,24 +52,12 @@ void translateMesh(scene::IMesh *mesh, v3f vec); /*! * Sets a constant color for all vertices in the mesh buffer. */ -void setMeshBufferColor(scene::IMeshBuffer *buf, const video::SColor &color); +void setMeshBufferColor(scene::IMeshBuffer *buf, const video::SColor color); /* Set a constant color for all vertices in the mesh */ -void setMeshColor(scene::IMesh *mesh, const video::SColor &color); - - -/* - Sets texture coords for vertices in the mesh buffer. - `uv[]` must have `count` elements -*/ -void setMeshBufferTextureCoords(scene::IMeshBuffer *buf, const v2f *uv, u32 count); - -/* - Set a constant color for an animated mesh -*/ -void setAnimatedMeshColor(scene::IAnimatedMeshSceneNode *node, const video::SColor &color); +void setMeshColor(scene::IMesh *mesh, const video::SColor color); /*! * Overwrites the color of a mesh buffer. diff --git a/src/client/minimap.cpp b/src/client/minimap.cpp index b2fc0882d..13dca5fe9 100644 --- a/src/client/minimap.cpp +++ b/src/client/minimap.cpp @@ -201,7 +201,7 @@ Minimap::Minimap(Client *client) addMode(MINIMAP_TYPE_RADAR, 128); // Initialize minimap data - data = new MinimapData; + data = std::make_unique(); data->map_invalidated = true; data->minimap_shape_round = g_settings->getBool("minimap_shape_round"); @@ -209,11 +209,11 @@ Minimap::Minimap(Client *client) setModeIndex(0); // Create mesh buffer for minimap - m_meshbuffer = getMinimapMeshBuffer(); + m_meshbuffer = createMinimapMeshBuffer(); // Initialize and start thread - m_minimap_update_thread = new MinimapUpdateThread(); - m_minimap_update_thread->data = data; + m_minimap_update_thread = std::make_unique(); + m_minimap_update_thread->data = data.get(); m_minimap_update_thread->start(); } @@ -222,7 +222,7 @@ Minimap::~Minimap() m_minimap_update_thread->stop(); m_minimap_update_thread->wait(); - m_meshbuffer->drop(); + m_meshbuffer.reset(); if (data->minimap_mask_round) data->minimap_mask_round->drop(); @@ -232,12 +232,10 @@ Minimap::~Minimap() driver->removeTexture(data->texture); driver->removeTexture(data->heightmap_texture); - for (MinimapMarker *m : m_markers) - delete m; m_markers.clear(); - delete data; - delete m_minimap_update_thread; + data.reset(); + m_minimap_update_thread.reset(); } void Minimap::addBlock(v3s16 pos, MinimapMapblock *data) @@ -552,9 +550,9 @@ v3f Minimap::getYawVec() return v3f(1.0, 0.0, 1.0); } -scene::SMeshBuffer *Minimap::getMinimapMeshBuffer() +irr_ptr Minimap::createMinimapMeshBuffer() { - scene::SMeshBuffer *buf = new scene::SMeshBuffer(); + auto buf = make_irr(); auto &vertices = buf->Vertices->Data; auto &indices = buf->Indices->Data; vertices.resize(4); @@ -612,7 +610,6 @@ void Minimap::drawMinimap(core::rect rect) tex.MinFilter = video::ETMINF_LINEAR_MIPMAP_LINEAR; tex.MagFilter = video::ETMAGF_LINEAR; }); - material.Lighting = false; material.TextureLayers[0].Texture = minimap_texture; material.TextureLayers[1].Texture = data->heightmap_texture; @@ -629,7 +626,7 @@ void Minimap::drawMinimap(core::rect rect) // Draw minimap driver->setTransform(video::ETS_WORLD, matrix); driver->setMaterial(material); - driver->drawMeshBuffer(m_meshbuffer); + driver->drawMeshBuffer(m_meshbuffer.get()); // Draw overlay video::ITexture *minimap_overlay = data->minimap_shape_round ? @@ -637,7 +634,7 @@ void Minimap::drawMinimap(core::rect rect) material.TextureLayers[0].Texture = minimap_overlay; material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; driver->setMaterial(material); - driver->drawMeshBuffer(m_meshbuffer); + driver->drawMeshBuffer(m_meshbuffer.get()); // Draw player marker on minimap if (data->minimap_shape_round) { @@ -649,7 +646,7 @@ void Minimap::drawMinimap(core::rect rect) material.TextureLayers[0].Texture = data->player_marker; driver->setTransform(video::ETS_WORLD, matrix); driver->setMaterial(material); - driver->drawMeshBuffer(m_meshbuffer); + driver->drawMeshBuffer(m_meshbuffer.get()); // Reset transformations driver->setTransform(video::ETS_VIEW, oldViewMat); @@ -687,17 +684,17 @@ void Minimap::drawMinimap(core::rect rect) } } -MinimapMarker* Minimap::addMarker(scene::ISceneNode *parent_node) +MinimapMarker *Minimap::addMarker(scene::ISceneNode *parent_node) { - MinimapMarker *m = new MinimapMarker(parent_node); - m_markers.push_back(m); - return m; + auto m = std::make_unique(parent_node); + auto ret = m.get(); + m_markers.push_back(std::move(m)); + return ret; } void Minimap::removeMarker(MinimapMarker **m) { - m_markers.remove(*m); - delete *m; + m_markers.remove_if([ptr = *m](const auto &up) { return up.get() == ptr; }); *m = nullptr; } @@ -711,7 +708,7 @@ void Minimap::updateActiveMarkers() data->mode.scan_height / 2, data->mode.map_size / 2); - for (MinimapMarker *marker : m_markers) { + for (auto &&marker : m_markers) { v3s16 pos = floatToInt(marker->parent_node->getAbsolutePosition() + cam_offset, BS) - pos_offset; if (pos.X < 0 || pos.X > data->mode.map_size || diff --git a/src/client/minimap.h b/src/client/minimap.h index f819deaf4..0c419fa63 100644 --- a/src/client/minimap.h +++ b/src/client/minimap.h @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "../hud.h" #include "irrlichttypes_extrabloated.h" +#include "irr_ptr.h" #include "util/thread.h" #include "voxel.h" #include @@ -148,7 +149,7 @@ public: void blitMinimapPixelsToImageSurface(video::IImage *map_image, video::IImage *heightmap_image); - scene::SMeshBuffer *getMinimapMeshBuffer(); + irr_ptr createMinimapMeshBuffer(); MinimapMarker* addMarker(scene::ISceneNode *parent_node); void removeMarker(MinimapMarker **marker); @@ -158,20 +159,20 @@ public: video::IVideoDriver *driver; Client* client; - MinimapData *data; + std::unique_ptr data; private: ITextureSource *m_tsrc; IShaderSource *m_shdrsrc; const NodeDefManager *m_ndef; - MinimapUpdateThread *m_minimap_update_thread = nullptr; - scene::SMeshBuffer *m_meshbuffer; + std::unique_ptr m_minimap_update_thread; + irr_ptr m_meshbuffer; bool m_enable_shaders; std::vector m_modes; size_t m_current_mode_index; u16 m_surface_mode_scan_height; f32 m_angle; std::mutex m_mutex; - std::list m_markers; + std::list> m_markers; std::list m_active_markers; }; diff --git a/src/client/particles.cpp b/src/client/particles.cpp index c19282424..1eab93579 100644 --- a/src/client/particles.cpp +++ b/src/client/particles.cpp @@ -989,7 +989,6 @@ video::SMaterial ParticleManager::getMaterialForParticle(const ClientParticleTex video::SMaterial material; // Texture - material.Lighting = false; material.BackfaceCulling = false; material.FogEnable = true; material.forEachTexture([] (auto &tex) { diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index 4400dd90e..c4933e062 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -36,7 +36,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "inputhandler.h" #include "gettext.h" #include "filesys.h" -#include "../gui/guiSkin.h" #include "irrlicht_changes/static_text.h" #include "irr_ptr.h" @@ -126,27 +125,6 @@ IShaderConstantSetter *FogShaderConstantSetterFactory::create() /* Other helpers */ -static gui::GUISkin *createSkin(gui::IGUIEnvironment *environment, - gui::EGUI_SKIN_TYPE type, video::IVideoDriver *driver) -{ - gui::GUISkin *skin = new gui::GUISkin(type, driver); - - gui::IGUIFont *builtinfont = environment->getBuiltInFont(); - gui::IGUIFontBitmap *bitfont = nullptr; - if (builtinfont && builtinfont->getType() == gui::EGFT_BITMAP) - bitfont = (gui::IGUIFontBitmap*)builtinfont; - - gui::IGUISpriteBank *bank = 0; - skin->setFont(builtinfont); - - if (bitfont) - bank = bitfont->getSpriteBank(); - - skin->setSpriteBank(bank); - - return skin; -} - static std::optional chooseVideoDriver() { auto &&configured_name = g_settings->get("video_driver"); @@ -250,11 +228,6 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver) s_singleton = this; - auto skin = createSkin(m_device->getGUIEnvironment(), - gui::EGST_WINDOWS_METALLIC, driver); - m_device->getGUIEnvironment()->setSkin(skin); - skin->drop(); - g_settings->registerChangedCallback("fullscreen", settingChangedCallback, this); g_settings->registerChangedCallback("window_maximized", settingChangedCallback, this); } diff --git a/src/client/shader.cpp b/src/client/shader.cpp index dae53ff96..ad37ea4c1 100644 --- a/src/client/shader.cpp +++ b/src/client/shader.cpp @@ -218,15 +218,15 @@ class MainShaderConstantSetter : public IShaderConstantSetter CachedVertexShaderSetting m_texture{"mTexture"}; // commonly used way to pass material color to shader - video::SColor m_emissive_color; - CachedPixelShaderSetting m_emissive_color_setting{"emissiveColor"}; + video::SColor m_material_color; + CachedPixelShaderSetting m_material_color_setting{"materialColor"}; public: ~MainShaderConstantSetter() = default; virtual void onSetMaterial(const video::SMaterial& material) override { - m_emissive_color = material.EmissiveColor; + m_material_color = material.ColorParam; } virtual void onSetConstants(video::IMaterialRendererServices *services) override @@ -249,13 +249,13 @@ public: m_world_view_proj.set(worldViewProj, services); if (driver->getDriverType() == video::EDT_OGLES2 || driver->getDriverType() == video::EDT_OPENGL3) { - core::matrix4 texture = driver->getTransform(video::ETS_TEXTURE_0); + auto &texture = driver->getTransform(video::ETS_TEXTURE_0); m_world_view.set(worldView, services); m_texture.set(texture, services); } - video::SColorf emissive_color(m_emissive_color); - m_emissive_color_setting.set(emissive_color, services); + video::SColorf colorf(m_material_color); + m_material_color_setting.set(colorf, services); } }; @@ -573,6 +573,7 @@ ShaderInfo ShaderSource::generateShader(const std::string &name, } else { shaders_header << "#version 100\n"; } + // cf. EVertexAttributes.h for the predefined ones vertex_header = R"( precision mediump float; @@ -582,7 +583,7 @@ ShaderInfo ShaderSource::generateShader(const std::string &name, attribute highp vec4 inVertexPosition; attribute lowp vec4 inVertexColor; - attribute mediump vec4 inTexCoord0; + attribute mediump vec2 inTexCoord0; attribute mediump vec3 inVertexNormal; attribute mediump vec4 inVertexTangent; attribute mediump vec4 inVertexBinormal; @@ -688,6 +689,15 @@ ShaderInfo ShaderSource::generateShader(const std::string &name, if (g_settings->getBool("shadow_poisson_filter")) shaders_header << "#define POISSON_FILTER 1\n"; + if (g_settings->getBool("enable_water_reflections")) + shaders_header << "#define ENABLE_WATER_REFLECTIONS 1\n"; + + if (g_settings->getBool("enable_translucent_foliage")) + shaders_header << "#define ENABLE_TRANSLUCENT_FOLIAGE 1\n"; + + if (g_settings->getBool("enable_node_specular")) + shaders_header << "#define ENABLE_NODE_SPECULAR 1\n"; + s32 shadow_filter = g_settings->getS32("shadow_filters"); shaders_header << "#define SHADOW_FILTER " << shadow_filter << "\n"; diff --git a/src/client/shadows/dynamicshadowsrender.h b/src/client/shadows/dynamicshadowsrender.h index c4ffb39e2..d4437be68 100644 --- a/src/client/shadows/dynamicshadowsrender.h +++ b/src/client/shadows/dynamicshadowsrender.h @@ -94,9 +94,11 @@ public: bool is_active() const { return m_shadows_enabled && shadowMapTextureFinal != nullptr; } void setTimeOfDay(float isDay) { m_time_day = isDay; }; void setShadowIntensity(float shadow_intensity); + void setShadowTint(video::SColor shadow_tint) { m_shadow_tint = shadow_tint; } s32 getShadowSamples() const { return m_shadow_samples; } float getShadowStrength() const { return m_shadows_enabled ? m_shadow_strength : 0.0f; } + video::SColor getShadowTint() const { return m_shadow_tint; } float getTimeOfDay() const { return m_time_day; } f32 getPerspectiveBiasXY() { return m_perspective_bias_xy; } @@ -131,6 +133,7 @@ private: std::vector m_shadow_node_array; float m_shadow_strength; + video::SColor m_shadow_tint{ 255, 0, 0, 0 }; float m_shadow_strength_gamma; float m_shadow_map_max_distance; float m_shadow_map_texture_size; diff --git a/src/client/shadows/shadowsScreenQuad.cpp b/src/client/shadows/shadowsScreenQuad.cpp index f0eb9ab4a..d1713578d 100644 --- a/src/client/shadows/shadowsScreenQuad.cpp +++ b/src/client/shadows/shadowsScreenQuad.cpp @@ -23,7 +23,6 @@ with this program; if not, write to the Free Software Foundation, Inc., shadowScreenQuad::shadowScreenQuad() { Material.Wireframe = false; - Material.Lighting = false; video::SColor color(0x0); Vertices[0] = video::S3DVertex( diff --git a/src/client/shadows/shadowsshadercallbacks.cpp b/src/client/shadows/shadowsshadercallbacks.cpp index 32d3e36be..fc4f73186 100644 --- a/src/client/shadows/shadowsshadercallbacks.cpp +++ b/src/client/shadows/shadowsshadercallbacks.cpp @@ -40,6 +40,9 @@ void ShadowConstantSetter::onSetConstants(video::IMaterialRendererServices *serv f32 ShadowStrength = shadow->getShadowStrength(); m_shadow_strength.set(&ShadowStrength, services); + video::SColor ShadowTint = shadow->getShadowTint(); + m_shadow_tint.set(ShadowTint, services); + f32 timeOfDay = shadow->getTimeOfDay(); m_time_of_day.set(&timeOfDay, services); diff --git a/src/client/shadows/shadowsshadercallbacks.h b/src/client/shadows/shadowsshadercallbacks.h index af83f021e..beac25d72 100644 --- a/src/client/shadows/shadowsshadercallbacks.h +++ b/src/client/shadows/shadowsshadercallbacks.h @@ -31,6 +31,7 @@ class ShadowConstantSetter : public IShaderConstantSetter CachedPixelShaderSetting m_light_direction{"v_LightDirection"}; CachedPixelShaderSetting m_texture_res{"f_textureresolution"}; CachedPixelShaderSetting m_shadow_strength{"f_shadow_strength"}; + CachedPixelShaderSetting m_shadow_tint{ "shadow_tint" }; CachedPixelShaderSetting m_time_of_day{"f_timeofday"}; CachedPixelShaderSetting m_shadowfar{"f_shadowfar"}; CachedPixelShaderSetting m_camera_pos{"CameraPos"}; diff --git a/src/client/sky.cpp b/src/client/sky.cpp index f309d444c..65577418e 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include "client/mesh.h" #include "client/tile.h" #include "noise.h" // easeCurve #include "profiler.h" @@ -38,7 +39,6 @@ using namespace irr::core; static video::SMaterial baseMaterial() { video::SMaterial mat; - mat.Lighting = false; mat.ZBuffer = video::ECFN_DISABLED; mat.ZWriteEnable = video::EZW_OFF; mat.AntiAliasing = 0; @@ -77,10 +77,9 @@ Sky::Sky(s32 id, RenderingEngine *rendering_engine, ITextureSource *tsrc, IShade // Create materials m_materials[0] = baseMaterial(); - // FIXME: shouldn't this check m_enable_shaders? - m_materials[0].MaterialType = ssrc->getShaderInfo(ssrc->getShader("stars_shader", TILE_MATERIAL_ALPHA)).material; - m_materials[0].Lighting = true; - m_materials[0].ColorMaterial = video::ECM_NONE; + m_materials[0].MaterialType = m_enable_shaders ? + ssrc->getShaderInfo(ssrc->getShader("stars_shader", TILE_MATERIAL_ALPHA)).material : + video::EMT_TRANSPARENT_ALPHA_CHANNEL; m_materials[1] = baseMaterial(); m_materials[1].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; @@ -95,7 +94,6 @@ Sky::Sky(s32 id, RenderingEngine *rendering_engine, ITextureSource *tsrc, IShade for (int i = 5; i < 11; i++) { m_materials[i] = baseMaterial(); - m_materials[i].Lighting = true; m_materials[i].MaterialType = video::EMT_SOLID; } @@ -169,7 +167,8 @@ void Sky::render() video::SColor texel_color (255, texel->getRed(), texel->getGreen(), texel->getBlue()); m_sun_tonemap->unlock(); - m_materials[3].EmissiveColor = texel_color; + // Only accessed by our code later, not used by a shader + m_materials[3].ColorParam = texel_color; } if (m_moon_tonemap) { @@ -178,7 +177,8 @@ void Sky::render() video::SColor texel_color (255, texel->getRed(), texel->getGreen(), texel->getBlue()); m_moon_tonemap->unlock(); - m_materials[4].EmissiveColor = texel_color; + // Only accessed by our code later, not used by a shader + m_materials[4].ColorParam = texel_color; } const f32 t = 1.0f; @@ -465,11 +465,11 @@ void Sky::update(float time_of_day, float time_brightness, // which keeps previous behavior. if (m_sun_tonemap && m_default_tint) { pointcolor_sun_f.r = pointcolor_light * - (float)m_materials[3].EmissiveColor.getRed() / 255; + (float)m_materials[3].ColorParam.getRed() / 255; pointcolor_sun_f.b = pointcolor_light * - (float)m_materials[3].EmissiveColor.getBlue() / 255; + (float)m_materials[3].ColorParam.getBlue() / 255; pointcolor_sun_f.g = pointcolor_light * - (float)m_materials[3].EmissiveColor.getGreen() / 255; + (float)m_materials[3].ColorParam.getGreen() / 255; } else if (!m_default_tint) { pointcolor_sun_f = m_sky_params.fog_sun_tint; } else { @@ -498,11 +498,11 @@ void Sky::update(float time_of_day, float time_brightness, } if (m_moon_tonemap && m_default_tint) { pointcolor_moon_f.r = pointcolor_light * - (float)m_materials[4].EmissiveColor.getRed() / 255; + (float)m_materials[4].ColorParam.getRed() / 255; pointcolor_moon_f.b = pointcolor_light * - (float)m_materials[4].EmissiveColor.getBlue() / 255; + (float)m_materials[4].ColorParam.getBlue() / 255; pointcolor_moon_f.g = pointcolor_light * - (float)m_materials[4].EmissiveColor.getGreen() / 255; + (float)m_materials[4].ColorParam.getGreen() / 255; } video::SColor pointcolor_sun = pointcolor_sun_f.toSColor(); @@ -603,11 +603,8 @@ void Sky::draw_sun(video::IVideoDriver *driver, const video::SColor &suncolor, // Another magic number that contributes to the ratio 1.57 sun/moon size // difference. float d = (sunsize * 1.7) * m_sun_params.scale; - video::SColor c; - if (m_sun_tonemap) - c = video::SColor(0, 0, 0, 0); - else - c = video::SColor(255, 255, 255, 255); + video::SColor c = m_sun_tonemap ? m_materials[3].ColorParam : + video::SColor(255, 255, 255, 255); draw_sky_body(vertices, -d, d, c); place_sky_body(vertices, 90, wicked_time_of_day * 360 - 90); driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); @@ -660,11 +657,8 @@ void Sky::draw_moon(video::IVideoDriver *driver, const video::SColor &mooncolor, // Another magic number that contributes to the ratio 1.57 sun/moon size // difference. float d = (moonsize * 1.9) * m_moon_params.scale; - video::SColor c; - if (m_moon_tonemap) - c = video::SColor(0, 0, 0, 0); - else - c = video::SColor(255, 255, 255, 255); + video::SColor c = m_sun_tonemap ? m_materials[4].ColorParam : + video::SColor(255, 255, 255, 255); draw_sky_body(vertices, -d, d, c); place_sky_body(vertices, -90, wicked_time_of_day * 360 - 90); driver->drawIndexedTriangleList(&vertices[0], 4, indices, 2); @@ -688,7 +682,10 @@ void Sky::draw_stars(video::IVideoDriver * driver, float wicked_time_of_day) color.a *= alpha; if (color.a <= 0.0f) // Stars are only drawn when not fully transparent return; - m_materials[0].EmissiveColor = color.toSColor(); + if (m_enable_shaders) + m_materials[0].ColorParam = color.toSColor(); + else + setMeshBufferColor(m_stars.get(), color.toSColor()); auto sky_rotation = core::matrix4().setRotationAxisRadians(2.0f * M_PI * (wicked_time_of_day - 0.25f), v3f(0.0f, 0.0f, 1.0f)); auto world_matrix = driver->getTransform(video::ETS_WORLD); @@ -742,7 +739,6 @@ void Sky::setSunTexture(const std::string &sun_texture, m_sun_params.tonemap = sun_tonemap; m_sun_tonemap = tsrc->isKnownSourceImage(sun_tonemap) ? tsrc->getTexture(sun_tonemap) : nullptr; - m_materials[3].Lighting = !!m_sun_tonemap; if (m_sun_params.texture == sun_texture && !m_first_update) return; @@ -762,7 +758,6 @@ void Sky::setSunTexture(const std::string &sun_texture, m_materials[3].setTexture(0, m_sun_texture); m_materials[3].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; disableTextureFiltering(m_materials[3]); - m_materials[3].Lighting = !!m_sun_tonemap; } } @@ -786,7 +781,6 @@ void Sky::setMoonTexture(const std::string &moon_texture, m_moon_params.tonemap = moon_tonemap; m_moon_tonemap = tsrc->isKnownSourceImage(moon_tonemap) ? tsrc->getTexture(moon_tonemap) : nullptr; - m_materials[4].Lighting = !!m_moon_tonemap; if (m_moon_params.texture == moon_texture && !m_first_update) return; @@ -806,7 +800,6 @@ void Sky::setMoonTexture(const std::string &moon_texture, m_materials[4].setTexture(0, m_moon_texture); m_materials[4].MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; disableTextureFiltering(m_materials[4]); - m_materials[4].Lighting = !!m_moon_tonemap; } } @@ -835,7 +828,6 @@ void Sky::updateStars() vertices.reserve(4 * m_star_params.count); indices.reserve(6 * m_star_params.count); - video::SColor fallback_color = m_star_params.starcolor; // used on GLES 2 “without shaders” PcgRandom rgen(m_seed); float d = (0.006 / 2) * m_star_params.scale; for (u16 i = 0; i < m_star_params.count; i++) { @@ -854,10 +846,10 @@ void Sky::updateStars() a.rotateVect(p1); a.rotateVect(p2); a.rotateVect(p3); - vertices.push_back(video::S3DVertex(p, {}, fallback_color, {})); - vertices.push_back(video::S3DVertex(p1, {}, fallback_color, {})); - vertices.push_back(video::S3DVertex(p2, {}, fallback_color, {})); - vertices.push_back(video::S3DVertex(p3, {}, fallback_color, {})); + vertices.push_back(video::S3DVertex(p, {}, {}, {})); + vertices.push_back(video::S3DVertex(p1, {}, {}, {})); + vertices.push_back(video::S3DVertex(p2, {}, {}, {})); + vertices.push_back(video::S3DVertex(p3, {}, {}, {})); } for (u16 i = 0; i < m_star_params.count; i++) { indices.push_back(i * 4 + 0); @@ -867,7 +859,8 @@ void Sky::updateStars() indices.push_back(i * 4 + 3); indices.push_back(i * 4 + 0); } - m_stars->setHardwareMappingHint(scene::EHM_STATIC); + if (m_enable_shaders) + m_stars->setHardwareMappingHint(scene::EHM_STATIC); } void Sky::setSkyColors(const SkyColor &sky_color) diff --git a/src/client/sound/sound_manager.cpp b/src/client/sound/sound_manager.cpp index 679d3a155..fc171d565 100644 --- a/src/client/sound/sound_manager.cpp +++ b/src/client/sound/sound_manager.cpp @@ -26,9 +26,12 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "sound_singleton.h" #include "util/numeric.h" // myrand() +#include "util/tracy_wrapper.h" #include "filesys.h" #include "porting.h" +#include + namespace sound { void OpenALSoundManager::stepStreams(f32 dtime) @@ -347,6 +350,13 @@ void OpenALSoundManager::updateListener(const v3f &pos_, const v3f &vel_, void OpenALSoundManager::setListenerGain(f32 gain) { +#if defined(__APPLE__) + /* macOS OpenAL implementation ignore setting AL_GAIN to zero + * so we use smallest possible value + */ + if (gain == 0.0f) + gain = std::numeric_limits::min(); +#endif alListenerf(AL_GAIN, gain); } @@ -492,6 +502,8 @@ void *OpenALSoundManager::run() u64 t_step_start = porting::getTimeMs(); while (true) { + auto framemarker = FrameMarker("OpenALSoundManager::run()-frame").started(); + auto get_time_since_last_step = [&] { return (f32)(porting::getTimeMs() - t_step_start); }; diff --git a/src/client/texturesource.cpp b/src/client/texturesource.cpp index a4222f414..f18fa6cbf 100644 --- a/src/client/texturesource.cpp +++ b/src/client/texturesource.cpp @@ -137,7 +137,6 @@ public: video::ITexture* getNormalTexture(const std::string &name); video::SColor getTextureAverageColor(const std::string &name); - video::ITexture *getShaderFlagsTexture(bool normamap_present); private: @@ -541,25 +540,3 @@ video::SColor TextureSource::getTextureAverageColor(const std::string &name) return c; } - - -video::ITexture *TextureSource::getShaderFlagsTexture(bool normalmap_present) -{ - std::string tname = "__shaderFlagsTexture"; - tname += normalmap_present ? "1" : "0"; - - if (isKnownSourceImage(tname)) { - return getTexture(tname); - } - - video::IVideoDriver *driver = RenderingEngine::get_video_driver(); - video::IImage *flags_image = driver->createImage( - video::ECF_A8R8G8B8, core::dimension2d(1, 1)); - sanity_check(flags_image); - video::SColor c(255, normalmap_present ? 255 : 0, 0, 0); - flags_image->setPixel(0, 0, c); - insertSourceImage(tname, flags_image); - flags_image->drop(); - return getTexture(tname); - -} diff --git a/src/client/texturesource.h b/src/client/texturesource.h index d4880ed4c..324c58e4f 100644 --- a/src/client/texturesource.h +++ b/src/client/texturesource.h @@ -71,7 +71,6 @@ public: virtual bool isKnownSourceImage(const std::string &name)=0; virtual video::ITexture* getNormalTexture(const std::string &name)=0; virtual video::SColor getTextureAverageColor(const std::string &name)=0; - virtual video::ITexture *getShaderFlagsTexture(bool normalmap_present)=0; }; class IWritableTextureSource : public ITextureSource @@ -93,7 +92,6 @@ public: virtual void rebuildImagesAndTextures()=0; virtual video::ITexture* getNormalTexture(const std::string &name)=0; virtual video::SColor getTextureAverageColor(const std::string &name)=0; - virtual video::ITexture *getShaderFlagsTexture(bool normalmap_present)=0; }; IWritableTextureSource *createTextureSource(); diff --git a/src/client/tile.h b/src/client/tile.h index d761eefdd..f41b127bf 100644 --- a/src/client/tile.h +++ b/src/client/tile.h @@ -62,8 +62,6 @@ struct FrameSpec u32 texture_id = 0; video::ITexture *texture = nullptr; - video::ITexture *normal_texture = nullptr; - video::ITexture *flags_texture = nullptr; }; #define MAX_TILE_LAYERS 2 @@ -114,8 +112,6 @@ struct TileLayer // Ordered for size, please do not reorder video::ITexture *texture = nullptr; - video::ITexture *normal_texture = nullptr; - video::ITexture *flags_texture = nullptr; u32 shader_id = 0; diff --git a/src/client/wieldmesh.cpp b/src/client/wieldmesh.cpp index 66f89efb1..e66214ae6 100644 --- a/src/client/wieldmesh.cpp +++ b/src/client/wieldmesh.cpp @@ -194,10 +194,9 @@ private: static ExtrusionMeshCache *g_extrusion_mesh_cache = nullptr; -WieldMeshSceneNode::WieldMeshSceneNode(scene::ISceneManager *mgr, s32 id, bool lighting): +WieldMeshSceneNode::WieldMeshSceneNode(scene::ISceneManager *mgr, s32 id): scene::ISceneNode(mgr->getRootSceneNode(), mgr, id), - m_material_type(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF), - m_lighting(lighting) + m_material_type(video::EMT_TRANSPARENT_ALPHA_CHANNEL_REF) { m_enable_shaders = g_settings->getBool("enable_shaders"); m_anisotropic_filter = g_settings->getBool("anisotropic_filter"); @@ -306,9 +305,6 @@ void WieldMeshSceneNode::setExtruded(const std::string &imagename, }); // mipmaps cause "thin black line" artifacts material.UseMipMaps = false; - if (m_enable_shaders) { - material.setTexture(2, tsrc->getShaderFlagsTexture(false)); - } } } @@ -343,7 +339,6 @@ static scene::SMesh *createSpecialNodeMesh(Client *client, MapNode n, if (p.layer.material_flags & MATERIAL_FLAG_ANIMATION) { const FrameSpec &frame = (*p.layer.frames)[0]; p.layer.texture = frame.texture; - p.layer.normal_texture = frame.normal_texture; } for (video::S3DVertex &v : p.vertices) { v.Color.setAlpha(255); @@ -394,8 +389,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che // overlay is white, if present m_colors.emplace_back(true, video::SColor(0xFFFFFFFF)); // initialize the color - if (!m_lighting) - setColor(video::SColor(0xFFFFFFFF)); + setColor(video::SColor(0xFFFFFFFF)); return; } @@ -472,8 +466,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che } // initialize the color - if (!m_lighting) - setColor(video::SColor(0xFFFFFFFF)); + setColor(video::SColor(0xFFFFFFFF)); return; } else { const std::string inventory_image = item.getInventoryImage(idef); @@ -489,8 +482,7 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che m_colors.emplace_back(true, video::SColor(0xFFFFFFFF)); // initialize the color - if (!m_lighting) - setColor(video::SColor(0xFFFFFFFF)); + setColor(video::SColor(0xFFFFFFFF)); return; } @@ -500,7 +492,6 @@ void WieldMeshSceneNode::setItem(const ItemStack &item, Client *client, bool che void WieldMeshSceneNode::setColor(video::SColor c) { - assert(!m_lighting); scene::IMesh *mesh = m_meshnode->getMesh(); if (!mesh) return; @@ -539,7 +530,7 @@ void WieldMeshSceneNode::setNodeLightColor(video::SColor color) if (m_enable_shaders) { for (u32 i = 0; i < m_meshnode->getMaterialCount(); ++i) { video::SMaterial &material = m_meshnode->getMaterial(i); - material.EmissiveColor = color; + material.ColorParam = color; } } else { setColor(color); @@ -569,11 +560,6 @@ void WieldMeshSceneNode::changeToMesh(scene::IMesh *mesh) mesh->setHardwareMappingHint(scene::EHM_DYNAMIC); } - m_meshnode->forEachMaterial([this] (auto &mat) { - mat.Lighting = m_lighting; - // need to normalize normals when lighting is enabled (because of setScale()) - mat.NormalizeNormals = m_lighting; - }); m_meshnode->setVisible(true); } @@ -671,7 +657,6 @@ void getItemMesh(Client *client, const ItemStack &item, ItemMesh *result) tex.MagFilter = video::ETMAGF_NEAREST; }); material.BackfaceCulling = cull_backface; - material.Lighting = false; } rotateMeshXZby(mesh, -45); @@ -724,7 +709,6 @@ scene::SMesh *getExtrudedMesh(ITextureSource *tsrc, tex.MagFilter = video::ETMAGF_NEAREST; }); material.BackfaceCulling = true; - material.Lighting = false; material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; material.MaterialTypeParam = 0.5f; } @@ -772,16 +756,6 @@ void postProcessNodeMesh(scene::SMesh *mesh, const ContentFeatures &f, } else { material.setTexture(0, layer->texture); } - if (use_shaders) { - if (layer->normal_texture) { - if (layer->animation_frame_count > 1) { - const FrameSpec &animation_frame = (*layer->frames)[0]; - material.setTexture(1, animation_frame.normal_texture); - } else - material.setTexture(1, layer->normal_texture); - } - material.setTexture(2, layer->flags_texture); - } if (apply_scale && tile->world_aligned) { u32 n = buf->getVertexCount(); diff --git a/src/client/wieldmesh.h b/src/client/wieldmesh.h index e2c6cd445..08240c8ef 100644 --- a/src/client/wieldmesh.h +++ b/src/client/wieldmesh.h @@ -104,7 +104,7 @@ struct ItemMesh class WieldMeshSceneNode : public scene::ISceneNode { public: - WieldMeshSceneNode(scene::ISceneManager *mgr, s32 id = -1, bool lighting = false); + WieldMeshSceneNode(scene::ISceneManager *mgr, s32 id = -1); virtual ~WieldMeshSceneNode(); void setCube(const ContentFeatures &f, v3f wield_scale); @@ -132,9 +132,6 @@ private: scene::IMeshSceneNode *m_meshnode = nullptr; video::E_MATERIAL_TYPE m_material_type; - // True if SMaterial::Lighting should be enabled. - bool m_lighting; - bool m_enable_shaders; bool m_anisotropic_filter; bool m_bilinear_filter; diff --git a/src/clientdynamicinfo.cpp b/src/clientdynamicinfo.cpp index c206018f3..12bc23abd 100644 --- a/src/clientdynamicinfo.cpp +++ b/src/clientdynamicinfo.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "client/renderingengine.h" +#include "gui/guiFormSpecMenu.h" #include "gui/touchcontrols.h" ClientDynamicInfo ClientDynamicInfo::getCurrent() @@ -37,19 +38,22 @@ ClientDynamicInfo ClientDynamicInfo::getCurrent() return { screen_size, real_gui_scaling, real_hud_scaling, - ClientDynamicInfo::calculateMaxFSSize(screen_size, gui_scaling), + ClientDynamicInfo::calculateMaxFSSize(screen_size, density, gui_scaling), touch_controls }; } -v2f32 ClientDynamicInfo::calculateMaxFSSize(v2u32 render_target_size, f32 gui_scaling) +v2f32 ClientDynamicInfo::calculateMaxFSSize(v2u32 render_target_size, f32 density, f32 gui_scaling) { - f32 factor = (g_settings->getBool("touch_gui") ? 10 : 15) / gui_scaling; - f32 ratio = (f32)render_target_size.X / (f32)render_target_size.Y; - if (ratio < 1) - return { factor, factor / ratio }; - else - return { factor * ratio, factor }; + // must stay in sync with GUIFormSpecMenu::calculateImgsize + + const double screen_dpi = density * 96; + + // assume padding[0,0] since max_formspec_size is used for fullscreen formspecs + double prefer_imgsize = GUIFormSpecMenu::getImgsize(render_target_size, + screen_dpi, gui_scaling); + return v2f32(render_target_size.X / prefer_imgsize, + render_target_size.Y / prefer_imgsize); } #endif diff --git a/src/clientdynamicinfo.h b/src/clientdynamicinfo.h index 39faeeecc..c43fcb8d8 100644 --- a/src/clientdynamicinfo.h +++ b/src/clientdynamicinfo.h @@ -42,6 +42,6 @@ public: static ClientDynamicInfo getCurrent(); private: - static v2f32 calculateMaxFSSize(v2u32 render_target_size, f32 gui_scaling); + static v2f32 calculateMaxFSSize(v2u32 render_target_size, f32 density, f32 gui_scaling); #endif }; diff --git a/src/cmake_config.h.in b/src/cmake_config.h.in index a8eb53edd..5dc6e4b74 100644 --- a/src/cmake_config.h.in +++ b/src/cmake_config.h.in @@ -41,3 +41,4 @@ #cmakedefine01 BUILD_UNITTESTS #cmakedefine01 BUILD_BENCHMARKS #cmakedefine01 USE_SDL2 +#cmakedefine01 BUILD_WITH_TRACY diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index ddc7cc9df..22317e394 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -275,6 +275,7 @@ void set_default_settings() settings->setDefault("view_bobbing_amount", "1.0"); settings->setDefault("fall_bobbing_amount", "0.03"); settings->setDefault("enable_3d_clouds", "true"); + settings->setDefault("soft_clouds", "false"); settings->setDefault("cloud_radius", "12"); settings->setDefault("menu_clouds", "true"); settings->setDefault("translucent_liquids", "true"); @@ -335,6 +336,9 @@ void set_default_settings() settings->setDefault("bloom_intensity", "0.05"); settings->setDefault("bloom_radius", "1"); settings->setDefault("enable_volumetric_lighting", "false"); + settings->setDefault("enable_water_reflections", "false"); + settings->setDefault("enable_translucent_foliage", "false"); + settings->setDefault("enable_node_specular", "false"); // Effects Shadows settings->setDefault("enable_dynamic_shadows", "false"); diff --git a/src/emerge.cpp b/src/emerge.cpp index 425e294b8..788e2b745 100644 --- a/src/emerge.cpp +++ b/src/emerge.cpp @@ -31,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filesys.h" #include "log.h" #include "servermap.h" +#include "database/database.h" #include "mapblock.h" #include "mapgen/mg_biome.h" #include "mapgen/mg_ore.h" @@ -185,10 +186,22 @@ SchematicManager *EmergeManager::getWritableSchematicManager() return schemmgr; } +void EmergeManager::initMap(MapDatabaseAccessor *holder) +{ + FATAL_ERROR_IF(m_db, "Map database already initialized."); + assert(holder->dbase); + m_db = holder; +} + +void EmergeManager::resetMap() +{ + FATAL_ERROR_IF(m_threads_active, "Threads are still active."); + m_db = nullptr; +} void EmergeManager::initMapgens(MapgenParams *params) { - FATAL_ERROR_IF(!m_mapgens.empty(), "Mapgen already initialised."); + FATAL_ERROR_IF(!m_mapgens.empty(), "Mapgen already initialized."); mgparams = params; @@ -303,6 +316,12 @@ bool EmergeManager::enqueueBlockEmergeEx( } +size_t EmergeManager::getQueueSize() +{ + MutexAutoLock queuelock(m_queue_mutex); + return m_blocks_enqueued.size(); +} + bool EmergeManager::isBlockInQueue(v3s16 pos) { MutexAutoLock queuelock(m_queue_mutex); @@ -466,7 +485,7 @@ void EmergeThread::signal() } -bool EmergeThread::pushBlock(const v3s16 &pos) +bool EmergeThread::pushBlock(v3s16 pos) { m_block_queue.push(pos); return true; @@ -491,7 +510,7 @@ void EmergeThread::cancelPendingItems() } -void EmergeThread::runCompletionCallbacks(const v3s16 &pos, EmergeAction action, +void EmergeThread::runCompletionCallbacks(v3s16 pos, EmergeAction action, const EmergeCallbackList &callbacks) { m_emerge->reportCompletedEmerge(action); @@ -524,21 +543,38 @@ bool EmergeThread::popBlockEmerge(v3s16 *pos, BlockEmergeData *bedata) } -EmergeAction EmergeThread::getBlockOrStartGen( - const v3s16 &pos, bool allow_gen, MapBlock **block, BlockMakeData *bmdata) +EmergeAction EmergeThread::getBlockOrStartGen(const v3s16 pos, bool allow_gen, + const std::string *from_db, MapBlock **block, BlockMakeData *bmdata) { - MutexAutoLock envlock(m_server->m_env_mutex); + //TimeTaker tt("", nullptr, PRECISION_MICRO); + Server::EnvAutoLock envlock(m_server); + //g_profiler->avg("EmergeThread: lock wait time [us]", tt.stop()); + + auto block_ok = [] (MapBlock *b) { + return b && b->isGenerated(); + }; // 1). Attempt to fetch block from memory *block = m_map->getBlockNoCreateNoEx(pos); if (*block) { - if ((*block)->isGenerated()) + if (block_ok(*block)) { + // if we just read it from the db but the block exists that means + // someone else was faster. don't touch it to prevent data loss. + if (from_db) + verbosestream << "getBlockOrStartGen: block loading raced" << std::endl; return EMERGE_FROM_MEMORY; + } } else { - // 2). Attempt to load block from disk if it was not in the memory - *block = m_map->loadBlock(pos); - if (*block && (*block)->isGenerated()) + if (!from_db) { + // 2). We should attempt loading it return EMERGE_FROM_DISK; + } + // 2). Second invocation, we have the data + if (!from_db->empty()) { + *block = m_map->loadBlock(*from_db, pos); + if (block_ok(*block)) + return EMERGE_FROM_DISK; + } } // 3). Attempt to start generation @@ -553,7 +589,7 @@ EmergeAction EmergeThread::getBlockOrStartGen( MapBlock *EmergeThread::finishGen(v3s16 pos, BlockMakeData *bmdata, std::map *modified_blocks) { - MutexAutoLock envlock(m_server->m_env_mutex); + Server::EnvAutoLock envlock(m_server); ScopeProfiler sp(g_profiler, "EmergeThread: after Mapgen::makeChunk", SPT_AVG); @@ -643,7 +679,8 @@ void *EmergeThread::run() BEGIN_DEBUG_EXCEPTION_HANDLER v3s16 pos; - std::map modified_blocks; + std::map modified_blocks; + std::string databuf; m_map = &m_server->m_env->getServerMap(); m_emerge = m_server->getEmergeManager(); @@ -669,13 +706,30 @@ void *EmergeThread::run() continue; } + g_profiler->add(m_name + ": processed [#]", 1); + if (blockpos_over_max_limit(pos)) continue; bool allow_gen = bedata.flags & BLOCK_EMERGE_ALLOW_GEN; EMERGE_DBG_OUT("pos=" << pos << " allow_gen=" << allow_gen); - action = getBlockOrStartGen(pos, allow_gen, &block, &bmdata); + action = getBlockOrStartGen(pos, allow_gen, nullptr, &block, &bmdata); + + /* Try to load it */ + if (action == EMERGE_FROM_DISK) { + auto &m_db = *m_emerge->m_db; + { + ScopeProfiler sp(g_profiler, "EmergeThread: load block - async (sum)"); + MutexAutoLock dblock(m_db.mutex); + m_db.loadBlock(pos, databuf); + } + // actually load it, then decide again + action = getBlockOrStartGen(pos, allow_gen, &databuf, &block, &bmdata); + databuf.clear(); + } + + /* Generate it */ if (action == EMERGE_GENERATED) { bool error = false; m_trans_liquid = &bmdata.transforming_liquid; @@ -716,7 +770,7 @@ void *EmergeThread::run() MapEditEvent event; event.type = MEET_OTHER; event.setModifiedBlocks(modified_blocks); - MutexAutoLock envlock(m_server->m_env_mutex); + Server::EnvAutoLock envlock(m_server); m_map->dispatchEvent(event); } modified_blocks.clear(); diff --git a/src/emerge.h b/src/emerge.h index d7f018feb..cbdcc4c7c 100644 --- a/src/emerge.h +++ b/src/emerge.h @@ -46,6 +46,7 @@ class DecorationManager; class SchematicManager; class Server; class ModApiMapgen; +struct MapDatabaseAccessor; // Structure containing inputs/outputs for chunk generation struct BlockMakeData { @@ -173,6 +174,10 @@ public: SchematicManager *getWritableSchematicManager(); void initMapgens(MapgenParams *mgparams); + /// @param holder non-owned reference that must stay alive + void initMap(MapDatabaseAccessor *holder); + /// resets the reference + void resetMap(); void startThreads(); void stopThreads(); @@ -191,6 +196,7 @@ public: EmergeCompletionCallback callback, void *callback_param); + size_t getQueueSize(); bool isBlockInQueue(v3s16 pos); Mapgen *getCurrentMapgen(); @@ -206,6 +212,9 @@ private: std::vector m_threads; bool m_threads_active = false; + // The map database + MapDatabaseAccessor *m_db = nullptr; + std::mutex m_queue_mutex; std::map m_blocks_enqueued; std::unordered_map m_peer_queue_count; diff --git a/src/emerge_internal.h b/src/emerge_internal.h index 439c8227b..08e36778d 100644 --- a/src/emerge_internal.h +++ b/src/emerge_internal.h @@ -40,7 +40,7 @@ class EmergeScripting; class EmergeThread : public Thread { public: bool enable_mapgen_debug_info; - int id; + const int id; // Index of this thread EmergeThread(Server *server, int ethreadid); ~EmergeThread() = default; @@ -49,7 +49,7 @@ public: void signal(); // Requires queue mutex held - bool pushBlock(const v3s16 &pos); + bool pushBlock(v3s16 pos); void cancelPendingItems(); @@ -59,7 +59,7 @@ public: protected: void runCompletionCallbacks( - const v3s16 &pos, EmergeAction action, + v3s16 pos, EmergeAction action, const EmergeCallbackList &callbacks); private: @@ -79,8 +79,20 @@ private: bool popBlockEmerge(v3s16 *pos, BlockEmergeData *bedata); - EmergeAction getBlockOrStartGen( - const v3s16 &pos, bool allow_gen, MapBlock **block, BlockMakeData *data); + /** + * Try to get a block from memory and decide what to do. + * + * @param pos block position + * @param from_db serialized block data, optional + * (for second call after EMERGE_FROM_DISK was returned) + * @param allow_gen allow invoking mapgen? + * @param block output pointer for block + * @param data info for mapgen + * @return what to do for this block + */ + EmergeAction getBlockOrStartGen(v3s16 pos, bool allow_gen, + const std::string *from_db, MapBlock **block, BlockMakeData *data); + MapBlock *finishGen(v3s16 pos, BlockMakeData *bmdata, std::map *modified_blocks); diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index 73bbecb02..04a03609d 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -19,7 +19,6 @@ set(gui_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/guiScene.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiScrollBar.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiScrollContainer.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/guiSkin.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiTable.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiHyperText.cpp ${CMAKE_CURRENT_SOURCE_DIR}/guiVolumeChange.cpp diff --git a/src/gui/guiButton.cpp b/src/gui/guiButton.cpp index d78433edd..9592ba922 100644 --- a/src/gui/guiButton.cpp +++ b/src/gui/guiButton.cpp @@ -253,8 +253,8 @@ void GUIButton::draw() setFromState(); } - GUISkin* skin = dynamic_cast(Environment->getSkin()); video::IVideoDriver* driver = Environment->getVideoDriver(); + IGUISkin *skin = Environment->getSkin(); // END PATCH if (DrawBorder) @@ -737,7 +737,7 @@ void GUIButton::setFromStyle(const StyleSpec& style) Padding.UpperLeftCorner + BgMiddle.UpperLeftCorner, Padding.LowerRightCorner + BgMiddle.LowerRightCorner); - GUISkin* skin = dynamic_cast(Environment->getSkin()); + IGUISkin *skin = Environment->getSkin(); core::vector2d defaultPressOffset( skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X), skin->getSize(irr::gui::EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y)); diff --git a/src/gui/guiButton.h b/src/gui/guiButton.h index 4fad8747c..dd71788ba 100644 --- a/src/gui/guiButton.h +++ b/src/gui/guiButton.h @@ -10,7 +10,6 @@ #include "IGUISpriteBank.h" #include "ITexture.h" #include "SColor.h" -#include "guiSkin.h" #include "StyleSpec.h" using namespace irr; diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp index fdc13fa14..4a3d53f51 100644 --- a/src/gui/guiEngine.cpp +++ b/src/gui/guiEngine.cpp @@ -40,6 +40,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include "client/imagefilters.h" +#include "util/tracy_wrapper.h" #if USE_SOUND #include "client/sound/sound_openal.h" @@ -141,10 +142,6 @@ GUIEngine::GUIEngine(JoystickController *joystick, // create texture source m_texture_source = std::make_unique(rendering_engine->get_video_driver()); - // create shader source - // (currently only used by clouds) - m_shader_source.reset(createShaderSource()); - // create soundmanager #if USE_SOUND if (g_settings->getBool("enable_sound") && g_sound_manager_singleton.get()) { @@ -295,10 +292,6 @@ void GUIEngine::run() IrrlichtDevice *device = m_rendering_engine->get_raw_device(); video::IVideoDriver *driver = device->getVideoDriver(); - // Always create clouds because they may or may not be - // needed based on the game selected - cloudInit(); - unsigned int text_height = g_fontengine->getTextHeight(); // Reset fog color @@ -329,9 +322,12 @@ void GUIEngine::run() fps_control.reset(); - while (m_rendering_engine->run() && !m_startgame && !m_kill) { + auto framemarker = FrameMarker("GUIEngine::run()-frame").started(); + while (m_rendering_engine->run() && !m_startgame && !m_kill) { + framemarker.end(); fps_control.limit(device, &dtime); + framemarker.start(); if (device->isWindowVisible()) { // check if we need to update the "upper left corner"-text @@ -371,6 +367,7 @@ void GUIEngine::run() m_menu->getAndroidUIInput(); #endif } + framemarker.end(); m_script->beforeClose(); @@ -390,8 +387,6 @@ GUIEngine::~GUIEngine() m_irr_toplefttext->remove(); - m_cloud.clouds.reset(); - // delete textures for (image_definition &texture : m_textures) { if (texture.texture) @@ -399,26 +394,11 @@ GUIEngine::~GUIEngine() } } -/******************************************************************************/ -void GUIEngine::cloudInit() -{ - m_shader_source->addShaderConstantSetterFactory( - new FogShaderConstantSetterFactory()); - - m_cloud.clouds = make_irr(m_smgr, m_shader_source.get(), -1, rand()); - m_cloud.clouds->setHeight(100.0f); - m_cloud.clouds->update(v3f(0, 0, 0), video::SColor(255,240,240,255)); - - m_cloud.camera = m_smgr->addCameraSceneNode(0, - v3f(0,0,0), v3f(0, 60, 100)); - m_cloud.camera->setFarValue(10000); -} - /******************************************************************************/ void GUIEngine::drawClouds(float dtime) { - m_cloud.clouds->step(dtime*3); - m_smgr->drawAll(); + g_menuclouds->step(dtime * 3); + g_menucloudsmgr->drawAll(); } /******************************************************************************/ diff --git a/src/gui/guiEngine.h b/src/gui/guiEngine.h index fa4e1ebd3..2df0a0b60 100644 --- a/src/gui/guiEngine.h +++ b/src/gui/guiEngine.h @@ -203,8 +203,6 @@ private: MainMenuData *m_data = nullptr; /** texture source */ std::unique_ptr m_texture_source; - /** shader source */ - std::unique_ptr m_shader_source; /** sound manager */ std::unique_ptr m_sound_manager; @@ -279,23 +277,11 @@ private: /** and text that is in it */ EnrichedString m_toplefttext; - /** initialize cloud subsystem */ - void cloudInit(); /** do preprocessing for cloud subsystem */ void drawClouds(float dtime); - /** internam data required for drawing clouds */ - struct clouddata { - /** pointer to cloud class */ - irr_ptr clouds; - /** camera required for drawing clouds */ - scene::ICameraSceneNode *camera = nullptr; - }; - /** is drawing of clouds enabled atm */ - bool m_clouds_enabled = true; - /** data used to draw clouds */ - clouddata m_cloud; + bool m_clouds_enabled = true; static void fullscreenChangedCallback(const std::string &name, void *data); }; diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 8b572276c..40a445a0c 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -3133,58 +3133,7 @@ void GUIFormSpecMenu::regenerateGui(v2u32 screensize) offset = v2s32(0,0); } - const double gui_scaling = g_settings->getFloat("gui_scaling", 0.5f, 42.0f); - const double screen_dpi = RenderingEngine::getDisplayDensity() * 96; - - double use_imgsize; - if (m_lock) { - // In fixed-size mode, inventory image size - // is 0.53 inch multiplied by the gui_scaling - // config parameter. This magic size is chosen - // to make the main menu (15.5 inventory images - // wide, including border) just fit into the - // default window (800 pixels wide) at 96 DPI - // and default scaling (1.00). - use_imgsize = 0.5555 * screen_dpi * gui_scaling; - } else { - // Variables for the maximum imgsize that can fit in the screen. - double fitx_imgsize; - double fity_imgsize; - - v2f padded_screensize( - mydata.screensize.X * (1.0f - mydata.padding.X * 2.0f), - mydata.screensize.Y * (1.0f - mydata.padding.Y * 2.0f) - ); - - if (mydata.real_coordinates) { - fitx_imgsize = padded_screensize.X / mydata.invsize.X; - fity_imgsize = padded_screensize.Y / mydata.invsize.Y; - } else { - // The maximum imgsize in the old coordinate system also needs to - // factor in padding and spacing along with 0.1 inventory slot spare - // and help text space, hence the magic numbers. - fitx_imgsize = padded_screensize.X / - ((5.0 / 4.0) * (0.5 + mydata.invsize.X)); - fity_imgsize = padded_screensize.Y / - ((15.0 / 13.0) * (0.85 + mydata.invsize.Y)); - } - - s32 min_screen_dim = std::min(padded_screensize.X, padded_screensize.Y); - - double prefer_imgsize; - if (g_settings->getBool("touch_gui")) { - // 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, - std::min(fitx_imgsize, fity_imgsize)); - } + double use_imgsize = calculateImgsize(mydata); // Everything else is scaled in proportion to the // inventory image size. The inventory slot spacing @@ -4027,10 +3976,9 @@ bool GUIFormSpecMenu::preprocessEvent(const SEvent& event) } if (event.EventType == irr::EET_JOYSTICK_INPUT_EVENT) { - /* TODO add a check like: - if (event.JoystickEvent != joystick_we_listen_for) + if (event.JoystickEvent.Joystick != m_joystick->getJoystickId()) return false; - */ + bool handled = m_joystick->handleEvent(event.JoystickEvent); if (handled) { if (m_joystick->wasKeyDown(KeyType::ESC)) { @@ -5072,3 +5020,68 @@ std::array GUIFormSpecMenu::getStyleForElement return ret; } + +double GUIFormSpecMenu::getFixedImgsize(double screen_dpi, double gui_scaling) +{ + // In fixed-size mode, inventory image size + // is 0.53 inch multiplied by the gui_scaling + // config parameter. This magic size is chosen + // to make the main menu (15.5 inventory images + // wide, including border) just fit into the + // default window (800 pixels wide) at 96 DPI + // and default scaling (1.00). + return 0.5555 * screen_dpi * gui_scaling; +} + +double GUIFormSpecMenu::getImgsize(v2u32 avail_screensize, double screen_dpi, double gui_scaling) +{ + double fixed_imgsize = getFixedImgsize(screen_dpi, gui_scaling); + + s32 min_screen_dim = std::min(avail_screensize.X, avail_screensize.Y); + double prefer_imgsize = min_screen_dim / 15 * gui_scaling; + // Use the available space more effectively on small windows/screens. + // This is especially important for mobile platforms. + prefer_imgsize = std::max(prefer_imgsize, fixed_imgsize); + return prefer_imgsize; +} + +double GUIFormSpecMenu::calculateImgsize(const parserData &data) +{ + // must stay in sync with ClientDynamicInfo::calculateMaxFSSize + + const double screen_dpi = RenderingEngine::getDisplayDensity() * 96; + const double gui_scaling = g_settings->getFloat("gui_scaling", 0.5f, 42.0f); + + // Fixed-size mode + if (m_lock) + return getFixedImgsize(screen_dpi, gui_scaling); + + // Variables for the maximum imgsize that can fit in the screen. + double fitx_imgsize; + double fity_imgsize; + + v2f padded_screensize( + data.screensize.X * (1.0f - data.padding.X * 2.0f), + data.screensize.Y * (1.0f - data.padding.Y * 2.0f) + ); + + if (data.real_coordinates) { + fitx_imgsize = padded_screensize.X / data.invsize.X; + fity_imgsize = padded_screensize.Y / data.invsize.Y; + } else { + // The maximum imgsize in the old coordinate system also needs to + // factor in padding and spacing along with 0.1 inventory slot spare + // and help text space, hence the magic numbers. + fitx_imgsize = padded_screensize.X / + ((5.0 / 4.0) * (0.5 + data.invsize.X)); + fity_imgsize = padded_screensize.Y / + ((15.0 / 13.0) * (0.85 + data.invsize.Y)); + } + + double prefer_imgsize = getImgsize(v2u32(padded_screensize.X, padded_screensize.Y), + screen_dpi, gui_scaling); + + // Try to use the preferred imgsize, but if that's bigger than the maximum + // size, use the maximum size. + return std::min(prefer_imgsize, std::min(fitx_imgsize, fity_imgsize)); +} diff --git a/src/gui/guiFormSpecMenu.h b/src/gui/guiFormSpecMenu.h index 12add12e6..7c4be4301 100644 --- a/src/gui/guiFormSpecMenu.h +++ b/src/gui/guiFormSpecMenu.h @@ -296,6 +296,11 @@ public: void getAndroidUIInput(); #endif + // Returns the fixed formspec coordinate size for the given parameters. + static double getFixedImgsize(double screen_dpi, double gui_scaling); + // Returns the preferred non-fixed formspec coordinate size for the given parameters. + static double getImgsize(v2u32 avail_screensize, double screen_dpi, double gui_scaling); + protected: v2s32 getBasePos() const { @@ -514,6 +519,9 @@ private: // used by getAbsoluteRect s32 m_tabheader_upper_edge = 0; + + // Determines the size (in pixels) of formspec coordinate units. + double calculateImgsize(const parserData &data); }; class FormspecFormSource: public IFormSource diff --git a/src/gui/guiScene.cpp b/src/gui/guiScene.cpp index a26cfa93f..9293ebe22 100644 --- a/src/gui/guiScene.cpp +++ b/src/gui/guiScene.cpp @@ -67,7 +67,6 @@ void GUIScene::setTexture(u32 idx, video::ITexture *texture) material.MaterialType = video::EMT_TRANSPARENT_ALPHA_CHANNEL; material.MaterialTypeParam = 0.5f; material.TextureLayers[0].Texture = texture; - material.Lighting = false; material.FogEnable = true; material.TextureLayers[0].MinFilter = video::ETMINF_NEAREST_MIPMAP_NEAREST; material.TextureLayers[0].MagFilter = video::ETMAGF_NEAREST; diff --git a/src/gui/guiSkin.cpp b/src/gui/guiSkin.cpp deleted file mode 100644 index 0ecc80f02..000000000 --- a/src/gui/guiSkin.cpp +++ /dev/null @@ -1,1037 +0,0 @@ -// Copyright (C) 2002-2012 Nikolaus Gebhardt -// Copyright (C) 2019 Irrlick -// -// This file is part of the "Irrlicht Engine". -// For conditions of distribution and use, see copyright notice in irrlicht.h - -#include "guiSkin.h" - -#include "IGUIFont.h" -#include "IGUISpriteBank.h" -#include "IGUIElement.h" -#include "IVideoDriver.h" -#include "IAttributes.h" - -namespace irr -{ -namespace gui -{ - -GUISkin::GUISkin(EGUI_SKIN_TYPE type, video::IVideoDriver* driver) -: SpriteBank(0), Driver(driver), Type(type) -{ - #ifdef _DEBUG - setDebugName("GUISkin"); - #endif - - if ((Type == EGST_WINDOWS_CLASSIC) || (Type == EGST_WINDOWS_METALLIC)) - { - Colors[EGDC_3D_DARK_SHADOW] = video::SColor(101,50,50,50); - Colors[EGDC_3D_SHADOW] = video::SColor(101,130,130,130); - Colors[EGDC_3D_FACE] = video::SColor(220,100,100,100); - Colors[EGDC_3D_HIGH_LIGHT] = video::SColor(101,255,255,255); - Colors[EGDC_3D_LIGHT] = video::SColor(101,210,210,210); - Colors[EGDC_ACTIVE_BORDER] = video::SColor(101,16,14,115); - Colors[EGDC_ACTIVE_CAPTION] = video::SColor(255,255,255,255); - Colors[EGDC_APP_WORKSPACE] = video::SColor(101,100,100,100); - Colors[EGDC_BUTTON_TEXT] = video::SColor(240,10,10,10); - Colors[EGDC_GRAY_TEXT] = video::SColor(240,130,130,130); - Colors[EGDC_HIGH_LIGHT] = video::SColor(101,8,36,107); - Colors[EGDC_HIGH_LIGHT_TEXT] = video::SColor(240,255,255,255); - Colors[EGDC_INACTIVE_BORDER] = video::SColor(101,165,165,165); - Colors[EGDC_INACTIVE_CAPTION] = video::SColor(255,30,30,30); - Colors[EGDC_TOOLTIP] = video::SColor(200,0,0,0); - Colors[EGDC_TOOLTIP_BACKGROUND] = video::SColor(200,255,255,225); - Colors[EGDC_SCROLLBAR] = video::SColor(101,230,230,230); - Colors[EGDC_WINDOW] = video::SColor(101,255,255,255); - Colors[EGDC_WINDOW_SYMBOL] = video::SColor(200,10,10,10); - Colors[EGDC_ICON] = video::SColor(200,255,255,255); - Colors[EGDC_ICON_HIGH_LIGHT] = video::SColor(200,8,36,107); - Colors[EGDC_GRAY_WINDOW_SYMBOL] = video::SColor(240,100,100,100); - Colors[EGDC_EDITABLE] = video::SColor(255,255,255,255); - Colors[EGDC_GRAY_EDITABLE] = video::SColor(255,120,120,120); - Colors[EGDC_FOCUSED_EDITABLE] = video::SColor(255,240,240,255); - - - Sizes[EGDS_SCROLLBAR_SIZE] = 14; - Sizes[EGDS_MENU_HEIGHT] = 30; - Sizes[EGDS_WINDOW_BUTTON_WIDTH] = 15; - Sizes[EGDS_CHECK_BOX_WIDTH] = 18; - Sizes[EGDS_MESSAGE_BOX_WIDTH] = 500; - Sizes[EGDS_MESSAGE_BOX_HEIGHT] = 200; - Sizes[EGDS_BUTTON_WIDTH] = 80; - Sizes[EGDS_BUTTON_HEIGHT] = 30; - - Sizes[EGDS_TEXT_DISTANCE_X] = 2; - Sizes[EGDS_TEXT_DISTANCE_Y] = 0; - - Sizes[EGDS_TITLEBARTEXT_DISTANCE_X] = 2; - Sizes[EGDS_TITLEBARTEXT_DISTANCE_Y] = 0; - } - else - { - //0x80a6a8af - Colors[EGDC_3D_DARK_SHADOW] = 0x60767982; - //Colors[EGDC_3D_FACE] = 0xc0c9ccd4; // tab background - Colors[EGDC_3D_FACE] = 0xc0cbd2d9; // tab background - Colors[EGDC_3D_SHADOW] = 0x50e4e8f1; // tab background, and left-top highlight - Colors[EGDC_3D_HIGH_LIGHT] = 0x40c7ccdc; - Colors[EGDC_3D_LIGHT] = 0x802e313a; - Colors[EGDC_ACTIVE_BORDER] = 0x80404040; // window title - Colors[EGDC_ACTIVE_CAPTION] = 0xffd0d0d0; - Colors[EGDC_APP_WORKSPACE] = 0xc0646464; // unused - Colors[EGDC_BUTTON_TEXT] = 0xd0161616; - Colors[EGDC_GRAY_TEXT] = 0x3c141414; - Colors[EGDC_HIGH_LIGHT] = 0x6c606060; - Colors[EGDC_HIGH_LIGHT_TEXT] = 0xd0e0e0e0; - Colors[EGDC_INACTIVE_BORDER] = 0xf0a5a5a5; - Colors[EGDC_INACTIVE_CAPTION] = 0xffd2d2d2; - Colors[EGDC_TOOLTIP] = 0xf00f2033; - Colors[EGDC_TOOLTIP_BACKGROUND] = 0xc0cbd2d9; - Colors[EGDC_SCROLLBAR] = 0xf0e0e0e0; - Colors[EGDC_WINDOW] = 0xf0f0f0f0; - Colors[EGDC_WINDOW_SYMBOL] = 0xd0161616; - Colors[EGDC_ICON] = 0xd0161616; - Colors[EGDC_ICON_HIGH_LIGHT] = 0xd0606060; - Colors[EGDC_GRAY_WINDOW_SYMBOL] = 0x3c101010; - Colors[EGDC_EDITABLE] = 0xf0ffffff; - Colors[EGDC_GRAY_EDITABLE] = 0xf0cccccc; - Colors[EGDC_FOCUSED_EDITABLE] = 0xf0fffff0; - - Sizes[EGDS_SCROLLBAR_SIZE] = 14; - Sizes[EGDS_MENU_HEIGHT] = 48; - Sizes[EGDS_WINDOW_BUTTON_WIDTH] = 15; - Sizes[EGDS_CHECK_BOX_WIDTH] = 18; - Sizes[EGDS_MESSAGE_BOX_WIDTH] = 500; - Sizes[EGDS_MESSAGE_BOX_HEIGHT] = 200; - Sizes[EGDS_BUTTON_WIDTH] = 80; - Sizes[EGDS_BUTTON_HEIGHT] = 30; - - Sizes[EGDS_TEXT_DISTANCE_X] = 3; - Sizes[EGDS_TEXT_DISTANCE_Y] = 2; - - Sizes[EGDS_TITLEBARTEXT_DISTANCE_X] = 3; - Sizes[EGDS_TITLEBARTEXT_DISTANCE_Y] = 2; - } - - Sizes[EGDS_MESSAGE_BOX_GAP_SPACE] = 15; - Sizes[EGDS_MESSAGE_BOX_MIN_TEXT_WIDTH] = 0; - Sizes[EGDS_MESSAGE_BOX_MAX_TEXT_WIDTH] = 500; - Sizes[EGDS_MESSAGE_BOX_MIN_TEXT_HEIGHT] = 0; - Sizes[EGDS_MESSAGE_BOX_MAX_TEXT_HEIGHT] = 99999; - - Sizes[EGDS_BUTTON_PRESSED_IMAGE_OFFSET_X] = 1; - Sizes[EGDS_BUTTON_PRESSED_IMAGE_OFFSET_Y] = 1; - Sizes[EGDS_BUTTON_PRESSED_TEXT_OFFSET_X] = 0; - Sizes[EGDS_BUTTON_PRESSED_TEXT_OFFSET_Y] = 2; - - Texts[EGDT_MSG_BOX_OK] = L"OK"; - Texts[EGDT_MSG_BOX_CANCEL] = L"Cancel"; - Texts[EGDT_MSG_BOX_YES] = L"Yes"; - Texts[EGDT_MSG_BOX_NO] = L"No"; - Texts[EGDT_WINDOW_CLOSE] = L"Close"; - Texts[EGDT_WINDOW_RESTORE] = L"Restore"; - Texts[EGDT_WINDOW_MINIMIZE] = L"Minimize"; - Texts[EGDT_WINDOW_MAXIMIZE] = L"Maximize"; - - Icons[EGDI_WINDOW_MAXIMIZE] = 225; - Icons[EGDI_WINDOW_RESTORE] = 226; - Icons[EGDI_WINDOW_CLOSE] = 227; - Icons[EGDI_WINDOW_MINIMIZE] = 228; - Icons[EGDI_CURSOR_UP] = 229; - Icons[EGDI_CURSOR_DOWN] = 230; - Icons[EGDI_CURSOR_LEFT] = 231; - Icons[EGDI_CURSOR_RIGHT] = 232; - Icons[EGDI_MENU_MORE] = 232; - Icons[EGDI_CHECK_BOX_CHECKED] = 233; - Icons[EGDI_DROP_DOWN] = 234; - Icons[EGDI_SMALL_CURSOR_UP] = 235; - Icons[EGDI_SMALL_CURSOR_DOWN] = 236; - Icons[EGDI_RADIO_BUTTON_CHECKED] = 237; - Icons[EGDI_MORE_LEFT] = 238; - Icons[EGDI_MORE_RIGHT] = 239; - Icons[EGDI_MORE_UP] = 240; - Icons[EGDI_MORE_DOWN] = 241; - Icons[EGDI_WINDOW_RESIZE] = 242; - Icons[EGDI_EXPAND] = 243; - Icons[EGDI_COLLAPSE] = 244; - - Icons[EGDI_FILE] = 245; - Icons[EGDI_DIRECTORY] = 246; - - for (u32 i=0; idrop(); - } - - if (SpriteBank) - SpriteBank->drop(); -} - - -//! returns default color -video::SColor GUISkin::getColor(EGUI_DEFAULT_COLOR color) const -{ - if ((u32)color < EGDC_COUNT) - return Colors[color]; - else - return video::SColor(); -} - - -//! sets a default color -void GUISkin::setColor(EGUI_DEFAULT_COLOR which, video::SColor newColor) -{ - if ((u32)which < EGDC_COUNT) - Colors[which] = newColor; -} - - -//! returns size for the given size type -s32 GUISkin::getSize(EGUI_DEFAULT_SIZE size) const -{ - if ((u32)size < EGDS_COUNT) - return Sizes[size]; - else - return 0; -} - - -//! sets a default size -void GUISkin::setSize(EGUI_DEFAULT_SIZE which, s32 size) -{ - if ((u32)which < EGDS_COUNT) - Sizes[which] = size; -} - - -//! returns the default font -IGUIFont* GUISkin::getFont(EGUI_DEFAULT_FONT which) const -{ - if (((u32)which < EGDF_COUNT) && Fonts[which]) - return Fonts[which]; - else - return Fonts[EGDF_DEFAULT]; -} - - -//! sets a default font -void GUISkin::setFont(IGUIFont* font, EGUI_DEFAULT_FONT which) -{ - if ((u32)which >= EGDF_COUNT) - return; - - if (font) - { - font->grab(); - if (Fonts[which]) - Fonts[which]->drop(); - - Fonts[which] = font; - } -} - - -//! gets the sprite bank stored -IGUISpriteBank* GUISkin::getSpriteBank() const -{ - return SpriteBank; -} - - -//! set a new sprite bank or remove one by passing 0 -void GUISkin::setSpriteBank(IGUISpriteBank* bank) -{ - if (bank) - bank->grab(); - - if (SpriteBank) - SpriteBank->drop(); - - SpriteBank = bank; -} - - -//! Returns a default icon -u32 GUISkin::getIcon(EGUI_DEFAULT_ICON icon) const -{ - if ((u32)icon < EGDI_COUNT) - return Icons[icon]; - else - return 0; -} - - -//! Sets a default icon -void GUISkin::setIcon(EGUI_DEFAULT_ICON icon, u32 index) -{ - if ((u32)icon < EGDI_COUNT) - Icons[icon] = index; -} - - -//! Returns a default text. For example for Message box button captions: -//! "OK", "Cancel", "Yes", "No" and so on. -const wchar_t* GUISkin::getDefaultText(EGUI_DEFAULT_TEXT text) const -{ - if ((u32)text < EGDT_COUNT) - return Texts[text].c_str(); - else - return Texts[0].c_str(); -} - - -//! Sets a default text. For example for Message box button captions: -//! "OK", "Cancel", "Yes", "No" and so on. -void GUISkin::setDefaultText(EGUI_DEFAULT_TEXT which, const wchar_t* newText) -{ - if ((u32)which < EGDT_COUNT) - Texts[which] = newText; -} - - -//! draws a standard 3d button pane -/** Used for drawing for example buttons in normal state. -It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and -EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. -\param rect: Defining area where to draw. -\param clip: Clip area. -\param element: Pointer to the element which wishes to draw this. This parameter -is usually not used by ISkin, but can be used for example by more complex -implementations to find out how to draw the part exactly. */ -// PATCH -void GUISkin::drawColored3DButtonPaneStandard(IGUIElement* element, - const core::rect& r, - const core::rect* clip, - const video::SColor* colors) -{ - if (!Driver) - return; - - if (!colors) - colors = Colors; - - core::rect rect = r; - - if ( Type == EGST_BURNING_SKIN ) - { - rect.UpperLeftCorner.X -= 1; - rect.UpperLeftCorner.Y -= 1; - rect.LowerRightCorner.X += 1; - rect.LowerRightCorner.Y += 1; - draw3DSunkenPane(element, - colors[ EGDC_WINDOW ].getInterpolated( 0xFFFFFFFF, 0.9f ) - ,false, true, rect, clip); - return; - } - - Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); - - rect.LowerRightCorner.X -= 1; - rect.LowerRightCorner.Y -= 1; - Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); - - rect.UpperLeftCorner.X += 1; - rect.UpperLeftCorner.Y += 1; - Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); - - rect.LowerRightCorner.X -= 1; - rect.LowerRightCorner.Y -= 1; - - if (!UseGradient) - { - Driver->draw2DRectangle(colors[EGDC_3D_FACE], rect, clip); - } - else - { - const video::SColor c1 = colors[EGDC_3D_FACE]; - const video::SColor c2 = c1.getInterpolated(colors[EGDC_3D_DARK_SHADOW], 0.4f); - Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip); - } -} -// END PATCH - - -//! draws a pressed 3d button pane -/** Used for drawing for example buttons in pressed state. -It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and -EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. -\param rect: Defining area where to draw. -\param clip: Clip area. -\param element: Pointer to the element which wishes to draw this. This parameter -is usually not used by ISkin, but can be used for example by more complex -implementations to find out how to draw the part exactly. */ -// PATCH -void GUISkin::drawColored3DButtonPanePressed(IGUIElement* element, - const core::rect& r, - const core::rect* clip, - const video::SColor* colors) -{ - if (!Driver) - return; - - if (!colors) - colors = Colors; - - core::rect rect = r; - Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); - - rect.LowerRightCorner.X -= 1; - rect.LowerRightCorner.Y -= 1; - Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); - - rect.UpperLeftCorner.X += 1; - rect.UpperLeftCorner.Y += 1; - Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); - - rect.UpperLeftCorner.X += 1; - rect.UpperLeftCorner.Y += 1; - - if (!UseGradient) - { - Driver->draw2DRectangle(colors[EGDC_3D_FACE], rect, clip); - } - else - { - const video::SColor c1 = colors[EGDC_3D_FACE]; - const video::SColor c2 = c1.getInterpolated(colors[EGDC_3D_DARK_SHADOW], 0.4f); - Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip); - } -} -// END PATCH - - -//! draws a sunken 3d pane -/** Used for drawing the background of edit, combo or check boxes. -\param element: Pointer to the element which wishes to draw this. This parameter -is usually not used by ISkin, but can be used for example by more complex -implementations to find out how to draw the part exactly. -\param bgcolor: Background color. -\param flat: Specifies if the sunken pane should be flat or displayed as sunken -deep into the ground. -\param rect: Defining area where to draw. -\param clip: Clip area. */ -// PATCH -void GUISkin::drawColored3DSunkenPane(IGUIElement* element, video::SColor bgcolor, - bool flat, bool fillBackGround, - const core::rect& r, - const core::rect* clip, - const video::SColor* colors) -{ - if (!Driver) - return; - - if (!colors) - colors = Colors; - - core::rect rect = r; - - if (fillBackGround) - Driver->draw2DRectangle(bgcolor, rect, clip); - - if (flat) - { - // draw flat sunken pane - - rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 1; - Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); // top - - ++rect.UpperLeftCorner.Y; - rect.LowerRightCorner.Y = r.LowerRightCorner.Y; - rect.LowerRightCorner.X = rect.UpperLeftCorner.X + 1; - Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); // left - - rect = r; - ++rect.UpperLeftCorner.Y; - rect.UpperLeftCorner.X = rect.LowerRightCorner.X - 1; - Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); // right - - rect = r; - ++rect.UpperLeftCorner.X; - rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1; - --rect.LowerRightCorner.X; - Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); // bottom - } - else - { - // draw deep sunken pane - rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 1; - Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); // top - ++rect.UpperLeftCorner.X; - ++rect.UpperLeftCorner.Y; - --rect.LowerRightCorner.X; - ++rect.LowerRightCorner.Y; - Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); - - rect.UpperLeftCorner.X = r.UpperLeftCorner.X; - rect.UpperLeftCorner.Y = r.UpperLeftCorner.Y+1; - rect.LowerRightCorner.X = rect.UpperLeftCorner.X + 1; - rect.LowerRightCorner.Y = r.LowerRightCorner.Y; - Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); // left - ++rect.UpperLeftCorner.X; - ++rect.UpperLeftCorner.Y; - ++rect.LowerRightCorner.X; - --rect.LowerRightCorner.Y; - Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); - - rect = r; - rect.UpperLeftCorner.X = rect.LowerRightCorner.X - 1; - ++rect.UpperLeftCorner.Y; - Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); // right - --rect.UpperLeftCorner.X; - ++rect.UpperLeftCorner.Y; - --rect.LowerRightCorner.X; - --rect.LowerRightCorner.Y; - Driver->draw2DRectangle(colors[EGDC_3D_LIGHT], rect, clip); - - rect = r; - ++rect.UpperLeftCorner.X; - rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1; - --rect.LowerRightCorner.X; - Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); // bottom - ++rect.UpperLeftCorner.X; - --rect.UpperLeftCorner.Y; - --rect.LowerRightCorner.X; - --rect.LowerRightCorner.Y; - Driver->draw2DRectangle(colors[EGDC_3D_LIGHT], rect, clip); - } -} -// END PATCH - -//! draws a window background -// return where to draw title bar text. -// PATCH -core::rect GUISkin::drawColored3DWindowBackground(IGUIElement* element, - bool drawTitleBar, video::SColor titleBarColor, - const core::rect& r, - const core::rect* clip, - core::rect* checkClientArea, - const video::SColor* colors) -{ - if (!Driver) - { - if ( checkClientArea ) - { - *checkClientArea = r; - } - return r; - } - - if (!colors) - colors = Colors; - - core::rect rect = r; - - // top border - rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 1; - if ( !checkClientArea ) - { - Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); - } - - // left border - rect.LowerRightCorner.Y = r.LowerRightCorner.Y; - rect.LowerRightCorner.X = rect.UpperLeftCorner.X + 1; - if ( !checkClientArea ) - { - Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); - } - - // right border dark outer line - rect.UpperLeftCorner.X = r.LowerRightCorner.X - 1; - rect.LowerRightCorner.X = r.LowerRightCorner.X; - rect.UpperLeftCorner.Y = r.UpperLeftCorner.Y; - rect.LowerRightCorner.Y = r.LowerRightCorner.Y; - if ( !checkClientArea ) - { - Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); - } - - // right border bright innner line - rect.UpperLeftCorner.X -= 1; - rect.LowerRightCorner.X -= 1; - rect.UpperLeftCorner.Y += 1; - rect.LowerRightCorner.Y -= 1; - if ( !checkClientArea ) - { - Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); - } - - // bottom border dark outer line - rect.UpperLeftCorner.X = r.UpperLeftCorner.X; - rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1; - rect.LowerRightCorner.Y = r.LowerRightCorner.Y; - rect.LowerRightCorner.X = r.LowerRightCorner.X; - if ( !checkClientArea ) - { - Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); - } - - // bottom border bright inner line - rect.UpperLeftCorner.X += 1; - rect.LowerRightCorner.X -= 1; - rect.UpperLeftCorner.Y -= 1; - rect.LowerRightCorner.Y -= 1; - if ( !checkClientArea ) - { - Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); - } - - // client area for background - rect = r; - rect.UpperLeftCorner.X +=1; - rect.UpperLeftCorner.Y +=1; - rect.LowerRightCorner.X -= 2; - rect.LowerRightCorner.Y -= 2; - if (checkClientArea) - { - *checkClientArea = rect; - } - - if ( !checkClientArea ) - { - if (!UseGradient) - { - Driver->draw2DRectangle(colors[EGDC_3D_FACE], rect, clip); - } - else if ( Type == EGST_BURNING_SKIN ) - { - const video::SColor c1 = colors[EGDC_WINDOW].getInterpolated ( 0xFFFFFFFF, 0.9f ); - const video::SColor c2 = colors[EGDC_WINDOW].getInterpolated ( 0xFFFFFFFF, 0.8f ); - - Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip); - } - else - { - const video::SColor c2 = colors[EGDC_3D_SHADOW]; - const video::SColor c1 = colors[EGDC_3D_FACE]; - Driver->draw2DRectangle(rect, c1, c1, c1, c2, clip); - } - } - - // title bar - rect = r; - rect.UpperLeftCorner.X += 2; - rect.UpperLeftCorner.Y += 2; - rect.LowerRightCorner.X -= 2; - rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + getSize(EGDS_WINDOW_BUTTON_WIDTH) + 2; - - if (drawTitleBar ) - { - if (checkClientArea) - { - (*checkClientArea).UpperLeftCorner.Y = rect.LowerRightCorner.Y; - } - else - { - // draw title bar - //if (!UseGradient) - // Driver->draw2DRectangle(titleBarColor, rect, clip); - //else - if ( Type == EGST_BURNING_SKIN ) - { - const video::SColor c = titleBarColor.getInterpolated( video::SColor(titleBarColor.getAlpha(),255,255,255), 0.8f); - Driver->draw2DRectangle(rect, titleBarColor, titleBarColor, c, c, clip); - } - else - { - const video::SColor c = titleBarColor.getInterpolated(video::SColor(titleBarColor.getAlpha(),0,0,0), 0.2f); - Driver->draw2DRectangle(rect, titleBarColor, c, titleBarColor, c, clip); - } - } - } - - return rect; -} -// END PATCH - - -//! draws a standard 3d menu pane -/** Used for drawing for menus and context menus. -It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and -EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. -\param element: Pointer to the element which wishes to draw this. This parameter -is usually not used by ISkin, but can be used for example by more complex -implementations to find out how to draw the part exactly. -\param rect: Defining area where to draw. -\param clip: Clip area. */ -// PATCH -void GUISkin::drawColored3DMenuPane(IGUIElement* element, - const core::rect& r, const core::rect* clip, - const video::SColor* colors) -{ - if (!Driver) - return; - - if (!colors) - colors = Colors; - - core::rect rect = r; - - if ( Type == EGST_BURNING_SKIN ) - { - rect.UpperLeftCorner.Y -= 3; - draw3DButtonPaneStandard(element, rect, clip); - return; - } - - // in this skin, this is exactly what non pressed buttons look like, - // so we could simply call - // draw3DButtonPaneStandard(element, rect, clip); - // here. - // but if the skin is transparent, this doesn't look that nice. So - // We draw it a little bit better, with some more draw2DRectangle calls, - // but there aren't that much menus visible anyway. - - rect.LowerRightCorner.Y = rect.UpperLeftCorner.Y + 1; - Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); - - rect.LowerRightCorner.Y = r.LowerRightCorner.Y; - rect.LowerRightCorner.X = rect.UpperLeftCorner.X + 1; - Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], rect, clip); - - rect.UpperLeftCorner.X = r.LowerRightCorner.X - 1; - rect.LowerRightCorner.X = r.LowerRightCorner.X; - rect.UpperLeftCorner.Y = r.UpperLeftCorner.Y; - rect.LowerRightCorner.Y = r.LowerRightCorner.Y; - Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); - - rect.UpperLeftCorner.X -= 1; - rect.LowerRightCorner.X -= 1; - rect.UpperLeftCorner.Y += 1; - rect.LowerRightCorner.Y -= 1; - Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); - - rect.UpperLeftCorner.X = r.UpperLeftCorner.X; - rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1; - rect.LowerRightCorner.Y = r.LowerRightCorner.Y; - rect.LowerRightCorner.X = r.LowerRightCorner.X; - Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], rect, clip); - - rect.UpperLeftCorner.X += 1; - rect.LowerRightCorner.X -= 1; - rect.UpperLeftCorner.Y -= 1; - rect.LowerRightCorner.Y -= 1; - Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); - - rect = r; - rect.UpperLeftCorner.X +=1; - rect.UpperLeftCorner.Y +=1; - rect.LowerRightCorner.X -= 2; - rect.LowerRightCorner.Y -= 2; - - if (!UseGradient) - Driver->draw2DRectangle(colors[EGDC_3D_FACE], rect, clip); - else - { - const video::SColor c1 = colors[EGDC_3D_FACE]; - const video::SColor c2 = colors[EGDC_3D_SHADOW]; - Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip); - } -} -// END PATCH - - -//! draws a standard 3d tool bar -/** Used for drawing for toolbars and menus. -\param element: Pointer to the element which wishes to draw this. This parameter -is usually not used by ISkin, but can be used for example by more complex -implementations to find out how to draw the part exactly. -\param rect: Defining area where to draw. -\param clip: Clip area. */ -// PATCH -void GUISkin::drawColored3DToolBar(IGUIElement* element, - const core::rect& r, - const core::rect* clip, - const video::SColor* colors) -{ - if (!Driver) - return; - - if (!colors) - colors = Colors; - - core::rect rect = r; - - rect.UpperLeftCorner.X = r.UpperLeftCorner.X; - rect.UpperLeftCorner.Y = r.LowerRightCorner.Y - 1; - rect.LowerRightCorner.Y = r.LowerRightCorner.Y; - rect.LowerRightCorner.X = r.LowerRightCorner.X; - Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], rect, clip); - - rect = r; - rect.LowerRightCorner.Y -= 1; - - if (!UseGradient) - { - Driver->draw2DRectangle(colors[EGDC_3D_FACE], rect, clip); - } - else - if ( Type == EGST_BURNING_SKIN ) - { - const video::SColor c1 = 0xF0000000 | colors[EGDC_3D_FACE].color; - const video::SColor c2 = 0xF0000000 | colors[EGDC_3D_SHADOW].color; - - rect.LowerRightCorner.Y += 1; - Driver->draw2DRectangle(rect, c1, c2, c1, c2, clip); - } - else - { - const video::SColor c1 = colors[EGDC_3D_FACE]; - const video::SColor c2 = colors[EGDC_3D_SHADOW]; - Driver->draw2DRectangle(rect, c1, c1, c2, c2, clip); - } -} -// END PATCH - -//! draws a tab button -/** Used for drawing for tab buttons on top of tabs. -\param element: Pointer to the element which wishes to draw this. This parameter -is usually not used by ISkin, but can be used for example by more complex -implementations to find out how to draw the part exactly. -\param active: Specifies if the tab is currently active. -\param rect: Defining area where to draw. -\param clip: Clip area. */ -// PATCH -void GUISkin::drawColored3DTabButton(IGUIElement* element, bool active, - const core::rect& frameRect, const core::rect* clip, EGUI_ALIGNMENT alignment, - const video::SColor* colors) -{ - if (!Driver) - return; - - if (!colors) - colors = Colors; - - core::rect tr = frameRect; - - if ( alignment == EGUIA_UPPERLEFT ) - { - tr.LowerRightCorner.X -= 2; - tr.LowerRightCorner.Y = tr.UpperLeftCorner.Y + 1; - tr.UpperLeftCorner.X += 1; - Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); - - // draw left highlight - tr = frameRect; - tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1; - tr.UpperLeftCorner.Y += 1; - Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); - - // draw grey background - tr = frameRect; - tr.UpperLeftCorner.X += 1; - tr.UpperLeftCorner.Y += 1; - tr.LowerRightCorner.X -= 2; - Driver->draw2DRectangle(colors[EGDC_3D_FACE], tr, clip); - - // draw right middle gray shadow - tr.LowerRightCorner.X += 1; - tr.UpperLeftCorner.X = tr.LowerRightCorner.X - 1; - Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], tr, clip); - - tr.LowerRightCorner.X += 1; - tr.UpperLeftCorner.X += 1; - tr.UpperLeftCorner.Y += 1; - Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], tr, clip); - } - else - { - tr.LowerRightCorner.X -= 2; - tr.UpperLeftCorner.Y = tr.LowerRightCorner.Y - 1; - tr.UpperLeftCorner.X += 1; - Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); - - // draw left highlight - tr = frameRect; - tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1; - tr.LowerRightCorner.Y -= 1; - Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); - - // draw grey background - tr = frameRect; - tr.UpperLeftCorner.X += 1; - tr.UpperLeftCorner.Y -= 1; - tr.LowerRightCorner.X -= 2; - tr.LowerRightCorner.Y -= 1; - Driver->draw2DRectangle(colors[EGDC_3D_FACE], tr, clip); - - // draw right middle gray shadow - tr.LowerRightCorner.X += 1; - tr.UpperLeftCorner.X = tr.LowerRightCorner.X - 1; - //tr.LowerRightCorner.Y -= 1; - Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], tr, clip); - - tr.LowerRightCorner.X += 1; - tr.UpperLeftCorner.X += 1; - tr.LowerRightCorner.Y -= 1; - Driver->draw2DRectangle(colors[EGDC_3D_DARK_SHADOW], tr, clip); - } -} -// END PATCH - - -//! draws a tab control body -/** \param element: Pointer to the element which wishes to draw this. This parameter -is usually not used by ISkin, but can be used for example by more complex -implementations to find out how to draw the part exactly. -\param border: Specifies if the border should be drawn. -\param background: Specifies if the background should be drawn. -\param rect: Defining area where to draw. -\param clip: Clip area. */ -// PATCH -void GUISkin::drawColored3DTabBody(IGUIElement* element, bool border, bool background, - const core::rect& rect, const core::rect* clip, s32 tabHeight, EGUI_ALIGNMENT alignment, - const video::SColor* colors) -{ - if (!Driver) - return; - - if (!colors) - colors = Colors; - - core::rect tr = rect; - - if ( tabHeight == -1 ) - tabHeight = getSize(gui::EGDS_BUTTON_HEIGHT); - - // draw border. - if (border) - { - if ( alignment == EGUIA_UPPERLEFT ) - { - // draw left hightlight - tr.UpperLeftCorner.Y += tabHeight + 2; - tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1; - Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); - - // draw right shadow - tr.UpperLeftCorner.X = rect.LowerRightCorner.X - 1; - tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1; - Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], tr, clip); - - // draw lower shadow - tr = rect; - tr.UpperLeftCorner.Y = tr.LowerRightCorner.Y - 1; - Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], tr, clip); - } - else - { - // draw left hightlight - tr.LowerRightCorner.Y -= tabHeight + 2; - tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1; - Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); - - // draw right shadow - tr.UpperLeftCorner.X = rect.LowerRightCorner.X - 1; - tr.LowerRightCorner.X = tr.UpperLeftCorner.X + 1; - Driver->draw2DRectangle(colors[EGDC_3D_SHADOW], tr, clip); - - // draw lower shadow - tr = rect; - tr.LowerRightCorner.Y = tr.UpperLeftCorner.Y + 1; - Driver->draw2DRectangle(colors[EGDC_3D_HIGH_LIGHT], tr, clip); - } - } - - if (background) - { - if ( alignment == EGUIA_UPPERLEFT ) - { - tr = rect; - tr.UpperLeftCorner.Y += tabHeight + 2; - tr.LowerRightCorner.X -= 1; - tr.UpperLeftCorner.X += 1; - tr.LowerRightCorner.Y -= 1; - } - else - { - tr = rect; - tr.UpperLeftCorner.X += 1; - tr.UpperLeftCorner.Y -= 1; - tr.LowerRightCorner.X -= 1; - tr.LowerRightCorner.Y -= tabHeight + 2; - //tr.UpperLeftCorner.X += 1; - } - - if (!UseGradient) - Driver->draw2DRectangle(colors[EGDC_3D_FACE], tr, clip); - else - { - video::SColor c1 = colors[EGDC_3D_FACE]; - video::SColor c2 = colors[EGDC_3D_SHADOW]; - Driver->draw2DRectangle(tr, c1, c1, c2, c2, clip); - } - } -} -// END PATCH - - -//! draws an icon, usually from the skin's sprite bank -/** \param parent: Pointer to the element which wishes to draw this icon. -This parameter is usually not used by IGUISkin, but can be used for example -by more complex implementations to find out how to draw the part exactly. -\param icon: Specifies the icon to be drawn. -\param position: The position to draw the icon -\param starttime: The time at the start of the animation -\param currenttime: The present time, used to calculate the frame number -\param loop: Whether the animation should loop or not -\param clip: Clip area. */ -// PATCH -void GUISkin::drawColoredIcon(IGUIElement* element, EGUI_DEFAULT_ICON icon, - const core::position2di position, - u32 starttime, u32 currenttime, - bool loop, const core::rect* clip, - const video::SColor* colors) -{ - if (!SpriteBank) - return; - - if (!colors) - colors = Colors; - - bool gray = element && !element->isEnabled(); - SpriteBank->draw2DSprite(Icons[icon], position, clip, - colors[gray? EGDC_GRAY_WINDOW_SYMBOL : EGDC_WINDOW_SYMBOL], starttime, currenttime, loop, true); -} -// END PATCH - - -EGUI_SKIN_TYPE GUISkin::getType() const -{ - return Type; -} - - -//! draws a 2d rectangle. -void GUISkin::draw2DRectangle(IGUIElement* element, - const video::SColor &color, const core::rect& pos, - const core::rect* clip) -{ - Driver->draw2DRectangle(color, pos, clip); -} - - -//! gets the colors -// PATCH -void GUISkin::getColors(video::SColor* colors) -{ - u32 i; - for (i=0; i -#include "ITexture.h" - -namespace irr -{ -namespace video -{ - class IVideoDriver; -} -namespace gui -{ - class GUISkin : public IGUISkin - { - public: - - GUISkin(EGUI_SKIN_TYPE type, video::IVideoDriver* driver); - - //! destructor - virtual ~GUISkin(); - - //! returns display density scaling factor - virtual float getScale() const { return Scale; } - - //! sets display density scaling factor - virtual void setScale(float scale) { Scale = scale; } - - //! returns default color - virtual video::SColor getColor(EGUI_DEFAULT_COLOR color) const; - - //! sets a default color - virtual void setColor(EGUI_DEFAULT_COLOR which, video::SColor newColor); - - //! returns size for the given size type - virtual s32 getSize(EGUI_DEFAULT_SIZE size) const; - - //! sets a default size - virtual void setSize(EGUI_DEFAULT_SIZE which, s32 size); - - //! returns the default font - virtual IGUIFont* getFont(EGUI_DEFAULT_FONT which=EGDF_DEFAULT) const; - - //! sets a default font - virtual void setFont(IGUIFont* font, EGUI_DEFAULT_FONT which=EGDF_DEFAULT); - - //! sets the sprite bank used for drawing icons - virtual void setSpriteBank(IGUISpriteBank* bank); - - //! gets the sprite bank used for drawing icons - virtual IGUISpriteBank* getSpriteBank() const; - - //! Returns a default icon - /** Returns the sprite index within the sprite bank */ - virtual u32 getIcon(EGUI_DEFAULT_ICON icon) const; - - //! Sets a default icon - /** Sets the sprite index used for drawing icons like arrows, - close buttons and ticks in checkboxes - \param icon: Enum specifying which icon to change - \param index: The sprite index used to draw this icon */ - virtual void setIcon(EGUI_DEFAULT_ICON icon, u32 index); - - //! Returns a default text. - /** For example for Message box button captions: - "OK", "Cancel", "Yes", "No" and so on. */ - virtual const wchar_t* getDefaultText(EGUI_DEFAULT_TEXT text) const; - - //! Sets a default text. - /** For example for Message box button captions: - "OK", "Cancel", "Yes", "No" and so on. */ - virtual void setDefaultText(EGUI_DEFAULT_TEXT which, const wchar_t* newText); - - //! draws a standard 3d button pane - /** Used for drawing for example buttons in normal state. - It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and - EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. - \param rect: Defining area where to draw. - \param clip: Clip area. - \param element: Pointer to the element which wishes to draw this. This parameter - is usually not used by ISkin, but can be used for example by more complex - implementations to find out how to draw the part exactly. */ - virtual void draw3DButtonPaneStandard(IGUIElement* element, - const core::rect& rect, - const core::rect* clip=0) - { - drawColored3DButtonPaneStandard(element, rect,clip); - } - - virtual void drawColored3DButtonPaneStandard(IGUIElement* element, - const core::rect& rect, - const core::rect* clip=0, - const video::SColor* colors=0); - - //! draws a pressed 3d button pane - /** Used for drawing for example buttons in pressed state. - It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and - EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. - \param rect: Defining area where to draw. - \param clip: Clip area. - \param element: Pointer to the element which wishes to draw this. This parameter - is usually not used by ISkin, but can be used for example by more complex - implementations to find out how to draw the part exactly. */ - virtual void draw3DButtonPanePressed(IGUIElement* element, - const core::rect& rect, - const core::rect* clip=0) - { - drawColored3DButtonPanePressed(element, rect, clip); - } - - virtual void drawColored3DButtonPanePressed(IGUIElement* element, - const core::rect& rect, - const core::rect* clip=0, - const video::SColor* colors=0); - - //! draws a sunken 3d pane - /** Used for drawing the background of edit, combo or check boxes. - \param element: Pointer to the element which wishes to draw this. This parameter - is usually not used by ISkin, but can be used for example by more complex - implementations to find out how to draw the part exactly. - \param bgcolor: Background color. - \param flat: Specifies if the sunken pane should be flat or displayed as sunken - deep into the ground. - \param rect: Defining area where to draw. - \param clip: Clip area. */ - virtual void draw3DSunkenPane(IGUIElement* element, - video::SColor bgcolor, bool flat, - bool fillBackGround, - const core::rect& rect, - const core::rect* clip=0) - { - drawColored3DSunkenPane(element, bgcolor, flat, fillBackGround, rect, clip); - } - - virtual void drawColored3DSunkenPane(IGUIElement* element, - video::SColor bgcolor, bool flat, - bool fillBackGround, - const core::rect& rect, - const core::rect* clip=0, - const video::SColor* colors=0); - - //! draws a window background - /** Used for drawing the background of dialogs and windows. - \param element: Pointer to the element which wishes to draw this. This parameter - is usually not used by ISkin, but can be used for example by more complex - implementations to find out how to draw the part exactly. - \param titleBarColor: Title color. - \param drawTitleBar: True to enable title drawing. - \param rect: Defining area where to draw. - \param clip: Clip area. - \param checkClientArea: When set to non-null the function will not draw anything, - but will instead return the clientArea which can be used for drawing by the calling window. - That is the area without borders and without titlebar. - \return Returns rect where it would be good to draw title bar text. This will - work even when checkClientArea is set to a non-null value.*/ - virtual core::rect draw3DWindowBackground(IGUIElement* element, - bool drawTitleBar, video::SColor titleBarColor, - const core::rect& rect, - const core::rect* clip, - core::rect* checkClientArea) - { - return drawColored3DWindowBackground(element, drawTitleBar, titleBarColor, - rect, clip, checkClientArea); - } - - virtual core::rect drawColored3DWindowBackground(IGUIElement* element, - bool drawTitleBar, video::SColor titleBarColor, - const core::rect& rect, - const core::rect* clip, - core::rect* checkClientArea, - const video::SColor* colors=0); - - //! draws a standard 3d menu pane - /** Used for drawing for menus and context menus. - It uses the colors EGDC_3D_DARK_SHADOW, EGDC_3D_HIGH_LIGHT, EGDC_3D_SHADOW and - EGDC_3D_FACE for this. See EGUI_DEFAULT_COLOR for details. - \param element: Pointer to the element which wishes to draw this. This parameter - is usually not used by ISkin, but can be used for example by more complex - implementations to find out how to draw the part exactly. - \param rect: Defining area where to draw. - \param clip: Clip area. */ - virtual void draw3DMenuPane(IGUIElement* element, - const core::rect& rect, - const core::rect* clip=0) - { - drawColored3DMenuPane(element, rect, clip); - } - - virtual void drawColored3DMenuPane(IGUIElement* element, - const core::rect& rect, - const core::rect* clip=0, - const video::SColor* colors=0); - - //! draws a standard 3d tool bar - /** Used for drawing for toolbars and menus. - \param element: Pointer to the element which wishes to draw this. This parameter - is usually not used by ISkin, but can be used for example by more complex - implementations to find out how to draw the part exactly. - \param rect: Defining area where to draw. - \param clip: Clip area. */ - virtual void draw3DToolBar(IGUIElement* element, - const core::rect& rect, - const core::rect* clip=0) - { - drawColored3DToolBar(element, rect, clip); - } - - virtual void drawColored3DToolBar(IGUIElement* element, - const core::rect& rect, - const core::rect* clip=0, - const video::SColor* colors=0); - - //! draws a tab button - /** Used for drawing for tab buttons on top of tabs. - \param element: Pointer to the element which wishes to draw this. This parameter - is usually not used by ISkin, but can be used for example by more complex - implementations to find out how to draw the part exactly. - \param active: Specifies if the tab is currently active. - \param rect: Defining area where to draw. - \param clip: Clip area. */ - virtual void draw3DTabButton(IGUIElement* element, bool active, - const core::rect& rect, const core::rect* clip=0, EGUI_ALIGNMENT alignment=EGUIA_UPPERLEFT) - { - drawColored3DTabButton(element, active, rect, clip, alignment); - } - - virtual void drawColored3DTabButton(IGUIElement* element, bool active, - const core::rect& rect, const core::rect* clip=0, EGUI_ALIGNMENT alignment=EGUIA_UPPERLEFT, - const video::SColor* colors=0); - - //! draws a tab control body - /** \param element: Pointer to the element which wishes to draw this. This parameter - is usually not used by ISkin, but can be used for example by more complex - implementations to find out how to draw the part exactly. - \param border: Specifies if the border should be drawn. - \param background: Specifies if the background should be drawn. - \param rect: Defining area where to draw. - \param clip: Clip area. */ - virtual void draw3DTabBody(IGUIElement* element, bool border, bool background, - const core::rect& rect, const core::rect* clip=0, s32 tabHeight=-1, EGUI_ALIGNMENT alignment=EGUIA_UPPERLEFT) - { - drawColored3DTabBody(element, border, background, rect, clip, tabHeight, alignment); - } - - virtual void drawColored3DTabBody(IGUIElement* element, bool border, bool background, - const core::rect& rect, const core::rect* clip=0, s32 tabHeight=-1, EGUI_ALIGNMENT alignment=EGUIA_UPPERLEFT, - const video::SColor* colors=0); - - //! draws an icon, usually from the skin's sprite bank - /** \param element: Pointer to the element which wishes to draw this icon. - This parameter is usually not used by IGUISkin, but can be used for example - by more complex implementations to find out how to draw the part exactly. - \param icon: Specifies the icon to be drawn. - \param position: The position to draw the icon - \param starttime: The time at the start of the animation - \param currenttime: The present time, used to calculate the frame number - \param loop: Whether the animation should loop or not - \param clip: Clip area. */ - virtual void drawIcon(IGUIElement* element, EGUI_DEFAULT_ICON icon, - const core::position2di position, - u32 starttime=0, u32 currenttime=0, - bool loop=false, const core::rect* clip=0) - { - drawColoredIcon(element, icon, position, starttime, currenttime, loop, clip); - } - - virtual void drawColoredIcon(IGUIElement* element, EGUI_DEFAULT_ICON icon, - const core::position2di position, - u32 starttime=0, u32 currenttime=0, - bool loop=false, const core::rect* clip=0, - const video::SColor* colors=0); - - //! draws a 2d rectangle. - /** \param element: Pointer to the element which wishes to draw this icon. - This parameter is usually not used by IGUISkin, but can be used for example - by more complex implementations to find out how to draw the part exactly. - \param color: Color of the rectangle to draw. The alpha component specifies how - transparent the rectangle will be. - \param pos: Position of the rectangle. - \param clip: Pointer to rectangle against which the rectangle will be clipped. - If the pointer is null, no clipping will be performed. */ - virtual void draw2DRectangle(IGUIElement* element, const video::SColor &color, - const core::rect& pos, const core::rect* clip = 0); - - - //! get the type of this skin - virtual EGUI_SKIN_TYPE getType() const; - - //! gets the colors - virtual void getColors(video::SColor* colors); // ::PATCH: - - private: - - float Scale = 1.0f; - video::SColor Colors[EGDC_COUNT]; - s32 Sizes[EGDS_COUNT]; - u32 Icons[EGDI_COUNT]; - IGUIFont* Fonts[EGDF_COUNT]; - IGUISpriteBank* SpriteBank; - core::stringw Texts[EGDT_COUNT]; - video::IVideoDriver* Driver; - bool UseGradient; - - EGUI_SKIN_TYPE Type; - }; - - #define set3DSkinColors(skin, button_color) \ - { \ - skin->setColor(EGDC_3D_FACE, button_color); \ - skin->setColor(EGDC_3D_DARK_SHADOW, button_color, 0.25f); \ - skin->setColor(EGDC_3D_SHADOW, button_color, 0.5f); \ - skin->setColor(EGDC_3D_LIGHT, button_color); \ - skin->setColor(EGDC_3D_HIGH_LIGHT, button_color, 1.5f); \ - } - - #define getElementSkinColor(color) \ - { \ - if (!Colors) \ - { \ - IGUISkin* skin = Environment->getSkin(); \ - if (skin) \ - return skin->getColor(color); \ - } \ - return Colors[color]; \ - } - - #define setElementSkinColor(which, newColor, shading) \ - { \ - if (!Colors) \ - { \ - Colors = new video::SColor[EGDC_COUNT]; \ - GUISkin* skin = (GUISkin *)Environment->getSkin(); \ - if (skin) \ - skin->getColors(Colors); \ - } \ - Colors[which] = newColor; \ - setShading(Colors[which],shading); \ - } -} // end namespace gui -//! Sets the shading -inline void setShading(video::SColor &color,f32 s) // :PATCH: -{ - if (s < 1.0f) - { - color.setRed(color.getRed() * s); - color.setGreen(color.getGreen() * s); - color.setBlue(color.getBlue() * s); - } - else if (s > 1.0f) - { - s -= 1.0f; - - color.setRed(color.getRed() + (255 - color.getRed()) * s); - color.setGreen(color.getGreen() + (255 - color.getGreen()) * s); - color.setBlue(color.getBlue() + (255 - color.getBlue()) * s); - } -} -} // end namespace irr - -#endif diff --git a/src/gui/touchcontrols.cpp b/src/gui/touchcontrols.cpp index 1e4f8a99b..4a673ccf3 100644 --- a/src/gui/touchcontrols.cpp +++ b/src/gui/touchcontrols.cpp @@ -412,6 +412,10 @@ TouchControls::TouchControls(IrrlichtDevice *device, ISimpleTextureSource *tsrc) pos.X += spacing.X; } + + m_status_text = grab_gui_element( + m_guienv->addStaticText(L"", recti(), false, false)); + m_status_text->setVisible(false); } void TouchControls::addButton(std::vector &buttons, touch_gui_button_id id, @@ -477,16 +481,13 @@ void TouchControls::handleReleaseEvent(size_t pointer_id) m_pointer_downpos.erase(pointer_id); m_pointer_pos.erase(pointer_id); - if (m_overflow_open) { - buttons_handleRelease(m_overflow_buttons, pointer_id, m_device->getVideoDriver(), - m_receiver, m_texturesource); - return; - } - // handle buttons if (buttons_handleRelease(m_buttons, pointer_id, m_device->getVideoDriver(), m_receiver, m_texturesource)) return; + if (buttons_handleRelease(m_overflow_buttons, pointer_id, m_device->getVideoDriver(), + m_receiver, m_texturesource)) + return; if (m_has_move_id && pointer_id == m_move_id) { // handle the point used for moving view @@ -513,9 +514,7 @@ void TouchControls::handleReleaseEvent(size_t pointer_id) m_joystick_status_aux1 = false; applyJoystickStatus(); - m_joystick_btn_off->setVisible(true); - m_joystick_btn_bg->setVisible(false); - m_joystick_btn_center->setVisible(false); + updateVisibility(); } else { infostream << "TouchControls::translateEvent released unknown button: " << pointer_id << std::endl; @@ -572,9 +571,6 @@ void TouchControls::translateEvent(const SEvent &event) toggleOverflowMenu(); // refresh since visibility of buttons has changed element = m_guienv->getRootGUIElement()->getElementFromPoint(touch_pos); - // restore after releaseAll in toggleOverflowMenu - m_pointer_downpos[pointer_id] = touch_pos; - m_pointer_pos[pointer_id] = touch_pos; // continue processing, but avoid accidentally placing a node // when closing the overflow menu prevent_short_tap = true; @@ -600,9 +596,7 @@ void TouchControls::translateEvent(const SEvent &event) m_joystick_id = pointer_id; m_joystick_has_really_moved = false; - m_joystick_btn_off->setVisible(false); - m_joystick_btn_bg->setVisible(true); - m_joystick_btn_center->setVisible(true); + updateVisibility(); // If it's a fixed joystick, don't move the joystick "button". if (!m_fixed_joystick) @@ -633,9 +627,6 @@ void TouchControls::translateEvent(const SEvent &event) } else { assert(event.TouchInput.Event == ETIE_MOVED); - if (m_overflow_open) - return; - if (!(m_has_joystick_id && m_fixed_joystick) && m_pointer_pos[event.TouchInput.ID] == touch_pos) return; @@ -721,13 +712,9 @@ void TouchControls::applyJoystickStatus() void TouchControls::step(float dtime) { - if (m_overflow_open) { - buttons_step(m_overflow_buttons, dtime, m_device->getVideoDriver(), m_receiver, m_texturesource); - return; - } - // simulate keyboard repeats buttons_step(m_buttons, dtime, m_device->getVideoDriver(), m_receiver, m_texturesource); + buttons_step(m_overflow_buttons, dtime, m_device->getVideoDriver(), m_receiver, m_texturesource); // joystick applyJoystickStatus(); @@ -774,7 +761,6 @@ void TouchControls::setVisible(bool visible) return; m_visible = visible; - // order matters if (!visible) { releaseAll(); m_overflow_open = false; @@ -784,7 +770,8 @@ void TouchControls::setVisible(bool visible) void TouchControls::toggleOverflowMenu() { - releaseAll(); // must be done first + // no releaseAll here so that you can e.g. continue holding the joystick + // while the overflow menu is open m_overflow_open = !m_overflow_open; updateVisibility(); } @@ -795,7 +782,10 @@ void TouchControls::updateVisibility() for (auto &button : m_buttons) button.gui_button->setVisible(regular_visible); m_overflow_btn->setVisible(regular_visible); - m_joystick_btn_off->setVisible(regular_visible); + + m_joystick_btn_off->setVisible(regular_visible && !m_has_joystick_id); + m_joystick_btn_bg->setVisible(regular_visible && m_has_joystick_id); + m_joystick_btn_center->setVisible(regular_visible && m_has_joystick_id); bool overflow_visible = m_visible && m_overflow_open; m_overflow_bg->setVisible(overflow_visible); diff --git a/src/gui/touchcontrols.h b/src/gui/touchcontrols.h index 0a86fe34e..102c85f09 100644 --- a/src/gui/touchcontrols.h +++ b/src/gui/touchcontrols.h @@ -177,6 +177,9 @@ public: void registerHotbarRect(u16 index, const recti &rect); std::optional getHotbarSelection(); + bool isStatusTextOverriden() { return m_overflow_open; } + IGUIStaticText *getStatusText() { return m_status_text.get(); } + private: IrrlichtDevice *m_device = nullptr; IGUIEnvironment *m_guienv = nullptr; @@ -235,6 +238,8 @@ private: std::vector> m_overflow_button_titles; std::vector m_overflow_button_rects; + std::shared_ptr m_status_text; + void toggleOverflowMenu(); void updateVisibility(); void releaseAll(); diff --git a/src/irrlicht_changes/CGUITTFont.cpp b/src/irrlicht_changes/CGUITTFont.cpp index 4f5f52b4e..022ab46b0 100644 --- a/src/irrlicht_changes/CGUITTFont.cpp +++ b/src/irrlicht_changes/CGUITTFont.cpp @@ -1098,13 +1098,9 @@ core::array CGUITTFont::addTextSceneNode(const wchar_t* text // the default font material SMaterial mat; - mat.Lighting = true; mat.ZWriteEnable = video::EZW_OFF; - mat.NormalizeNormals = true; - mat.ColorMaterial = video::ECM_NONE; mat.MaterialType = use_transparency ? video::EMT_TRANSPARENT_ALPHA_CHANNEL : video::EMT_SOLID; mat.MaterialTypeParam = 0.01f; - mat.DiffuseColor = color; wchar_t current_char = 0, previous_char = 0; u32 n = 0; diff --git a/src/itemdef.cpp b/src/itemdef.cpp index ad2ed4847..220c6fbb6 100644 --- a/src/itemdef.cpp +++ b/src/itemdef.cpp @@ -45,7 +45,8 @@ TouchInteraction::TouchInteraction() pointed_object = TouchInteractionMode_USER; } -TouchInteractionMode TouchInteraction::getMode(PointedThingType pointed_type) const +TouchInteractionMode TouchInteraction::getMode(const ItemDefinition &selected_def, + PointedThingType pointed_type) const { TouchInteractionMode result; switch (pointed_type) { @@ -63,7 +64,9 @@ TouchInteractionMode TouchInteraction::getMode(PointedThingType pointed_type) co } if (result == TouchInteractionMode_USER) { - if (pointed_type == POINTEDTHING_OBJECT) + if (pointed_type == POINTEDTHING_OBJECT && !selected_def.usable) + // Only apply when we're actually able to punch the object, i.e. when + // the selected item has no on_use callback defined. result = g_settings->get("touch_punch_gesture") == "long_tap" ? LONG_DIG_SHORT_PLACE : SHORT_DIG_LONG_PLACE; else diff --git a/src/itemdef.h b/src/itemdef.h index 4a227ebe1..44fab8d91 100644 --- a/src/itemdef.h +++ b/src/itemdef.h @@ -71,7 +71,8 @@ struct TouchInteraction TouchInteraction(); // Returns the right mode for the pointed thing and resolves any occurrence // of TouchInteractionMode_USER into an actual mode. - TouchInteractionMode getMode(PointedThingType pointed_type) const; + TouchInteractionMode getMode(const ItemDefinition &selected_def, + PointedThingType pointed_type) const; void serialize(std::ostream &os) const; void deSerialize(std::istream &is); }; diff --git a/src/itemstackmetadata.cpp b/src/itemstackmetadata.cpp index be1715e1a..a2fc67c46 100644 --- a/src/itemstackmetadata.cpp +++ b/src/itemstackmetadata.cpp @@ -89,11 +89,11 @@ void ItemStackMetadata::deSerialize(std::istream &is) while (!fnd.at_end()) { std::string name = fnd.next(DESERIALIZE_KV_DELIM_STR); std::string var = fnd.next(DESERIALIZE_PAIR_DELIM_STR); - m_stringvars[name] = var; + m_stringvars[name] = std::move(var); } } else { // BACKWARDS COMPATIBILITY - m_stringvars[""] = in; + m_stringvars[""] = std::move(in); } } updateToolCapabilities(); diff --git a/src/lighting.h b/src/lighting.h index 262a48b5d..fbf10b1c9 100644 --- a/src/lighting.h +++ b/src/lighting.h @@ -18,7 +18,9 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #pragma once +#include "SColor.h" +using namespace irr; /** * Parameters for automatic exposure compensation @@ -54,4 +56,5 @@ struct Lighting float shadow_intensity {0.0f}; float saturation {1.0f}; float volumetric_light_strength {0.0f}; + video::SColor shadow_tint {255, 0, 0, 0}; }; diff --git a/src/main.cpp b/src/main.cpp index 9f358bb66..9f737b86d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -531,7 +531,6 @@ static bool setup_log_params(const Settings &cmd_args) if (cmd_args.getFlag("trace")) { dstream << _("Enabling trace level debug output") << std::endl; g_logger.addOutput(&stderr_output, LL_TRACE); - socket_enable_debug_output = true; } return true; @@ -1279,8 +1278,7 @@ static bool recompress_map_database(const GameParams &game_params, const Setting { MapBlock mb(v3s16(0,0,0), &server); - u8 ver = readU8(iss); - mb.deSerialize(iss, ver, true); + ServerMap::deSerializeBlock(&mb, iss); oss.str(""); oss.clear(); diff --git a/src/mapgen/cavegen.cpp b/src/mapgen/cavegen.cpp index 47272142f..e6ab66980 100644 --- a/src/mapgen/cavegen.cpp +++ b/src/mapgen/cavegen.cpp @@ -82,8 +82,6 @@ void CavesNoiseIntersection::generateCaves(MMVManip *vm, const v3s16 &em = vm->m_area.getExtent(); u32 index2d = 0; // Biomemap index - s16 *biome_transitions = m_bmgn->getBiomeTransitions(); - for (s16 z = nmin.Z; z <= nmax.Z; z++) for (s16 x = nmin.X; x <= nmax.X; x++, index2d++) { bool column_is_open = false; // Is column open to overground @@ -101,8 +99,7 @@ void CavesNoiseIntersection::generateCaves(MMVManip *vm, u16 depth_riverbed = biome->depth_riverbed; u16 nplaced = 0; - int cur_biome_depth = 0; - s16 biome_y_min = biome_transitions[cur_biome_depth]; + s16 biome_y_min = m_bmgn->getNextTransitionY(nmax.Y); // Don't excavate the overgenerated stone at nmax.Y + 1, // this creates a 'roof' over the tunnel, preventing light in @@ -114,15 +111,12 @@ void CavesNoiseIntersection::generateCaves(MMVManip *vm, // We need this check to make sure that biomes don't generate too far down if (y < biome_y_min) { biome = m_bmgn->getBiomeAtIndex(index2d, v3s16(x, y, z)); + biome_y_min = m_bmgn->getNextTransitionY(y); - // Finding the height of the next biome - // On first iteration this may loop a couple times after than it should just run once - while (y < biome_y_min) { - biome_y_min = biome_transitions[++cur_biome_depth]; + if (x == nmin.X && z == nmin.Z && false) { + dstream << "cavegen: biome at " << y << " is " << biome->name + << ", next at " << biome_y_min << std::endl; } - - /*if (x == nmin.X && z == nmin.Z) - printf("Cave: check @ %i -> %s -> again at %i\n", y, biome->name.c_str(), biome_y_min);*/ } content_t c = vm->m_data[vi].getContent(); diff --git a/src/mapgen/mapgen.cpp b/src/mapgen/mapgen.cpp index 80ffebc9e..0b821e02e 100644 --- a/src/mapgen/mapgen.cpp +++ b/src/mapgen/mapgen.cpp @@ -649,8 +649,6 @@ void MapgenBasic::generateBiomes() noise_filler_depth->perlinMap2D(node_min.X, node_min.Z); - s16 *biome_transitions = biomegen->getBiomeTransitions(); - for (s16 z = node_min.Z; z <= node_max.Z; z++) for (s16 x = node_min.X; x <= node_max.X; x++, index++) { Biome *biome = NULL; @@ -661,8 +659,7 @@ void MapgenBasic::generateBiomes() u16 depth_riverbed = 0; u32 vi = vm->m_area.index(x, node_max.Y, z); - int cur_biome_depth = 0; - s16 biome_y_min = biome_transitions[cur_biome_depth]; + s16 biome_y_min = biomegen->getNextTransitionY(node_max.Y); // Check node at base of mapchunk above, either a node of a previously // generated mapchunk or if not, a node of overgenerated base terrain. @@ -695,15 +692,7 @@ void MapgenBasic::generateBiomes() if (!biome || y < biome_y_min) { // (Re)calculate biome biome = biomegen->getBiomeAtIndex(index, v3s16(x, y, z)); - - // Finding the height of the next biome - // On first iteration this may loop a couple times after than it should just run once - while (y < biome_y_min) { - biome_y_min = biome_transitions[++cur_biome_depth]; - } - - /*if (x == node_min.X && z == node_min.Z) - printf("Map: check @ %i -> %s -> again at %i\n", y, biome->name.c_str(), biome_y_min);*/ + biome_y_min = biomegen->getNextTransitionY(y); } // Add biome to biomemap at first stone surface detected diff --git a/src/mapgen/mg_biome.cpp b/src/mapgen/mg_biome.cpp index 83ecbd691..b270a5413 100644 --- a/src/mapgen/mg_biome.cpp +++ b/src/mapgen/mg_biome.cpp @@ -149,47 +149,36 @@ BiomeGenOriginal::BiomeGenOriginal(BiomeManager *biomemgr, // is disabled. memset(biomemap, 0, sizeof(biome_t) * m_csize.X * m_csize.Z); - // Calculating the bounding position of each biome so we know when we might switch - // First gathering all heights where we might switch - std::vector temp_transition_heights; - temp_transition_heights.reserve(m_bmgr->getNumObjects() * 2); + // Calculate cache of Y transition points + std::vector values; + values.reserve(m_bmgr->getNumObjects() * 2); for (size_t i = 0; i < m_bmgr->getNumObjects(); i++) { Biome *b = (Biome *)m_bmgr->getRaw(i); - temp_transition_heights.push_back(b->max_pos.Y); - temp_transition_heights.push_back(b->min_pos.Y); + values.push_back(b->max_pos.Y); + values.push_back(b->min_pos.Y); } - // Sorting the biome transition points - std::sort(temp_transition_heights.begin(), temp_transition_heights.end(), std::greater()); + std::sort(values.begin(), values.end(), std::greater<>()); + values.erase(std::unique(values.begin(), values.end()), values.end()); - // Getting rid of duplicate biome transition points - s16 last = temp_transition_heights[0]; - size_t out_pos = 1; - for (size_t i = 1; i < temp_transition_heights.size(); i++){ - if (temp_transition_heights[i] != last) { - last = temp_transition_heights[i]; - temp_transition_heights[out_pos++] = last; - } - } - - biome_transitions = new s16[out_pos]; - memcpy(biome_transitions, temp_transition_heights.data(), sizeof(s16) * out_pos); + m_transitions_y = std::move(values); } BiomeGenOriginal::~BiomeGenOriginal() { delete []biomemap; - delete []biome_transitions; delete noise_heat; delete noise_humidity; delete noise_heat_blend; delete noise_humidity_blend; } -s16* BiomeGenOriginal::getBiomeTransitions() const +s16 BiomeGenOriginal::getNextTransitionY(s16 y) const { - return biome_transitions; + // Find first value that is less than y using binary search + auto it = std::lower_bound(m_transitions_y.begin(), m_transitions_y.end(), y, std::greater_equal<>()); + return (it == m_transitions_y.end()) ? S16_MIN : *it; } BiomeGen *BiomeGenOriginal::clone(BiomeManager *biomemgr) const diff --git a/src/mapgen/mg_biome.h b/src/mapgen/mg_biome.h index 567a0fe81..389b36ee9 100644 --- a/src/mapgen/mg_biome.h +++ b/src/mapgen/mg_biome.h @@ -128,11 +128,14 @@ public: // Same as above, but uses a raw numeric index correlating to the (x,z) position. virtual Biome *getBiomeAtIndex(size_t index, v3s16 pos) const = 0; - virtual s16 *getBiomeTransitions() const = 0; + // Returns the next lower y position at which the biome could change. + // You can use this to optimize calls to getBiomeAtIndex(). + virtual s16 getNextTransitionY(s16 y) const { + return y == S16_MIN ? y : (y - 1); + }; // Result of calcBiomes bulk computation. biome_t *biomemap = nullptr; - s16 *biome_transitions = nullptr; protected: BiomeManager *m_bmgr = nullptr; @@ -167,7 +170,7 @@ struct BiomeParamsOriginal : public BiomeParams { NoiseParams np_humidity_blend; }; -class BiomeGenOriginal : public BiomeGen { +class BiomeGenOriginal final : public BiomeGen { public: BiomeGenOriginal(BiomeManager *biomemgr, const BiomeParamsOriginal *params, v3s16 chunksize); @@ -189,7 +192,7 @@ public: Biome *getBiomeAtIndex(size_t index, v3s16 pos) const; Biome *calcBiomeFromNoise(float heat, float humidity, v3s16 pos) const; - s16 *getBiomeTransitions() const; + s16 getNextTransitionY(s16 y) const; float *heatmap; float *humidmap; @@ -201,6 +204,9 @@ private: Noise *noise_humidity; Noise *noise_heat_blend; Noise *noise_humidity_blend; + + // ordered descending + std::vector m_transitions_y; }; diff --git a/src/network/clientopcodes.cpp b/src/network/clientopcodes.cpp index d426d3fe7..7d0840754 100644 --- a/src/network/clientopcodes.cpp +++ b/src/network/clientopcodes.cpp @@ -81,7 +81,7 @@ const ToClientCommandHandler toClientCommandTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_MOVE_PLAYER", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_MovePlayer }, // 0x34 { "TOCLIENT_ACCESS_DENIED_LEGACY", TOCLIENT_STATE_NOT_CONNECTED, &Client::handleCommand_AccessDenied }, // 0x35 { "TOCLIENT_FOV", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Fov }, // 0x36 - { "TOCLIENT_DEATHSCREEN", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DeathScreen }, // 0x37 + { "TOCLIENT_DEATHSCREEN_LEGACY", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_DeathScreenLegacy }, // 0x37 { "TOCLIENT_MEDIA", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_Media }, // 0x38 null_command_handler, { "TOCLIENT_NODEDEF", TOCLIENT_STATE_CONNECTED, &Client::handleCommand_NodeDef }, // 0x3a @@ -198,7 +198,7 @@ const ServerCommandFactory serverCommandFactoryTable[TOSERVER_NUM_MSG_TYPES] = { "TOSERVER_DAMAGE", 0, true }, // 0x35 null_command_factory, // 0x36 { "TOSERVER_PLAYERITEM", 0, true }, // 0x37 - { "TOSERVER_RESPAWN", 0, true }, // 0x38 + { "TOSERVER_RESPAWN_LEGACY", 0, true }, // 0x38 { "TOSERVER_INTERACT", 0, true }, // 0x39 { "TOSERVER_REMOVED_SOUNDS", 2, true }, // 0x3a { "TOSERVER_NODEMETA_FIELDS", 0, true }, // 0x3b diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 4eefd1c59..2716879f4 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -204,7 +204,6 @@ void Client::handleCommand_AccessDenied(NetworkPacket* pkt) // to be processed even if the serialization format has // not been agreed yet, the same as TOCLIENT_INIT. m_access_denied = true; - m_access_denied_reason = "Unknown"; if (pkt->getCommand() != TOCLIENT_ACCESS_DENIED) { // Legacy code from 0.4.12 and older but is still used @@ -223,29 +222,23 @@ void Client::handleCommand_AccessDenied(NetworkPacket* pkt) u8 denyCode; *pkt >> denyCode; - if (denyCode == SERVER_ACCESSDENIED_SHUTDOWN || - denyCode == SERVER_ACCESSDENIED_CRASH) { + if (pkt->getRemainingBytes() > 0) *pkt >> m_access_denied_reason; - if (m_access_denied_reason.empty()) + + if (m_access_denied_reason.empty()) { + if (denyCode >= SERVER_ACCESSDENIED_MAX) { + m_access_denied_reason = gettext("Unknown disconnect reason."); + } else if (denyCode != SERVER_ACCESSDENIED_CUSTOM_STRING) { m_access_denied_reason = gettext(accessDeniedStrings[denyCode]); + } + } + + if (denyCode == SERVER_ACCESSDENIED_TOO_MANY_USERS) { + m_access_denied_reconnect = true; + } else if (pkt->getRemainingBytes() > 0) { u8 reconnect; *pkt >> reconnect; m_access_denied_reconnect = reconnect & 1; - } else if (denyCode == SERVER_ACCESSDENIED_CUSTOM_STRING) { - *pkt >> m_access_denied_reason; - } else if (denyCode == SERVER_ACCESSDENIED_TOO_MANY_USERS) { - m_access_denied_reason = gettext(accessDeniedStrings[denyCode]); - m_access_denied_reconnect = true; - } else if (denyCode < SERVER_ACCESSDENIED_MAX) { - m_access_denied_reason = gettext(accessDeniedStrings[denyCode]); - } else { - // Allow us to add new error messages to the - // protocol without raising the protocol version, if we want to. - // Until then (which may be never), this is outside - // of the defined protocol. - *pkt >> m_access_denied_reason; - if (m_access_denied_reason.empty()) - m_access_denied_reason = "Unknown"; } } @@ -660,20 +653,10 @@ void Client::handleCommand_MovePlayerRel(NetworkPacket *pkt) player->addPosition(added_pos); } -void Client::handleCommand_DeathScreen(NetworkPacket* pkt) +void Client::handleCommand_DeathScreenLegacy(NetworkPacket* pkt) { - bool set_camera_point_target; - v3f camera_point_target; - - *pkt >> set_camera_point_target; - *pkt >> camera_point_target; - ClientEvent *event = new ClientEvent(); - event->type = CE_DEATHSCREEN; - event->deathscreen.set_camera_point_target = set_camera_point_target; - event->deathscreen.camera_point_target_x = camera_point_target.X; - event->deathscreen.camera_point_target_y = camera_point_target.Y; - event->deathscreen.camera_point_target_z = camera_point_target.Z; + event->type = CE_DEATHSCREEN_LEGACY; m_client_event_queue.push(event); } @@ -1483,6 +1466,7 @@ void Client::handleCommand_CloudParams(NetworkPacket* pkt) f32 density; video::SColor color_bright; video::SColor color_ambient; + video::SColor color_shadow = video::SColor(255, 204, 204, 204); f32 height; f32 thickness; v2f speed; @@ -1490,6 +1474,10 @@ void Client::handleCommand_CloudParams(NetworkPacket* pkt) *pkt >> density >> color_bright >> color_ambient >> height >> thickness >> speed; + if (pkt->getRemainingBytes() >= 4) { + *pkt >> color_shadow; + } + ClientEvent *event = new ClientEvent(); event->type = CE_CLOUD_PARAMS; event->cloud_params.density = density; @@ -1498,6 +1486,7 @@ void Client::handleCommand_CloudParams(NetworkPacket* pkt) // we avoid using new() and delete() for no good reason event->cloud_params.color_bright = color_bright.color; event->cloud_params.color_ambient = color_ambient.color; + event->cloud_params.color_shadow = color_shadow.color; event->cloud_params.height = height; event->cloud_params.thickness = thickness; // same here: deconstruct to skip constructor @@ -1828,4 +1817,6 @@ void Client::handleCommand_SetLighting(NetworkPacket *pkt) } if (pkt->getRemainingBytes() >= 4) *pkt >> lighting.volumetric_light_strength; + if (pkt->getRemainingBytes() >= 4) + *pkt >> lighting.shadow_tint; } diff --git a/src/network/mtp/impl.cpp b/src/network/mtp/impl.cpp index 00535945e..1ef5eb853 100644 --- a/src/network/mtp/impl.cpp +++ b/src/network/mtp/impl.cpp @@ -739,18 +739,19 @@ void Channel::UpdateTimers(float dtime) if (packet_loss_counter > 1.0f) { packet_loss_counter -= 1.0f; - unsigned int packet_loss = 11; /* use a neutral value for initialization */ - unsigned int packets_successful = 0; - //unsigned int packet_too_late = 0; + unsigned int packet_loss; + unsigned int packets_successful; + unsigned int packet_too_late; bool reasonable_amount_of_data_transmitted = false; { MutexAutoLock internal(m_internal_mutex); packet_loss = current_packet_loss; - //packet_too_late = current_packet_too_late; + packet_too_late = current_packet_too_late; packets_successful = current_packet_successful; + // has half the window even been used? if (current_bytes_transfered > (unsigned int) (m_window_size*512/2)) { reasonable_amount_of_data_transmitted = true; } @@ -759,6 +760,11 @@ void Channel::UpdateTimers(float dtime) current_packet_successful = 0; } + // Packets too late means either packet duplication along the way + // or we were too fast in resending it (which should be self-regulating). + // Count this a signal of congestion, like packet loss. + packet_loss = std::min(packet_loss + packet_too_late, packets_successful); + /* dynamic window size */ float successful_to_lost_ratio = 0.0f; bool done = false; @@ -989,19 +995,27 @@ bool UDPPeer::isTimedOut(float timeout, std::string &reason) void UDPPeer::reportRTT(float rtt) { - if (rtt < 0.0) { + if (rtt < 0) return; - } RTTStatistics(rtt,"rudp",MAX_RELIABLE_WINDOW_SIZE*10); // use this value to decide the resend timeout - float timeout = getStat(AVG_RTT) * RESEND_TIMEOUT_FACTOR; + const float rtt_stat = getStat(AVG_RTT); + if (rtt_stat < 0) + return; + float timeout = rtt_stat * RESEND_TIMEOUT_FACTOR; if (timeout < RESEND_TIMEOUT_MIN) timeout = RESEND_TIMEOUT_MIN; if (timeout > RESEND_TIMEOUT_MAX) timeout = RESEND_TIMEOUT_MAX; + float timeout_old = getResendTimeout(); setResendTimeout(timeout); + + if (std::abs(timeout - timeout_old) >= 0.001f) { + dout_con << m_connection->getDesc() << " set resend timeout " << timeout + << " (rtt=" << rtt_stat << ") for peer id: " << id << std::endl; + } } bool UDPPeer::Ping(float dtime,SharedBuffer& data) @@ -1121,7 +1135,7 @@ bool UDPPeer::processReliableSendCommand( u16 packets_available = toadd.size(); /* we didn't get a single sequence number no need to fill queue */ if (!have_initial_sequence_number) { - LOG(derr_con << m_connection->getDesc() << "Ran out of sequence numbers!" << std::endl); + dout_con << m_connection->getDesc() << " No sequence numbers available!" << std::endl; return false; } diff --git a/src/network/mtp/internal.h b/src/network/mtp/internal.h index f6ab1f159..4cf6cb57a 100644 --- a/src/network/mtp/internal.h +++ b/src/network/mtp/internal.h @@ -354,15 +354,24 @@ private: static ConnectionCommandPtr create(ConnectionCommandType type); }; -/* maximum window size to use, 0xFFFF is theoretical maximum. don't think about +/* + * Window sizes to use, in packets (not bytes!). + * 0xFFFF is theoretical maximum. don't think about * touching it, the less you're away from it the more likely data corruption * will occur + * + * Note: window sizes directly translate to maximum possible throughput, e.g. + * (2048 * 512 bytes) / 33ms = 15 MiB/s */ + +// Due to backwards compatibility we have different window sizes for what we'll +// accept from peers vs. what we use for sending. #define MAX_RELIABLE_WINDOW_SIZE 0x8000 +#define MAX_RELIABLE_WINDOW_SIZE_SEND 2048 /* starting value for window size */ -#define START_RELIABLE_WINDOW_SIZE 0x400 +#define START_RELIABLE_WINDOW_SIZE 64 /* minimum value for window size */ -#define MIN_RELIABLE_WINDOW_SIZE 0x40 +#define MIN_RELIABLE_WINDOW_SIZE 32 class Channel { @@ -430,7 +439,7 @@ public: void setWindowSize(long size) { - m_window_size = (u16)rangelim(size, MIN_RELIABLE_WINDOW_SIZE, MAX_RELIABLE_WINDOW_SIZE); + m_window_size = (u16)rangelim(size, MIN_RELIABLE_WINDOW_SIZE, MAX_RELIABLE_WINDOW_SIZE_SEND); } private: diff --git a/src/network/mtp/threads.cpp b/src/network/mtp/threads.cpp index d1a1e2a34..50b7fa1e3 100644 --- a/src/network/mtp/threads.cpp +++ b/src/network/mtp/threads.cpp @@ -35,7 +35,6 @@ namespace con #define PROFILE(a) #undef DEBUG_CONNECTION_KBPS #else -/* this mutex is used to achieve log message consistency */ #define PROFILE(a) a //#define DEBUG_CONNECTION_KBPS #undef DEBUG_CONNECTION_KBPS @@ -221,7 +220,8 @@ void ConnectionSendThread::runTimeouts(float dtime, u32 peer_packet_quota) } float resend_timeout = udpPeer->getResendTimeout(); - for (Channel &channel : udpPeer->channels) { + for (int ch = 0; ch < CHANNEL_COUNT; ch++) { + auto &channel = udpPeer->channels[ch]; // Remove timed out incomplete unreliable split packets channel.incoming_splits.removeUnreliableTimedOuts(dtime, peer_timeout); @@ -242,8 +242,8 @@ void ConnectionSendThread::runTimeouts(float dtime, u32 peer_packet_quota) if (!timed_outs.empty()) { dout_con << m_connection->getDesc() << "Skipping re-send of " << timed_outs.size() << - " timed-out reliables to peer_id " << udpPeer->id - << " (half-open)." << std::endl; + " timed-out reliables to peer_id=" << udpPeer->id + << " channel=" << ch << " (half-open)." << std::endl; } continue; } @@ -256,7 +256,14 @@ void ConnectionSendThread::runTimeouts(float dtime, u32 peer_packet_quota) for (const auto &k : timed_outs) resendReliable(channel, k.get(), resend_timeout); + auto ws_old = channel.getWindowSize(); channel.UpdateTimers(dtime); + auto ws_new = channel.getWindowSize(); + if (ws_old != ws_new) { + dout_con << m_connection->getDesc() << + "Window size adjusted to " << ws_new << " for peer_id=" + << udpPeer->id << " channel=" << ch << std::endl; + } } /* send ping if necessary */ @@ -309,12 +316,12 @@ void ConnectionSendThread::rawSend(const BufferedPacket *p) assert(p); try { m_connection->m_udpSocket.Send(p->address, p->data, p->size()); - LOG(dout_con << m_connection->getDesc() - << " rawSend: " << p->size() - << " bytes sent" << std::endl); + //LOG(dout_con << m_connection->getDesc() + // << " rawSend: " << p->size() + // << " bytes sent" << std::endl); } catch (SendFailedException &e) { LOG(derr_con << m_connection->getDesc() - << "Connection::rawSend(): SendFailedException: " + << "SendFailedException: " << e.what() << " to " << p->address.serializeString() << std::endl); } } @@ -327,6 +334,7 @@ void ConnectionSendThread::sendAsPacketReliable(BufferedPacketPtr &p, Channel *c channel->outgoing_reliables_sent.insert(p, (channel->readOutgoingSequenceNumber() - MAX_RELIABLE_WINDOW_SIZE) % (MAX_RELIABLE_WINDOW_SIZE + 1)); + // wtf is this calculation?? ^ } catch (AlreadyExistsException &e) { LOG(derr_con << m_connection->getDesc() @@ -685,9 +693,9 @@ void ConnectionSendThread::sendPackets(float dtime, u32 peer_packet_quota) PROFILE(ScopeProfiler peerprofiler(g_profiler, peerIdentifier.str(), SPT_AVG)); - LOG(dout_con << m_connection->getDesc() - << " Handle per peer queues: peer_id=" << peerId - << " packet quota: " << peer->m_increment_packets_remaining << std::endl); + //LOG(dout_con << m_connection->getDesc() + // << " Handle per peer queues: peer_id=" << peerId + // << " packet quota: " << peer->m_increment_packets_remaining << std::endl); // first send queued reliable packets for all peers (if possible) for (unsigned int i = 0; i < CHANNEL_COUNT; i++) { @@ -1190,7 +1198,7 @@ SharedBuffer ConnectionReceiveThread::handlePacketType_Control(Channel *chan // an overflow is quite unlikely but as it'd result in major // rtt miscalculation we handle it here if (current_time > p->absolute_send_time) { - float rtt = (current_time - p->absolute_send_time) / 1000.0; + float rtt = (current_time - p->absolute_send_time) / 1000.0f; // Let peer calculate stuff according to it // (avg_rtt and resend_timeout) @@ -1335,12 +1343,6 @@ SharedBuffer ConnectionReceiveThread::handlePacketType_Reliable(Channel *cha << ", seqnum: " << seqnum << std::endl;) m_connection->sendAck(peer->id, channelnum, seqnum); - // we already have this packet so this one was on wire at least - // the current timeout - // we don't know how long this packet was on wire don't do silly guessing - // dynamic_cast(&peer)-> - // reportRTT(dynamic_cast(&peer)->getResendTimeout()); - throw ProcessedSilentlyException("Retransmitting ack for old packet"); } } diff --git a/src/network/networkprotocol.h b/src/network/networkprotocol.h index 5af9859ae..aeb827608 100644 --- a/src/network/networkprotocol.h +++ b/src/network/networkprotocol.h @@ -229,6 +229,14 @@ with this program; if not, write to the Free Software Foundation, Inc., [bump for 5.9.1] PROTOCOL VERSION 46: Move default hotbar from client-side C++ to server-side builtin Lua + Add shadow tint to Lighting packets + Add shadow color to CloudParam packets + Move death screen to server and make it a regular formspec + The server no longer triggers the hardcoded client-side death + formspec, but the client still supports it for compatibility with + old servers. + Rename TOCLIENT_DEATHSCREEN to TOCLIENT_DEATHSCREEN_LEGACY + Rename TOSERVER_RESPAWN to TOSERVER_RESPAWN_LEGACY [scheduled bump for 5.10.0] */ @@ -388,10 +396,10 @@ enum ToClientCommand : u16 f32 transition_time */ - TOCLIENT_DEATHSCREEN = 0x37, + TOCLIENT_DEATHSCREEN_LEGACY = 0x37, /* - u8 bool set camera point target - v3f1000 camera point target (to point the death cause or whatever) + u8 bool unused + v3f1000 unused */ TOCLIENT_MEDIA = 0x38, @@ -999,10 +1007,7 @@ enum ToServerCommand : u16 [2] u16 item */ - TOSERVER_RESPAWN = 0x38, - /* - u16 TOSERVER_RESPAWN - */ + TOSERVER_RESPAWN_LEGACY = 0x38, TOSERVER_INTERACT = 0x39, /* diff --git a/src/network/serveropcodes.cpp b/src/network/serveropcodes.cpp index 1cb413492..4b87c3fbe 100644 --- a/src/network/serveropcodes.cpp +++ b/src/network/serveropcodes.cpp @@ -82,7 +82,7 @@ const ToServerCommandHandler toServerCommandTable[TOSERVER_NUM_MSG_TYPES] = { "TOSERVER_DAMAGE", TOSERVER_STATE_INGAME, &Server::handleCommand_Damage }, // 0x35 null_command_handler, // 0x36 { "TOSERVER_PLAYERITEM", TOSERVER_STATE_INGAME, &Server::handleCommand_PlayerItem }, // 0x37 - { "TOSERVER_RESPAWN", TOSERVER_STATE_INGAME, &Server::handleCommand_Respawn }, // 0x38 + null_command_handler, // 0x38 { "TOSERVER_INTERACT", TOSERVER_STATE_INGAME, &Server::handleCommand_Interact }, // 0x39 { "TOSERVER_REMOVED_SOUNDS", TOSERVER_STATE_INGAME, &Server::handleCommand_RemovedSounds }, // 0x3a { "TOSERVER_NODEMETA_FIELDS", TOSERVER_STATE_INGAME, &Server::handleCommand_NodeMetaFields }, // 0x3b @@ -181,7 +181,7 @@ const ClientCommandFactory clientCommandFactoryTable[TOCLIENT_NUM_MSG_TYPES] = { "TOCLIENT_MOVE_PLAYER", 0, true }, // 0x34 null_command_factory, // 0x35 { "TOCLIENT_FOV", 0, true }, // 0x36 - { "TOCLIENT_DEATHSCREEN", 0, true }, // 0x37 + null_command_factory, // 0x37 { "TOCLIENT_MEDIA", 2, true }, // 0x38 null_command_factory, // 0x39 { "TOCLIENT_NODEDEF", 0, true }, // 0x3A diff --git a/src/network/serverpackethandler.cpp b/src/network/serverpackethandler.cpp index 7d8555c8e..c17d32e41 100644 --- a/src/network/serverpackethandler.cpp +++ b/src/network/serverpackethandler.cpp @@ -858,33 +858,6 @@ void Server::handleCommand_PlayerItem(NetworkPacket* pkt) playersao->getPlayer()->setWieldIndex(item); } -void Server::handleCommand_Respawn(NetworkPacket* pkt) -{ - session_t peer_id = pkt->getPeerId(); - RemotePlayer *player = m_env->getPlayer(peer_id); - if (player == NULL) { - errorstream << - "Server::ProcessData(): Canceling: No player for peer_id=" << - peer_id << " disconnecting peer!" << std::endl; - DisconnectPeer(peer_id); - return; - } - - PlayerSAO *playersao = player->getPlayerSAO(); - assert(playersao); - - if (!playersao->isDead()) - return; - - RespawnPlayer(peer_id); - - actionstream << player->getName() << " respawns at " - << (playersao->getBasePosition() / BS) << std::endl; - - // ActiveObject is added to environment in AsyncRunStep after - // the previous addition has been successfully removed -} - bool Server::checkInteractDistance(RemotePlayer *player, const f32 d, const std::string &what) { ItemStack selected_item, hand_item; diff --git a/src/network/socket.cpp b/src/network/socket.cpp index 9fbbaa34e..17e71d860 100644 --- a/src/network/socket.cpp +++ b/src/network/socket.cpp @@ -50,9 +50,6 @@ typedef int socklen_t; #define SOCKET_ERR_STR(e) strerror(e) #endif -// Set to true to enable verbose debug output -bool socket_enable_debug_output = false; // yuck - static bool g_sockets_initialized = false; // Initialize sockets @@ -104,12 +101,6 @@ bool UDPSocket::init(bool ipv6, bool noExceptions) m_addr_family = ipv6 ? AF_INET6 : AF_INET; m_handle = socket(m_addr_family, SOCK_DGRAM, IPPROTO_UDP); - if (socket_enable_debug_output) { - tracestream << "UDPSocket(" << (int)m_handle - << ")::UDPSocket(): ipv6 = " << (ipv6 ? "true" : "false") - << std::endl; - } - if (m_handle < 0) { if (noExceptions) { return false; @@ -135,11 +126,6 @@ bool UDPSocket::init(bool ipv6, bool noExceptions) UDPSocket::~UDPSocket() { - if (socket_enable_debug_output) { - tracestream << "UDPSocket( " << (int)m_handle << ")::~UDPSocket()" - << std::endl; - } - if (m_handle >= 0) { #ifdef _WIN32 closesocket(m_handle); @@ -151,12 +137,6 @@ UDPSocket::~UDPSocket() void UDPSocket::Bind(Address addr) { - if (socket_enable_debug_output) { - tracestream << "UDPSocket(" << (int)m_handle - << ")::Bind(): " << addr.serializeString() << ":" - << addr.getPort() << std::endl; - } - if (addr.getFamily() != m_addr_family) { const char *errmsg = "Socket and bind address families do not match"; @@ -202,30 +182,6 @@ void UDPSocket::Send(const Address &destination, const void *data, int size) if (INTERNET_SIMULATOR) dumping_packet = myrand() % INTERNET_SIMULATOR_PACKET_LOSS == 0; - if (socket_enable_debug_output) { - // Print packet destination and size - tracestream << (int)m_handle << " -> "; - destination.print(tracestream); - tracestream << ", size=" << size; - - // Print packet contents - tracestream << ", data="; - for (int i = 0; i < size && i < 20; i++) { - if (i % 2 == 0) - tracestream << " "; - unsigned int a = ((const unsigned char *)data)[i]; - tracestream << std::hex << std::setw(2) << std::setfill('0') << a; - } - - if (size > 20) - tracestream << "..."; - - if (dumping_packet) - tracestream << " (DUMPED BY INTERNET_SIMULATOR)"; - - tracestream << std::endl; - } - if (dumping_packet) { // Lol let's forget it tracestream << "UDPSocket::Send(): INTERNET_SIMULATOR: dumping packet." @@ -302,26 +258,6 @@ int UDPSocket::Receive(Address &sender, void *data, int size) sender = Address(address_ip, address_port); } - if (socket_enable_debug_output) { - // Print packet sender and size - tracestream << (int)m_handle << " <- "; - sender.print(tracestream); - tracestream << ", size=" << received; - - // Print packet contents - tracestream << ", data="; - for (int i = 0; i < received && i < 20; i++) { - if (i % 2 == 0) - tracestream << " "; - unsigned int a = ((const unsigned char *)data)[i]; - tracestream << std::hex << std::setw(2) << std::setfill('0') << a; - } - if (received > 20) - tracestream << "..."; - - tracestream << std::endl; - } - return received; } diff --git a/src/network/socket.h b/src/network/socket.h index c3758a9d8..28b69c7b8 100644 --- a/src/network/socket.h +++ b/src/network/socket.h @@ -25,8 +25,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "irrlichttypes.h" #include "networkexceptions.h" -extern bool socket_enable_debug_output; - void sockets_init(); void sockets_cleanup(); diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 2914cc3aa..811753c89 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -712,8 +712,6 @@ static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer, if (!tile.world_aligned) layer->scale = 1; - layer->flags_texture = tsrc->getShaderFlagsTexture(layer->normal_texture ? true : false); - // Material flags layer->material_flags = 0; if (backface_culling) @@ -753,18 +751,13 @@ static void fillTileAttribs(ITextureSource *tsrc, TileLayer *layer, std::ostringstream os(std::ios::binary); for (int i = 0; i < frame_count; i++) { - FrameSpec frame; - os.str(""); os << tiledef.name; tiledef.animation.getTextureModifer(os, layer->texture->getOriginalSize(), i); + FrameSpec &frame = (*layer->frames)[i]; frame.texture = tsrc->getTextureForMesh(os.str(), &frame.texture_id); - if (layer->normal_texture) - frame.normal_texture = tsrc->getNormalTexture(os.str()); - frame.flags_texture = layer->flags_texture; - (*layer->frames)[i] = frame; } } } diff --git a/src/nodemetadata.cpp b/src/nodemetadata.cpp index a11503ebe..a86db15ad 100644 --- a/src/nodemetadata.cpp +++ b/src/nodemetadata.cpp @@ -62,14 +62,14 @@ void NodeMetadata::serialize(std::ostream &os, u8 version, bool disk) const void NodeMetadata::deSerialize(std::istream &is, u8 version) { clear(); - int num_vars = readU32(is); - for(int i=0; i= 2) { if (readU8(is) == 1) - markPrivate(name, true); + m_privatevars.insert(name); } } @@ -89,12 +89,12 @@ bool NodeMetadata::empty() const } -void NodeMetadata::markPrivate(const std::string &name, bool set) +bool NodeMetadata::markPrivate(const std::string &name, bool set) { if (set) - m_privatevars.insert(name); + return m_privatevars.insert(name).second; else - m_privatevars.erase(name); + return m_privatevars.erase(name) > 0; } int NodeMetadata::countNonPrivate() const @@ -144,6 +144,8 @@ void NodeMetadataList::serialize(std::ostream &os, u8 blockver, bool disk, writeS16(os, p.Z); } else { // Serialize positions within a mapblock + static_assert(MAP_BLOCKSIZE * MAP_BLOCKSIZE * MAP_BLOCKSIZE <= U16_MAX, + "position too big to serialize"); u16 p16 = (p.Z * MAP_BLOCKSIZE + p.Y) * MAP_BLOCKSIZE + p.X; writeU16(os, p16); } @@ -246,8 +248,7 @@ void NodeMetadataList::set(v3s16 p, NodeMetadata *d) void NodeMetadataList::clear() { if (m_is_metadata_owner) { - NodeMetadataMap::const_iterator it; - for (it = m_data.begin(); it != m_data.end(); ++it) + for (auto it = m_data.begin(); it != m_data.end(); ++it) delete it->second; } m_data.clear(); diff --git a/src/nodemetadata.h b/src/nodemetadata.h index da277aabd..3c2a67f53 100644 --- a/src/nodemetadata.h +++ b/src/nodemetadata.h @@ -57,7 +57,10 @@ public: { return m_privatevars.count(name) != 0; } - void markPrivate(const std::string &name, bool set); + + /// Marks a key as private. + /// @return metadata modified? + bool markPrivate(const std::string &name, bool set); private: int countNonPrivate() const; diff --git a/src/object_properties.cpp b/src/object_properties.cpp index 5fb6a7d41..7a70714a2 100644 --- a/src/object_properties.cpp +++ b/src/object_properties.cpp @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" #include "util/serialize.h" #include +#include static const video::SColor NULL_BGCOLOR{0, 1, 1, 1}; @@ -85,6 +86,27 @@ std::string ObjectProperties::dump() const return os.str(); } +static auto tie(const ObjectProperties &o) +{ + // Make sure to add new members to this list! + return std::tie( + o.textures, o.colors, o.collisionbox, o.selectionbox, o.visual, o.mesh, + o.damage_texture_modifier, o.nametag, o.infotext, o.wield_item, o.visual_size, + o.nametag_color, o.nametag_bgcolor, o.spritediv, o.initial_sprite_basepos, + o.stepheight, o.automatic_rotate, o.automatic_face_movement_dir_offset, + o.automatic_face_movement_max_rotation_per_sec, o.eye_height, o.zoom_fov, + o.hp_max, o.breath_max, o.glow, o.pointable, o.physical, o.collideWithObjects, + o.rotate_selectionbox, o.is_visible, o.makes_footstep_sound, + o.automatic_face_movement_dir, o.backface_culling, o.static_save, o.use_texture_alpha, + o.shaded, o.show_on_minimap + ); +} + +bool ObjectProperties::operator==(const ObjectProperties &other) const +{ + return tie(*this) == tie(other); +} + bool ObjectProperties::validate() { const char *func = "ObjectProperties::validate(): "; diff --git a/src/object_properties.h b/src/object_properties.h index 1f8384c77..88c2a2678 100644 --- a/src/object_properties.h +++ b/src/object_properties.h @@ -20,11 +20,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include -#include #include #include "irrlichttypes_bloated.h" #include -#include #include #include "util/pointabilities.h" @@ -77,28 +75,10 @@ struct ObjectProperties std::string dump() const; -private: - auto tie() const { - // Make sure to add new members to this list! - return std::tie( - textures, colors, collisionbox, selectionbox, visual, mesh, damage_texture_modifier, - nametag, infotext, wield_item, visual_size, nametag_color, nametag_bgcolor, - spritediv, initial_sprite_basepos, stepheight, automatic_rotate, - automatic_face_movement_dir_offset, automatic_face_movement_max_rotation_per_sec, - eye_height, zoom_fov, hp_max, breath_max, glow, pointable, physical, - collideWithObjects, rotate_selectionbox, is_visible, makes_footstep_sound, - automatic_face_movement_dir, backface_culling, static_save, use_texture_alpha, - shaded, show_on_minimap - ); - } - -public: - bool operator==(const ObjectProperties &other) const { - return tie() == other.tie(); - }; + bool operator==(const ObjectProperties &other) const; bool operator!=(const ObjectProperties &other) const { - return tie() != other.tie(); - }; + return !(*this == other); + } /** * Check limits of some important properties that'd cause exceptions later on. diff --git a/src/player.cpp b/src/player.cpp index fd902aa83..fd25626ca 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "log.h" #include "porting.h" // strlcpy +#include bool is_valid_player_name(std::string_view name) { @@ -229,3 +230,19 @@ void PlayerControl::unpackKeysPressed(u32 keypress_bits) place = keypress_bits & (1 << 8); zoom = keypress_bits & (1 << 9); } + +static auto tie(const PlayerPhysicsOverride &o) +{ + // Make sure to add new members to this list! + return std::tie( + o.speed, o.jump, o.gravity, o.sneak, o.sneak_glitch, o.new_move, o.speed_climb, + o.speed_crouch, o.liquid_fluidity, o.liquid_fluidity_smooth, o.liquid_sink, + o.acceleration_default, o.acceleration_air, o.speed_fast, o.acceleration_fast, + o.speed_walk + ); +} + +bool PlayerPhysicsOverride::operator==(const PlayerPhysicsOverride &other) const +{ + return tie(*this) == tie(other); +} diff --git a/src/player.h b/src/player.h index 7d92808cf..53411fea4 100644 --- a/src/player.h +++ b/src/player.h @@ -24,10 +24,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "constants.h" #include "util/basic_macros.h" #include "util/string.h" -#include #include #include -#include #include #define PLAYERNAME_SIZE 20 @@ -133,23 +131,10 @@ struct PlayerPhysicsOverride float acceleration_fast = 1.f; float speed_walk = 1.f; -private: - auto tie() const { - // Make sure to add new members to this list! - return std::tie( - speed, jump, gravity, sneak, sneak_glitch, new_move, speed_climb, speed_crouch, - liquid_fluidity, liquid_fluidity_smooth, liquid_sink, acceleration_default, - acceleration_air, speed_fast, acceleration_fast, speed_walk - ); - } - -public: - bool operator==(const PlayerPhysicsOverride &other) const { - return tie() == other.tie(); - }; + bool operator==(const PlayerPhysicsOverride &other) const; bool operator!=(const PlayerPhysicsOverride &other) const { - return tie() != other.tie(); - }; + return !(*this == other); + } }; class Map; diff --git a/src/porting.cpp b/src/porting.cpp index da972926a..f6409c56c 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -73,6 +73,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filesys.h" #include "log.h" #include "util/string.h" +#include "util/tracy_wrapper.h" #include #include #include @@ -960,6 +961,8 @@ void TrackFreedMemory(size_t amount) void TriggerMemoryTrim() { + ZoneScoped; + constexpr auto MO = std::memory_order_relaxed; if (memory_freed.load(MO) >= MEMORY_TRIM_THRESHOLD) { // Synchronize call diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index e9907f304..bdd2514e6 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -32,6 +32,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/client.h" #endif +#if BUILD_WITH_TRACY + #include "tracy/TracyLua.hpp" +#endif extern "C" { #include "lualib.h" @@ -95,6 +98,11 @@ ScriptApiBase::ScriptApiBase(ScriptingType type): lua_pushstring(m_luastack, LUA_BITLIBNAME); lua_call(m_luastack, 1, 0); +#if BUILD_WITH_TRACY + // Load tracy lua bindings + tracy::LuaRegister(m_luastack); +#endif + // Make the ScriptApiBase* accessible to ModApiBase #if INDIRECT_SCRIPTAPI_RIDX *(void **)(lua_newuserdata(m_luastack, sizeof(void *))) = this; diff --git a/src/script/cpp_api/s_client.cpp b/src/script/cpp_api/s_client.cpp index 6faa0695c..78d0ec44d 100644 --- a/src/script/cpp_api/s_client.cpp +++ b/src/script/cpp_api/s_client.cpp @@ -125,21 +125,6 @@ void ScriptApiClient::on_hp_modification(int32_t newhp) } } -void ScriptApiClient::on_death() -{ - SCRIPTAPI_PRECHECKHEADER - - // Get registered shutdown hooks - lua_getglobal(L, "core"); - lua_getfield(L, -1, "registered_on_death"); - // Call callbacks - try { - runCallbacks(0, RUN_CALLBACKS_MODE_FIRST); - } catch (LuaError &e) { - getClient()->setFatalError(e); - } -} - void ScriptApiClient::environment_step(float dtime) { SCRIPTAPI_PRECHECKHEADER diff --git a/src/script/cpp_api/s_client.h b/src/script/cpp_api/s_client.h index 8a5523d0d..74cc0d898 100644 --- a/src/script/cpp_api/s_client.h +++ b/src/script/cpp_api/s_client.h @@ -49,7 +49,6 @@ public: void on_damage_taken(int32_t damage_amount); void on_hp_modification(int32_t newhp); - void on_death(); void environment_step(float dtime); void on_formspec_input(const std::string &formname, const StringMap &fields); diff --git a/src/script/cpp_api/s_env.cpp b/src/script/cpp_api/s_env.cpp index 3cbb13cd2..007622d52 100644 --- a/src/script/cpp_api/s_env.cpp +++ b/src/script/cpp_api/s_env.cpp @@ -25,8 +25,110 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "mapgen/mapgen.h" #include "lua_api/l_env.h" #include "server.h" +#include "scripting_server.h" #include "script/common/c_content.h" +/* + LuaABM & LuaLBM +*/ + +class LuaABM : public ActiveBlockModifier { +private: + const int m_id; + + std::vector m_trigger_contents; + std::vector m_required_neighbors; + std::vector m_without_neighbors; + float m_trigger_interval; + u32 m_trigger_chance; + bool m_simple_catch_up; + s16 m_min_y; + s16 m_max_y; +public: + LuaABM(int id, + const std::vector &trigger_contents, + const std::vector &required_neighbors, + const std::vector &without_neighbors, + float trigger_interval, u32 trigger_chance, bool simple_catch_up, + s16 min_y, s16 max_y): + m_id(id), + m_trigger_contents(trigger_contents), + m_required_neighbors(required_neighbors), + m_without_neighbors(without_neighbors), + m_trigger_interval(trigger_interval), + m_trigger_chance(trigger_chance), + m_simple_catch_up(simple_catch_up), + m_min_y(min_y), + m_max_y(max_y) + { + } + virtual const std::vector &getTriggerContents() const + { + return m_trigger_contents; + } + virtual const std::vector &getRequiredNeighbors() const + { + return m_required_neighbors; + } + virtual const std::vector &getWithoutNeighbors() const + { + return m_without_neighbors; + } + virtual float getTriggerInterval() + { + return m_trigger_interval; + } + virtual u32 getTriggerChance() + { + return m_trigger_chance; + } + virtual bool getSimpleCatchUp() + { + return m_simple_catch_up; + } + virtual s16 getMinY() + { + return m_min_y; + } + virtual s16 getMaxY() + { + return m_max_y; + } + + virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n, + u32 active_object_count, u32 active_object_count_wider) + { + auto *script = env->getScriptIface(); + script->triggerABM(m_id, p, n, active_object_count, active_object_count_wider); + } +}; + +class LuaLBM : public LoadingBlockModifierDef +{ +private: + int m_id; +public: + LuaLBM(int id, + const std::vector &trigger_contents, + const std::string &name, bool run_at_every_load): + m_id(id) + { + this->run_at_every_load = run_at_every_load; + this->trigger_contents = trigger_contents; + this->name = name; + } + + virtual void trigger(ServerEnvironment *env, MapBlock *block, + const std::unordered_set &positions, float dtime_s) + { + auto *script = env->getScriptIface(); + script->triggerLBM(m_id, block, positions, dtime_s); + } +}; + +/* + ScriptApiEnv +*/ void ScriptApiEnv::environment_OnGenerated(v3s16 minp, v3s16 maxp, u32 blockseed) @@ -46,7 +148,6 @@ void ScriptApiEnv::environment_OnGenerated(v3s16 minp, v3s16 maxp, void ScriptApiEnv::environment_Step(float dtime) { SCRIPTAPI_PRECHECKHEADER - //infostream << "scriptapi_environment_step" << std::endl; // Get core.registered_globalsteps lua_getglobal(L, "core"); @@ -76,12 +177,40 @@ void ScriptApiEnv::player_event(ServerActiveObject *player, const std::string &t void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env) { SCRIPTAPI_PRECHECKHEADER + + assert(env); verbosestream << "ScriptApiEnv: Environment initialized" << std::endl; setEnv(env); - /* - Add {Loading,Active}BlockModifiers to environment - */ + readABMs(); + readLBMs(); +} + +// Reads a single or a list of node names into a vector +bool ScriptApiEnv::read_nodenames(lua_State *L, int idx, std::vector &to) +{ + if (lua_istable(L, idx)) { + const int table = idx < 0 ? (lua_gettop(L) + idx + 1) : idx; + lua_pushnil(L); + while (lua_next(L, table)) { + // key at index -2 and value at index -1 + luaL_checktype(L, -1, LUA_TSTRING); + to.emplace_back(readParam(L, -1)); + // removes value, keeps key for next iteration + lua_pop(L, 1); + } + } else if (lua_isstring(L, idx)) { + to.emplace_back(readParam(L, idx)); + } else { + return false; + } + return true; +} + +void ScriptApiEnv::readABMs() +{ + SCRIPTAPI_PRECHECKHEADER + auto *env = reinterpret_cast(getEnv()); // Get core.registered_abms lua_getglobal(L, "core"); @@ -100,36 +229,17 @@ void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env) std::vector trigger_contents; lua_getfield(L, current_abm, "nodenames"); - if (lua_istable(L, -1)) { - int table = lua_gettop(L); - lua_pushnil(L); - while (lua_next(L, table)) { - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TSTRING); - trigger_contents.emplace_back(readParam(L, -1)); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } else if (lua_isstring(L, -1)) { - trigger_contents.emplace_back(readParam(L, -1)); - } + read_nodenames(L, -1, trigger_contents); lua_pop(L, 1); std::vector required_neighbors; lua_getfield(L, current_abm, "neighbors"); - if (lua_istable(L, -1)) { - int table = lua_gettop(L); - lua_pushnil(L); - while (lua_next(L, table)) { - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TSTRING); - required_neighbors.emplace_back(readParam(L, -1)); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } else if (lua_isstring(L, -1)) { - required_neighbors.emplace_back(readParam(L, -1)); - } + read_nodenames(L, -1, required_neighbors); + lua_pop(L, 1); + + std::vector without_neighbors; + lua_getfield(L, current_abm, "without_neighbors"); + read_nodenames(L, -1, without_neighbors); lua_pop(L, 1); float trigger_interval = 10.0; @@ -151,8 +261,9 @@ void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env) luaL_checktype(L, current_abm + 1, LUA_TFUNCTION); lua_pop(L, 1); - LuaABM *abm = new LuaABM(L, id, trigger_contents, required_neighbors, - trigger_interval, trigger_chance, simple_catch_up, min_y, max_y); + LuaABM *abm = new LuaABM(id, trigger_contents, required_neighbors, + without_neighbors, trigger_interval, trigger_chance, + simple_catch_up, min_y, max_y); env->addActiveBlockModifier(abm); @@ -160,6 +271,12 @@ void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env) lua_pop(L, 1); } lua_pop(L, 1); +} + +void ScriptApiEnv::readLBMs() +{ + SCRIPTAPI_PRECHECKHEADER + auto *env = reinterpret_cast(getEnv()); // Get core.registered_lbms lua_getglobal(L, "core"); @@ -177,21 +294,9 @@ void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env) int id = lua_tonumber(L, -2); int current_lbm = lua_gettop(L); - std::set trigger_contents; + std::vector trigger_contents; lua_getfield(L, current_lbm, "nodenames"); - if (lua_istable(L, -1)) { - int table = lua_gettop(L); - lua_pushnil(L); - while (lua_next(L, table)) { - // key at index -2 and value at index -1 - luaL_checktype(L, -1, LUA_TSTRING); - trigger_contents.insert(readParam(L, -1)); - // removes value, keeps key for next iteration - lua_pop(L, 1); - } - } else if (lua_isstring(L, -1)) { - trigger_contents.insert(readParam(L, -1)); - } + read_nodenames(L, -1, trigger_contents); lua_pop(L, 1); std::string name; @@ -200,11 +305,7 @@ void ScriptApiEnv::initializeEnvironment(ServerEnvironment *env) bool run_at_every_load = getboolfield_default(L, current_lbm, "run_at_every_load", false); - lua_getfield(L, current_lbm, "action"); - luaL_checktype(L, current_lbm + 1, LUA_TFUNCTION); - lua_pop(L, 1); - - LuaLBM *lbm = new LuaLBM(L, id, trigger_contents, name, + LuaLBM *lbm = new LuaLBM(id, trigger_contents, name, run_at_every_load); env->addLoadingBlockModifierDef(lbm); @@ -288,7 +389,7 @@ void ScriptApiEnv::on_liquid_transformed( int index = 1; lua_createtable(L, list.size(), 0); lua_createtable(L, list.size(), 0); - for(std::pair p : list) { + for(auto &p : list) { lua_pushnumber(L, index); push_v3s16(L, p.first); lua_rawset(L, -4); @@ -332,3 +433,73 @@ bool ScriptApiEnv::has_on_mapblocks_changed() luaL_checktype(L, -1, LUA_TTABLE); return lua_objlen(L, -1) > 0; } + +void ScriptApiEnv::triggerABM(int id, v3s16 p, MapNode n, + u32 active_object_count, u32 active_object_count_wider) +{ + SCRIPTAPI_PRECHECKHEADER + + int error_handler = PUSH_ERROR_HANDLER(L); + + // Get registered_abms + lua_getglobal(L, "core"); + lua_getfield(L, -1, "registered_abms"); + luaL_checktype(L, -1, LUA_TTABLE); + lua_remove(L, -2); // Remove core + + // Get registered_abms[m_id] + lua_pushinteger(L, id); + lua_gettable(L, -2); + FATAL_ERROR_IF(lua_isnil(L, -1), "Entry with given id not found in registered_abms table"); + lua_remove(L, -2); // Remove registered_abms + + setOriginFromTable(-1); + + // Call action + luaL_checktype(L, -1, LUA_TTABLE); + lua_getfield(L, -1, "action"); + luaL_checktype(L, -1, LUA_TFUNCTION); + lua_remove(L, -2); // Remove registered_abms[m_id] + push_v3s16(L, p); + pushnode(L, n); + lua_pushnumber(L, active_object_count); + lua_pushnumber(L, active_object_count_wider); + + int result = lua_pcall(L, 4, 0, error_handler); + if (result) + scriptError(result, "LuaABM::trigger"); + + lua_pop(L, 1); // Pop error handler +} + +void ScriptApiEnv::triggerLBM(int id, MapBlock *block, + const std::unordered_set &positions, float dtime_s) +{ + SCRIPTAPI_PRECHECKHEADER + + int error_handler = PUSH_ERROR_HANDLER(L); + + const v3s16 pos_of_block = block->getPosRelative(); + + // Get core.run_lbm + lua_getglobal(L, "core"); + lua_getfield(L, -1, "run_lbm"); + luaL_checktype(L, -1, LUA_TFUNCTION); + lua_remove(L, -2); // Remove core + + // Call it + lua_pushinteger(L, id); + lua_createtable(L, positions.size(), 0); + int i = 1; + for (auto &p : positions) { + push_v3s16(L, pos_of_block + p); + lua_rawseti(L, -2, i++); + } + lua_pushnumber(L, dtime_s); + + int result = lua_pcall(L, 3, 0, error_handler); + if (result) + scriptError(result, "LuaLBM::trigger"); + + lua_pop(L, 1); // Pop error handler +} diff --git a/src/script/cpp_api/s_env.h b/src/script/cpp_api/s_env.h index bc4c4cd4d..4722cb522 100644 --- a/src/script/cpp_api/s_env.h +++ b/src/script/cpp_api/s_env.h @@ -26,6 +26,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include class ServerEnvironment; +class MapBlock; struct ScriptCallbackState; class ScriptApiEnv : virtual public ScriptApiBase @@ -55,5 +56,20 @@ public: // Determines whether there are any on_mapblocks_changed callbacks bool has_on_mapblocks_changed(); + // Initializes environment and loads some definitions from Lua void initializeEnvironment(ServerEnvironment *env); + + void triggerABM(int id, v3s16 p, MapNode n, + u32 active_object_count, u32 active_object_count_wider); + + void triggerLBM(int id, MapBlock *block, + const std::unordered_set &positions, float dtime_s); + +private: + void readABMs(); + + void readLBMs(); + + // Reads a single or a list of node names into a vector + static bool read_nodenames(lua_State *L, int idx, std::vector &to); }; diff --git a/src/script/cpp_api/s_security.cpp b/src/script/cpp_api/s_security.cpp index 7c8ba8931..9a4b0763b 100644 --- a/src/script/cpp_api/s_security.cpp +++ b/src/script/cpp_api/s_security.cpp @@ -109,7 +109,12 @@ void ScriptApiSecurity::initializeSecurity() "string", "table", "math", - "bit" + "bit", + // Not sure if completely safe. But if someone enables tracy, they'll + // know what they do. +#if BUILD_WITH_TRACY + "tracy", +#endif }; static const char *io_whitelist[] = { "close", @@ -303,6 +308,11 @@ void ScriptApiSecurity::initializeSecurityClient() "table", "math", "bit", + // Not sure if completely safe. But if someone enables tracy, they'll + // know what they do. +#if BUILD_WITH_TRACY + "tracy", +#endif }; static const char *os_whitelist[] = { "clock", diff --git a/src/script/lua_api/l_client.cpp b/src/script/lua_api/l_client.cpp index da19ed0ea..3bd9fc04d 100644 --- a/src/script/lua_api/l_client.cpp +++ b/src/script/lua_api/l_client.cpp @@ -157,13 +157,6 @@ int ModApiClient::l_show_formspec(lua_State *L) return 1; } -// send_respawn() -int ModApiClient::l_send_respawn(lua_State *L) -{ - getClient(L)->sendRespawn(); - return 0; -} - // disconnect() int ModApiClient::l_disconnect(lua_State *L) { @@ -348,7 +341,6 @@ void ModApiClient::Initialize(lua_State *L, int top) API_FCT(clear_out_chat_queue); API_FCT(get_player_names); API_FCT(show_formspec); - API_FCT(send_respawn); API_FCT(gettext); API_FCT(get_node_or_nil); API_FCT(disconnect); diff --git a/src/script/lua_api/l_client.h b/src/script/lua_api/l_client.h index e960dc4cf..d726bc8a3 100644 --- a/src/script/lua_api/l_client.h +++ b/src/script/lua_api/l_client.h @@ -51,9 +51,6 @@ private: // show_formspec(name, formspec) static int l_show_formspec(lua_State *L); - // send_respawn() - static int l_send_respawn(lua_State *L); - // disconnect() static int l_disconnect(lua_State *L); diff --git a/src/script/lua_api/l_env.cpp b/src/script/lua_api/l_env.cpp index f5ed2804c..726300b07 100644 --- a/src/script/lua_api/l_env.cpp +++ b/src/script/lua_api/l_env.cpp @@ -65,93 +65,6 @@ const EnumString ModApiEnvBase::es_BlockStatusType[] = /////////////////////////////////////////////////////////////////////////////// - -void LuaABM::trigger(ServerEnvironment *env, v3s16 p, MapNode n, - u32 active_object_count, u32 active_object_count_wider) -{ - ServerScripting *scriptIface = env->getScriptIface(); - scriptIface->realityCheck(); - - lua_State *L = scriptIface->getStack(); - sanity_check(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - int error_handler = PUSH_ERROR_HANDLER(L); - - // Get registered_abms - lua_getglobal(L, "core"); - lua_getfield(L, -1, "registered_abms"); - luaL_checktype(L, -1, LUA_TTABLE); - lua_remove(L, -2); // Remove core - - // Get registered_abms[m_id] - lua_pushinteger(L, m_id); - lua_gettable(L, -2); - if(lua_isnil(L, -1)) - FATAL_ERROR(""); - lua_remove(L, -2); // Remove registered_abms - - scriptIface->setOriginFromTable(-1); - - // Call action - luaL_checktype(L, -1, LUA_TTABLE); - lua_getfield(L, -1, "action"); - luaL_checktype(L, -1, LUA_TFUNCTION); - lua_remove(L, -2); // Remove registered_abms[m_id] - push_v3s16(L, p); - pushnode(L, n); - lua_pushnumber(L, active_object_count); - lua_pushnumber(L, active_object_count_wider); - - int result = lua_pcall(L, 4, 0, error_handler); - if (result) - scriptIface->scriptError(result, "LuaABM::trigger"); - - lua_pop(L, 1); // Pop error handler -} - -void LuaLBM::trigger(ServerEnvironment *env, v3s16 p, - const MapNode n, const float dtime_s) -{ - ServerScripting *scriptIface = env->getScriptIface(); - scriptIface->realityCheck(); - - lua_State *L = scriptIface->getStack(); - sanity_check(lua_checkstack(L, 20)); - StackUnroller stack_unroller(L); - - int error_handler = PUSH_ERROR_HANDLER(L); - - // Get registered_lbms - lua_getglobal(L, "core"); - lua_getfield(L, -1, "registered_lbms"); - luaL_checktype(L, -1, LUA_TTABLE); - lua_remove(L, -2); // Remove core - - // Get registered_lbms[m_id] - lua_pushinteger(L, m_id); - lua_gettable(L, -2); - FATAL_ERROR_IF(lua_isnil(L, -1), "Entry with given id not found in registered_lbms table"); - lua_remove(L, -2); // Remove registered_lbms - - scriptIface->setOriginFromTable(-1); - - // Call action - luaL_checktype(L, -1, LUA_TTABLE); - lua_getfield(L, -1, "action"); - luaL_checktype(L, -1, LUA_TFUNCTION); - lua_remove(L, -2); // Remove registered_lbms[m_id] - push_v3s16(L, p); - pushnode(L, n); - lua_pushnumber(L, dtime_s); - - int result = lua_pcall(L, 3, 0, error_handler); - if (result) - scriptIface->scriptError(result, "LuaLBM::trigger"); - - lua_pop(L, 1); // Pop error handler -} - int LuaRaycast::l_next(lua_State *L) { GET_PLAIN_ENV_PTR; @@ -247,7 +160,7 @@ void LuaEmergeAreaCallback(v3s16 blockpos, EmergeAction action, void *param) // state must be protected by envlock Server *server = state->script->getServer(); - MutexAutoLock envlock(server->m_env_mutex); + Server::EnvAutoLock envlock(server); state->refcount--; @@ -340,6 +253,31 @@ int ModApiEnv::l_swap_node(lua_State *L) return 1; } +// bulk_swap_node([pos1, pos2, ...], node) +// pos = {x=num, y=num, z=num} +int ModApiEnv::l_bulk_swap_node(lua_State *L) +{ + GET_ENV_PTR; + + luaL_checktype(L, 1, LUA_TTABLE); + + s32 len = lua_objlen(L, 1); + + MapNode n = readnode(L, 2); + + // Do it + bool succeeded = true; + for (s32 i = 1; i <= len; i++) { + lua_rawgeti(L, 1, i); + if (!env->swapNode(read_v3s16(L, -1), n)) + succeeded = false; + lua_pop(L, 1); + } + + lua_pushboolean(L, succeeded); + return 1; +} + // get_node_raw(x, y, z) -> content, param1, param2, pos_ok int ModApiEnv::l_get_node_raw(lua_State *L) { @@ -836,10 +774,7 @@ int ModApiEnv::l_get_timeofday(lua_State *L) { GET_PLAIN_ENV_PTR; - // Do it - int timeofday_mh = env->getTimeOfDay(); - float timeofday_f = (float)timeofday_mh / 24000.0f; - lua_pushnumber(L, timeofday_f); + lua_pushnumber(L, env->getTimeOfDayF()); return 1; } @@ -857,8 +792,7 @@ int ModApiEnv::l_get_gametime(lua_State *L) { GET_ENV_PTR; - int game_time = env->getGameTime(); - lua_pushnumber(L, game_time); + lua_pushnumber(L, env->getGameTime()); return 1; } @@ -1468,6 +1402,7 @@ void ModApiEnv::Initialize(lua_State *L, int top) API_FCT(bulk_set_node); API_FCT(add_node); API_FCT(swap_node); + API_FCT(bulk_swap_node); API_FCT(add_item); API_FCT(remove_node); API_FCT(get_node_raw); diff --git a/src/script/lua_api/l_env.h b/src/script/lua_api/l_env.h index 2ed0eb114..ba0f2eb61 100644 --- a/src/script/lua_api/l_env.h +++ b/src/script/lua_api/l_env.h @@ -20,9 +20,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include "lua_api/l_base.h" -#include "serverenvironment.h" #include "raycast.h" +class ServerScripting; + // base class containing helpers class ModApiEnvBase : public ModApiBase { protected: @@ -64,6 +65,10 @@ private: // pos = {x=num, y=num, z=num} static int l_bulk_set_node(lua_State *L); + // bulk_swap_node([pos1, pos2, ...], node) + // pos = {x=num, y=num, z=num} + static int l_bulk_swap_node(lua_State *L); + static int l_add_node(lua_State *L); // remove_node(pos) @@ -281,82 +286,6 @@ public: static void InitializeEmerge(lua_State *L, int top); }; -class LuaABM : public ActiveBlockModifier { -private: - int m_id; - - std::vector m_trigger_contents; - std::vector m_required_neighbors; - float m_trigger_interval; - u32 m_trigger_chance; - bool m_simple_catch_up; - s16 m_min_y; - s16 m_max_y; -public: - LuaABM(lua_State *L, int id, - const std::vector &trigger_contents, - const std::vector &required_neighbors, - float trigger_interval, u32 trigger_chance, bool simple_catch_up, s16 min_y, s16 max_y): - m_id(id), - m_trigger_contents(trigger_contents), - m_required_neighbors(required_neighbors), - m_trigger_interval(trigger_interval), - m_trigger_chance(trigger_chance), - m_simple_catch_up(simple_catch_up), - m_min_y(min_y), - m_max_y(max_y) - { - } - virtual const std::vector &getTriggerContents() const - { - return m_trigger_contents; - } - virtual const std::vector &getRequiredNeighbors() const - { - return m_required_neighbors; - } - virtual float getTriggerInterval() - { - return m_trigger_interval; - } - virtual u32 getTriggerChance() - { - return m_trigger_chance; - } - virtual bool getSimpleCatchUp() - { - return m_simple_catch_up; - } - virtual s16 getMinY() - { - return m_min_y; - } - virtual s16 getMaxY() - { - return m_max_y; - } - virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n, - u32 active_object_count, u32 active_object_count_wider); -}; - -class LuaLBM : public LoadingBlockModifierDef -{ -private: - int m_id; -public: - LuaLBM(lua_State *L, int id, - const std::set &trigger_contents, - const std::string &name, - bool run_at_every_load): - m_id(id) - { - this->run_at_every_load = run_at_every_load; - this->trigger_contents = trigger_contents; - this->name = name; - } - virtual void trigger(ServerEnvironment *env, v3s16 p, MapNode n, float dtime_s); -}; - //! Lua wrapper for RaycastState objects class LuaRaycast : public ModApiBase { diff --git a/src/script/lua_api/l_itemstackmeta.cpp b/src/script/lua_api/l_itemstackmeta.cpp index ebabf7bae..730fab3b4 100644 --- a/src/script/lua_api/l_itemstackmeta.cpp +++ b/src/script/lua_api/l_itemstackmeta.cpp @@ -41,7 +41,7 @@ void ItemStackMetaRef::clearMeta() void ItemStackMetaRef::reportMetadataChange(const std::string *name) { - // TODO + // nothing to do } // Exported functions @@ -89,7 +89,6 @@ ItemStackMetaRef::~ItemStackMetaRef() void ItemStackMetaRef::create(lua_State *L, LuaItemStack *istack) { ItemStackMetaRef *o = new ItemStackMetaRef(istack); - //infostream<<"NodeMetaRef::create: o="<(getmeta(false)); + bool is_private_change = meta && name && meta->isPrivate(*name); + // If the metadata is now empty, get rid of it if (meta && meta->empty()) { clearMeta(); @@ -67,7 +69,7 @@ void NodeMetaRef::reportMetadataChange(const std::string *name) MapEditEvent event; event.type = MEET_BLOCK_NODE_METADATA_CHANGED; event.setPositionModified(m_p); - event.is_private_change = name && meta && meta->isPrivate(*name); + event.is_private_change = is_private_change; m_env->getMap().dispatchEvent(event); } @@ -94,21 +96,24 @@ int NodeMetaRef::l_mark_as_private(lua_State *L) NodeMetaRef *ref = checkObject(L, 1); NodeMetadata *meta = dynamic_cast(ref->getmeta(true)); - assert(meta); + if (!meta) + return 0; + bool modified = false; if (lua_istable(L, 2)) { lua_pushnil(L); while (lua_next(L, 2) != 0) { // key at index -2 and value at index -1 luaL_checktype(L, -1, LUA_TSTRING); - meta->markPrivate(readParam(L, -1), true); + modified |= meta->markPrivate(readParam(L, -1), true); // removes value, keeps key for next iteration lua_pop(L, 1); } } else if (lua_isstring(L, 2)) { - meta->markPrivate(readParam(L, 2), true); + modified |= meta->markPrivate(readParam(L, 2), true); } - ref->reportMetadataChange(); + if (modified) + ref->reportMetadataChange(); return 0; } @@ -145,12 +150,13 @@ bool NodeMetaRef::handleFromTable(lua_State *L, int table, IMetadata *_meta) Inventory *inv = meta->getInventory(); lua_getfield(L, table, "inventory"); if (lua_istable(L, -1)) { + auto *gamedef = getGameDef(L); int inventorytable = lua_gettop(L); lua_pushnil(L); while (lua_next(L, inventorytable) != 0) { // key at index -2 and value at index -1 - std::string name = luaL_checkstring(L, -2); - read_inventory_list(L, -1, inv, name.c_str(), getServer(L)); + const char *name = luaL_checkstring(L, -2); + read_inventory_list(L, -1, inv, name, gamedef); lua_pop(L, 1); // Remove value, keep key for next iteration } lua_pop(L, 1); @@ -177,7 +183,6 @@ NodeMetaRef::NodeMetaRef(IMetadata *meta): void NodeMetaRef::create(lua_State *L, v3s16 p, ServerEnvironment *env) { NodeMetaRef *o = new NodeMetaRef(p, env); - //infostream<<"NodeMetaRef::create: o="<(L, 1); - RemotePlayer *player = getplayer(ref); - if (player == nullptr) + auto *psao = getplayersao(ref); + if (psao == nullptr) return 0; - getServer(L)->RespawnPlayer(player->getPeerId()); + psao->respawn(); lua_pushboolean(L, true); return 1; } diff --git a/src/script/lua_api/l_playermeta.cpp b/src/script/lua_api/l_playermeta.cpp index e2e6ed8da..e937c145c 100644 --- a/src/script/lua_api/l_playermeta.cpp +++ b/src/script/lua_api/l_playermeta.cpp @@ -38,7 +38,7 @@ void PlayerMetaRef::clearMeta() void PlayerMetaRef::reportMetadataChange(const std::string *name) { - // TODO + // the server saves these on its own } // Creates an PlayerMetaRef and leaves it on top of stack @@ -54,9 +54,6 @@ void PlayerMetaRef::create(lua_State *L, IMetadata *metadata) void PlayerMetaRef::Register(lua_State *L) { registerMetadataClass(L, className, methods); - - // Cannot be created from Lua - // lua_register(L, className, create_object); } const char PlayerMetaRef::className[] = "PlayerMetaRef"; diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index 69f5cf9a0..82170f936 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -365,7 +365,7 @@ int ModApiServer::l_ban_player(lua_State *L) return 1; } -// disconnect_player(name, [reason]) -> success +// disconnect_player(name[, reason[, reconnect]]) -> success int ModApiServer::l_disconnect_player(lua_State *L) { NO_MAP_LOCK_REQUIRED; @@ -388,7 +388,9 @@ int ModApiServer::l_disconnect_player(lua_State *L) return 1; } - server->DenyAccess(player->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING, message); + bool reconnect = readParam(L, 3, false); + + server->DenyAccess(player->getPeerId(), SERVER_ACCESSDENIED_CUSTOM_STRING, message, reconnect); lua_pushboolean(L, true); return 1; } diff --git a/src/script/lua_api/l_server.h b/src/script/lua_api/l_server.h index 505dce735..a0fae79bd 100644 --- a/src/script/lua_api/l_server.h +++ b/src/script/lua_api/l_server.h @@ -106,7 +106,7 @@ private: // unban_player_or_ip() static int l_unban_player_or_ip(lua_State *L); - // disconnect_player(name, [reason]) -> success + // disconnect_player(name[, reason[, reconnect]]) -> success static int l_disconnect_player(lua_State *L); // remove_player(name) diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index 79eb38629..75a11a050 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -45,6 +45,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "my_sha256.h" #include "util/png.h" #include "player.h" +#include "daynightratio.h" #include // only available in zstd 1.3.5+ @@ -626,6 +627,31 @@ int ModApiUtil::l_colorspec_to_bytes(lua_State *L) return 0; } +// colorspec_to_table(colorspec) +int ModApiUtil::l_colorspec_to_table(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + video::SColor color(0); + if (read_color(L, 1, &color)) { + push_ARGB8(L, color); + return 1; + } + + return 0; +} + +// time_to_day_night_ratio(time_of_day) +int ModApiUtil::l_time_to_day_night_ratio(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + float time_of_day = lua_tonumber(L, 1) * 24000; + u32 dnr = time_to_daynight_ratio(time_of_day, true); + lua_pushnumber(L, dnr / 1000.0f); + return 1; +} + // encode_png(w, h, data, level) int ModApiUtil::l_encode_png(lua_State *L) { @@ -726,6 +752,8 @@ void ModApiUtil::Initialize(lua_State *L, int top) API_FCT(sha256); API_FCT(colorspec_to_colorstring); API_FCT(colorspec_to_bytes); + API_FCT(colorspec_to_table); + API_FCT(time_to_day_night_ratio); API_FCT(encode_png); @@ -761,6 +789,8 @@ void ModApiUtil::InitializeClient(lua_State *L, int top) API_FCT(sha256); API_FCT(colorspec_to_colorstring); API_FCT(colorspec_to_bytes); + API_FCT(colorspec_to_table); + API_FCT(time_to_day_night_ratio); API_FCT(get_last_run_mod); API_FCT(set_last_run_mod); @@ -805,6 +835,8 @@ void ModApiUtil::InitializeAsync(lua_State *L, int top) API_FCT(sha256); API_FCT(colorspec_to_colorstring); API_FCT(colorspec_to_bytes); + API_FCT(colorspec_to_table); + API_FCT(time_to_day_night_ratio); API_FCT(encode_png); diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h index e0daf3e79..89cc684e1 100644 --- a/src/script/lua_api/l_util.h +++ b/src/script/lua_api/l_util.h @@ -122,6 +122,12 @@ private: // colorspec_to_bytes(colorspec) static int l_colorspec_to_bytes(lua_State *L); + // colorspec_to_table(colorspec) + static int l_colorspec_to_table(lua_State *L); + + // time_to_day_night_ratio(time_of_day) + static int l_time_to_day_night_ratio(lua_State *L); + // encode_png(w, h, data, level) static int l_encode_png(lua_State *L); diff --git a/src/server.cpp b/src/server.cpp index 7ce21acab..acebfae38 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -76,6 +76,8 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "particles.h" #include "gettext.h" #include "network/lan.h" +#include "util/tracy_wrapper.h" + class ClientNotFoundException : public BaseException { @@ -102,6 +104,8 @@ private: void *ServerThread::run() { + ZoneScoped; + BEGIN_DEBUG_EXCEPTION_HANDLER /* @@ -111,6 +115,7 @@ void *ServerThread::run() * server-step frequency. Receive() is used for waiting between the steps. */ + auto framemarker = FrameMarker("ServerThread::run()-frame").started(); try { m_server->AsyncRunStep(0.0f, true); } catch (con::ConnectionBindFailed &e) { @@ -120,17 +125,23 @@ void *ServerThread::run() } catch (ModError &e) { m_server->setAsyncFatalError(e.what()); } + framemarker.end(); float dtime = 0.0f; while (!stopRequested()) { + framemarker.start(); ScopeProfiler spm(g_profiler, "Server::RunStep() (max)", SPT_MAX); u64 t0 = porting::getTimeUs(); - const Server::StepSettings step_settings = m_server->getStepSettings(); + const auto step_settings = m_server->getStepSettings(); try { + // see explanation inside + if (dtime > step_settings.steplen) + m_server->yieldToOtherThreads(dtime); + m_server->AsyncRunStep(step_settings.pause ? 0.0f : dtime); const float remaining_time = step_settings.steplen @@ -150,6 +161,7 @@ void *ServerThread::run() } dtime = 1e-6f * (porting::getTimeUs() - t0); + framemarker.end(); } END_DEBUG_EXCEPTION_HANDLER @@ -347,7 +359,7 @@ Server::~Server() m_emerge->stopThreads(); if (m_env) { - MutexAutoLock envlock(m_env_mutex); + EnvAutoLock envlock(this); infostream << "Server: Executing shutdown hooks" << std::endl; try { @@ -466,7 +478,7 @@ void Server::init() } //lock environment - MutexAutoLock envlock(m_env_mutex); + EnvAutoLock envlock(this); // Create the Map (loads map_meta.txt, overriding configured mapgen params) auto startup_server_map = std::make_unique(m_path_world, this, @@ -623,6 +635,9 @@ void Server::step() void Server::AsyncRunStep(float dtime, bool initial_step) { + ZoneScoped; + auto framemarker = FrameMarker("Server::AsyncRunStep()-frame").started(); + { // Send blocks to clients SendBlocks(dtime); @@ -659,9 +674,9 @@ void Server::AsyncRunStep(float dtime, bool initial_step) } { - MutexAutoLock lock(m_env_mutex); + EnvAutoLock lock(this); float max_lag = m_env->getMaxLagEstimate(); - constexpr float lag_warn_threshold = 2.0f; + constexpr float lag_warn_threshold = 1.0f; // Decrease value gradually, halve it every minute. if (m_max_lag_decrease.step(dtime, 0.5f)) { @@ -692,7 +707,7 @@ void Server::AsyncRunStep(float dtime, bool initial_step) static const float map_timer_and_unload_dtime = 2.92; if(m_map_timer_and_unload_interval.step(dtime, map_timer_and_unload_dtime)) { - MutexAutoLock lock(m_env_mutex); + EnvAutoLock lock(this); // Run Map's timers and unload unused data ScopeProfiler sp(g_profiler, "Server: map timer and unload"); m_env->getMap().timerUpdate(map_timer_and_unload_dtime, @@ -710,7 +725,7 @@ void Server::AsyncRunStep(float dtime, bool initial_step) */ if (m_admin_chat) { if (!m_admin_chat->command_queue.empty()) { - MutexAutoLock lock(m_env_mutex); + EnvAutoLock lock(this); while (!m_admin_chat->command_queue.empty()) { ChatEvent *evt = m_admin_chat->command_queue.pop_frontNoEx(); handleChatInterfaceEvent(evt); @@ -731,7 +746,7 @@ void Server::AsyncRunStep(float dtime, bool initial_step) { m_liquid_transform_timer -= m_liquid_transform_every; - MutexAutoLock lock(m_env_mutex); + EnvAutoLock lock(this); ScopeProfiler sp(g_profiler, "Server: liquid transform"); @@ -796,7 +811,7 @@ void Server::AsyncRunStep(float dtime, bool initial_step) */ { //infostream<<"Server: Checking added and deleted active objects"<getFloat("server_map_save_interval"); if (counter >= save_interval) { counter = 0.0; - MutexAutoLock lock(m_env_mutex); + EnvAutoLock lock(this); ScopeProfiler sp(g_profiler, "Server: map saving (sum)"); @@ -1075,6 +1090,9 @@ void Server::AsyncRunStep(float dtime, bool initial_step) void Server::Receive(float timeout) { + ZoneScoped; + auto framemarker = FrameMarker("Server::Receive()-frame").started(); + const u64 t0 = porting::getTimeUs(); const float timeout_us = timeout * 1e6f; auto remaining_time_us = [&]() -> float { @@ -1120,6 +1138,52 @@ void Server::Receive(float timeout) } } +void Server::yieldToOtherThreads(float dtime) +{ + /* + * Problem: the server thread and emerge thread compete for the envlock. + * While the emerge thread needs it just once or twice for every processed item + * the server thread uses it much more generously. + * This is usually not a problem as the server sleeps between steps, which leaves + * enough chance. But if the server is overloaded it's busy all the time and + * - even with a fair envlock - the emerge thread can't get up to speed. + * This generally has a much worse impact on gameplay than server lag itself + * ever would. + * + * Workaround: If we detect that the server is overloaded, introduce some careful + * artificial sleeps to leave the emerge threads enough chance to do their job. + * + * In the future the emerge code should be reworked to exclusively use a result + * queue, thereby avoiding this problem (and terrible workaround). + */ + + // don't activate workaround too quickly + constexpr size_t MIN_EMERGE_QUEUE_SIZE = 32; + const size_t qs_initial = m_emerge->getQueueSize(); + if (qs_initial < MIN_EMERGE_QUEUE_SIZE) + return; + + // give the thread a chance to run for every 28ms (on average) + // this was experimentally determined + const float QUANTUM = 28.0f / 1000; + // put an upper limit to not cause too much lag, also so this doesn't become self-sustaining + const int SLEEP_MAX = 10; + + int sleep_count = std::clamp(dtime / QUANTUM, 1, SLEEP_MAX); + + ScopeProfiler sp(g_profiler, "Server::yieldTo...() sleep", SPT_AVG); + size_t qs = qs_initial; + while (sleep_count-- > 0) { + sleep_ms(1); + // abort if we don't make progress + size_t qs2 = m_emerge->getQueueSize(); + if (qs2 >= qs || qs2 == 0) + break; + qs = qs2; + } + g_profiler->avg("Server::yieldTo...() progress [#]", qs_initial - qs); +} + PlayerSAO* Server::StageTwoClientInit(session_t peer_id) { std::string playername; @@ -1166,10 +1230,6 @@ PlayerSAO* Server::StageTwoClientInit(session_t peer_id) // Send HP SendPlayerHP(playersao, false); - // Send death screen - if (playersao->isDead()) - SendDeathscreen(peer_id, false, v3f(0,0,0)); - // Send Breath SendPlayerBreath(playersao); @@ -1202,7 +1262,7 @@ inline void Server::handleCommand(NetworkPacket *pkt) void Server::ProcessData(NetworkPacket *pkt) { // Environment is locked first. - MutexAutoLock envlock(m_env_mutex); + EnvAutoLock envlock(this); ScopeProfiler sp(g_profiler, "Server: Process network packet (sum)"); u32 peer_id = pkt->getPeerId(); @@ -1403,25 +1463,12 @@ void Server::SendBreath(session_t peer_id, u16 breath) } void Server::SendAccessDenied(session_t peer_id, AccessDeniedCode reason, - const std::string &custom_reason, bool reconnect) + std::string_view custom_reason, bool reconnect) { assert(reason < SERVER_ACCESSDENIED_MAX); NetworkPacket pkt(TOCLIENT_ACCESS_DENIED, 1, peer_id); - pkt << (u8)reason; - if (reason == SERVER_ACCESSDENIED_CUSTOM_STRING) - pkt << custom_reason; - else if (reason == SERVER_ACCESSDENIED_SHUTDOWN || - reason == SERVER_ACCESSDENIED_CRASH) - pkt << custom_reason << (u8)reconnect; - Send(&pkt); -} - -void Server::SendDeathscreen(session_t peer_id, bool set_camera_point_target, - v3f camera_point_target) -{ - NetworkPacket pkt(TOCLIENT_DEATHSCREEN, 1 + sizeof(v3f), peer_id); - pkt << set_camera_point_target << camera_point_target; + pkt << (u8)reason << custom_reason << (u8)reconnect; Send(&pkt); } @@ -1855,7 +1902,7 @@ void Server::SendCloudParams(session_t peer_id, const CloudParams ¶ms) { NetworkPacket pkt(TOCLIENT_CLOUD_PARAMS, 0, peer_id); pkt << params.density << params.color_bright << params.color_ambient - << params.height << params.thickness << params.speed; + << params.height << params.thickness << params.speed << params.color_shadow; Send(&pkt); } @@ -1885,7 +1932,7 @@ void Server::SendSetLighting(session_t peer_id, const Lighting &lighting) << lighting.exposure.speed_bright_dark << lighting.exposure.center_weight_power; - pkt << lighting.volumetric_light_strength; + pkt << lighting.volumetric_light_strength << lighting.shadow_tint; Send(&pkt); } @@ -2387,8 +2434,7 @@ void Server::SendBlockNoLock(session_t peer_id, MapBlock *block, u8 ver, void Server::SendBlocks(float dtime) { - MutexAutoLock envlock(m_env_mutex); - //TODO check if one big lock could be faster then multiple small ones + EnvAutoLock envlock(this); std::vector queue; @@ -2719,7 +2765,7 @@ void Server::sendRequestedMedia(session_t peer_id, void Server::stepPendingDynMediaCallbacks(float dtime) { - MutexAutoLock lock(m_env_mutex); + EnvAutoLock lock(this); for (auto it = m_pending_dyn_media.begin(); it != m_pending_dyn_media.end();) { it->second.expiry_timer -= dtime; @@ -2815,32 +2861,8 @@ void Server::HandlePlayerDeath(PlayerSAO *playersao, const PlayerHPChangeReason // Trigger scripted stuff m_script->on_dieplayer(playersao, reason); - - SendDeathscreen(playersao->getPeerID(), false, v3f(0,0,0)); } -void Server::RespawnPlayer(session_t peer_id) -{ - PlayerSAO *playersao = getPlayerSAO(peer_id); - assert(playersao); - - infostream << "Server::RespawnPlayer(): Player " - << playersao->getPlayer()->getName() - << " respawns" << std::endl; - - const auto *prop = playersao->accessObjectProperties(); - playersao->setHP(prop->hp_max, - PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN)); - playersao->setBreath(prop->breath_max); - - bool repositioned = m_script->on_respawnplayer(playersao); - if (!repositioned) { - // setPos will send the new position to client - playersao->setPos(findSpawnPos()); - } -} - - void Server::DenySudoAccess(session_t peer_id) { NetworkPacket pkt(TOCLIENT_DENY_SUDO_MODE, 0, peer_id); @@ -2849,7 +2871,7 @@ void Server::DenySudoAccess(session_t peer_id) void Server::DenyAccess(session_t peer_id, AccessDeniedCode reason, - const std::string &custom_reason, bool reconnect) + std::string_view custom_reason, bool reconnect) { SendAccessDenied(peer_id, reason, custom_reason, reconnect); m_clients.event(peer_id, CSE_SetDenied); @@ -2962,7 +2984,7 @@ void Server::DeleteClient(session_t peer_id, ClientDeletionReason reason) } } { - MutexAutoLock env_lock(m_env_mutex); + EnvAutoLock envlock(this); m_clients.DeleteClient(peer_id); } } @@ -4155,7 +4177,7 @@ Translations *Server::getTranslationLanguage(const std::string &lang_code) std::unordered_map Server::getMediaList() { - MutexAutoLock env_lock(m_env_mutex); + EnvAutoLock envlock(this); std::unordered_map ret; for (auto &it : m_media) { diff --git a/src/server.h b/src/server.h index 4b5583088..38f53253c 100644 --- a/src/server.h +++ b/src/server.h @@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "util/metricsbackend.h" #include "serverenvironment.h" #include "server/clientiface.h" +#include "threading/ordered_mutex.h" #include "chatmessage.h" #include "sound.h" #include "translation.h" @@ -167,9 +168,12 @@ public: // Actual processing is done in another thread. // This just checks if there was an error in that thread. void step(); + // This is run by ServerThread and does the actual processing void AsyncRunStep(float dtime, bool initial_step = false); void Receive(float timeout); + void yieldToOtherThreads(float dtime); + PlayerSAO* StageTwoClientInit(session_t peer_id); /* @@ -194,7 +198,6 @@ public: void handleCommand_ChatMessage(NetworkPacket* pkt); void handleCommand_Damage(NetworkPacket* pkt); void handleCommand_PlayerItem(NetworkPacket* pkt); - void handleCommand_Respawn(NetworkPacket* pkt); void handleCommand_Interact(NetworkPacket* pkt); void handleCommand_RemovedSounds(NetworkPacket* pkt); void handleCommand_NodeMetaFields(NetworkPacket* pkt); @@ -357,15 +360,13 @@ public: void setLighting(RemotePlayer *player, const Lighting &lighting); - void RespawnPlayer(session_t peer_id); - /* con::PeerHandler implementation. */ void peerAdded(con::IPeer *peer); void deletingPeer(con::IPeer *peer, bool timeout); void DenySudoAccess(session_t peer_id); void DenyAccess(session_t peer_id, AccessDeniedCode reason, - const std::string &custom_reason = "", bool reconnect = false); + std::string_view custom_reason = "", bool reconnect = false); void kickAllPlayers(AccessDeniedCode reason, const std::string &str_reason, bool reconnect); void acceptAuth(session_t peer_id, bool forSudoMode); @@ -428,8 +429,14 @@ public: // Bind address Address m_bind_addr; - // Environment mutex (envlock) - std::mutex m_env_mutex; + // Public helper for taking the envlock in a scope + class EnvAutoLock { + public: + EnvAutoLock(Server *server): m_lock(server->m_env_mutex) {} + + private: + std::lock_guard m_lock; + }; protected: /* Do not add more members here, this is only required to make unit tests work. */ @@ -486,9 +493,7 @@ private: void SendHP(session_t peer_id, u16 hp, bool effect); void SendBreath(session_t peer_id, u16 breath); void SendAccessDenied(session_t peer_id, AccessDeniedCode reason, - const std::string &custom_reason, bool reconnect = false); - void SendDeathscreen(session_t peer_id, bool set_camera_point_target, - v3f camera_point_target); + std::string_view custom_reason, bool reconnect = false); void SendItemDef(session_t peer_id, IItemDefManager *itemdef, u16 protocol_version); void SendNodeDef(session_t peer_id, const NodeDefManager *nodedef, u16 protocol_version); @@ -601,11 +606,13 @@ private: */ PlayerSAO *emergePlayer(const char *name, session_t peer_id, u16 proto_version); - void handlePeerChanges(); - /* Variables */ + + // Environment mutex (envlock) + ordered_mutex m_env_mutex; + // World directory std::string m_path_world; std::string m_path_mod_data; diff --git a/src/server/clientiface.h b/src/server/clientiface.h index f930e7f3e..3f5ba6434 100644 --- a/src/server/clientiface.h +++ b/src/server/clientiface.h @@ -126,7 +126,7 @@ class EmergeManager; | TOCLIENT_INVENTORY | | | | | TOCLIENT_HP (opt) | \-----------------/ | | TOCLIENT_BREATH | | -| TOCLIENT_DEATHSCREEN | | +| TOCLIENT_DEATHSCREEN_LEGACY | | +-----------------------------+ | | | v | diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 11922b2c6..61d328ca7 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -519,12 +519,13 @@ void PlayerSAO::rightClick(ServerActiveObject *clicker) void PlayerSAO::setHP(s32 target_hp, const PlayerHPChangeReason &reason, bool from_client) { - target_hp = rangelim(target_hp, 0, U16_MAX); - - if (target_hp == m_hp) + if (target_hp == m_hp || (m_hp == 0 && target_hp < 0)) return; // Nothing to do - s32 hp_change = m_env->getScriptIface()->on_player_hpchange(this, target_hp - (s32)m_hp, reason); + // Protect against overflow. + s32 hp_change = std::max((s64)target_hp - (s64)m_hp, S32_MIN); + + hp_change = m_env->getScriptIface()->on_player_hpchange(this, hp_change, reason); hp_change = std::min(hp_change, U16_MAX); // Protect against overflow s32 hp = (s32)m_hp + hp_change; @@ -558,6 +559,21 @@ void PlayerSAO::setBreath(const u16 breath, bool send) m_env->getGameDef()->SendPlayerBreath(this); } +void PlayerSAO::respawn() +{ + infostream << "PlayerSAO::respawn(): Player " << m_player->getName() + << " respawns" << std::endl; + + setHP(m_prop.hp_max, PlayerHPChangeReason(PlayerHPChangeReason::RESPAWN)); + setBreath(m_prop.breath_max); + + bool repositioned = m_env->getScriptIface()->on_respawnplayer(this); + if (!repositioned) { + // setPos will send the new position to client + setPos(m_env->getGameDef()->findSpawnPos()); + } +} + Inventory *PlayerSAO::getInventory() const { return m_player ? &m_player->inventory : nullptr; diff --git a/src/server/player_sao.h b/src/server/player_sao.h index 95bd1d109..487e37957 100644 --- a/src/server/player_sao.h +++ b/src/server/player_sao.h @@ -124,6 +124,7 @@ public: void setHPRaw(u16 hp) { m_hp = hp; } u16 getBreath() const { return m_breath; } void setBreath(const u16 breath, bool send = true); + void respawn(); /* Inventory interface diff --git a/src/server/unit_sao.h b/src/server/unit_sao.h index c7f8c4aec..32b5dd30e 100644 --- a/src/server/unit_sao.h +++ b/src/server/unit_sao.h @@ -31,7 +31,7 @@ public: UnitSAO(ServerEnvironment *env, v3f pos); virtual ~UnitSAO() = default; - u16 getHP() const { return m_hp; } + u16 getHP() const override { return m_hp; } // Use a function, if isDead can be defined by other conditions bool isDead() const { return m_hp == 0; } @@ -59,39 +59,39 @@ public: { return itemgroup_get(getArmorGroups(), "immortal"); } - void setArmorGroups(const ItemGroupList &armor_groups); - const ItemGroupList &getArmorGroups() const; + void setArmorGroups(const ItemGroupList &armor_groups) override; + const ItemGroupList &getArmorGroups() const override; // Animation void setAnimation(v2f frame_range, float frame_speed, float frame_blend, - bool frame_loop); + bool frame_loop) override; void getAnimation(v2f *frame_range, float *frame_speed, float *frame_blend, - bool *frame_loop); - void setAnimationSpeed(float frame_speed); + bool *frame_loop) override; + void setAnimationSpeed(float frame_speed) override; // Bone position - void setBoneOverride(const std::string &bone, const BoneOverride &props); - BoneOverride getBoneOverride(const std::string &bone); + void setBoneOverride(const std::string &bone, const BoneOverride &props) override; + BoneOverride getBoneOverride(const std::string &bone) override; const std::unordered_map - &getBoneOverrides() const { return m_bone_override; }; + &getBoneOverrides() const override { return m_bone_override; }; // Attachments - ServerActiveObject *getParent() const; + ServerActiveObject *getParent() const override; inline bool isAttached() const { return m_attachment_parent_id != 0; } void setAttachment(object_t parent_id, const std::string &bone, v3f position, - v3f rotation, bool force_visible); + v3f rotation, bool force_visible) override; void getAttachment(object_t *parent_id, std::string *bone, v3f *position, - v3f *rotation, bool *force_visible) const; + v3f *rotation, bool *force_visible) const override; void clearChildAttachments() override; void addAttachmentChild(object_t child_id) override; void removeAttachmentChild(object_t child_id) override; - const std::unordered_set &getAttachmentChildIds() const { + const std::unordered_set &getAttachmentChildIds() const override { return m_attachment_child_ids; } // Object properties - ObjectProperties *accessObjectProperties(); - void notifyObjectPropertiesModified(); + ObjectProperties *accessObjectProperties() override; + void notifyObjectPropertiesModified() override; void sendOutdatedData(); // Update packets @@ -125,11 +125,11 @@ protected: object_t m_attachment_parent_id = 0; void clearAnyAttachments(); - virtual void onMarkedForDeactivation() { + virtual void onMarkedForDeactivation() override { ServerActiveObject::onMarkedForDeactivation(); clearAnyAttachments(); } - virtual void onMarkedForRemoval() { + virtual void onMarkedForRemoval() override { ServerActiveObject::onMarkedForRemoval(); clearAnyAttachments(); } diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 24e4a587e..813184de1 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -77,11 +77,11 @@ ABMWithState::ABMWithState(ActiveBlockModifier *abm_): LBMManager */ -void LBMContentMapping::deleteContents() +LBMContentMapping::~LBMContentMapping() { - for (auto &it : lbm_list) { + map.clear(); + for (auto &it : lbm_list) delete it; - } } void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef) @@ -90,29 +90,32 @@ void LBMContentMapping::addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamed // Unknown names get added to the global NameIdMapping. const NodeDefManager *nodedef = gamedef->ndef(); + FATAL_ERROR_IF(CONTAINS(lbm_list, lbm_def), "Same LBM registered twice"); lbm_list.push_back(lbm_def); - for (const std::string &nodeTrigger: lbm_def->trigger_contents) { - std::vector c_ids; - bool found = nodedef->getIds(nodeTrigger, c_ids); + std::vector c_ids; + + for (const auto &node : lbm_def->trigger_contents) { + bool found = nodedef->getIds(node, c_ids); if (!found) { - content_t c_id = gamedef->allocateUnknownNodeId(nodeTrigger); + content_t c_id = gamedef->allocateUnknownNodeId(node); if (c_id == CONTENT_IGNORE) { // Seems it can't be allocated. - warningstream << "Could not internalize node name \"" << nodeTrigger + warningstream << "Could not internalize node name \"" << node << "\" while loading LBM \"" << lbm_def->name << "\"." << std::endl; continue; } c_ids.push_back(c_id); } - - for (content_t c_id : c_ids) { - map[c_id].push_back(lbm_def); - } } + + SORT_AND_UNIQUE(c_ids); + + for (content_t c_id : c_ids) + map[c_id].push_back(lbm_def); } -const std::vector * +const LBMContentMapping::lbm_vector * LBMContentMapping::lookup(content_t c) const { lbm_map::const_iterator it = map.find(c); @@ -130,9 +133,7 @@ LBMManager::~LBMManager() delete m_lbm_def.second; } - for (auto &it : m_lbm_lookup) { - (it.second).deleteContents(); - } + m_lbm_lookup.clear(); } void LBMManager::addLBMDef(LoadingBlockModifierDef *lbm_def) @@ -236,7 +237,7 @@ std::string LBMManager::createIntroductionTimesString() std::ostringstream oss; for (const auto &it : m_lbm_lookup) { u32 time = it.first; - const std::vector &lbm_list = it.second.lbm_list; + auto &lbm_list = it.second.getList(); for (const auto &lbm_def : lbm_list) { // Don't add if the LBM runs at every load, // then introducement time is hardcoded @@ -255,41 +256,74 @@ void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, // Precondition, we need m_lbm_lookup to be initialized FATAL_ERROR_IF(!m_query_mode, "attempted to query on non fully set up LBMManager"); - v3s16 pos_of_block = block->getPosRelative(); - v3s16 pos; - MapNode n; - content_t c; - auto it = getLBMsIntroducedAfter(stamp); - for (; it != m_lbm_lookup.end(); ++it) { - // Cache previous version to speedup lookup which has a very high performance - // penalty on each call + + // Collect a list of all LBMs and associated positions + struct LBMToRun { + std::unordered_set p; // node positions + std::unordered_set l; + }; + std::unordered_map to_run; + + // Note: the iteration count of this outer loop is typically very low, so it's ok. + for (auto it = getLBMsIntroducedAfter(stamp); it != m_lbm_lookup.end(); ++it) { + v3s16 pos; + content_t c; + + // Cache previous lookups since it has a high performance penalty. content_t previous_c = CONTENT_IGNORE; - const std::vector *lbm_list = nullptr; + const LBMContentMapping::lbm_vector *lbm_list = nullptr; + LBMToRun *batch = nullptr; for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++) for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++) { - n = block->getNodeNoCheck(pos); - c = n.getContent(); + c = block->getNodeNoCheck(pos).getContent(); - // If content_t are not matching perform an LBM lookup + bool c_changed = false; if (previous_c != c) { + c_changed = true; lbm_list = it->second.lookup(c); + batch = &to_run[c]; previous_c = c; } if (!lbm_list) continue; - for (auto lbmdef : *lbm_list) { - lbmdef->trigger(env, pos + pos_of_block, n, dtime_s); - if (block->isOrphan()) - return; - n = block->getNodeNoCheck(pos); - if (n.getContent() != c) - break; // The node was changed and the LBMs no longer apply + batch->p.insert(pos); + if (c_changed) { + batch->l.insert(lbm_list->begin(), lbm_list->end()); + } else { + // we were here before so the list must be filled + assert(!batch->l.empty()); } } } + + // Actually run them + bool first = true; + for (auto &[c, batch] : to_run) { + for (auto &lbm_def : batch.l) { + if (!first) { + // The fun part: since any LBM call can change the nodes inside of he + // block, we have to recheck the positions to see if the wanted node + // is still there. + // Note that we don't rescan the whole block, we don't want to include new changes. + for (auto it2 = batch.p.begin(); it2 != batch.p.end(); ) { + if (block->getNodeNoCheck(*it2).getContent() != c) + it2 = batch.p.erase(it2); + else + ++it2; + } + } + first = false; + + if (batch.p.empty()) + break; + lbm_def->trigger(env, block, batch.p, dtime_s); + if (block->isOrphan()) + return; + } + } } /* @@ -792,11 +826,10 @@ void ServerEnvironment::loadDefaultMeta() struct ActiveABM { ActiveBlockModifier *abm; - int chance; std::vector required_neighbors; - bool check_required_neighbors; // false if required_neighbors is known to be empty - s16 min_y; - s16 max_y; + std::vector without_neighbors; + int chance; + s16 min_y, max_y; }; #define CONTENT_TYPE_CACHE_MAX 64 @@ -812,16 +845,16 @@ public: bool use_timers): m_env(env) { - if(dtime_s < 0.001) + if (dtime_s < 0.001f) return; const NodeDefManager *ndef = env->getGameDef()->ndef(); for (ABMWithState &abmws : abms) { ActiveBlockModifier *abm = abmws.abm; float trigger_interval = abm->getTriggerInterval(); - if(trigger_interval < 0.001) - trigger_interval = 0.001; + if (trigger_interval < 0.001f) + trigger_interval = 0.001f; float actual_interval = dtime_s; - if(use_timers){ + if (use_timers) { abmws.timer += dtime_s; if(abmws.timer < trigger_interval) continue; @@ -831,6 +864,7 @@ public: float chance = abm->getTriggerChance(); if (chance == 0) chance = 1; + ActiveABM aabm; aabm.abm = abm; if (abm->getSimpleCatchUp()) { @@ -848,25 +882,25 @@ public: aabm.max_y = abm->getMaxY(); // Trigger neighbors - const std::vector &required_neighbors_s = - abm->getRequiredNeighbors(); - for (const std::string &required_neighbor_s : required_neighbors_s) { - ndef->getIds(required_neighbor_s, aabm.required_neighbors); - } - aabm.check_required_neighbors = !required_neighbors_s.empty(); + for (const auto &s : abm->getRequiredNeighbors()) + ndef->getIds(s, aabm.required_neighbors); + SORT_AND_UNIQUE(aabm.required_neighbors); + + for (const auto &s : abm->getWithoutNeighbors()) + ndef->getIds(s, aabm.without_neighbors); + SORT_AND_UNIQUE(aabm.without_neighbors); // Trigger contents - const std::vector &contents_s = abm->getTriggerContents(); - for (const std::string &content_s : contents_s) { - std::vector ids; - ndef->getIds(content_s, ids); - for (content_t c : ids) { - if (c >= m_aabms.size()) - m_aabms.resize(c + 256, NULL); - if (!m_aabms[c]) - m_aabms[c] = new std::vector; - m_aabms[c]->push_back(aabm); - } + std::vector ids; + for (const auto &s : abm->getTriggerContents()) + ndef->getIds(s, ids); + SORT_AND_UNIQUE(ids); + for (content_t c : ids) { + if (c >= m_aabms.size()) + m_aabms.resize(c + 256, nullptr); + if (!m_aabms[c]) + m_aabms[c] = new std::vector; + m_aabms[c]->push_back(aabm); } } } @@ -967,8 +1001,11 @@ public: continue; // Check neighbors - if (aabm.check_required_neighbors) { + const bool check_required_neighbors = !aabm.required_neighbors.empty(); + const bool check_without_neighbors = !aabm.without_neighbors.empty(); + if (check_required_neighbors || check_without_neighbors) { v3s16 p1; + bool have_required = false; for(p1.X = p0.X-1; p1.X <= p0.X+1; p1.X++) for(p1.Y = p0.Y-1; p1.Y <= p0.Y+1; p1.Y++) for(p1.Z = p0.Z-1; p1.Z <= p0.Z+1; p1.Z++) @@ -986,12 +1023,25 @@ public: MapNode n = map->getNode(p1 + block->getPosRelative()); c = n.getContent(); } - if (CONTAINS(aabm.required_neighbors, c)) - goto neighbor_found; + if (check_required_neighbors && !have_required) { + if (CONTAINS(aabm.required_neighbors, c)) { + if (!check_without_neighbors) + goto neighbor_found; + have_required = true; + } + } + if (check_without_neighbors) { + if (CONTAINS(aabm.without_neighbors, c)) + goto neighbor_invalid; + } } + if (have_required || !check_required_neighbors) + goto neighbor_found; // No required neighbor found + neighbor_invalid: continue; } + neighbor_found: abms_run++; diff --git a/src/serverenvironment.h b/src/serverenvironment.h index 7a388d21c..0b00fac91 100644 --- a/src/serverenvironment.h +++ b/src/serverenvironment.h @@ -63,6 +63,9 @@ public: // Set of required neighbors (trigger doesn't happen if none are found) // Empty = do not check neighbors virtual const std::vector &getRequiredNeighbors() const = 0; + // Set of without neighbors (trigger doesn't happen if any are found) + // Empty = do not check neighbors + virtual const std::vector &getWithoutNeighbors() const = 0; // Trigger interval in seconds virtual float getTriggerInterval() = 0; // Random chance of (1 / return value), 0 is disallowed @@ -90,29 +93,40 @@ struct ABMWithState struct LoadingBlockModifierDef { // Set of contents to trigger on - std::set trigger_contents; + std::vector trigger_contents; std::string name; bool run_at_every_load = false; virtual ~LoadingBlockModifierDef() = default; - virtual void trigger(ServerEnvironment *env, v3s16 p, - MapNode n, float dtime_s) {}; + /// @brief Called to invoke LBM + /// @param env environment + /// @param block the block in question + /// @param positions set of node positions (block-relative!) + /// @param dtime_s game time since last deactivation + virtual void trigger(ServerEnvironment *env, MapBlock *block, + const std::unordered_set &positions, float dtime_s) {}; }; -struct LBMContentMapping +class LBMContentMapping { - typedef std::unordered_map> lbm_map; - lbm_map map; +public: + typedef std::vector lbm_vector; + typedef std::unordered_map lbm_map; - std::vector lbm_list; - - // Needs to be separate method (not inside destructor), - // because the LBMContentMapping may be copied and destructed - // many times during operation in the lbm_lookup_map. - void deleteContents(); + LBMContentMapping() = default; void addLBM(LoadingBlockModifierDef *lbm_def, IGameDef *gamedef); const lbm_map::mapped_type *lookup(content_t c) const; + const lbm_vector &getList() const { return lbm_list; } + + // This struct owns the LBM pointers. + ~LBMContentMapping(); + DISABLE_CLASS_COPY(LBMContentMapping); + ALLOW_CLASS_MOVE(LBMContentMapping); + +private: + lbm_vector lbm_list; + lbm_map map; }; class LBMManager diff --git a/src/servermap.cpp b/src/servermap.cpp index 0248497c1..f57e5b5e4 100644 --- a/src/servermap.cpp +++ b/src/servermap.cpp @@ -51,6 +51,18 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "database/database-postgresql.h" #endif +/* + Helpers +*/ + +void MapDatabaseAccessor::loadBlock(v3s16 blockpos, std::string &ret) +{ + ret.clear(); + dbase->loadBlock(blockpos, &ret); + if (ret.empty() && dbase_ro) + dbase_ro->loadBlock(blockpos, &ret); +} + /* ServerMap */ @@ -67,7 +79,7 @@ ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef, emerge->map_settings_mgr = &settings_mgr; /* - Try to load map; if not found, create a new one. + Try to open map; if not found, create a new one. */ // Determine which database backend to use @@ -79,10 +91,10 @@ ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef, conf.set("backend", "sqlite3"); } std::string backend = conf.get("backend"); - dbase = createDatabase(backend, savedir, conf); + m_db.dbase = createDatabase(backend, savedir, conf); if (conf.exists("readonly_backend")) { std::string readonly_dir = savedir + DIR_DELIM + "readonly"; - dbase_ro = createDatabase(conf.get("readonly_backend"), readonly_dir, conf); + m_db.dbase_ro = createDatabase(conf.get("readonly_backend"), readonly_dir, conf); } if (!conf.updateConfigFile(conf_path.c_str())) errorstream << "ServerMap::ServerMap(): Failed to update world.mt!" << std::endl; @@ -90,6 +102,9 @@ ServerMap::ServerMap(const std::string &savedir, IGameDef *gamedef, m_savedir = savedir; m_map_saving_enabled = false; + // Inform EmergeManager of db handles + m_emerge->initMap(&m_db); + m_save_time_counter = mb->addCounter( "minetest_map_save_time", "Time spent saving blocks (in microseconds)"); m_save_count_counter = mb->addCounter( @@ -159,11 +174,15 @@ ServerMap::~ServerMap() << ", exception: " << e.what() << std::endl; } - /* - Close database if it was opened - */ - delete dbase; - delete dbase_ro; + m_emerge->resetMap(); + + { + MutexAutoLock dblock(m_db.mutex); + delete m_db.dbase; + m_db.dbase = nullptr; + delete m_db.dbase_ro; + m_db.dbase_ro = nullptr; + } deleteDetachedBlocks(); } @@ -547,9 +566,10 @@ void ServerMap::save(ModifiedState save_level) void ServerMap::listAllLoadableBlocks(std::vector &dst) { - dbase->listAllLoadableBlocks(dst); - if (dbase_ro) - dbase_ro->listAllLoadableBlocks(dst); + MutexAutoLock dblock(m_db.mutex); + m_db.dbase->listAllLoadableBlocks(dst); + if (m_db.dbase_ro) + m_db.dbase_ro->listAllLoadableBlocks(dst); } void ServerMap::listAllLoadedBlocks(std::vector &dst) @@ -597,17 +617,21 @@ MapDatabase *ServerMap::createDatabase( void ServerMap::beginSave() { - dbase->beginSave(); + MutexAutoLock dblock(m_db.mutex); + m_db.dbase->beginSave(); } void ServerMap::endSave() { - dbase->endSave(); + MutexAutoLock dblock(m_db.mutex); + m_db.dbase->endSave(); } bool ServerMap::saveBlock(MapBlock *block) { - return saveBlock(block, dbase, m_map_compression_level); + // FIXME: serialization happens under mutex + MutexAutoLock dblock(m_db.mutex); + return saveBlock(block, m_db.dbase, m_map_compression_level); } bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db, int compression_level) @@ -634,18 +658,27 @@ bool ServerMap::saveBlock(MapBlock *block, MapDatabase *db, int compression_leve return ret; } -void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load) +void ServerMap::deSerializeBlock(MapBlock *block, std::istream &is) { + ScopeProfiler sp(g_profiler, "ServerMap: deSer block", SPT_AVG, PRECISION_MICRO); + + u8 version = readU8(is); + if (is.fail()) + throw SerializationError("Failed to read MapBlock version"); + + block->deSerialize(is, version, true); +} + +MapBlock *ServerMap::loadBlock(const std::string &blob, v3s16 p3d, bool save_after_load) +{ + ScopeProfiler sp(g_profiler, "ServerMap: load block", SPT_AVG, PRECISION_MICRO); + MapBlock *block = nullptr; + bool created_new = false; + try { - std::istringstream is(*blob, std::ios_base::binary); + v2s16 p2d(p3d.X, p3d.Z); + MapSector *sector = createSector(p2d); - u8 version = readU8(is); - - if(is.fail()) - throw SerializationError("ServerMap::loadBlock(): Failed" - " to read MapBlock version"); - - MapBlock *block = nullptr; std::unique_ptr block_created_new; block = sector->getBlockNoCreateNoEx(p3d.Y); if (!block) { @@ -654,31 +687,16 @@ void ServerMap::loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool } { - ScopeProfiler sp(g_profiler, "ServerMap: deSer block", SPT_AVG, PRECISION_MICRO); - block->deSerialize(is, version, true); + std::istringstream iss(blob, std::ios_base::binary); + deSerializeBlock(block, iss); } // If it's a new block, insert it to the map if (block_created_new) { sector->insertBlock(std::move(block_created_new)); - ReflowScan scanner(this, m_emerge->ndef); - scanner.scan(block, &m_transforming_liquid); + created_new = true; } - - /* - Save blocks loaded in old format in new format - */ - - //if(version < SER_FMT_VER_HIGHEST_READ || save_after_load) - // Only save if asked to; no need to update version - if(save_after_load) - saveBlock(block); - - // We just loaded it from, so it's up-to-date. - block->resetModified(); - } - catch(SerializationError &e) - { + } catch (SerializationError &e) { errorstream<<"Invalid block data in database" <<" ("<ndef); + scanner.scan(block, &m_transforming_liquid); - std::string ret; - dbase->loadBlock(blockpos, &ret); - if (!ret.empty()) { - loadBlock(&ret, blockpos, createSector(p2d), false); - } else if (dbase_ro) { - dbase_ro->loadBlock(blockpos, &ret); - if (!ret.empty()) { - loadBlock(&ret, blockpos, createSector(p2d), false); - } - } else { - return NULL; - } - - MapBlock *block = getBlockNoCreateNoEx(blockpos); - if (created_new && (block != NULL)) { std::map modified_blocks; // Fix lighting if necessary voxalgo::update_block_border_lighting(this, block, modified_blocks); if (!modified_blocks.empty()) { - //Modified lighting, send event MapEditEvent event; event.type = MEET_OTHER; event.setModifiedBlocks(modified_blocks); dispatchEvent(event); } } + + if (save_after_load) + saveBlock(block); + + // We just loaded it, so it's up-to-date. + block->resetModified(); + return block; } +MapBlock* ServerMap::loadBlock(v3s16 blockpos) +{ + std::string data; + { + ScopeProfiler sp(g_profiler, "ServerMap: load block - sync (sum)"); + MutexAutoLock dblock(m_db.mutex); + m_db.loadBlock(blockpos, data); + } + + if (!data.empty()) + return loadBlock(data, blockpos); + return getBlockNoCreateNoEx(blockpos); +} + bool ServerMap::deleteBlock(v3s16 blockpos) { - if (!dbase->deleteBlock(blockpos)) + MutexAutoLock dblock(m_db.mutex); + if (!m_db.dbase->deleteBlock(blockpos)) return false; MapBlock *block = getBlockNoCreateNoEx(blockpos); diff --git a/src/servermap.h b/src/servermap.h index 7a8a84b9b..3a2102668 100644 --- a/src/servermap.h +++ b/src/servermap.h @@ -33,9 +33,22 @@ class IRollbackManager; class EmergeManager; class ServerEnvironment; struct BlockMakeData; - class MetricsBackend; +// TODO: this could wrap all calls to MapDatabase, including locking +struct MapDatabaseAccessor { + /// Lock, to be taken for any operation + std::mutex mutex; + /// Main database + MapDatabase *dbase = nullptr; + /// Fallback database for read operations + MapDatabase *dbase_ro = nullptr; + + /// Load a block, taking dbase_ro into account. + /// @note call locked + void loadBlock(v3s16 blockpos, std::string &ret); +}; + /* ServerMap @@ -75,7 +88,7 @@ public: MapBlock *createBlock(v3s16 p); /* - Forcefully get a block from somewhere. + Forcefully get a block from somewhere (blocking!). - Memory - Load from disk - Create blank filled with CONTENT_IGNORE @@ -114,9 +127,16 @@ public: bool saveBlock(MapBlock *block) override; static bool saveBlock(MapBlock *block, MapDatabase *db, int compression_level = -1); - MapBlock* loadBlock(v3s16 p); - // Database version - void loadBlock(std::string *blob, v3s16 p3d, MapSector *sector, bool save_after_load=false); + + // Load block in a synchronous fashion + MapBlock *loadBlock(v3s16 p); + /// Load a block that was already read from disk. Used by EmergeManager. + /// @return non-null block (but can be blank) + MapBlock *loadBlock(const std::string &blob, v3s16 p, bool save_after_load=false); + + // Helper for deserializing blocks from disk + // @throws SerializationError + static void deSerializeBlock(MapBlock *block, std::istream &is); // Blocks are removed from the map but not deleted from memory until // deleteDetachedBlocks() is called, since pointers to them may still exist @@ -185,8 +205,8 @@ private: This is reset to false when written on disk. */ bool m_map_metadata_changed = true; - MapDatabase *dbase = nullptr; - MapDatabase *dbase_ro = nullptr; + + MapDatabaseAccessor m_db; // Map metrics MetricGaugePtr m_loaded_blocks_gauge; diff --git a/src/skyparams.h b/src/skyparams.h index 2ff918f36..52a15810b 100644 --- a/src/skyparams.h +++ b/src/skyparams.h @@ -81,6 +81,7 @@ struct CloudParams float density; video::SColor color_bright; video::SColor color_ambient; + video::SColor color_shadow; float thickness; float height; v2f speed; @@ -160,6 +161,7 @@ public: clouds.density = 0.4f; clouds.color_bright = video::SColor(229, 240, 240, 255); clouds.color_ambient = video::SColor(255, 0, 0, 0); + clouds.color_shadow = video::SColor(255, 204, 204, 204); clouds.thickness = 16.0f; clouds.height = 120; clouds.speed = v2f(0.0f, -2.0f); diff --git a/src/threading/ordered_mutex.h b/src/threading/ordered_mutex.h new file mode 100644 index 000000000..f7fb4d309 --- /dev/null +++ b/src/threading/ordered_mutex.h @@ -0,0 +1,46 @@ +// Minetest +// SPDX-License-Identifier: LGPL-2.1-or-later + +#pragma once + +#include +#include + +/* + Fair mutex based on ticketing approach. + Satisfies `Mutex` C++11 requirements. +*/ +class ordered_mutex { +public: + ordered_mutex() : next_ticket(0), counter(0) {} + + void lock() + { + std::unique_lock autolock(cv_lock); + const auto ticket = next_ticket++; + cv.wait(autolock, [&] { return counter == ticket; }); + } + + bool try_lock() + { + std::lock_guard autolock(cv_lock); + if (counter != next_ticket) + return false; + next_ticket++; + return true; + } + + void unlock() + { + { + std::lock_guard autolock(cv_lock); + counter++; + } + cv.notify_all(); // intentionally outside lock + } + +private: + std::condition_variable cv; + std::mutex cv_lock; + uint_fast32_t next_ticket, counter; +}; diff --git a/src/unittest/test_irrptr.cpp b/src/unittest/test_irrptr.cpp index befeefc73..61d1e302c 100644 --- a/src/unittest/test_irrptr.cpp +++ b/src/unittest/test_irrptr.cpp @@ -21,6 +21,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "exceptions.h" #include "irr_ptr.h" +#include "IReferenceCounted.h" class TestIrrPtr : public TestBase { diff --git a/src/unittest/test_servermodmanager.cpp b/src/unittest/test_servermodmanager.cpp index f26734ab3..03fdc7042 100644 --- a/src/unittest/test_servermodmanager.cpp +++ b/src/unittest/test_servermodmanager.cpp @@ -122,7 +122,7 @@ void TestServerModManager::testGetMods() ServerModManager sm(m_worlddir); const auto &mods = sm.getMods(); // `ls ./games/devtest/mods | wc -l` + 1 (test mod) - UASSERTEQ(std::size_t, mods.size(), 32 + 1); + UASSERTEQ(std::size_t, mods.size(), 33 + 1); // Ensure we found basenodes mod (part of devtest) // and test_mod (for testing MINETEST_MOD_PATH). diff --git a/src/unittest/test_voxelmanipulator.cpp b/src/unittest/test_voxelmanipulator.cpp index acc2707e7..6ea0ca9af 100644 --- a/src/unittest/test_voxelmanipulator.cpp +++ b/src/unittest/test_voxelmanipulator.cpp @@ -87,7 +87,7 @@ void TestVoxelManipulator::testVoxelManipulator(const NodeDefManager *nodedef) v.print(infostream, nodedef); infostream << "*** Setting (-1,0,-1)=2 ***" << std::endl; - v.setNodeNoRef(v3s16(-1,0,-1), MapNode(t_CONTENT_GRASS)); + v.setNode(v3s16(-1,0,-1), MapNode(t_CONTENT_GRASS)); v.print(infostream, nodedef); UASSERT(v.getNode(v3s16(-1,0,-1)).getContent() == t_CONTENT_GRASS); diff --git a/src/util/basic_macros.h b/src/util/basic_macros.h index 7c3746417..f0fd4eb48 100644 --- a/src/util/basic_macros.h +++ b/src/util/basic_macros.h @@ -28,6 +28,12 @@ with this program; if not, write to the Free Software Foundation, Inc., // Requires #define CONTAINS(c, v) (std::find((c).begin(), (c).end(), (v)) != (c).end()) +// Requires +#define SORT_AND_UNIQUE(c) do { \ + std::sort((c).begin(), (c).end()); \ + (c).erase(std::unique((c).begin(), (c).end()), (c).end()); \ + } while (0) + // To disable copy constructors and assignment operations for some class // 'Foobar', add the macro DISABLE_CLASS_COPY(Foobar) in the class definition. // Note this also disables copying for any classes derived from 'Foobar' as well diff --git a/src/util/numeric.h b/src/util/numeric.h index cfe0317a1..758b55968 100644 --- a/src/util/numeric.h +++ b/src/util/numeric.h @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "SColor.h" #include #include +#include #define rangelim(d, min, max) ((d) < (min) ? (min) : ((d) > (max) ? (max) : (d))) #define myfloor(x) ((x) < 0.0 ? (int)(x) - 1 : (int)(x)) diff --git a/src/util/srp.cpp b/src/util/srp.cpp index 56b2aa763..b1dfa76a4 100644 --- a/src/util/srp.cpp +++ b/src/util/srp.cpp @@ -51,6 +51,7 @@ #endif #include "my_sha256.h" +#include "porting.h" #include "srp.h" //#define CSRP_USE_SHA1 @@ -70,26 +71,6 @@ printf("\n"); }*/ -static int g_initialized = 0; - -#define RAND_BUFF_MAX 128 -static unsigned int g_rand_idx; -static unsigned char g_rand_buff[RAND_BUFF_MAX]; - -void *(*srp_alloc)(size_t) = &malloc; -void *(*srp_realloc)(void *, size_t) = &realloc; -void (*srp_free)(void *) = &free; - -void srp_set_memory_functions( - void *(*new_srp_alloc)(size_t), - void *(*new_srp_realloc)(void *, size_t), - void (*new_srp_free)(void *)) -{ - srp_alloc = new_srp_alloc; - srp_realloc = new_srp_realloc; - srp_free = new_srp_free; -} - typedef struct { mpz_t N; mpz_t g; @@ -189,13 +170,13 @@ static void delete_ng(NGConstant *ng) if (ng) { mpz_clear(ng->N); mpz_clear(ng->g); - srp_free(ng); + free(ng); } } static NGConstant *new_ng(SRP_NGType ng_type, const char *n_hex, const char *g_hex) { - NGConstant *ng = (NGConstant *)srp_alloc(sizeof(NGConstant)); + NGConstant *ng = (NGConstant *)malloc(sizeof(NGConstant)); if (!ng) return 0; @@ -402,17 +383,17 @@ static SRP_Result H_nn( size_t len_n1 = mpz_num_bytes(n1); size_t len_n2 = mpz_num_bytes(n2); size_t nbytes = len_N + len_N; - unsigned char *bin = (unsigned char *)srp_alloc(nbytes); + unsigned char *bin = (unsigned char *)malloc(nbytes); if (!bin) return SRP_ERR; if (len_n1 > len_N || len_n2 > len_N) { - srp_free(bin); + free(bin); return SRP_ERR; } memset(bin, 0, nbytes); mpz_to_bin(n1, bin + (len_N - len_n1)); mpz_to_bin(n2, bin + (len_N + len_N - len_n2)); hash(alg, bin, nbytes, buff); - srp_free(bin); + free(bin); mpz_from_bin(buff, hash_length(alg), result); return SRP_OK; } @@ -422,12 +403,12 @@ static SRP_Result H_ns(mpz_t result, SRP_HashAlgorithm alg, const unsigned char { unsigned char buff[CSRP_MAX_HASH]; size_t nbytes = len_n + len_bytes; - unsigned char *bin = (unsigned char *)srp_alloc(nbytes); + unsigned char *bin = (unsigned char *)malloc(nbytes); if (!bin) return SRP_ERR; memcpy(bin, n, len_n); memcpy(bin + len_n, bytes, len_bytes); hash(alg, bin, nbytes, buff); - srp_free(bin); + free(bin); mpz_from_bin(buff, hash_length(alg), result); return SRP_OK; } @@ -454,22 +435,22 @@ static int calculate_x(mpz_t result, SRP_HashAlgorithm alg, const unsigned char static SRP_Result update_hash_n(SRP_HashAlgorithm alg, HashCTX *ctx, const mpz_t n) { size_t len = mpz_num_bytes(n); - unsigned char *n_bytes = (unsigned char *)srp_alloc(len); + unsigned char *n_bytes = (unsigned char *)malloc(len); if (!n_bytes) return SRP_ERR; mpz_to_bin(n, n_bytes); hash_update(alg, ctx, n_bytes, len); - srp_free(n_bytes); + free(n_bytes); return SRP_OK; } static SRP_Result hash_num(SRP_HashAlgorithm alg, const mpz_t n, unsigned char *dest) { int nbytes = mpz_num_bytes(n); - unsigned char *bin = (unsigned char *)srp_alloc(nbytes); + unsigned char *bin = (unsigned char *)malloc(nbytes); if (!bin) return SRP_ERR; mpz_to_bin(n, bin); hash(alg, bin, nbytes, dest); - srp_free(bin); + free(bin); return SRP_OK; } @@ -521,60 +502,23 @@ static SRP_Result calculate_H_AMK(SRP_HashAlgorithm alg, unsigned char *dest, return SRP_OK; } -static SRP_Result fill_buff() -{ - g_rand_idx = 0; - -#ifdef WIN32 - HCRYPTPROV wctx; -#else - FILE *fp = 0; -#endif - -#ifdef WIN32 - - if (!CryptAcquireContext(&wctx, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) - return SRP_ERR; - if (!CryptGenRandom(wctx, sizeof(g_rand_buff), (BYTE *)g_rand_buff)) return SRP_ERR; - if (!CryptReleaseContext(wctx, 0)) return SRP_ERR; - -#else - fp = fopen("/dev/urandom", "r"); - - if (!fp) return SRP_ERR; - - if (fread(g_rand_buff, sizeof(g_rand_buff), 1, fp) != 1) { fclose(fp); return SRP_ERR; } - if (fclose(fp)) return SRP_ERR; -#endif - return SRP_OK; -} - static SRP_Result mpz_fill_random(mpz_t num) { - // was call: BN_rand(num, 256, -1, 0); - if (RAND_BUFF_MAX - g_rand_idx < 32) - if (fill_buff() != SRP_OK) return SRP_ERR; - mpz_from_bin((const unsigned char *)(&g_rand_buff[g_rand_idx]), 32, num); - g_rand_idx += 32; + unsigned char random_buf[32]; + if (!porting::secure_rand_fill_buf(random_buf, sizeof(random_buf))) + return SRP_ERR; + mpz_from_bin(random_buf, sizeof(random_buf), num); return SRP_OK; } -static SRP_Result init_random() -{ - if (g_initialized) return SRP_OK; - SRP_Result ret = fill_buff(); - g_initialized = (ret == SRP_OK); - return ret; -} - #define srp_dbg_num(num, text) ; /*void srp_dbg_num(mpz_t num, char * prevtext) { int len_num = mpz_num_bytes(num); - char *bytes_num = (char*) srp_alloc(len_num); + char *bytes_num = (char*) malloc(len_num); mpz_to_bin(num, (unsigned char *) bytes_num); srp_dbg_data(bytes_num, len_num, prevtext); - srp_free(bytes_num); + free(bytes_num); }*/ @@ -600,18 +544,13 @@ SRP_Result srp_create_salted_verification_key( SRP_HashAlgorithm alg, if (!ng) goto error_and_exit; - if (init_random() != SRP_OK) /* Only happens once */ - goto error_and_exit; - if (*bytes_s == NULL) { size_t size_to_fill = 16; *len_s = size_to_fill; - if (RAND_BUFF_MAX - g_rand_idx < size_to_fill) - if (fill_buff() != SRP_OK) goto error_and_exit; - *bytes_s = (unsigned char *)srp_alloc(size_to_fill); + *bytes_s = (unsigned char *)malloc(size_to_fill); if (!*bytes_s) goto error_and_exit; - memcpy(*bytes_s, &g_rand_buff[g_rand_idx], size_to_fill); - g_rand_idx += size_to_fill; + if (!porting::secure_rand_fill_buf(*bytes_s, size_to_fill)) + goto error_and_exit; } if (!calculate_x( @@ -624,7 +563,7 @@ SRP_Result srp_create_salted_verification_key( SRP_HashAlgorithm alg, *len_v = mpz_num_bytes(v); - *bytes_v = (unsigned char *)srp_alloc(*len_v); + *bytes_v = (unsigned char *)malloc(*len_v); if (!*bytes_v) goto error_and_exit; @@ -673,22 +612,16 @@ struct SRPVerifier *srp_verifier_new(SRP_HashAlgorithm alg, if (!ng) goto cleanup_and_exit; - ver = (struct SRPVerifier *)srp_alloc(sizeof(struct SRPVerifier)); + ver = (struct SRPVerifier *)malloc(sizeof(struct SRPVerifier)); if (!ver) goto cleanup_and_exit; - if (init_random() != SRP_OK) { /* Only happens once */ - srp_free(ver); - ver = 0; - goto cleanup_and_exit; - } - - ver->username = (char *)srp_alloc(ulen); + ver->username = (char *)malloc(ulen); ver->hash_alg = alg; ver->ng = ng; if (!ver->username) { - srp_free(ver); + free(ver); ver = 0; goto cleanup_and_exit; } @@ -733,7 +666,7 @@ struct SRPVerifier *srp_verifier_new(SRP_HashAlgorithm alg, } *len_B = mpz_num_bytes(B); - *bytes_B = (unsigned char *)srp_alloc(*len_B); + *bytes_B = (unsigned char *)malloc(*len_B); if (!*bytes_B) { *len_B = 0; @@ -744,7 +677,7 @@ struct SRPVerifier *srp_verifier_new(SRP_HashAlgorithm alg, ver->bytes_B = *bytes_B; } else { - srp_free(ver); + free(ver); ver = 0; } @@ -761,8 +694,8 @@ cleanup_and_exit: mpz_clear(tmp3); return ver; ver_cleanup_and_exit: - srp_free(ver->username); - srp_free(ver); + free(ver->username); + free(ver); ver = 0; goto cleanup_and_exit; } @@ -771,10 +704,10 @@ void srp_verifier_delete(struct SRPVerifier *ver) { if (ver) { delete_ng(ver->ng); - srp_free(ver->username); - srp_free(ver->bytes_B); + free(ver->username); + free(ver->bytes_B); memset(ver, 0, sizeof(*ver)); - srp_free(ver); + free(ver); } } @@ -818,15 +751,12 @@ struct SRPUser *srp_user_new(SRP_HashAlgorithm alg, SRP_NGType ng_type, const unsigned char *bytes_password, size_t len_password, const char *n_hex, const char *g_hex) { - struct SRPUser *usr = (struct SRPUser *)srp_alloc(sizeof(struct SRPUser)); + struct SRPUser *usr = (struct SRPUser *)malloc(sizeof(struct SRPUser)); size_t ulen = strlen(username) + 1; size_t uvlen = strlen(username_for_verifier) + 1; if (!usr) goto err_exit; - if (init_random() != SRP_OK) /* Only happens once */ - goto err_exit; - usr->hash_alg = alg; usr->ng = new_ng(ng_type, n_hex, g_hex); @@ -836,9 +766,9 @@ struct SRPUser *srp_user_new(SRP_HashAlgorithm alg, SRP_NGType ng_type, if (!usr->ng) goto err_exit; - usr->username = (char *)srp_alloc(ulen); - usr->username_verifier = (char *)srp_alloc(uvlen); - usr->password = (unsigned char *)srp_alloc(len_password); + usr->username = (char *)malloc(ulen); + usr->username_verifier = (char *)malloc(uvlen); + usr->password = (unsigned char *)malloc(len_password); usr->password_len = len_password; if (!usr->username || !usr->password || !usr->username_verifier) goto err_exit; @@ -859,13 +789,13 @@ err_exit: mpz_clear(usr->A); mpz_clear(usr->S); delete_ng(usr->ng); - srp_free(usr->username); - srp_free(usr->username_verifier); + free(usr->username); + free(usr->username_verifier); if (usr->password) { memset(usr->password, 0, usr->password_len); - srp_free(usr->password); + free(usr->password); } - srp_free(usr); + free(usr); } return 0; @@ -882,14 +812,14 @@ void srp_user_delete(struct SRPUser *usr) memset(usr->password, 0, usr->password_len); - srp_free(usr->username); - srp_free(usr->username_verifier); - srp_free(usr->password); + free(usr->username); + free(usr->username_verifier); + free(usr->password); - if (usr->bytes_A) srp_free(usr->bytes_A); + if (usr->bytes_A) free(usr->bytes_A); memset(usr, 0, sizeof(*usr)); - srp_free(usr); + free(usr); } } @@ -928,7 +858,7 @@ SRP_Result srp_user_start_authentication(struct SRPUser *usr, char **username, mpz_powm(usr->A, usr->ng->g, usr->a, usr->ng->N); *len_A = mpz_num_bytes(usr->A); - *bytes_A = (unsigned char *)srp_alloc(*len_A); + *bytes_A = (unsigned char *)malloc(*len_A); if (!*bytes_A) goto error_and_exit; diff --git a/src/util/srp.h b/src/util/srp.h index ac66dc936..fc4d2dc89 100644 --- a/src/util/srp.h +++ b/src/util/srp.h @@ -79,15 +79,6 @@ typedef enum { SRP_OK, } SRP_Result; -/* Sets the memory functions used by srp. - * Note: this doesn't set the memory functions used by gmp, - * but it is supported to have different functions for srp and gmp. - * Don't call this after you have already allocated srp structures. - */ -void srp_set_memory_functions( - void *(*new_srp_alloc) (size_t), - void *(*new_srp_realloc) (void *, size_t), - void (*new_srp_free) (void *)); /* Out: bytes_v, len_v * diff --git a/src/util/timetaker.cpp b/src/util/timetaker.cpp index a18d813ba..47d8ab83a 100644 --- a/src/util/timetaker.cpp +++ b/src/util/timetaker.cpp @@ -35,7 +35,7 @@ u64 TimeTaker::stop(bool quiet) if (m_result != nullptr) { (*m_result) += dtime; } else { - if (!quiet) { + if (!quiet && !m_name.empty()) { infostream << m_name << " took " << dtime << TimePrecision_units[m_precision] << std::endl; } diff --git a/src/util/tracy_wrapper.h b/src/util/tracy_wrapper.h new file mode 100644 index 000000000..0c61ba837 --- /dev/null +++ b/src/util/tracy_wrapper.h @@ -0,0 +1,200 @@ +/* +Minetest +Copyright (C) 2024 DS + +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. +*/ + +/* + * Wrapper for , so that we can use Tracy's macros without + * having it as mandatory dependency. + * + * For annotations that you don't intend to upstream, you can also include + * directly (which also works in irr/). + */ + +#pragma once + +#include "config.h" +#include "util/basic_macros.h" + +#if BUILD_WITH_TRACY + +#include // IWYU pragma: export + +#else + +// Copied from Tracy.hpp + +#define TracyNoop + +#define ZoneNamed(x,y) +#define ZoneNamedN(x,y,z) +#define ZoneNamedC(x,y,z) +#define ZoneNamedNC(x,y,z,w) + +#define ZoneTransient(x,y) +#define ZoneTransientN(x,y,z) + +#define ZoneScoped +#define ZoneScopedN(x) +#define ZoneScopedC(x) +#define ZoneScopedNC(x,y) + +#define ZoneText(x,y) +#define ZoneTextV(x,y,z) +#define ZoneTextF(x,...) +#define ZoneTextVF(x,y,...) +#define ZoneName(x,y) +#define ZoneNameV(x,y,z) +#define ZoneNameF(x,...) +#define ZoneNameVF(x,y,...) +#define ZoneColor(x) +#define ZoneColorV(x,y) +#define ZoneValue(x) +#define ZoneValueV(x,y) +#define ZoneIsActive false +#define ZoneIsActiveV(x) false + +#define FrameMark +#define FrameMarkNamed(x) +#define FrameMarkStart(x) +#define FrameMarkEnd(x) + +#define FrameImage(x,y,z,w,a) + +#define TracyLockable( type, varname ) type varname +#define TracyLockableN( type, varname, desc ) type varname +#define TracySharedLockable( type, varname ) type varname +#define TracySharedLockableN( type, varname, desc ) type varname +#define LockableBase( type ) type +#define SharedLockableBase( type ) type +#define LockMark(x) (void)x +#define LockableName(x,y,z) + +#define TracyPlot(x,y) +#define TracyPlotConfig(x,y,z,w,a) + +#define TracyMessage(x,y) +#define TracyMessageL(x) +#define TracyMessageC(x,y,z) +#define TracyMessageLC(x,y) +#define TracyAppInfo(x,y) + +#define TracyAlloc(x,y) +#define TracyFree(x) +#define TracySecureAlloc(x,y) +#define TracySecureFree(x) + +#define TracyAllocN(x,y,z) +#define TracyFreeN(x,y) +#define TracySecureAllocN(x,y,z) +#define TracySecureFreeN(x,y) + +#define ZoneNamedS(x,y,z) +#define ZoneNamedNS(x,y,z,w) +#define ZoneNamedCS(x,y,z,w) +#define ZoneNamedNCS(x,y,z,w,a) + +#define ZoneTransientS(x,y,z) +#define ZoneTransientNS(x,y,z,w) + +#define ZoneScopedS(x) +#define ZoneScopedNS(x,y) +#define ZoneScopedCS(x,y) +#define ZoneScopedNCS(x,y,z) + +#define TracyAllocS(x,y,z) +#define TracyFreeS(x,y) +#define TracySecureAllocS(x,y,z) +#define TracySecureFreeS(x,y) + +#define TracyAllocNS(x,y,z,w) +#define TracyFreeNS(x,y,z) +#define TracySecureAllocNS(x,y,z,w) +#define TracySecureFreeNS(x,y,z) + +#define TracyMessageS(x,y,z) +#define TracyMessageLS(x,y) +#define TracyMessageCS(x,y,z,w) +#define TracyMessageLCS(x,y,z) + +#define TracySourceCallbackRegister(x,y) +#define TracyParameterRegister(x,y) +#define TracyParameterSetup(x,y,z,w) +#define TracyIsConnected false +#define TracyIsStarted false +#define TracySetProgramName(x) + +#define TracyFiberEnter(x) +#define TracyFiberEnterHint(x,y) +#define TracyFiberLeave + +#endif + + +// Helper for making sure frames end in all possible control flow path +class FrameMarker +{ + const char *m_name; + bool m_started = false; + +public: + FrameMarker(const char *name) : m_name(name) {} + + ~FrameMarker() { end(); } + + DISABLE_CLASS_COPY(FrameMarker) + + FrameMarker(FrameMarker &&other) noexcept : + m_name(other.m_name), m_started(other.m_started) + { + other.m_started = false; + } + + FrameMarker &operator=(FrameMarker &&other) noexcept + { + if (&other != this) { + end(); + m_name = other.m_name; + m_started = other.m_started; + other.m_started = false; + } + return *this; + } + + FrameMarker &&started() && + { + if (!m_started) { + FrameMarkStart(m_name); + m_started = true; + } + return std::move(*this); + } + + void start() + { + // no move happens, because we drop the reference + (void)std::move(*this).started(); + } + + void end() + { + if (m_started) { + m_started = false; + FrameMarkEnd(m_name); + } + } +}; diff --git a/src/voxel.h b/src/voxel.h index 286e09abe..882a59b77 100644 --- a/src/voxel.h +++ b/src/voxel.h @@ -392,36 +392,36 @@ public: VoxelArea voxel_area(p); addArea(voxel_area); - if (m_flags[m_area.index(p)] & VOXELFLAG_NO_DATA) { - /*dstream<<"EXCEPT: VoxelManipulator::getNode(): " - <<"p=("< Thread Local Storage (TLS) is totally broken -name=llvm-mingw-20231128-ucrt-ubuntu-20.04-x86_64.tar.xz -wget "https://github.com/mstorsjo/llvm-mingw/releases/download/20231128/$name" -O "$name" +date=20240619 +name=llvm-mingw-${date}-ucrt-ubuntu-20.04-x86_64.tar.xz +wget "https://github.com/mstorsjo/llvm-mingw/releases/download/$date/$name" -O "$name" sha256sum -w -c <(grep -F "$name" "$topdir/sha256sums.txt") tar -xaf "$name" -C "$1" --strip-components=1 rm -f "$name" diff --git a/util/buildbot/sha256sums.txt b/util/buildbot/sha256sums.txt index 0587e6ea1..310ab6a6e 100644 --- a/util/buildbot/sha256sums.txt +++ b/util/buildbot/sha256sums.txt @@ -1,7 +1,7 @@ -753dc38c591e078eae6a0a6b25f69826211256f444f3691a170670d8a12988f9 curl-8.5.0-win32.zip -aa86abc3eb054d74d5fe15996f281cf84230a61b4ab7b3a702ab7dbb71e1203f curl-8.5.0-win64.zip -3e9d7bbca953b96dfd65acc28baaa87e8881aab29809ba03b9c9aefe3d071189 freetype-2.13.2-win32.zip -acf901e93aedbcfa92eb3aab1def252676af845b1747ca5c3e7c5866576168cc freetype-2.13.2-win64.zip +627d4111ee655a68e806251974ba9d0337efac19cb07d499689c44c328a23775 curl-8.9.1-win32.zip +ed906726531388441d7f93fc0a1c9d567d476fbc8cfbae19dc5a0f7288949abe curl-8.9.1-win64.zip +7a94b9e69d4872489228ad7cca6a16117a433f809d9b20fa3e44e1616a33c5d7 freetype-2.13.3-win32.zip +f7d882319790f72ebc8eff00526388432bd26bff3a56c4ef5cce0a829bbbef0d freetype-2.13.3-win64.zip 41b10766de2773f0f0851fde16b363024685e0397f4bb2e5cd2a7be196960a01 gettext-0.20.2-win32.zip 1ceed167ff16fea944f76ab6ea2969160c71a67419259b17c9c523e7a01eb883 gettext-0.20.2-win64.zip 53dfd31285f470fcf0dca88217c5cf9c557729af6d103afae5936e72ddc38d3c libjpeg-3.0.1-win32.zip @@ -10,20 +10,20 @@ f54e9a577e2db47ed28f4a01e74181d2c607627c551d30f48263e01b59e84f67 libleveldb-1.2 2f039848a4e6c05a2347fe5a7fa63c430dd08d1bc88235645a863c859e14f5f8 libleveldb-1.23-win64.zip 0df94afb8efa361cceb132ecf9491720afbc45ba844a7b1c94607295829b53ca libogg-1.3.5-win32.zip 5c4acb4c99429a04b5e69650719b2eb17616bf52837d2372a0f859952eebce48 libogg-1.3.5-win64.zip -6baf4e819bfb3573760524b5dc9a04b5e479090d6d2046b86cf39a3107c0071f libpng-1.6.40-win32.zip -c02e029f01fce44baea7f4aecfd2564bd8a03507c0c6af8b03339ae0452c8b7d libpng-1.6.40-win64.zip +fb61536bfce414fdecb30dfbdc8b26e87969ee30b420f5fb8542f7573a1c1d12 libpng-1.6.43-win32.zip +ccd0b8ecbaa07028067a99dd4314ec7799445f80a28ddc86fa3f6bf25700177b libpng-1.6.43-win64.zip 456ece10a2be4247b27fbe88f88ddd54aae604736a6b76ba9a922b602fe40f40 libvorbis-1.3.7-win32.zip 57f4db02da00556895bb63fafa6e46b5f7dac87c25fde27af4315f56a1aa7a86 libvorbis-1.3.7-win64.zip -0f21ff3be90311092fe32e0e30878ef3ae9d9437b8d9ac25ef279e0d84e9bb8e llvm-mingw-20231128-ucrt-ubuntu-20.04-x86_64.tar.xz -da6ad10632cf172992158e9ea0977a87914b5d5de93a972c3430b6a412237556 luajit-20240125-win32.zip -2b1dabe83d478b398cf9226d96de7fa62c973365c4aea70d27ba5782fb49d2d0 luajit-20240125-win64.zip +27d33157cc252c29ad6f777a96a0d94176fea1b534ff09b5071485def143b90e llvm-mingw-20240619-ucrt-ubuntu-20.04-x86_64.tar.xz +5380bbb0bfd4482b5774e4f7c0ff58cc0857477b88a88a178316a464d3459cf1 luajit-20240905-win32.zip +5805c75c61bf948f790e6f845adc94a4946e43ab8a78c5b5b441550f8a665d2c luajit-20240905-win64.zip e2443451fe5c2066eb564c64b8a1762738a88b7fd749c8b5907fed45c785497b openal-soft-1.23.1-win32.zip cb041445a118469caefbad2647470cb8571c8337bce2adc07634011ab5625417 openal-soft-1.23.1-win64.zip -574e0847e622ff09ab23e2b22b77685a2ab6ee43de3e2932f3e8a14a4d7b9338 sdl2-2.30.3-win32.zip -6127afdfc7b6a4ade8caf9a7267748ffea974f729866dd5be96c7a69d2f0fee7 sdl2-2.30.3-win64.zip -326701086a0ed66e09a9f3ec4d971654c13b6bd79cfdd079c947ecdcd6409525 sqlite3-3.44.2-win32.zip -b2d474e3625f8f426b6cc5c0ecac831a1de46f7d1027bf4a9f6267b0b0411d42 sqlite3-3.44.2-win64.zip +af09a54f1f5d75ef6e1bf63662489ca57d44b6b522446638afe35e59b8456a3c sdl2-2.30.7-win32.zip +613abc34a84ed2c3b050314b340ba7e675879e8ed7848e6a28cd9c50262a33b0 sdl2-2.30.7-win64.zip +9685857ae0b418068ad4324e3711121bda97488d19235a0e68a6060162e014d7 sqlite3-3.46.1-win32.zip +7e2990619b1fd1d5ed654d1df77ea809d4332c2e914ea8bba53b2cf5acdf10ff sqlite3-3.46.1-win64.zip 8af10515d57dbfee5d2106cd66cafa2adeb4270d4c6047ccbf7e8b5d2d50681c zlib-1.3.1-win32.zip ad43f5d23052590c65633530743e5d622cc76b33c109072e6fd7b487aff56bca zlib-1.3.1-win64.zip -3564dabbe17ec4ecae1fb9a78fe48d9f7c71e2b1166456f6ee27e52fd9c84357 zstd-1.5.5-win32.zip -e61b1f327ce2d836d1f8ca00c40ac77d3ab5309135851c98229bbdf82b060ae5 zstd-1.5.5-win64.zip +e1bd36f6da039ee8c1694509f379a5023c05d6c90905a2cbb424f0395167570a zstd-1.5.6-win32.zip +f65b75b04b00f6bda859a7c60667f735c664a893bf7796b38393c16cc40a1a82 zstd-1.5.6-win64.zip diff --git a/util/test_multiplayer.sh b/util/test_multiplayer.sh index 624669ac1..b12908423 100755 --- a/util/test_multiplayer.sh +++ b/util/test_multiplayer.sh @@ -33,7 +33,8 @@ printf '%s\n' >"$testspath/client1.conf" \ printf '%s\n' >"$testspath/server.conf" \ max_block_send_distance=1 active_block_range=1 \ - devtest_unittests_autostart=true helper_mode=devtest + devtest_unittests_autostart=true helper_mode=devtest \ + "${serverconf:-}" ln -s "$dir/helper_mod" "$worldpath/worldmods/"