From 02d64a51ee185722ec1b6e2941b461bacf0db0de Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 23 Feb 2021 19:50:44 +0100 Subject: [PATCH 01/16] Continue with 5.5.0-dev --- CMakeLists.txt | 4 ++-- build/android/build.gradle | 4 ++-- doc/client_lua_api.txt | 2 +- doc/menu_lua_api.txt | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index f6a0d22fe..910213c09 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,12 +12,12 @@ set(CLANG_MINIMUM_VERSION "3.4") # Also remember to set PROTOCOL_VERSION in network/networkprotocol.h when releasing set(VERSION_MAJOR 5) -set(VERSION_MINOR 4) +set(VERSION_MINOR 5) set(VERSION_PATCH 0) set(VERSION_EXTRA "" CACHE STRING "Stuff to append to version string") # Change to false for releases -set(DEVELOPMENT_BUILD FALSE) +set(DEVELOPMENT_BUILD TRUE) set(VERSION_STRING "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_PATCH}") if(VERSION_EXTRA) diff --git a/build/android/build.gradle b/build/android/build.gradle index be9eaada4..3ba51a4bb 100644 --- a/build/android/build.gradle +++ b/build/android/build.gradle @@ -1,9 +1,9 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. project.ext.set("versionMajor", 5) // Version Major -project.ext.set("versionMinor", 4) // Version Minor +project.ext.set("versionMinor", 5) // Version Minor project.ext.set("versionPatch", 0) // Version Patch -project.ext.set("versionExtra", "") // Version Extra +project.ext.set("versionExtra", "-dev") // Version Extra project.ext.set("versionCode", 32) // Android Version Code // NOTE: +2 after each release! // +1 for ARM and +1 for ARM64 APK's, because diff --git a/doc/client_lua_api.txt b/doc/client_lua_api.txt index 098596481..c2c552440 100644 --- a/doc/client_lua_api.txt +++ b/doc/client_lua_api.txt @@ -1,4 +1,4 @@ -Minetest Lua Client Modding API Reference 5.4.0 +Minetest Lua Client Modding API Reference 5.5.0 ================================================ * More information at * Developer Wiki: diff --git a/doc/menu_lua_api.txt b/doc/menu_lua_api.txt index b3975bc1d..90ec527b0 100644 --- a/doc/menu_lua_api.txt +++ b/doc/menu_lua_api.txt @@ -1,4 +1,4 @@ -Minetest Lua Mainmenu API Reference 5.4.0 +Minetest Lua Mainmenu API Reference 5.5.0 ========================================= Introduction From 827224635bc131dbf4f6e41dd3d78c7a2d94da0f Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Wed, 24 Feb 2021 10:45:30 +0000 Subject: [PATCH 02/16] Use "Aux1" key name consistently everywhere --- README.md | 2 +- build/android/icons/aux1_btn.svg | 143 ++++++++++ build/android/icons/aux_btn.svg | 411 ----------------------------- builtin/settingtypes.txt | 16 +- src/client/game.cpp | 4 +- src/client/inputhandler.cpp | 4 +- src/client/joystick_controller.cpp | 6 +- src/client/keys.h | 2 +- src/defaultsettings.cpp | 4 +- src/gui/guiKeyChangeMenu.cpp | 6 +- src/gui/touchscreengui.cpp | 18 +- src/gui/touchscreengui.h | 8 +- textures/base/pack/aux1_btn.png | Bin 0 -> 1652 bytes textures/base/pack/aux_btn.png | Bin 1900 -> 0 bytes 14 files changed, 178 insertions(+), 446 deletions(-) create mode 100644 build/android/icons/aux1_btn.svg delete mode 100644 build/android/icons/aux_btn.svg create mode 100644 textures/base/pack/aux1_btn.png delete mode 100644 textures/base/pack/aux_btn.png diff --git a/README.md b/README.md index 58ec0c821..249f24a16 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ Some can be changed in the key config dialog in the settings tab. | P | Enable/disable pitch move mode | | J | Enable/disable fast mode (needs fast privilege) | | H | Enable/disable noclip mode (needs noclip privilege) | -| E | Move fast in fast mode | +| E | Aux1 (Move fast in fast mode. Games may add special features) | | C | Cycle through camera modes | | V | Cycle through minimap modes | | Shift + V | Change minimap orientation | diff --git a/build/android/icons/aux1_btn.svg b/build/android/icons/aux1_btn.svg new file mode 100644 index 000000000..e0ee97c0c --- /dev/null +++ b/build/android/icons/aux1_btn.svg @@ -0,0 +1,143 @@ + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + Aux1 + + + diff --git a/build/android/icons/aux_btn.svg b/build/android/icons/aux_btn.svg deleted file mode 100644 index 6bbefff67..000000000 --- a/build/android/icons/aux_btn.svg +++ /dev/null @@ -1,411 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - image/svg+xml - - - - - - - - - - - - - - - - - - - - AUX - - diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index f800f71ab..62f1ee2d0 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -75,7 +75,7 @@ free_move (Flying) bool false # If enabled, makes move directions relative to the player's pitch when flying or swimming. pitch_move (Pitch move mode) bool false -# Fast movement (via the "special" key). +# Fast movement (via the "Aux1" key). # This requires the "fast" privilege on the server. fast_move (Fast movement) bool false @@ -99,14 +99,14 @@ invert_mouse (Invert mouse) bool false # Mouse sensitivity multiplier. mouse_sensitivity (Mouse sensitivity) float 0.2 -# If enabled, "special" key instead of "sneak" key is used for climbing down and +# If enabled, "Aux1" key instead of "Sneak" key is used for climbing down and # descending. -aux1_descends (Special key for climbing/descending) bool false +aux1_descends (Aux1 key for climbing/descending) bool false # Double-tapping the jump key toggles fly mode. doubletap_jump (Double tap jump for fly) bool false -# If disabled, "special" key is used to fly fast if both fly and fast mode are +# If disabled, "Aux1" key is used to fly fast if both fly and fast mode are # enabled. always_fly_fast (Always fly and fast) bool true @@ -135,9 +135,9 @@ touchscreen_threshold (Touch screen threshold) int 20 0 100 # If disabled, virtual joystick will center to first-touch's position. fixed_virtual_joystick (Fixed virtual joystick) bool false -# (Android) Use virtual joystick to trigger "aux" button. -# If enabled, virtual joystick will also tap "aux" button when out of main circle. -virtual_joystick_triggers_aux (Virtual joystick triggers aux button) bool false +# (Android) Use virtual joystick to trigger "Aux1" button. +# If enabled, virtual joystick will also tap "Aux1" button when out of main circle. +virtual_joystick_triggers_aux1 (Virtual joystick triggers Aux1 button) bool false # Enable joysticks enable_joysticks (Enable joysticks) bool false @@ -199,7 +199,7 @@ keymap_inventory (Inventory key) key KEY_KEY_I # Key for moving fast in fast mode. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 -keymap_special1 (Special key) key KEY_KEY_E +keymap_aux1 (Aux1 key) key KEY_KEY_E # Key for opening the chat window. # See http://irrlicht.sourceforge.net/docu/namespaceirr.html#a54da2a0e231901735e3da1b0edf72eb3 diff --git a/src/client/game.cpp b/src/client/game.cpp index 3c58fb46f..d4e2fe7c3 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -2415,7 +2415,7 @@ void Game::updatePlayerControl(const CameraOrientation &cam) input->isKeyDown(KeyType::LEFT), input->isKeyDown(KeyType::RIGHT), isKeyDown(KeyType::JUMP), - isKeyDown(KeyType::SPECIAL1), + isKeyDown(KeyType::AUX1), isKeyDown(KeyType::SNEAK), isKeyDown(KeyType::ZOOM), isKeyDown(KeyType::DIG), @@ -2432,7 +2432,7 @@ void Game::updatePlayerControl(const CameraOrientation &cam) ( (u32)(isKeyDown(KeyType::LEFT) & 0x1) << 2) | ( (u32)(isKeyDown(KeyType::RIGHT) & 0x1) << 3) | ( (u32)(isKeyDown(KeyType::JUMP) & 0x1) << 4) | - ( (u32)(isKeyDown(KeyType::SPECIAL1) & 0x1) << 5) | + ( (u32)(isKeyDown(KeyType::AUX1) & 0x1) << 5) | ( (u32)(isKeyDown(KeyType::SNEAK) & 0x1) << 6) | ( (u32)(isKeyDown(KeyType::DIG) & 0x1) << 7) | ( (u32)(isKeyDown(KeyType::PLACE) & 0x1) << 8) | diff --git a/src/client/inputhandler.cpp b/src/client/inputhandler.cpp index 978baa320..b7e70fa6c 100644 --- a/src/client/inputhandler.cpp +++ b/src/client/inputhandler.cpp @@ -35,7 +35,7 @@ void KeyCache::populate() key[KeyType::LEFT] = getKeySetting("keymap_left"); key[KeyType::RIGHT] = getKeySetting("keymap_right"); key[KeyType::JUMP] = getKeySetting("keymap_jump"); - key[KeyType::SPECIAL1] = getKeySetting("keymap_special1"); + key[KeyType::AUX1] = getKeySetting("keymap_aux1"); key[KeyType::SNEAK] = getKeySetting("keymap_sneak"); key[KeyType::DIG] = getKeySetting("keymap_dig"); key[KeyType::PLACE] = getKeySetting("keymap_place"); @@ -219,7 +219,7 @@ void RandomInputHandler::step(float dtime) { static RandomInputHandlerSimData rnd_data[] = { { "keymap_jump", 0.0f, 40 }, - { "keymap_special1", 0.0f, 40 }, + { "keymap_aux1", 0.0f, 40 }, { "keymap_forward", 0.0f, 40 }, { "keymap_left", 0.0f, 40 }, { "keymap_dig", 0.0f, 30 }, diff --git a/src/client/joystick_controller.cpp b/src/client/joystick_controller.cpp index f61ae4ae6..919db5315 100644 --- a/src/client/joystick_controller.cpp +++ b/src/client/joystick_controller.cpp @@ -79,7 +79,7 @@ JoystickLayout create_default_layout() // Accessible without any modifier pressed JLO_B_PB(KeyType::JUMP, bm | 1 << 0, 1 << 0); - JLO_B_PB(KeyType::SPECIAL1, bm | 1 << 1, 1 << 1); + JLO_B_PB(KeyType::AUX1, bm | 1 << 1, 1 << 1); // Accessible with start button not pressed, but four pressed // TODO find usage for button 0 @@ -126,11 +126,11 @@ JoystickLayout create_xbox_layout() // 4 Buttons JLO_B_PB(KeyType::JUMP, 1 << 0, 1 << 0); // A/green JLO_B_PB(KeyType::ESC, 1 << 1, 1 << 1); // B/red - JLO_B_PB(KeyType::SPECIAL1, 1 << 2, 1 << 2); // X/blue + JLO_B_PB(KeyType::AUX1, 1 << 2, 1 << 2); // X/blue JLO_B_PB(KeyType::INVENTORY, 1 << 3, 1 << 3); // Y/yellow // Analog Sticks - JLO_B_PB(KeyType::SPECIAL1, 1 << 11, 1 << 11); // left + JLO_B_PB(KeyType::AUX1, 1 << 11, 1 << 11); // left JLO_B_PB(KeyType::SNEAK, 1 << 12, 1 << 12); // right // Triggers diff --git a/src/client/keys.h b/src/client/keys.h index 60a7a3c45..9f90da6b8 100644 --- a/src/client/keys.h +++ b/src/client/keys.h @@ -32,7 +32,7 @@ public: LEFT, RIGHT, JUMP, - SPECIAL1, + AUX1, SNEAK, AUTOFORWARD, DIG, diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index cda953082..9d155f76c 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -80,7 +80,7 @@ void set_default_settings() settings->setDefault("keymap_drop", "KEY_KEY_Q"); settings->setDefault("keymap_zoom", "KEY_KEY_Z"); settings->setDefault("keymap_inventory", "KEY_KEY_I"); - settings->setDefault("keymap_special1", "KEY_KEY_E"); + settings->setDefault("keymap_aux1", "KEY_KEY_E"); settings->setDefault("keymap_chat", "KEY_KEY_T"); settings->setDefault("keymap_cmd", "/"); settings->setDefault("keymap_cmd_local", "."); @@ -464,7 +464,7 @@ void set_default_settings() settings->setDefault("touchtarget", "true"); settings->setDefault("touchscreen_threshold","20"); settings->setDefault("fixed_virtual_joystick", "false"); - settings->setDefault("virtual_joystick_triggers_aux", "false"); + settings->setDefault("virtual_joystick_triggers_aux1", "false"); settings->setDefault("smooth_lighting", "false"); settings->setDefault("max_simultaneous_block_sends_per_client", "10"); settings->setDefault("emergequeue_limit_diskonly", "16"); diff --git a/src/gui/guiKeyChangeMenu.cpp b/src/gui/guiKeyChangeMenu.cpp index 4dcb47779..84678b629 100644 --- a/src/gui/guiKeyChangeMenu.cpp +++ b/src/gui/guiKeyChangeMenu.cpp @@ -46,7 +46,7 @@ enum GUI_ID_KEY_BACKWARD_BUTTON, GUI_ID_KEY_LEFT_BUTTON, GUI_ID_KEY_RIGHT_BUTTON, - GUI_ID_KEY_USE_BUTTON, + GUI_ID_KEY_AUX1_BUTTON, GUI_ID_KEY_FLY_BUTTON, GUI_ID_KEY_FAST_BUTTON, GUI_ID_KEY_JUMP_BUTTON, @@ -177,7 +177,7 @@ void GUIKeyChangeMenu::regenerateGui(v2u32 screensize) { core::rect rect(0, 0, option_w, 30 * s); rect += topleft + v2s32(option_x, option_y); - const wchar_t *text = wgettext("\"Special\" = climb down"); + const wchar_t *text = wgettext("\"Aux1\" = climb down"); Environment->addCheckBox(g_settings->getBool("aux1_descends"), rect, this, GUI_ID_CB_AUX1_DESCENDS, text); delete[] text; @@ -416,7 +416,7 @@ void GUIKeyChangeMenu::init_keys() this->add_key(GUI_ID_KEY_BACKWARD_BUTTON, wgettext("Backward"), "keymap_backward"); this->add_key(GUI_ID_KEY_LEFT_BUTTON, wgettext("Left"), "keymap_left"); this->add_key(GUI_ID_KEY_RIGHT_BUTTON, wgettext("Right"), "keymap_right"); - this->add_key(GUI_ID_KEY_USE_BUTTON, wgettext("Special"), "keymap_special1"); + this->add_key(GUI_ID_KEY_AUX1_BUTTON, wgettext("Aux1"), "keymap_aux1"); this->add_key(GUI_ID_KEY_JUMP_BUTTON, wgettext("Jump"), "keymap_jump"); this->add_key(GUI_ID_KEY_SNEAK_BUTTON, wgettext("Sneak"), "keymap_sneak"); this->add_key(GUI_ID_KEY_DROP_BUTTON, wgettext("Drop"), "keymap_drop"); diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchscreengui.cpp index e1a971462..78b18c2d9 100644 --- a/src/gui/touchscreengui.cpp +++ b/src/gui/touchscreengui.cpp @@ -40,7 +40,7 @@ const char **button_imagenames = (const char *[]) { "jump_btn.png", "down.png", "zoom.png", - "aux_btn.png" + "aux1_btn.png" }; const char **joystick_imagenames = (const char *[]) { @@ -80,8 +80,8 @@ static irr::EKEY_CODE id2keycode(touch_gui_button_id id) case zoom_id: key = "zoom"; break; - case special1_id: - key = "special1"; + case aux1_id: + key = "aux1"; break; case fly_id: key = "freemove"; @@ -425,7 +425,7 @@ TouchScreenGUI::TouchScreenGUI(IrrlichtDevice *device, IEventReceiver *receiver) m_touchscreen_threshold = g_settings->getU16("touchscreen_threshold"); m_fixed_joystick = g_settings->getBool("fixed_virtual_joystick"); - m_joystick_triggers_special1 = g_settings->getBool("virtual_joystick_triggers_aux"); + m_joystick_triggers_aux1 = g_settings->getBool("virtual_joystick_triggers_aux1"); m_screensize = m_device->getVideoDriver()->getScreenSize(); button_size = MYMIN(m_screensize.Y / 4.5f, porting::getDisplayDensity() * @@ -521,9 +521,9 @@ void TouchScreenGUI::init(ISimpleTextureSource *tsrc) m_screensize.Y - (3 * button_size)), L"z", false); - // init special1/aux button - if (!m_joystick_triggers_special1) - initButton(special1_id, + // init aux1 button + if (!m_joystick_triggers_aux1) + initButton(aux1_id, rect(m_screensize.X - (1.25 * button_size), m_screensize.Y - (2.5 * button_size), m_screensize.X - (0.25 * button_size), @@ -923,7 +923,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event) } if (distance > button_size) { - m_joystick_status[j_special1] = true; + m_joystick_status[j_aux1] = true; // move joystick "button" s32 ndx = button_size * dx / distance - button_size / 2.0f; s32 ndy = button_size * dy / distance - button_size / 2.0f; @@ -1039,7 +1039,7 @@ bool TouchScreenGUI::doubleTapDetection() void TouchScreenGUI::applyJoystickStatus() { for (unsigned int i = 0; i < 5; i++) { - if (i == 4 && !m_joystick_triggers_special1) + if (i == 4 && !m_joystick_triggers_aux1) continue; SEvent translated{}; diff --git a/src/gui/touchscreengui.h b/src/gui/touchscreengui.h index 0349624fa..ad5abae87 100644 --- a/src/gui/touchscreengui.h +++ b/src/gui/touchscreengui.h @@ -39,7 +39,7 @@ typedef enum jump_id = 0, crunch_id, zoom_id, - special1_id, + aux1_id, after_last_element_id, settings_starter_id, rare_controls_starter_id, @@ -69,7 +69,7 @@ typedef enum j_backward, j_left, j_right, - j_special1 + j_aux1 } touch_gui_joystick_move_id; typedef enum @@ -217,7 +217,7 @@ private: // forward, backward, left, right touch_gui_button_id m_joystick_names[5] = { - forward_id, backward_id, left_id, right_id, special1_id}; + forward_id, backward_id, left_id, right_id, aux1_id}; bool m_joystick_status[5] = {false, false, false, false, false}; /* @@ -237,7 +237,7 @@ private: int m_joystick_id = -1; bool m_joystick_has_really_moved = false; bool m_fixed_joystick = false; - bool m_joystick_triggers_special1 = false; + bool m_joystick_triggers_aux1 = false; button_info *m_joystick_btn_off = nullptr; button_info *m_joystick_btn_bg = nullptr; button_info *m_joystick_btn_center = nullptr; diff --git a/textures/base/pack/aux1_btn.png b/textures/base/pack/aux1_btn.png new file mode 100644 index 0000000000000000000000000000000000000000..8ceb09542b1ab7d92a6a9fb0f836200e3118ae0b GIT binary patch literal 1652 zcmV-)28;QLP))b+J)ola;L7rzm92o(cOo3RU8X@HrL_lT>4Wuo~}D7^r0izX+QY) z@zC9n<=Www5wMS&3jpsQmd92legF@p-Ei6tkb{Mq3qUX0Ck_B+75T36goY;#A@Aa> z<^#}yKBSv%zoQQS9*yQ~fBLFPvADUTMi+>?uWCVY>d6huC04~x@HCgwR{~d0X z<3TsbKwFIwld-;%AuBiRu~JYsdqir@Q*$@#gc2tq05m8)Y~I7!Q#LS%FgnZsw{7fO zYm?-bg@`V+`aMdz$OLtpLzD_-`I2Y9SC?8ig&RtYF?uQy0NBFI2BB}!GLUD}OsRp- zYm8aHXv;C8P`^hfqKN2DvmnElQDk>j8MQVQddlsCpfN0v2mt=HJnk^qSF#4%xjqsb zi^T~5t>WjDC+nG2`F>$8(Kk%vtzF=LG86UTWxYMMgX#9cdn491pOVlS)A_X zWh}}u{(fRnfPW_^O2d0Gti?)P*{Ww^gZKc*AG$5{1pMy6Y393q>rY|cgaBY!GUQfV z6I(`G==TAo2E3=@27p%aW6G7SUOCcWF6ti?%K9yAn3xX0RXJ;XnqE*Alcs7py`U@8 zFfnVa0IGNb{#wootpDu9gb*f)Q5ke{#BQ!=n_nn(XxlhARsgbnH=g%BfykBg_K5W- z2Vi>{?%Kg^tXSD9-W?GtJbn{10BK~mn0dMS7)7l=K>*_A6qgILzpET!A6uybZe#KI z0icC{n=VNNQR`0(0OlrBzJx}J&ZyGfCUp5^G{!EE9e})q<-#;v(WbK!y$s<*Lz7FF zilS`LTlR=<7*_OrsYvIIWrsWKMfc0HO7#1S{VMD{cpS zPA~w>?j5} z=s|efY+6(1y`Xe>VWQIwPlc}>Js-U;VLckivUYR_0P0x(zR^4Z3*U<>o&fV={;;rL zd;sF-X#F%vY5H4!Rq2s;z(%-^HFE*3t-|yb+46`TDCm@;J8>dTz_s)%v&NskZaP(? zKxq9&i9cAGCol?uC%C&}7t0IAjj%AQSDk^G(Rz4TtUnQLzu2#~yMHYV_ zyO!%0iqRH9iO{-&xD_LAQlx0pH%4o3B@wktXi9Y$6-r#YMr+2n z)~&7OwzO8Rkp^$QXk%}pa`i8`&vVZ2JkR-Xe&^dsvcdfZJ}rA1004lo7<0Q5rTnC@ z;K}txyhfb}KiL#}OIR4cZ1K|s5;68<06>88lYBc5SFk7M`4BWA(!E-gJC?rKDX6`)8r6lF zd_x6oy(Xx)y-kZFM?Lcw?fcTM2^qVw`jv7CxWvYne@=hk;9dN=Pze}OkOFXq7Mi`+ zRvW#-aQw~`4_WHhn@`DNHkRE0#@hFcJm9lHyRVoC@&=uX)hWsJIJ~O}C0#woX3xq=ziWHZHW*|WVt%nod{B`* z5UlEfXU%!Z9grsYePpYH!UIz;8peN<|J07xY!k6NGoi2`O02opYKR2y)d=^$!@7zT z_-?MaVXf2hd?4cvDmSJIjdhgF`R3uP7wVN~l*ki}ryybpZ~*SrcDeVw$J`4oQMbAO zY5yGy1cpg}7pg8o)Y&B_+kQ5lJ4qsxbURIvLOyQhPjeD6CMgMY{$Y^=x~M$K=K3fx zq9+6ym_<0-4xn4qK=L3HdMn!=E%fq$K4k&#N3c#k#Q5WG3H>hK&aT}kY$*Tq&u?k+ z;Q1o6a{FfZwwdutxa=17(-x>=dm@PVnn2 zmNpSRJ*u>N;3~Dbc#U5USKa_+dLCZQ?HHMdnt&T~Zu|RbTurEs)!>{&7ZaxT1=WBuHC(hI%MIt2I5Q3PVXLYX@EGN$!+* zpjoB+s;G~e1kS8@XmdvQNelaCP6lM&e{)p(3S}*wO{#aJZGC}T_5np84y8M8$t>i^ zvi-Po6#2P?1TO-ab%Um>aERW=P&lTmtn%?+5m+I>N1&7b^;Fsdu zuI;tDu1u?#AbcjpmIU1XkvLs(ylvA7-Ua_5M0*~k^ZRk$GAZIu9K1)k(e49HkWjgAEqn+y&@xeZPx1bda?_e6sb;;K(APN# z=W6tt{?dJ-Vyn4`W)Q9`$sUaz`f~*tlq<#7Th3E&V;6Ir zzQ9u}6{&z2LcKNX*KF0_-)5KgY5L_RkP8C(B0a=)XpdMalE}*SFQ$FGOJiG>x^%wX0^UoE?ytd+`zdW2^1Z(<0v| zZU)YXe%gOXyk5nIjjYIgBdQ2gqA`@a(%~0_Je-MBAtXT<3J5oqOdn8>_P^k&_OZb9 WdxbzJNK@O%HUh9{oO!jWOZ-3dkB9jH From 92f4c68c0ce9dfcd6e1321325bab8d4bfcd626af Mon Sep 17 00:00:00 2001 From: HybridDog <3192173+HybridDog@users.noreply.github.com> Date: Wed, 24 Feb 2021 11:46:39 +0100 Subject: [PATCH 03/16] Restructure teleport command code (#9706) --- builtin/game/chat.lua | 174 ++++++++++++++++++++---------------------- 1 file changed, 82 insertions(+), 92 deletions(-) diff --git a/builtin/game/chat.lua b/builtin/game/chat.lua index 945707623..ecd413e25 100644 --- a/builtin/game/chat.lua +++ b/builtin/game/chat.lua @@ -418,121 +418,111 @@ core.register_chatcommand("remove_player", { end, }) + +-- pos may be a non-integer position +local function find_free_position_near(pos) + local tries = { + {x=1, y=0, z=0}, + {x=-1, y=0, z=0}, + {x=0, y=0, z=1}, + {x=0, y=0, z=-1}, + } + for _, d in ipairs(tries) do + local p = vector.add(pos, d) + local n = core.get_node_or_nil(p) + if n then + local def = core.registered_nodes[n.name] + if def and not def.walkable then + return p + end + end + end + return pos +end + +-- Teleports player to

if possible +local function teleport_to_pos(name, p) + local lm = 31000 + if p.x < -lm or p.x > lm or p.y < -lm or p.y > lm + or p.z < -lm or p.z > lm then + return false, "Cannot teleport out of map bounds!" + end + local teleportee = core.get_player_by_name(name) + if not teleportee then + return false, "Cannot get player with name " .. name + end + if teleportee:get_attach() then + return false, "Cannot teleport, " .. name .. + " is attached to an object!" + end + teleportee:set_pos(p) + return true, "Teleporting " .. name .. " to " .. core.pos_to_string(p, 1) +end + +-- Teleports player next to player if possible +local function teleport_to_player(name, target_name) + if name == target_name then + return false, "One does not teleport to oneself." + end + local teleportee = core.get_player_by_name(name) + if not teleportee then + return false, "Cannot get teleportee with name " .. name + end + if teleportee:get_attach() then + return false, "Cannot teleport, " .. name .. + " is attached to an object!" + end + local target = core.get_player_by_name(target_name) + if not target then + return false, "Cannot get target player with name " .. target_name + end + local p = find_free_position_near(target:get_pos()) + teleportee:set_pos(p) + return true, "Teleporting " .. name .. " to " .. target_name .. " at " .. + core.pos_to_string(p, 1) +end + core.register_chatcommand("teleport", { - params = ",, | | ( ,,) | ( )", + params = ",, | | ,, | ", description = "Teleport to position or player", privs = {teleport=true}, func = function(name, param) - -- Returns (pos, true) if found, otherwise (pos, false) - local function find_free_position_near(pos) - local tries = { - {x=1,y=0,z=0}, - {x=-1,y=0,z=0}, - {x=0,y=0,z=1}, - {x=0,y=0,z=-1}, - } - for _, d in ipairs(tries) do - local p = {x = pos.x+d.x, y = pos.y+d.y, z = pos.z+d.z} - local n = core.get_node_or_nil(p) - if n and n.name then - local def = core.registered_nodes[n.name] - if def and not def.walkable then - return p, true - end - end - end - return pos, false - end - local p = {} - p.x, p.y, p.z = string.match(param, "^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$") - p.x = tonumber(p.x) - p.y = tonumber(p.y) - p.z = tonumber(p.z) + p.x, p.y, p.z = param:match("^([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$") + p = vector.apply(p, tonumber) if p.x and p.y and p.z then - - local lm = 31000 - if p.x < -lm or p.x > lm or p.y < -lm or p.y > lm or p.z < -lm or p.z > lm then - return false, "Cannot teleport out of map bounds!" - end - local teleportee = core.get_player_by_name(name) - if teleportee then - if teleportee:get_attach() then - return false, "Can't teleport, you're attached to an object!" - end - teleportee:set_pos(p) - return true, "Teleporting to "..core.pos_to_string(p) - end + return teleport_to_pos(name, p) end local target_name = param:match("^([^ ]+)$") - local teleportee = core.get_player_by_name(name) - - p = nil if target_name then - local target = core.get_player_by_name(target_name) - if target then - p = target:get_pos() - end + return teleport_to_player(name, target_name) end - if teleportee and p then - if teleportee:get_attach() then - return false, "Can't teleport, you're attached to an object!" - end - p = find_free_position_near(p) - teleportee:set_pos(p) - return true, "Teleporting to " .. target_name - .. " at "..core.pos_to_string(p) - end + local has_bring_priv = core.check_player_privs(name, {bring=true}) + local missing_bring_msg = "You don't have permission to teleport " .. + "other players (missing bring privilege)" - if not core.check_player_privs(name, {bring=true}) then - return false, "You don't have permission to teleport other players (missing bring privilege)" - end - - teleportee = nil - p = {} local teleportee_name teleportee_name, p.x, p.y, p.z = param:match( "^([^ ]+) +([%d.-]+)[, ] *([%d.-]+)[, ] *([%d.-]+)$") - p.x, p.y, p.z = tonumber(p.x), tonumber(p.y), tonumber(p.z) - if teleportee_name then - teleportee = core.get_player_by_name(teleportee_name) - end - if teleportee and p.x and p.y and p.z then - if teleportee:get_attach() then - return false, "Can't teleport, player is attached to an object!" + p = vector.apply(p, tonumber) + if teleportee_name and p.x and p.y and p.z then + if not has_bring_priv then + return false, missing_bring_msg end - teleportee:set_pos(p) - return true, "Teleporting " .. teleportee_name - .. " to " .. core.pos_to_string(p) + return teleport_to_pos(teleportee_name, p) end - teleportee = nil - p = nil teleportee_name, target_name = string.match(param, "^([^ ]+) +([^ ]+)$") - if teleportee_name then - teleportee = core.get_player_by_name(teleportee_name) - end - if target_name then - local target = core.get_player_by_name(target_name) - if target then - p = target:get_pos() + if teleportee_name and target_name then + if not has_bring_priv then + return false, missing_bring_msg end - end - if teleportee and p then - if teleportee:get_attach() then - return false, "Can't teleport, player is attached to an object!" - end - p = find_free_position_near(p) - teleportee:set_pos(p) - return true, "Teleporting " .. teleportee_name - .. " to " .. target_name - .. " at " .. core.pos_to_string(p) + return teleport_to_player(teleportee_name, target_name) end - return false, 'Invalid parameters ("' .. param - .. '") or player not found (see /help teleport)' + return false end, }) From 9f6167fc3bebb337ac065b638ba7222374c769d8 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Wed, 24 Feb 2021 10:47:50 +0000 Subject: [PATCH 04/16] Deprecate not providing mod.conf --- src/content/mods.cpp | 55 +++++++++++++++++++++++++++++--------------- 1 file changed, 37 insertions(+), 18 deletions(-) diff --git a/src/content/mods.cpp b/src/content/mods.cpp index 95ab0290a..434004b29 100644 --- a/src/content/mods.cpp +++ b/src/content/mods.cpp @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "settings.h" #include "porting.h" #include "convert_json.h" +#include "script/common/c_internal.h" bool parseDependsString(std::string &dep, std::unordered_set &symbols) { @@ -44,20 +45,24 @@ bool parseDependsString(std::string &dep, std::unordered_set &symbols) return !dep.empty(); } +static void log_mod_deprecation(const ModSpec &spec, const std::string &warning) +{ + auto handling_mode = get_deprecated_handling_mode(); + if (handling_mode != DeprecatedHandlingMode::Ignore) { + std::ostringstream os; + os << warning << " (" << spec.name << " at " << spec.path << ")" << std::endl; + + if (handling_mode == DeprecatedHandlingMode::Error) { + throw ModError(os.str()); + } else { + warningstream << os.str(); + } + } +} + void parseModContents(ModSpec &spec) { // NOTE: this function works in mutual recursion with getModsInPath - Settings info; - info.readConfigFile((spec.path + DIR_DELIM + "mod.conf").c_str()); - - if (info.exists("name")) - spec.name = info.get("name"); - - if (info.exists("author")) - spec.author = info.get("author"); - - if (info.exists("release")) - spec.release = info.getS32("release"); spec.depends.clear(); spec.optdepends.clear(); @@ -78,6 +83,20 @@ void parseModContents(ModSpec &spec) spec.modpack_content = getModsInPath(spec.path, true); } else { + Settings info; + info.readConfigFile((spec.path + DIR_DELIM + "mod.conf").c_str()); + + if (info.exists("name")) + spec.name = info.get("name"); + else + log_mod_deprecation(spec, "Mods not having a mod.conf file with the name is deprecated."); + + if (info.exists("author")) + spec.author = info.get("author"); + + if (info.exists("release")) + spec.release = info.getS32("release"); + // Attempt to load dependencies from mod.conf bool mod_conf_has_depends = false; if (info.exists("depends")) { @@ -109,6 +128,10 @@ void parseModContents(ModSpec &spec) std::vector dependencies; std::ifstream is((spec.path + DIR_DELIM + "depends.txt").c_str()); + + if (is.good()) + log_mod_deprecation(spec, "depends.txt is deprecated, please use mod.conf instead."); + while (is.good()) { std::string dep; std::getline(is, dep); @@ -127,14 +150,10 @@ void parseModContents(ModSpec &spec) } } - if (info.exists("description")) { + if (info.exists("description")) spec.desc = info.get("description"); - } else { - std::ifstream is((spec.path + DIR_DELIM + "description.txt") - .c_str()); - spec.desc = std::string((std::istreambuf_iterator(is)), - std::istreambuf_iterator()); - } + else if (fs::ReadFile(spec.path + DIR_DELIM + "description.txt", spec.desc)) + log_mod_deprecation(spec, "description.txt is deprecated, please use mod.conf instead."); } } From d51d0d77c4e88dec8f9670942e19595cbb3a0234 Mon Sep 17 00:00:00 2001 From: Yaman Qalieh Date: Wed, 24 Feb 2021 05:50:19 -0500 Subject: [PATCH 05/16] Allow toggling of texture pack by double clicking --- builtin/mainmenu/tab_content.lua | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/builtin/mainmenu/tab_content.lua b/builtin/mainmenu/tab_content.lua index 336730bf4..fb7f121f8 100644 --- a/builtin/mainmenu/tab_content.lua +++ b/builtin/mainmenu/tab_content.lua @@ -145,11 +145,26 @@ local function get_formspec(tabview, name, tabdata) return retval end +-------------------------------------------------------------------------------- +local function handle_doubleclick(pkg) + if pkg.type == "txp" then + if core.settings:get("texture_path") == pkg.path then + core.settings:set("texture_path", "") + else + core.settings:set("texture_path", pkg.path) + end + packages = nil + end +end + -------------------------------------------------------------------------------- local function handle_buttons(tabview, fields, tabname, tabdata) if fields["pkglist"] ~= nil then local event = core.explode_table_event(fields["pkglist"]) tabdata.selected_pkg = event.row + if event.type == "DCL" then + handle_doubleclick(packages:get_list()[tabdata.selected_pkg]) + end return true end From b5eda416cea3157ae3590fb7d229cd2cd17c3bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Wed, 24 Feb 2021 12:05:17 +0100 Subject: [PATCH 06/16] Slap u64 on everything time-y (#10984) --- doc/lua_api.txt | 1 - src/porting.h | 6 +++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/doc/lua_api.txt b/doc/lua_api.txt index d3165b9fd..c09578a15 100644 --- a/doc/lua_api.txt +++ b/doc/lua_api.txt @@ -3268,7 +3268,6 @@ Helper functions * returns true when the passed number represents NaN. * `minetest.get_us_time()` * returns time with microsecond precision. May not return wall time. - * This value might overflow on certain 32-bit systems! * `table.copy(table)`: returns a table * returns a deep copy of `table` * `table.indexof(list, val)`: returns the smallest numerical index containing diff --git a/src/porting.h b/src/porting.h index e4ebe36fd..93932e1d9 100644 --- a/src/porting.h +++ b/src/porting.h @@ -234,21 +234,21 @@ inline u64 getTimeMs() { struct timespec ts; os_get_clock(&ts); - return ts.tv_sec * 1000 + ts.tv_nsec / 1000000; + return ((u64) ts.tv_sec) * 1000LL + ((u64) ts.tv_nsec) / 1000000LL; } inline u64 getTimeUs() { struct timespec ts; os_get_clock(&ts); - return ts.tv_sec * 1000000 + ts.tv_nsec / 1000; + return ((u64) ts.tv_sec) * 1000000LL + ((u64) ts.tv_nsec) / 1000LL; } inline u64 getTimeNs() { struct timespec ts; os_get_clock(&ts); - return ts.tv_sec * 1000000000 + ts.tv_nsec; + return ((u64) ts.tv_sec) * 1000000000LL + ((u64) ts.tv_nsec); } #endif From 3edb1ddb8127aa7e8de867be50c695271091cb94 Mon Sep 17 00:00:00 2001 From: savilli <78875209+savilli@users.noreply.github.com> Date: Fri, 26 Feb 2021 23:21:20 +0300 Subject: [PATCH 07/16] Fix hud_change and hud_remove after hud_add (#10997) --- src/client/client.h | 8 ------- src/client/game.cpp | 30 ++++++++++++++++------- src/network/clientpackethandler.cpp | 37 +++++++++++------------------ 3 files changed, 35 insertions(+), 40 deletions(-) diff --git a/src/client/client.h b/src/client/client.h index 25a1b97ba..2dba1506e 100644 --- a/src/client/client.h +++ b/src/client/client.h @@ -415,11 +415,6 @@ public: return m_csm_restriction_flags & flag; } - inline std::unordered_map &getHUDTranslationMap() - { - return m_hud_server_to_client; - } - bool joinModChannel(const std::string &channel) override; bool leaveModChannel(const std::string &channel) override; bool sendModChannelMessage(const std::string &channel, @@ -556,9 +551,6 @@ private: // Relation of client id to object id std::unordered_map m_sounds_to_objects; - // Map server hud ids to client hud ids - std::unordered_map m_hud_server_to_client; - // Privileges std::unordered_set m_privileges; diff --git a/src/client/game.cpp b/src/client/game.cpp index d4e2fe7c3..15fa2af23 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -856,6 +856,9 @@ private: Hud *hud = nullptr; Minimap *mapper = nullptr; + // Map server hud ids to client hud ids + std::unordered_map m_hud_server_to_client; + GameRunData runData; Flags m_flags; @@ -2602,12 +2605,11 @@ void Game::handleClientEvent_HandleParticleEvent(ClientEvent *event, void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam) { LocalPlayer *player = client->getEnv().getLocalPlayer(); - auto &hud_server_to_client = client->getHUDTranslationMap(); u32 server_id = event->hudadd.server_id; // ignore if we already have a HUD with that ID - auto i = hud_server_to_client.find(server_id); - if (i != hud_server_to_client.end()) { + auto i = m_hud_server_to_client.find(server_id); + if (i != m_hud_server_to_client.end()) { delete event->hudadd.pos; delete event->hudadd.name; delete event->hudadd.scale; @@ -2635,7 +2637,7 @@ void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam) e->size = *event->hudadd.size; e->z_index = event->hudadd.z_index; e->text2 = *event->hudadd.text2; - hud_server_to_client[server_id] = player->addHud(e); + m_hud_server_to_client[server_id] = player->addHud(e); delete event->hudadd.pos; delete event->hudadd.name; @@ -2651,18 +2653,28 @@ void Game::handleClientEvent_HudAdd(ClientEvent *event, CameraOrientation *cam) void Game::handleClientEvent_HudRemove(ClientEvent *event, CameraOrientation *cam) { LocalPlayer *player = client->getEnv().getLocalPlayer(); - HudElement *e = player->removeHud(event->hudrm.id); - delete e; + + auto i = m_hud_server_to_client.find(event->hudrm.id); + if (i != m_hud_server_to_client.end()) { + HudElement *e = player->removeHud(i->second); + delete e; + m_hud_server_to_client.erase(i); + } + } void Game::handleClientEvent_HudChange(ClientEvent *event, CameraOrientation *cam) { LocalPlayer *player = client->getEnv().getLocalPlayer(); - u32 id = event->hudchange.id; - HudElement *e = player->getHud(id); + HudElement *e = nullptr; - if (e == NULL) { + auto i = m_hud_server_to_client.find(event->hudchange.id); + if (i != m_hud_server_to_client.end()) { + e = player->getHud(i->second); + } + + if (e == nullptr) { delete event->hudchange.v3fdata; delete event->hudchange.v2fdata; delete event->hudchange.sdata; diff --git a/src/network/clientpackethandler.cpp b/src/network/clientpackethandler.cpp index 65db02300..44bd81dac 100644 --- a/src/network/clientpackethandler.cpp +++ b/src/network/clientpackethandler.cpp @@ -1095,16 +1095,10 @@ void Client::handleCommand_HudRemove(NetworkPacket* pkt) *pkt >> server_id; - auto i = m_hud_server_to_client.find(server_id); - if (i != m_hud_server_to_client.end()) { - int client_id = i->second; - m_hud_server_to_client.erase(i); - - ClientEvent *event = new ClientEvent(); - event->type = CE_HUDRM; - event->hudrm.id = client_id; - m_client_event_queue.push(event); - } + ClientEvent *event = new ClientEvent(); + event->type = CE_HUDRM; + event->hudrm.id = server_id; + m_client_event_queue.push(event); } void Client::handleCommand_HudChange(NetworkPacket* pkt) @@ -1131,19 +1125,16 @@ void Client::handleCommand_HudChange(NetworkPacket* pkt) else *pkt >> intdata; - std::unordered_map::const_iterator i = m_hud_server_to_client.find(server_id); - if (i != m_hud_server_to_client.end()) { - ClientEvent *event = new ClientEvent(); - event->type = CE_HUDCHANGE; - event->hudchange.id = i->second; - event->hudchange.stat = (HudElementStat)stat; - event->hudchange.v2fdata = new v2f(v2fdata); - event->hudchange.v3fdata = new v3f(v3fdata); - event->hudchange.sdata = new std::string(sdata); - event->hudchange.data = intdata; - event->hudchange.v2s32data = new v2s32(v2s32data); - m_client_event_queue.push(event); - } + ClientEvent *event = new ClientEvent(); + event->type = CE_HUDCHANGE; + event->hudchange.id = server_id; + event->hudchange.stat = (HudElementStat)stat; + event->hudchange.v2fdata = new v2f(v2fdata); + event->hudchange.v3fdata = new v3f(v3fdata); + event->hudchange.sdata = new std::string(sdata); + event->hudchange.data = intdata; + event->hudchange.v2s32data = new v2s32(v2s32data); + m_client_event_queue.push(event); } void Client::handleCommand_HudSetFlags(NetworkPacket* pkt) From 225e69063fa0c3dc96f960aa79dfc568fe3d89a8 Mon Sep 17 00:00:00 2001 From: hecks <42101236+hecktest@users.noreply.github.com> Date: Fri, 26 Feb 2021 21:23:46 +0100 Subject: [PATCH 08/16] Keep mapblocks in memory if they're in range (#10714) Some other minor parts of clientmap.cpp have been cleaned up along the way --- src/client/clientmap.cpp | 45 +++++++++++++++++++++------------------- src/util/numeric.cpp | 8 ++----- src/util/numeric.h | 4 ++++ 3 files changed, 30 insertions(+), 27 deletions(-) diff --git a/src/client/clientmap.cpp b/src/client/clientmap.cpp index b9e0cc2ce..be8343009 100644 --- a/src/client/clientmap.cpp +++ b/src/client/clientmap.cpp @@ -165,6 +165,9 @@ void ClientMap::updateDrawList() v3s16 p_blocks_max; getBlocksInViewRange(cam_pos_nodes, &p_blocks_min, &p_blocks_max); + // Read the vision range, unless unlimited range is enabled. + float range = m_control.range_all ? 1e7 : m_control.wanted_range; + // Number of blocks currently loaded by the client u32 blocks_loaded = 0; // Number of blocks with mesh in rendering range @@ -182,6 +185,7 @@ void ClientMap::updateDrawList() occlusion_culling_enabled = false; } + // Uncomment to debug occluded blocks in the wireframe mode // TODO: Include this as a flag for an extended debugging setting //if (occlusion_culling_enabled && m_control.show_wireframe) @@ -218,32 +222,34 @@ void ClientMap::updateDrawList() continue; } - float range = 100000 * BS; - if (!m_control.range_all) - range = m_control.wanted_range * BS; + v3s16 block_coord = block->getPos(); + v3s16 block_position = block->getPosRelative() + MAP_BLOCKSIZE / 2; - float d = 0.0; - if (!isBlockInSight(block->getPos(), camera_position, - camera_direction, camera_fov, range, &d)) - continue; + // First, perform a simple distance check, with a padding of one extra block. + if (!m_control.range_all && + block_position.getDistanceFrom(cam_pos_nodes) > range + MAP_BLOCKSIZE) + continue; // Out of range, skip. + // Keep the block alive as long as it is in range. + block->resetUsageTimer(); blocks_in_range_with_mesh++; - /* - Occlusion culling - */ + // Frustum culling + float d = 0.0; + if (!isBlockInSight(block_coord, camera_position, + camera_direction, camera_fov, range * BS, &d)) + continue; + + // Occlusion culling if ((!m_control.range_all && d > m_control.wanted_range * BS) || (occlusion_culling_enabled && isBlockOccluded(block, cam_pos_nodes))) { blocks_occlusion_culled++; continue; } - // This block is in range. Reset usage timer. - block->resetUsageTimer(); - // Add to set block->refGrab(); - m_drawlist[block->getPos()] = block; + m_drawlist[block_coord] = block; sector_blocks_drawn++; } // foreach sectorblocks @@ -282,8 +288,6 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) const u32 daynight_ratio = m_client->getEnv().getDayNightRatio(); const v3f camera_position = m_camera_position; - const v3f camera_direction = m_camera_direction; - const f32 camera_fov = m_camera_fov; /* Get all blocks and draw all visible ones @@ -310,11 +314,10 @@ void ClientMap::renderMap(video::IVideoDriver* driver, s32 pass) if (!block->mesh) continue; - float d = 0.0; - if (!isBlockInSight(block->getPos(), camera_position, - camera_direction, camera_fov, 100000 * BS, &d)) - continue; - + v3f block_pos_r = intToFloat(block->getPosRelative() + MAP_BLOCKSIZE / 2, BS); + float d = camera_position.getDistanceFrom(block_pos_r); + d = MYMAX(0,d - BLOCK_MAX_RADIUS); + // Mesh animation if (pass == scene::ESNRP_SOLID) { //MutexAutoLock lock(block->mesh_mutex); diff --git a/src/util/numeric.cpp b/src/util/numeric.cpp index 1af3f66be..99e4cfb5c 100644 --- a/src/util/numeric.cpp +++ b/src/util/numeric.cpp @@ -106,10 +106,6 @@ u64 murmur_hash_64_ua(const void *key, int len, unsigned int seed) bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, f32 camera_fov, f32 range, f32 *distance_ptr) { - // Maximum radius of a block. The magic number is - // sqrt(3.0) / 2.0 in literal form. - static constexpr const f32 block_max_radius = 0.866025403784f * MAP_BLOCKSIZE * BS; - v3s16 blockpos_nodes = blockpos_b * MAP_BLOCKSIZE; // Block center position @@ -123,7 +119,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, v3f blockpos_relative = blockpos - camera_pos; // Total distance - f32 d = MYMAX(0, blockpos_relative.getLength() - block_max_radius); + f32 d = MYMAX(0, blockpos_relative.getLength() - BLOCK_MAX_RADIUS); if (distance_ptr) *distance_ptr = d; @@ -141,7 +137,7 @@ bool isBlockInSight(v3s16 blockpos_b, v3f camera_pos, v3f camera_dir, // such that a block that has any portion visible with the // current camera position will have the center visible at the // adjusted postion - f32 adjdist = block_max_radius / cos((M_PI - camera_fov) / 2); + f32 adjdist = BLOCK_MAX_RADIUS / cos((M_PI - camera_fov) / 2); // Block position relative to adjusted camera v3f blockpos_adj = blockpos - (camera_pos - camera_dir * adjdist); diff --git a/src/util/numeric.h b/src/util/numeric.h index 864ab7543..32a6f4312 100644 --- a/src/util/numeric.h +++ b/src/util/numeric.h @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #pragma once #include "basic_macros.h" +#include "constants.h" #include "irrlichttypes.h" #include "irr_v2d.h" #include "irr_v3d.h" @@ -36,6 +37,9 @@ with this program; if not, write to the Free Software Foundation, Inc., y = temp; \ } while (0) +// Maximum radius of a block. The magic number is +// sqrt(3.0) / 2.0 in literal form. +static constexpr const f32 BLOCK_MAX_RADIUS = 0.866025403784f * MAP_BLOCKSIZE * BS; inline s16 getContainerPos(s16 p, s16 d) { From b390bd2ea5c40cb96af1699a6a18f59dcdb1495b Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sun, 28 Feb 2021 17:10:40 +0000 Subject: [PATCH 09/16] pkgmgr: Fix crash when .conf release field is invalid Fixes #10942 --- builtin/mainmenu/pkgmgr.lua | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/builtin/mainmenu/pkgmgr.lua b/builtin/mainmenu/pkgmgr.lua index 19127d8d3..4aa05d838 100644 --- a/builtin/mainmenu/pkgmgr.lua +++ b/builtin/mainmenu/pkgmgr.lua @@ -90,7 +90,7 @@ local function load_texture_packs(txtpath, retval) retval[#retval + 1] = { name = item, author = conf:get("author"), - release = tonumber(conf:get("release") or "0"), + release = tonumber(conf:get("release")) or 0, list_name = name, type = "txp", path = path, @@ -135,7 +135,7 @@ function get_mods(path,retval,modpack) -- Read from config toadd.name = name toadd.author = mod_conf.author - toadd.release = tonumber(mod_conf.release or "0") + toadd.release = tonumber(mod_conf.release) or 0 toadd.path = prefix toadd.type = "mod" From ccdaf5de54108990fcdeb0b0ff4a4fc4cd998522 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sat, 23 Jan 2021 23:48:22 +0000 Subject: [PATCH 10/16] Disable clang-format, clean up scripts --- .github/workflows/cpp_lint.yml | 27 ++++++++++++++------------- util/ci/{lint.sh => clang-format.sh} | 25 +++++++++++++++++++++++-- util/fix_format.sh | 5 +++++ 3 files changed, 42 insertions(+), 15 deletions(-) rename util/ci/{lint.sh => clang-format.sh} (71%) create mode 100755 util/fix_format.sh diff --git a/.github/workflows/cpp_lint.yml b/.github/workflows/cpp_lint.yml index 1f97d105a..2bd884c7a 100644 --- a/.github/workflows/cpp_lint.yml +++ b/.github/workflows/cpp_lint.yml @@ -24,20 +24,21 @@ on: - '.github/workflows/**.yml' jobs: - clang_format: - runs-on: ubuntu-18.04 - steps: - - uses: actions/checkout@v2 - - name: Install clang-format - run: | - sudo apt-get install clang-format-9 -qyy - - name: Run clang-format - run: | - source ./util/ci/lint.sh - perform_lint - env: - CLANG_FORMAT: clang-format-9 +# clang_format: +# runs-on: ubuntu-18.04 +# steps: +# - uses: actions/checkout@v2 +# - name: Install clang-format +# run: | +# sudo apt-get install clang-format-9 -qyy +# +# - name: Run clang-format +# run: | +# source ./util/ci/clang-format.sh +# check_format +# env: +# CLANG_FORMAT: clang-format-9 clang_tidy: runs-on: ubuntu-18.04 diff --git a/util/ci/lint.sh b/util/ci/clang-format.sh similarity index 71% rename from util/ci/lint.sh rename to util/ci/clang-format.sh index 395445ca7..89576c656 100755 --- a/util/ci/lint.sh +++ b/util/ci/clang-format.sh @@ -1,6 +1,6 @@ #! /bin/bash -function perform_lint() { - echo "Performing LINT..." + +function setup_for_format() { if [ -z "${CLANG_FORMAT}" ]; then CLANG_FORMAT=clang-format fi @@ -8,6 +8,12 @@ function perform_lint() { CLANG_FORMAT_WHITELIST="util/ci/clang-format-whitelist.txt" files_to_lint="$(find src/ -name '*.cpp' -or -name '*.h')" +} + +function check_format() { + echo "Checking format..." + + setup_for_format local errorcount=0 local fail=0 @@ -41,3 +47,18 @@ function perform_lint() { echo "LINT OK" } + + +function fix_format() { + echo "Fixing format..." + + setup_for_format + + for f in ${files_to_lint}; do + whitelisted=$(awk '$1 == "'$f'" { print 1 }' "$CLANG_FORMAT_WHITELIST") + if [ -z "${whitelisted}" ]; then + echo "$f" + $CLANG_FORMAT -i "$f" + fi + done +} diff --git a/util/fix_format.sh b/util/fix_format.sh new file mode 100755 index 000000000..3cef6f58d --- /dev/null +++ b/util/fix_format.sh @@ -0,0 +1,5 @@ +#!/bin/bash -e + +. ./util/ci/clang-format.sh + +fix_format From c401a06f8a669d1fd4194010ccf23d7043d70fb1 Mon Sep 17 00:00:00 2001 From: Elias Fleckenstein <54945686+EliasFleckenstein03@users.noreply.github.com> Date: Mon, 1 Mar 2021 12:13:47 +0100 Subject: [PATCH 11/16] Make pkgmgr handle modpacks containing modpacks properly fixes #10550 --- builtin/mainmenu/pkgmgr.lua | 39 ++++++++++++++++--------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/builtin/mainmenu/pkgmgr.lua b/builtin/mainmenu/pkgmgr.lua index 4aa05d838..787936e31 100644 --- a/builtin/mainmenu/pkgmgr.lua +++ b/builtin/mainmenu/pkgmgr.lua @@ -413,18 +413,7 @@ function pkgmgr.is_modpack_entirely_enabled(data, name) end ---------- toggles or en/disables a mod or modpack and its dependencies -------- -function pkgmgr.enable_mod(this, toset) - local list = this.data.list:get_list() - local mod = list[this.data.selected_mod] - - -- Game mods can't be enabled or disabled - if mod.is_game_content then - return - end - - local toggled_mods = {} - - local enabled_mods = {} +local function toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, mod) if not mod.is_modpack then -- Toggle or en/disable the mod if toset == nil then @@ -443,19 +432,25 @@ function pkgmgr.enable_mod(this, toset) -- interleaved unsupported for i = 1, #list do if list[i].modpack == mod.name then - if toset == nil then - toset = not list[i].enabled - end - if list[i].enabled ~= toset then - list[i].enabled = toset - toggled_mods[#toggled_mods+1] = list[i].name - end - if toset then - enabled_mods[list[i].name] = true - end + toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, list[i]) end end end +end + +function pkgmgr.enable_mod(this, toset) + local list = this.data.list:get_list() + local mod = list[this.data.selected_mod] + + -- Game mods can't be enabled or disabled + if mod.is_game_content then + return + end + + local toggled_mods = {} + local enabled_mods = {} + toggle_mod_or_modpack(list, toggled_mods, enabled_mods, toset, mod) + if not toset then -- Mod(s) were disabled, so no dependencies need to be enabled table.sort(toggled_mods) From 3a2f55bc19bec1cb3f76d0edc30208a1eff11925 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Wed, 20 Jan 2021 16:58:59 +0100 Subject: [PATCH 12/16] Settings: Push groups in to_table as well --- src/script/lua_api/l_settings.cpp | 33 +++++++++++++++++++++++-------- src/settings.h | 2 ++ 2 files changed, 27 insertions(+), 8 deletions(-) diff --git a/src/script/lua_api/l_settings.cpp b/src/script/lua_api/l_settings.cpp index bcbaf15fa..85df8ab34 100644 --- a/src/script/lua_api/l_settings.cpp +++ b/src/script/lua_api/l_settings.cpp @@ -20,6 +20,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "lua_api/l_settings.h" #include "lua_api/l_internal.h" #include "cpp_api/s_security.h" +#include "threading/mutex_auto_lock.h" #include "util/string.h" // FlagDesc #include "settings.h" #include "noise.h" @@ -253,20 +254,36 @@ int LuaSettings::l_write(lua_State* L) return 1; } +static void push_settings_table(lua_State *L, const Settings *settings) +{ + std::vector keys = settings->getNames(); + lua_newtable(L); + for (const std::string &key : keys) { + std::string value; + Settings *group = nullptr; + + if (settings->getNoEx(key, value)) { + lua_pushstring(L, value.c_str()); + } else if (settings->getGroupNoEx(key, group)) { + // Recursively push tables + push_settings_table(L, group); + } else { + // Impossible case (multithreading) due to MutexAutoLock + continue; + } + + lua_setfield(L, -2, key.c_str()); + } +} + // to_table(self) -> {[key1]=value1,...} int LuaSettings::l_to_table(lua_State* L) { NO_MAP_LOCK_REQUIRED; LuaSettings* o = checkobject(L, 1); - std::vector keys = o->m_settings->getNames(); - - lua_newtable(L); - for (const std::string &key : keys) { - lua_pushstring(L, o->m_settings->get(key).c_str()); - lua_setfield(L, -2, key.c_str()); - } - + MutexAutoLock(o->m_settings->m_mutex); + push_settings_table(L, o->m_settings); return 1; } diff --git a/src/settings.h b/src/settings.h index b5e859ee0..e22d949d3 100644 --- a/src/settings.h +++ b/src/settings.h @@ -239,6 +239,8 @@ private: // Allow TestSettings to run sanity checks using private functions. friend class TestSettings; + // For sane mutex locking when iterating + friend class LuaSettings; void updateNoLock(const Settings &other); void clearNoLock(); From 1abb83b1abde10632442554c90549d81d1b39cd7 Mon Sep 17 00:00:00 2001 From: Muhammad Rifqi Priyo Susanto Date: Mon, 1 Mar 2021 19:37:32 +0700 Subject: [PATCH 13/16] Use vec4 for varTexCoord in interlaced shader (#11004) Somewhen in the past, inTexCoord0 was a vec2. Now, it is a vec4. --- client/shaders/3d_interlaced_merge/opengl_fragment.glsl | 2 +- client/shaders/3d_interlaced_merge/opengl_vertex.glsl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/client/shaders/3d_interlaced_merge/opengl_fragment.glsl b/client/shaders/3d_interlaced_merge/opengl_fragment.glsl index 7cba61b39..6d3ae5093 100644 --- a/client/shaders/3d_interlaced_merge/opengl_fragment.glsl +++ b/client/shaders/3d_interlaced_merge/opengl_fragment.glsl @@ -6,7 +6,7 @@ uniform sampler2D textureFlags; #define rightImage normalTexture #define maskImage textureFlags -varying mediump vec2 varTexCoord; +varying mediump vec4 varTexCoord; void main(void) { diff --git a/client/shaders/3d_interlaced_merge/opengl_vertex.glsl b/client/shaders/3d_interlaced_merge/opengl_vertex.glsl index 860049481..224b7d183 100644 --- a/client/shaders/3d_interlaced_merge/opengl_vertex.glsl +++ b/client/shaders/3d_interlaced_merge/opengl_vertex.glsl @@ -1,4 +1,4 @@ -varying mediump vec2 varTexCoord; +varying mediump vec4 varTexCoord; void main(void) { From 5b42b5a8c26811bd7cb152cbb5ffeee77abb8d66 Mon Sep 17 00:00:00 2001 From: Elias Fleckenstein <54945686+EliasFleckenstein03@users.noreply.github.com> Date: Thu, 4 Mar 2021 20:37:41 +0100 Subject: [PATCH 14/16] Add mod.conf to preview clientmod (#11020) --- clientmods/preview/mod.conf | 1 + 1 file changed, 1 insertion(+) create mode 100644 clientmods/preview/mod.conf diff --git a/clientmods/preview/mod.conf b/clientmods/preview/mod.conf new file mode 100644 index 000000000..4e56ec293 --- /dev/null +++ b/clientmods/preview/mod.conf @@ -0,0 +1 @@ +name = preview From ac8ac191691a13162667314358e96f07a65d0d1a Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 4 Mar 2021 20:38:28 +0100 Subject: [PATCH 15/16] Protect mg_name and mg_flags from being set by Lua (#11010) --- src/script/lua_api/l_settings.cpp | 45 ++++++++++++++++++++++++------- 1 file changed, 35 insertions(+), 10 deletions(-) diff --git a/src/script/lua_api/l_settings.cpp b/src/script/lua_api/l_settings.cpp index 85df8ab34..a82073ed4 100644 --- a/src/script/lua_api/l_settings.cpp +++ b/src/script/lua_api/l_settings.cpp @@ -27,12 +27,36 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "log.h" -#define SET_SECURITY_CHECK(L, name) \ - if (o->m_settings == g_settings && ScriptApiSecurity::isSecure(L) && \ - name.compare(0, 7, "secure.") == 0) { \ - throw LuaError("Attempt to set secure setting."); \ +/* This protects: + * 'secure.*' settings from being set + * some mapgen settings from being set + * (not security-criticial, just to avoid messing up user configs) + */ +#define CHECK_SETTING_SECURITY(L, name) \ + if (o->m_settings == g_settings) { \ + if (checkSettingSecurity(L, name) == -1) \ + return 0; \ } +static inline int checkSettingSecurity(lua_State* L, const std::string &name) +{ + if (ScriptApiSecurity::isSecure(L) && name.compare(0, 7, "secure.") == 0) + throw LuaError("Attempt to set secure setting."); + + bool is_mainmenu = false; +#ifndef SERVER + is_mainmenu = ModApiBase::getGuiEngine(L) != nullptr; +#endif + if (!is_mainmenu && (name == "mg_name" || name == "mg_flags")) { + errorstream << "Tried to set global setting " << name << ", ignoring. " + "minetest.set_mapgen_setting() should be used instead." << std::endl; + infostream << script_get_backtrace(L) << std::endl; + return -1; + } + + return 0; +} + LuaSettings::LuaSettings(Settings *settings, const std::string &filename) : m_settings(settings), m_filename(filename) @@ -130,6 +154,7 @@ int LuaSettings::l_get_np_group(lua_State *L) return 1; } +// get_flags(self, key) -> table or nil int LuaSettings::l_get_flags(lua_State *L) { NO_MAP_LOCK_REQUIRED; @@ -162,7 +187,7 @@ int LuaSettings::l_set(lua_State* L) std::string key = std::string(luaL_checkstring(L, 2)); const char* value = luaL_checkstring(L, 3); - SET_SECURITY_CHECK(L, key); + CHECK_SETTING_SECURITY(L, key); if (!o->m_settings->set(key, value)) throw LuaError("Invalid sequence found in setting parameters"); @@ -179,14 +204,14 @@ int LuaSettings::l_set_bool(lua_State* L) std::string key = std::string(luaL_checkstring(L, 2)); bool value = readParam(L, 3); - SET_SECURITY_CHECK(L, key); + CHECK_SETTING_SECURITY(L, key); o->m_settings->setBool(key, value); - return 1; + return 0; } -// set(self, key, value) +// set_np_group(self, key, value) int LuaSettings::l_set_np_group(lua_State *L) { NO_MAP_LOCK_REQUIRED; @@ -196,7 +221,7 @@ int LuaSettings::l_set_np_group(lua_State *L) NoiseParams value; read_noiseparams(L, 3, &value); - SET_SECURITY_CHECK(L, key); + CHECK_SETTING_SECURITY(L, key); o->m_settings->setNoiseParams(key, value); @@ -211,7 +236,7 @@ int LuaSettings::l_remove(lua_State* L) std::string key = std::string(luaL_checkstring(L, 2)); - SET_SECURITY_CHECK(L, key); + CHECK_SETTING_SECURITY(L, key); bool success = o->m_settings->remove(key); lua_pushboolean(L, success); From cafad6ac03348aa77e8ee4bb035840e73de4b2a9 Mon Sep 17 00:00:00 2001 From: Wuzzy Date: Fri, 5 Mar 2021 15:27:33 +0000 Subject: [PATCH 16/16] Translate builtin (#10693) This PR is the second attempt to translate builtin. Server-sent translation files can be added to `builtin/locale/`, whereas client-side translations depend on gettext. --- .gitignore | 3 +- builtin/client/chatcommands.lua | 9 +- builtin/client/death_formspec.lua | 2 +- builtin/common/chatcommands.lua | 69 +-- builtin/common/information_formspecs.lua | 28 +- builtin/game/chat.lua | 549 ++++++++++++----------- builtin/game/privileges.lua | 40 +- builtin/game/register.lua | 10 +- builtin/locale/template.txt | 224 +++++++++ builtin/profiler/init.lua | 18 +- src/server.cpp | 4 +- util/updatepo.sh | 1 + 12 files changed, 623 insertions(+), 334 deletions(-) create mode 100644 builtin/locale/template.txt diff --git a/.gitignore b/.gitignore index 52f8bc4f4..d951f2222 100644 --- a/.gitignore +++ b/.gitignore @@ -86,8 +86,7 @@ src/test_config.h src/cmake_config.h src/cmake_config_githash.h src/unittest/test_world/world.mt -src/lua/build/ -locale/ +/locale/ .directory *.cbp *.layout diff --git a/builtin/client/chatcommands.lua b/builtin/client/chatcommands.lua index 0e8d4dd03..a563a6627 100644 --- a/builtin/client/chatcommands.lua +++ b/builtin/client/chatcommands.lua @@ -1,6 +1,5 @@ -- Minetest: builtin/client/chatcommands.lua - core.register_on_sending_chat_message(function(message) if message:sub(1,2) == ".." then return false @@ -8,7 +7,7 @@ core.register_on_sending_chat_message(function(message) local first_char = message:sub(1,1) if first_char == "/" or first_char == "." then - core.display_chat_message(core.gettext("issued command: ") .. message) + core.display_chat_message(core.gettext("Issued command: ") .. message) end if first_char ~= "." then @@ -19,7 +18,7 @@ core.register_on_sending_chat_message(function(message) param = param or "" if not cmd then - core.display_chat_message(core.gettext("-!- Empty command")) + core.display_chat_message("-!- " .. core.gettext("Empty command.")) return true end @@ -36,7 +35,7 @@ core.register_on_sending_chat_message(function(message) core.display_chat_message(result) end else - core.display_chat_message(core.gettext("-!- Invalid command: ") .. cmd) + core.display_chat_message("-!- " .. core.gettext("Invalid command: ") .. cmd) end return true @@ -66,7 +65,7 @@ core.register_chatcommand("clear_chat_queue", { description = core.gettext("Clear the out chat queue"), func = function(param) core.clear_out_chat_queue() - return true, core.gettext("The out chat queue is now empty") + return true, core.gettext("The out chat queue is now empty.") end, }) diff --git a/builtin/client/death_formspec.lua b/builtin/client/death_formspec.lua index e755ac5c1..7df0cbd75 100644 --- a/builtin/client/death_formspec.lua +++ b/builtin/client/death_formspec.lua @@ -2,7 +2,7 @@ -- handled by the engine. core.register_on_death(function() - core.display_chat_message("You died.") + core.display_chat_message(core.gettext("You died.")) 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") .."]" diff --git a/builtin/common/chatcommands.lua b/builtin/common/chatcommands.lua index 52edda659..c945e7bdb 100644 --- a/builtin/common/chatcommands.lua +++ b/builtin/common/chatcommands.lua @@ -1,5 +1,9 @@ -- Minetest: builtin/common/chatcommands.lua +-- For server-side translations (if INIT == "game") +-- Otherwise, use core.gettext +local S = core.get_translator("__builtin") + core.registered_chatcommands = {} function core.register_chatcommand(cmd, def) @@ -29,25 +33,12 @@ function core.override_chatcommand(name, redefinition) core.registered_chatcommands[name] = chatcommand end -local cmd_marker = "/" - -local function gettext(...) - return ... -end - -local function gettext_replace(text, replace) - return text:gsub("$1", replace) -end - - -if INIT == "client" then - cmd_marker = "." - gettext = core.gettext - gettext_replace = fgettext_ne -end - local function do_help_cmd(name, param) local function format_help_line(cmd, def) + local cmd_marker = "/" + if INIT == "client" then + cmd_marker = "." + end local msg = core.colorize("#00ffff", cmd_marker .. cmd) if def.params and def.params ~= "" then msg = msg .. " " .. def.params @@ -65,9 +56,21 @@ local function do_help_cmd(name, param) end end table.sort(cmds) - return true, gettext("Available commands: ") .. table.concat(cmds, " ") .. "\n" - .. gettext_replace("Use '$1help ' to get more information," - .. " or '$1help all' to list everything.", cmd_marker) + local msg + if INIT == "game" then + msg = S("Available commands: @1", + table.concat(cmds, " ")) .. "\n" + .. S("Use '/help ' to get more " + .. "information, or '/help all' to list " + .. "everything.") + else + msg = core.gettext("Available commands: ") + .. table.concat(cmds, " ") .. "\n" + .. core.gettext("Use '.help ' to get more " + .. "information, or '.help all' to list " + .. "everything.") + end + return true, msg elseif param == "all" then local cmds = {} for cmd, def in pairs(core.registered_chatcommands) do @@ -76,19 +79,31 @@ local function do_help_cmd(name, param) end end table.sort(cmds) - return true, gettext("Available commands:").."\n"..table.concat(cmds, "\n") + local msg + if INIT == "game" then + msg = S("Available commands:") + else + msg = core.gettext("Available commands:") + end + return true, msg.."\n"..table.concat(cmds, "\n") elseif INIT == "game" and param == "privs" then local privs = {} for priv, def in pairs(core.registered_privileges) do privs[#privs + 1] = priv .. ": " .. def.description end table.sort(privs) - return true, "Available privileges:\n"..table.concat(privs, "\n") + return true, S("Available privileges:").."\n"..table.concat(privs, "\n") else local cmd = param local def = core.registered_chatcommands[cmd] if not def then - return false, gettext("Command not available: ")..cmd + local msg + if INIT == "game" then + msg = S("Command not available: @1", cmd) + else + msg = core.gettext("Command not available: ") .. cmd + end + return false, msg else return true, format_help_line(cmd, def) end @@ -97,16 +112,16 @@ end if INIT == "client" then core.register_chatcommand("help", { - params = gettext("[all | ]"), - description = gettext("Get help for commands"), + params = core.gettext("[all | ]"), + description = core.gettext("Get help for commands"), func = function(param) return do_help_cmd(nil, param) end, }) else core.register_chatcommand("help", { - params = "[all | privs | ]", - description = "Get help for commands or list privileges", + params = S("[all | privs | ]"), + description = S("Get help for commands or list privileges"), func = do_help_cmd, }) end diff --git a/builtin/common/information_formspecs.lua b/builtin/common/information_formspecs.lua index 3e2f1f079..e814b4c43 100644 --- a/builtin/common/information_formspecs.lua +++ b/builtin/common/information_formspecs.lua @@ -20,7 +20,8 @@ local LIST_FORMSPEC_DESCRIPTION = [[ button_exit[5,7;3,1;quit;%s] ]] -local formspec_escape = core.formspec_escape +local F = core.formspec_escape +local S = core.get_translator("__builtin") local check_player_privs = core.check_player_privs @@ -51,22 +52,23 @@ core.after(0, load_mod_command_tree) local function build_chatcommands_formspec(name, sel, copy) local rows = {} - rows[1] = "#FFF,0,Command,Parameters" + rows[1] = "#FFF,0,"..F(S("Command"))..","..F(S("Parameters")) - local description = "For more information, click on any entry in the list.\n" .. - "Double-click to copy the entry to the chat history." + local description = S("For more information, click on " + .. "any entry in the list.").. "\n" .. + S("Double-click to copy the entry to the chat history.") for i, data in ipairs(mod_cmds) do - rows[#rows + 1] = COLOR_BLUE .. ",0," .. formspec_escape(data[1]) .. "," + rows[#rows + 1] = COLOR_BLUE .. ",0," .. F(data[1]) .. "," for j, cmds in ipairs(data[2]) do local has_priv = check_player_privs(name, cmds[2].privs) rows[#rows + 1] = ("%s,1,%s,%s"):format( has_priv and COLOR_GREEN or COLOR_GRAY, - cmds[1], formspec_escape(cmds[2].params)) + cmds[1], F(cmds[2].params)) if sel == #rows then description = cmds[2].description if copy then - core.chat_send_player(name, ("Command: %s %s"):format( + core.chat_send_player(name, S("Command: @1 @2", core.colorize("#0FF", "/" .. cmds[1]), cmds[2].params)) end end @@ -74,9 +76,9 @@ local function build_chatcommands_formspec(name, sel, copy) end return LIST_FORMSPEC_DESCRIPTION:format( - "Available commands: (see also: /help )", + F(S("Available commands: (see also: /help )")), table.concat(rows, ","), sel or 0, - description, "Close" + F(description), F(S("Close")) ) end @@ -91,19 +93,19 @@ local function build_privs_formspec(name) table.sort(privs, function(a, b) return a[1] < b[1] end) local rows = {} - rows[1] = "#FFF,0,Privilege,Description" + rows[1] = "#FFF,0,"..F(S("Privilege"))..","..F(S("Description")) local player_privs = core.get_player_privs(name) for i, data in ipairs(privs) do rows[#rows + 1] = ("%s,0,%s,%s"):format( player_privs[data[1]] and COLOR_GREEN or COLOR_GRAY, - data[1], formspec_escape(data[2].description)) + data[1], F(data[2].description)) end return LIST_FORMSPEC:format( - "Available privileges:", + F(S("Available privileges:")), table.concat(rows, ","), - "Close" + F(S("Close")) ) end diff --git a/builtin/game/chat.lua b/builtin/game/chat.lua index ecd413e25..eb3364d60 100644 --- a/builtin/game/chat.lua +++ b/builtin/game/chat.lua @@ -1,5 +1,7 @@ -- Minetest: builtin/game/chat.lua +local S = core.get_translator("__builtin") + -- Helper function that implements search and replace without pattern matching -- Returns the string and a boolean indicating whether or not the string was modified local function safe_gsub(s, replace, with) @@ -52,7 +54,7 @@ core.register_on_chat_message(function(name, message) local cmd, param = string.match(message, "^/([^ ]+) *(.*)") if not cmd then - core.chat_send_player(name, "-!- Empty command") + core.chat_send_player(name, "-!- "..S("Empty command.")) return true end @@ -65,7 +67,7 @@ core.register_on_chat_message(function(name, message) local cmd_def = core.registered_chatcommands[cmd] if not cmd_def then - core.chat_send_player(name, "-!- Invalid command: " .. cmd) + core.chat_send_player(name, "-!- "..S("Invalid command: @1", cmd)) return true end local has_privs, missing_privs = core.check_player_privs(name, cmd_def.privs) @@ -73,7 +75,7 @@ core.register_on_chat_message(function(name, message) core.set_last_run_mod(cmd_def.mod_origin) local success, result = cmd_def.func(name, param) if success == false and result == nil then - core.chat_send_player(name, "-!- Invalid command usage") + core.chat_send_player(name, "-!- "..S("Invalid command usage.")) local help_def = core.registered_chatcommands["help"] if help_def then local _, helpmsg = help_def.func(name, cmd) @@ -85,9 +87,10 @@ core.register_on_chat_message(function(name, message) core.chat_send_player(name, result) end else - core.chat_send_player(name, "You don't have permission" - .. " to run this command (missing privileges: " - .. table.concat(missing_privs, ", ") .. ")") + core.chat_send_player(name, + S("You don't have permission to run this command " + .. "(missing privileges: @1).", + table.concat(missing_privs, ", "))) end return true -- Handled chat message end) @@ -107,12 +110,13 @@ local function parse_range_str(player_name, str) if args[1] == "here" then p1, p2 = core.get_player_radius_area(player_name, tonumber(args[2])) if p1 == nil then - return false, "Unable to get player " .. player_name .. " position" + return false, S("Unable to get position of player @1.", player_name) end else p1, p2 = core.string_to_area(str) if p1 == nil then - return false, "Incorrect area format. Expected: (x1,y1,z1) (x2,y2,z2)" + return false, S("Incorrect area format. " + .. "Expected: (x1,y1,z1) (x2,y2,z2)") end end @@ -123,9 +127,9 @@ end -- Chat commands -- core.register_chatcommand("me", { - params = "", - description = "Show chat action (e.g., '/me orders a pizza' displays" - .. " ' orders a pizza')", + params = S(""), + description = S("Show chat action (e.g., '/me orders a pizza' " + .. "displays ' orders a pizza')"), privs = {shout=true}, func = function(name, param) core.chat_send_all("* " .. name .. " " .. param) @@ -134,43 +138,44 @@ core.register_chatcommand("me", { }) core.register_chatcommand("admin", { - description = "Show the name of the server owner", + description = S("Show the name of the server owner"), func = function(name) local admin = core.settings:get("name") if admin then - return true, "The administrator of this server is " .. admin .. "." + return true, S("The administrator of this server is @1.", admin) else - return false, "There's no administrator named in the config file." + return false, S("There's no administrator named " + .. "in the config file.") end end, }) core.register_chatcommand("privs", { - params = "[]", - description = "Show privileges of yourself or another player", + params = S("[]"), + description = S("Show privileges of yourself or another player"), func = function(caller, param) param = param:trim() local name = (param ~= "" and param or caller) if not core.player_exists(name) then - return false, "Player " .. name .. " does not exist." + return false, S("Player @1 does not exist.", name) end - return true, "Privileges of " .. name .. ": " - .. core.privs_to_string( - core.get_player_privs(name), ", ") + return true, S("Privileges of @1: @2", name, + core.privs_to_string( + core.get_player_privs(name), ", ")) end, }) core.register_chatcommand("haspriv", { - params = "", - description = "Return list of all online players with privilege.", + params = S(""), + description = S("Return list of all online players with privilege"), privs = {basic_privs = true}, func = function(caller, param) param = param:trim() if param == "" then - return false, "Invalid parameters (see /help haspriv)" + return false, S("Invalid parameters (see /help haspriv).") end if not core.registered_privileges[param] then - return false, "Unknown privilege!" + return false, S("Unknown privilege!") end local privs = core.string_to_privs(param) local players_with_priv = {} @@ -180,19 +185,20 @@ core.register_chatcommand("haspriv", { table.insert(players_with_priv, player_name) end end - return true, "Players online with the \"" .. param .. "\" privilege: " .. - table.concat(players_with_priv, ", ") + return true, S("Players online with the \"@1\" privilege: @2", + param, + table.concat(players_with_priv, ", ")) end }) local function handle_grant_command(caller, grantname, grantprivstr) local caller_privs = core.get_player_privs(caller) if not (caller_privs.privs or caller_privs.basic_privs) then - return false, "Your privileges are insufficient." + return false, S("Your privileges are insufficient.") end if not core.get_auth_handler().get_auth(grantname) then - return false, "Player " .. grantname .. " does not exist." + return false, S("Player @1 does not exist.", grantname) end local grantprivs = core.string_to_privs(grantprivstr) if grantprivstr == "all" then @@ -204,10 +210,10 @@ local function handle_grant_command(caller, grantname, grantprivstr) core.string_to_privs(core.settings:get("basic_privs") or "interact,shout") for priv, _ in pairs(grantprivs) do if not basic_privs[priv] and not caller_privs.privs then - return false, "Your privileges are insufficient." + return false, S("Your privileges are insufficient.") end if not core.registered_privileges[priv] then - privs_unknown = privs_unknown .. "Unknown privilege: " .. priv .. "\n" + privs_unknown = privs_unknown .. S("Unknown privilege: @1", priv) .. "\n" end privs[priv] = true end @@ -221,33 +227,33 @@ local function handle_grant_command(caller, grantname, grantprivstr) core.set_player_privs(grantname, privs) core.log("action", caller..' granted ('..core.privs_to_string(grantprivs, ', ')..') privileges to '..grantname) if grantname ~= caller then - core.chat_send_player(grantname, caller - .. " granted you privileges: " - .. core.privs_to_string(grantprivs, ' ')) + core.chat_send_player(grantname, + S("@1 granted you privileges: @2", caller, + core.privs_to_string(grantprivs, ' '))) end - return true, "Privileges of " .. grantname .. ": " - .. core.privs_to_string( - core.get_player_privs(grantname), ' ') + return true, S("Privileges of @1: @2", grantname, + core.privs_to_string( + core.get_player_privs(grantname), ' ')) end core.register_chatcommand("grant", { - params = " ( | all)", - description = "Give privileges to player", + params = S(" ( | all)"), + description = S("Give privileges to player"), func = function(name, param) local grantname, grantprivstr = string.match(param, "([^ ]+) (.+)") if not grantname or not grantprivstr then - return false, "Invalid parameters (see /help grant)" + return false, S("Invalid parameters (see /help grant).") end return handle_grant_command(name, grantname, grantprivstr) end, }) core.register_chatcommand("grantme", { - params = " | all", - description = "Grant privileges to yourself", + params = S(" | all"), + description = S("Grant privileges to yourself"), func = function(name, param) if param == "" then - return false, "Invalid parameters (see /help grantme)" + return false, S("Invalid parameters (see /help grantme).") end return handle_grant_command(name, name, param) end, @@ -256,11 +262,11 @@ core.register_chatcommand("grantme", { local function handle_revoke_command(caller, revokename, revokeprivstr) local caller_privs = core.get_player_privs(caller) if not (caller_privs.privs or caller_privs.basic_privs) then - return false, "Your privileges are insufficient." + return false, S("Your privileges are insufficient.") end if not core.get_auth_handler().get_auth(revokename) then - return false, "Player " .. revokename .. " does not exist." + return false, S("Player @1 does not exist.", revokename) end local revokeprivs = core.string_to_privs(revokeprivstr) @@ -269,7 +275,7 @@ local function handle_revoke_command(caller, revokename, revokeprivstr) core.string_to_privs(core.settings:get("basic_privs") or "interact,shout") for priv, _ in pairs(revokeprivs) do if not basic_privs[priv] and not caller_privs.privs then - return false, "Your privileges are insufficient." + return false, S("Your privileges are insufficient.") end end @@ -292,43 +298,43 @@ local function handle_revoke_command(caller, revokename, revokeprivstr) ..core.privs_to_string(revokeprivs, ', ') ..') privileges from '..revokename) if revokename ~= caller then - core.chat_send_player(revokename, caller - .. " revoked privileges from you: " - .. core.privs_to_string(revokeprivs, ' ')) + core.chat_send_player(revokename, + S("@1 revoked privileges from you: @2", caller, + core.privs_to_string(revokeprivs, ' '))) end - return true, "Privileges of " .. revokename .. ": " - .. core.privs_to_string( - core.get_player_privs(revokename), ' ') + return true, S("Privileges of @1: @2", revokename, + core.privs_to_string( + core.get_player_privs(revokename), ' ')) end core.register_chatcommand("revoke", { - params = " ( | all)", - description = "Remove privileges from player", + params = S(" ( | all)"), + description = S("Remove privileges from player"), privs = {}, func = function(name, param) local revokename, revokeprivstr = string.match(param, "([^ ]+) (.+)") if not revokename or not revokeprivstr then - return false, "Invalid parameters (see /help revoke)" + return false, S("Invalid parameters (see /help revoke).") end return handle_revoke_command(name, revokename, revokeprivstr) end, }) core.register_chatcommand("revokeme", { - params = " | all", - description = "Revoke privileges from yourself", + params = S(" | all"), + description = S("Revoke privileges from yourself"), privs = {}, func = function(name, param) if param == "" then - return false, "Invalid parameters (see /help revokeme)" + return false, S("Invalid parameters (see /help revokeme).") end return handle_revoke_command(name, name, param) end, }) core.register_chatcommand("setpassword", { - params = " ", - description = "Set player's password", + params = S(" "), + description = S("Set player's password"), privs = {password=true}, func = function(name, param) local toname, raw_password = string.match(param, "^([^ ]+) +(.+)$") @@ -338,83 +344,83 @@ core.register_chatcommand("setpassword", { end if not toname then - return false, "Name field required" + return false, S("Name field required.") end - local act_str_past, act_str_pres + local msg_chat, msg_log, msg_ret if not raw_password then core.set_player_password(toname, "") - act_str_past = "cleared" - act_str_pres = "clears" + msg_chat = S("Your password was cleared by @1.", name) + msg_log = name .. " clears password of " .. toname .. "." + msg_ret = S("Password of player \"@1\" cleared.", toname) else core.set_player_password(toname, core.get_password_hash(toname, raw_password)) - act_str_past = "set" - act_str_pres = "sets" + msg_chat = S("Your password was set by @1.", name) + msg_log = name .. " sets password of " .. toname .. "." + msg_ret = S("Password of player \"@1\" set.", toname) end if toname ~= name then - core.chat_send_player(toname, "Your password was " - .. act_str_past .. " by " .. name) + core.chat_send_player(toname, msg_chat) end - core.log("action", name .. " " .. act_str_pres .. - " password of " .. toname .. ".") + core.log("action", msg_log) - return true, "Password of player \"" .. toname .. "\" " .. act_str_past + return true, msg_ret end, }) core.register_chatcommand("clearpassword", { - params = "", - description = "Set empty password for a player", + params = S(""), + description = S("Set empty password for a player"), privs = {password=true}, func = function(name, param) local toname = param if toname == "" then - return false, "Name field required" + return false, S("Name field required.") end core.set_player_password(toname, '') core.log("action", name .. " clears password of " .. toname .. ".") - return true, "Password of player \"" .. toname .. "\" cleared" + return true, S("Password of player \"@1\" cleared.", toname) end, }) core.register_chatcommand("auth_reload", { params = "", - description = "Reload authentication data", + description = S("Reload authentication data"), privs = {server=true}, func = function(name, param) local done = core.auth_reload() - return done, (done and "Done." or "Failed.") + return done, (done and S("Done.") or S("Failed.")) end, }) core.register_chatcommand("remove_player", { - params = "", - description = "Remove a player's data", + params = S(""), + description = S("Remove a player's data"), privs = {server=true}, func = function(name, param) local toname = param if toname == "" then - return false, "Name field required" + return false, S("Name field required.") end local rc = core.remove_player(toname) if rc == 0 then core.log("action", name .. " removed player data of " .. toname .. ".") - return true, "Player \"" .. toname .. "\" removed." + return true, S("Player \"@1\" removed.", toname) elseif rc == 1 then - return true, "No such player \"" .. toname .. "\" to remove." + return true, S("No such player \"@1\" to remove.", toname) elseif rc == 2 then - return true, "Player \"" .. toname .. "\" is connected, cannot remove." + return true, S("Player \"@1\" is connected, cannot remove.", toname) end - return false, "Unhandled remove_player return code " .. rc .. "" + return false, S("Unhandled remove_player return code @1.", tostring(rc)) end, }) @@ -445,46 +451,46 @@ local function teleport_to_pos(name, p) local lm = 31000 if p.x < -lm or p.x > lm or p.y < -lm or p.y > lm or p.z < -lm or p.z > lm then - return false, "Cannot teleport out of map bounds!" + return false, S("Cannot teleport out of map bounds!") end local teleportee = core.get_player_by_name(name) if not teleportee then - return false, "Cannot get player with name " .. name + return false, S("Cannot get player with name @1.", name) end if teleportee:get_attach() then - return false, "Cannot teleport, " .. name .. - " is attached to an object!" + return false, S("Cannot teleport, @1 " .. + "is attached to an object!", name) end teleportee:set_pos(p) - return true, "Teleporting " .. name .. " to " .. core.pos_to_string(p, 1) + return true, S("Teleporting @1 to @2.", name, core.pos_to_string(p, 1)) end -- Teleports player next to player if possible local function teleport_to_player(name, target_name) if name == target_name then - return false, "One does not teleport to oneself." + return false, S("One does not teleport to oneself.") end local teleportee = core.get_player_by_name(name) if not teleportee then - return false, "Cannot get teleportee with name " .. name + return false, S("Cannot get teleportee with name @1.", name) end if teleportee:get_attach() then - return false, "Cannot teleport, " .. name .. - " is attached to an object!" + return false, S("Cannot teleport, @1 " .. + "is attached to an object!", name) end local target = core.get_player_by_name(target_name) if not target then - return false, "Cannot get target player with name " .. target_name + return false, S("Cannot get target player with name @1.", target_name) end local p = find_free_position_near(target:get_pos()) teleportee:set_pos(p) - return true, "Teleporting " .. name .. " to " .. target_name .. " at " .. - core.pos_to_string(p, 1) + return true, S("Teleporting @1 to @2 at @3.", name, target_name, + core.pos_to_string(p, 1)) end core.register_chatcommand("teleport", { - params = ",, | | ,, | ", - description = "Teleport to position or player", + params = S(",, | | ,, | "), + description = S("Teleport to position or player"), privs = {teleport=true}, func = function(name, param) local p = {} @@ -500,8 +506,8 @@ core.register_chatcommand("teleport", { end local has_bring_priv = core.check_player_privs(name, {bring=true}) - local missing_bring_msg = "You don't have permission to teleport " .. - "other players (missing bring privilege)" + local missing_bring_msg = S("You don't have permission to teleport " .. + "other players (missing privilege: @1).", "bring") local teleportee_name teleportee_name, p.x, p.y, p.z = param:match( @@ -527,8 +533,8 @@ core.register_chatcommand("teleport", { }) core.register_chatcommand("set", { - params = "([-n] ) | ", - description = "Set or read server configuration setting", + params = S("([-n] ) | "), + description = S("Set or read server configuration setting"), privs = {server=true}, func = function(name, param) local arg, setname, setvalue = string.match(param, "(-[n]) ([^ ]+) (.+)") @@ -540,22 +546,23 @@ core.register_chatcommand("set", { setname, setvalue = string.match(param, "([^ ]+) (.+)") if setname and setvalue then if not core.settings:get(setname) then - return false, "Failed. Use '/set -n ' to create a new setting." + return false, S("Failed. Use '/set -n ' " + .. "to create a new setting.") end core.settings:set(setname, setvalue) - return true, setname .. " = " .. setvalue + return true, S("@1 = @2", setname, setvalue) end setname = string.match(param, "([^ ]+)") if setname then setvalue = core.settings:get(setname) if not setvalue then - setvalue = "" + setvalue = S("") end - return true, setname .. " = " .. setvalue + return true, S("@1 = @2", setname, setvalue) end - return false, "Invalid parameters (see /help set)." + return false, S("Invalid parameters (see /help set).") end, }) @@ -568,26 +575,27 @@ local function emergeblocks_callback(pos, action, num_calls_remaining, ctx) if ctx.current_blocks == ctx.total_blocks then core.chat_send_player(ctx.requestor_name, - string.format("Finished emerging %d blocks in %.2fms.", - ctx.total_blocks, (os.clock() - ctx.start_time) * 1000)) + S("Finished emerging @1 blocks in @2ms.", + ctx.total_blocks, + string.format("%.2f", (os.clock() - ctx.start_time) * 1000))) end end local function emergeblocks_progress_update(ctx) if ctx.current_blocks ~= ctx.total_blocks then core.chat_send_player(ctx.requestor_name, - string.format("emergeblocks update: %d/%d blocks emerged (%.1f%%)", + S("emergeblocks update: @1/@2 blocks emerged (@3%)", ctx.current_blocks, ctx.total_blocks, - (ctx.current_blocks / ctx.total_blocks) * 100)) + string.format("%.1f", (ctx.current_blocks / ctx.total_blocks) * 100))) core.after(2, emergeblocks_progress_update, ctx) end end core.register_chatcommand("emergeblocks", { - params = "(here []) | ( )", - description = "Load (or, if nonexistent, generate) map blocks " - .. "contained in area pos1 to pos2 ( and must be in parentheses)", + params = S("(here []) | ( )"), + description = S("Load (or, if nonexistent, generate) map blocks contained in " + .. "area pos1 to pos2 ( and must be in parentheses)"), privs = {server=true}, func = function(name, param) local p1, p2 = parse_range_str(name, param) @@ -605,15 +613,15 @@ core.register_chatcommand("emergeblocks", { core.emerge_area(p1, p2, emergeblocks_callback, context) core.after(2, emergeblocks_progress_update, context) - return true, "Started emerge of area ranging from " .. - core.pos_to_string(p1, 1) .. " to " .. core.pos_to_string(p2, 1) + return true, S("Started emerge of area ranging from @1 to @2.", + core.pos_to_string(p1, 1), core.pos_to_string(p2, 1)) end, }) core.register_chatcommand("deleteblocks", { - params = "(here []) | ( )", - description = "Delete map blocks contained in area pos1 to pos2 " - .. "( and must be in parentheses)", + params = S("(here []) | ( )"), + description = S("Delete map blocks contained in area pos1 to pos2 " + .. "( and must be in parentheses)"), privs = {server=true}, func = function(name, param) local p1, p2 = parse_range_str(name, param) @@ -622,18 +630,20 @@ core.register_chatcommand("deleteblocks", { end if core.delete_area(p1, p2) then - return true, "Successfully cleared area ranging from " .. - core.pos_to_string(p1, 1) .. " to " .. core.pos_to_string(p2, 1) + return true, S("Successfully cleared area " + .. "ranging from @1 to @2.", + core.pos_to_string(p1, 1), core.pos_to_string(p2, 1)) else - return false, "Failed to clear one or more blocks in area" + return false, S("Failed to clear one or more " + .. "blocks in area.") end end, }) core.register_chatcommand("fixlight", { - params = "(here []) | ( )", - description = "Resets lighting in the area between pos1 and pos2 " - .. "( and must be in parentheses)", + params = S("(here []) | ( )"), + description = S("Resets lighting in the area between pos1 and pos2 " + .. "( and must be in parentheses)"), privs = {server = true}, func = function(name, param) local p1, p2 = parse_range_str(name, param) @@ -642,17 +652,18 @@ core.register_chatcommand("fixlight", { end if core.fix_light(p1, p2) then - return true, "Successfully reset light in the area ranging from " .. - core.pos_to_string(p1, 1) .. " to " .. core.pos_to_string(p2, 1) + return true, S("Successfully reset light in the area " + .. "ranging from @1 to @2.", + core.pos_to_string(p1, 1), core.pos_to_string(p2, 1)) else - return false, "Failed to load one or more blocks in area" + return false, S("Failed to load one or more blocks in area.") end end, }) core.register_chatcommand("mods", { params = "", - description = "List mods installed on the server", + description = S("List mods installed on the server"), privs = {}, func = function(name, param) return true, table.concat(core.get_modnames(), ", ") @@ -664,117 +675,136 @@ local function handle_give_command(cmd, giver, receiver, stackstring) .. ', stackstring="' .. stackstring .. '"') local itemstack = ItemStack(stackstring) if itemstack:is_empty() then - return false, "Cannot give an empty item" + return false, S("Cannot give an empty item.") elseif (not itemstack:is_known()) or (itemstack:get_name() == "unknown") then - return false, "Cannot give an unknown item" + return false, S("Cannot give an unknown item.") -- Forbid giving 'ignore' due to unwanted side effects elseif itemstack:get_name() == "ignore" then - return false, "Giving 'ignore' is not allowed" + return false, S("Giving 'ignore' is not allowed.") end local receiverref = core.get_player_by_name(receiver) if receiverref == nil then - return false, receiver .. " is not a known player" + return false, S("@1 is not a known player.", receiver) end local leftover = receiverref:get_inventory():add_item("main", itemstack) local partiality if leftover:is_empty() then - partiality = "" + partiality = nil elseif leftover:get_count() == itemstack:get_count() then - partiality = "could not be " + partiality = false else - partiality = "partially " + partiality = true end -- The actual item stack string may be different from what the "giver" -- entered (e.g. big numbers are always interpreted as 2^16-1). stackstring = itemstack:to_string() - if giver == receiver then - local msg = "%q %sadded to inventory." - return true, msg:format(stackstring, partiality) + local msg + if partiality == true then + msg = S("@1 partially added to inventory.", stackstring) + elseif partiality == false then + msg = S("@1 could not be added to inventory.", stackstring) else - core.chat_send_player(receiver, ("%q %sadded to inventory.") - :format(stackstring, partiality)) - local msg = "%q %sadded to %s's inventory." - return true, msg:format(stackstring, partiality, receiver) + msg = S("@1 added to inventory.", stackstring) + end + if giver == receiver then + return true, msg + else + core.chat_send_player(receiver, msg) + local msg_other + if partiality == true then + msg_other = S("@1 partially added to inventory of @2.", + stackstring, receiver) + elseif partiality == false then + msg_other = S("@1 could not be added to inventory of @2.", + stackstring, receiver) + else + msg_other = S("@1 added to inventory of @2.", + stackstring, receiver) + end + return true, msg_other end end core.register_chatcommand("give", { - params = " [ []]", - description = "Give item to player", + params = S(" [ []]"), + description = S("Give item to player"), privs = {give=true}, func = function(name, param) local toname, itemstring = string.match(param, "^([^ ]+) +(.+)$") if not toname or not itemstring then - return false, "Name and ItemString required" + return false, S("Name and ItemString required.") end return handle_give_command("/give", name, toname, itemstring) end, }) core.register_chatcommand("giveme", { - params = " [ []]", - description = "Give item to yourself", + params = S(" [ []]"), + description = S("Give item to yourself"), privs = {give=true}, func = function(name, param) local itemstring = string.match(param, "(.+)$") if not itemstring then - return false, "ItemString required" + return false, S("ItemString required.") end return handle_give_command("/giveme", name, name, itemstring) end, }) core.register_chatcommand("spawnentity", { - params = " [,,]", - description = "Spawn entity at given (or your) position", + params = S(" [,,]"), + description = S("Spawn entity at given (or your) position"), privs = {give=true, interact=true}, func = function(name, param) local entityname, p = string.match(param, "^([^ ]+) *(.*)$") if not entityname then - return false, "EntityName required" + return false, S("EntityName required.") end core.log("action", ("%s invokes /spawnentity, entityname=%q") :format(name, entityname)) local player = core.get_player_by_name(name) if player == nil then core.log("error", "Unable to spawn entity, player is nil") - return false, "Unable to spawn entity, player is nil" + return false, S("Unable to spawn entity, player is nil.") end if not core.registered_entities[entityname] then - return false, "Cannot spawn an unknown entity" + return false, S("Cannot spawn an unknown entity.") end if p == "" then p = player:get_pos() else p = core.string_to_pos(p) if p == nil then - return false, "Invalid parameters ('" .. param .. "')" + return false, S("Invalid parameters (@1).", param) end end p.y = p.y + 1 local obj = core.add_entity(p, entityname) - local msg = obj and "%q spawned." or "%q failed to spawn." - return true, msg:format(entityname) + if obj then + return true, S("@1 spawned.", entityname) + else + return true, S("@1 failed to spawn.", entityname) + end end, }) core.register_chatcommand("pulverize", { params = "", - description = "Destroy item in hand", + description = S("Destroy item in hand"), func = function(name, param) local player = core.get_player_by_name(name) if not player then core.log("error", "Unable to pulverize, no player.") - return false, "Unable to pulverize, no player." + return false, S("Unable to pulverize, no player.") end local wielded_item = player:get_wielded_item() if wielded_item:is_empty() then - return false, "Unable to pulverize, no item in hand." + return false, S("Unable to pulverize, no item in hand.") end core.log("action", name .. " pulverized \"" .. wielded_item:get_name() .. " " .. wielded_item:get_count() .. "\"") player:set_wielded_item(nil) - return true, "An item was pulverized." + return true, S("An item was pulverized.") end, }) @@ -790,14 +820,15 @@ core.register_on_punchnode(function(pos, node, puncher) end) core.register_chatcommand("rollback_check", { - params = "[] [] []", - description = "Check who last touched a node or a node near it" - .. " within the time specified by . Default: range = 0," - .. " seconds = 86400 = 24h, limit = 5. Set to inf for no time limit", + params = S("[] [] []"), + description = S("Check who last touched a node or a node near it " + .. "within the time specified by . " + .. "Default: range = 0, seconds = 86400 = 24h, limit = 5. " + .. "Set to inf for no time limit"), privs = {rollback=true}, func = function(name, param) if not core.settings:get_bool("enable_rollback_recording") then - return false, "Rollback functions are disabled." + return false, S("Rollback functions are disabled.") end local range, seconds, limit = param:match("(%d+) *(%d*) *(%d*)") @@ -805,30 +836,30 @@ core.register_chatcommand("rollback_check", { seconds = tonumber(seconds) or 86400 limit = tonumber(limit) or 5 if limit > 100 then - return false, "That limit is too high!" + return false, S("That limit is too high!") end core.rollback_punch_callbacks[name] = function(pos, node, puncher) local name = puncher:get_player_name() - core.chat_send_player(name, "Checking " .. core.pos_to_string(pos) .. "...") + core.chat_send_player(name, S("Checking @1 ...", core.pos_to_string(pos))) local actions = core.rollback_get_node_actions(pos, range, seconds, limit) if not actions then - core.chat_send_player(name, "Rollback functions are disabled") + core.chat_send_player(name, S("Rollback functions are disabled.")) return end local num_actions = #actions if num_actions == 0 then - core.chat_send_player(name, "Nobody has touched" - .. " the specified location in " - .. seconds .. " seconds") + core.chat_send_player(name, + S("Nobody has touched the specified " + .. "location in @1 seconds.", + seconds)) return end local time = os.time() for i = num_actions, 1, -1 do local action = actions[i] core.chat_send_player(name, - ("%s %s %s -> %s %d seconds ago.") - :format( + S("@1 @2 @3 -> @4 @5 seconds ago.", core.pos_to_string(action.pos), action.actor, action.oldnode.name, @@ -837,110 +868,123 @@ core.register_chatcommand("rollback_check", { end end - return true, "Punch a node (range=" .. range .. ", seconds=" - .. seconds .. "s, limit=" .. limit .. ")" + return true, S("Punch a node (range=@1, seconds=@2, limit=@3).", + range, seconds, limit) end, }) core.register_chatcommand("rollback", { - params = "( []) | (: [])", - description = "Revert actions of a player. Default for is 60. Set to inf for no time limit", + params = S("( []) | (: [])"), + description = S("Revert actions of a player. " + .. "Default for is 60. " + .. "Set to inf for no time limit"), privs = {rollback=true}, func = function(name, param) if not core.settings:get_bool("enable_rollback_recording") then - return false, "Rollback functions are disabled." + return false, S("Rollback functions are disabled.") end local target_name, seconds = string.match(param, ":([^ ]+) *(%d*)") + local rev_msg if not target_name then local player_name player_name, seconds = string.match(param, "([^ ]+) *(%d*)") if not player_name then - return false, "Invalid parameters. See /help rollback" - .. " and /help rollback_check." + return false, S("Invalid parameters. " + .. "See /help rollback and " + .. "/help rollback_check.") end + seconds = tonumber(seconds) or 60 target_name = "player:"..player_name + rev_msg = S("Reverting actions of player '@1' since @2 seconds.", + player_name, seconds) + else + seconds = tonumber(seconds) or 60 + rev_msg = S("Reverting actions of @1 since @2 seconds.", + target_name, seconds) end - seconds = tonumber(seconds) or 60 - core.chat_send_player(name, "Reverting actions of " - .. target_name .. " since " - .. seconds .. " seconds.") + core.chat_send_player(name, rev_msg) local success, log = core.rollback_revert_actions_by( target_name, seconds) local response = "" if #log > 100 then - response = "(log is too long to show)\n" + response = S("(log is too long to show)").."\n" else for _, line in pairs(log) do response = response .. line .. "\n" end end - response = response .. "Reverting actions " - .. (success and "succeeded." or "FAILED.") + if success then + response = response .. S("Reverting actions succeeded.") + else + response = response .. S("Reverting actions FAILED.") + end return success, response end, }) core.register_chatcommand("status", { - description = "Show server status", + description = S("Show server status"), func = function(name, param) local status = core.get_server_status(name, false) if status and status ~= "" then return true, status end - return false, "This command was disabled by a mod or game" + return false, S("This command was disabled by a mod or game.") end, }) core.register_chatcommand("time", { - params = "[<0..23>:<0..59> | <0..24000>]", - description = "Show or set time of day", + params = S("[<0..23>:<0..59> | <0..24000>]"), + description = S("Show or set time of day"), privs = {}, func = function(name, param) if param == "" then local current_time = math.floor(core.get_timeofday() * 1440) local minutes = current_time % 60 local hour = (current_time - minutes) / 60 - return true, ("Current time is %d:%02d"):format(hour, minutes) + return true, S("Current time is @1:@2.", + string.format("%d", hour), + string.format("%02d", minutes)) end local player_privs = core.get_player_privs(name) if not player_privs.settime then - return false, "You don't have permission to run this command " .. - "(missing privilege: settime)." + return false, S("You don't have permission to run " + .. "this command (missing privilege: @1).", "settime") end local hour, minute = param:match("^(%d+):(%d+)$") if not hour then local new_time = tonumber(param) if not new_time then - return false, "Invalid time." + return false, S("Invalid time.") end -- Backward compatibility. core.set_timeofday((new_time % 24000) / 24000) core.log("action", name .. " sets time to " .. new_time) - return true, "Time of day changed." + return true, S("Time of day changed.") end hour = tonumber(hour) minute = tonumber(minute) if hour < 0 or hour > 23 then - return false, "Invalid hour (must be between 0 and 23 inclusive)." + return false, S("Invalid hour (must be between 0 and 23 inclusive).") elseif minute < 0 or minute > 59 then - return false, "Invalid minute (must be between 0 and 59 inclusive)." + return false, S("Invalid minute (must be between 0 and 59 inclusive).") end core.set_timeofday((hour * 60 + minute) / 1440) core.log("action", ("%s sets time to %d:%02d"):format(name, hour, minute)) - return true, "Time of day changed." + return true, S("Time of day changed.") end, }) core.register_chatcommand("days", { - description = "Show day count since world creation", + description = S("Show day count since world creation"), func = function(name, param) - return true, "Current day is " .. core.get_day_count() + return true, S("Current day is @1.", core.get_day_count()) end }) core.register_chatcommand("shutdown", { - params = "[ | -1] [reconnect] []", - description = "Shutdown server (-1 cancels a delayed shutdown)", + params = S("[ | -1] [reconnect] []"), + description = S("Shutdown server (-1 cancels a delayed shutdown)"), privs = {server=true}, func = function(name, param) local delay, reconnect, message @@ -953,7 +997,7 @@ core.register_chatcommand("shutdown", { if delay == 0 then core.log("action", name .. " shuts down server") - core.chat_send_all("*** Server shutting down (operator request).") + core.chat_send_all("*** "..S("Server shutting down (operator request).")) end core.request_shutdown(message:trim(), core.is_yes(reconnect), delay) return true @@ -961,65 +1005,65 @@ core.register_chatcommand("shutdown", { }) core.register_chatcommand("ban", { - params = "[]", - description = "Ban the IP of a player or show the ban list", + params = S("[]"), + description = S("Ban the IP of a player or show the ban list"), privs = {ban=true}, func = function(name, param) if param == "" then local ban_list = core.get_ban_list() if ban_list == "" then - return true, "The ban list is empty." + return true, S("The ban list is empty.") else - return true, "Ban list: " .. ban_list + return true, S("Ban list: @1", ban_list) end end if not core.get_player_by_name(param) then - return false, "Player is not online." + return false, S("Player is not online.") end if not core.ban_player(param) then - return false, "Failed to ban player." + return false, S("Failed to ban player.") end local desc = core.get_ban_description(param) core.log("action", name .. " bans " .. desc .. ".") - return true, "Banned " .. desc .. "." + return true, S("Banned @1.", desc) end, }) core.register_chatcommand("unban", { - params = " | ", - description = "Remove IP ban belonging to a player/IP", + params = S(" | "), + description = S("Remove IP ban belonging to a player/IP"), privs = {ban=true}, func = function(name, param) if not core.unban_player_or_ip(param) then - return false, "Failed to unban player/IP." + return false, S("Failed to unban player/IP.") end core.log("action", name .. " unbans " .. param) - return true, "Unbanned " .. param + return true, S("Unbanned @1.", param) end, }) core.register_chatcommand("kick", { - params = " []", - description = "Kick a player", + params = S(" []"), + description = S("Kick a player"), privs = {kick=true}, func = function(name, param) local tokick, reason = param:match("([^ ]+) (.+)") tokick = tokick or param if not core.kick_player(tokick, reason) then - return false, "Failed to kick player " .. tokick + return false, S("Failed to kick player @1.", tokick) end local log_reason = "" if reason then log_reason = " with reason \"" .. reason .. "\"" end core.log("action", name .. " kicks " .. tokick .. log_reason) - return true, "Kicked " .. tokick + return true, S("Kicked @1.", tokick) end, }) core.register_chatcommand("clearobjects", { - params = "[full | quick]", - description = "Clear all objects in world", + params = S("[full | quick]"), + description = S("Clear all objects in world"), privs = {server=true}, func = function(name, param) local options = {} @@ -1028,45 +1072,42 @@ core.register_chatcommand("clearobjects", { elseif param == "full" then options.mode = "full" else - return false, "Invalid usage, see /help clearobjects." + return false, S("Invalid usage, see /help clearobjects.") end core.log("action", name .. " clears all objects (" .. options.mode .. " mode).") - core.chat_send_all("Clearing all objects. This may take a long time." - .. " You may experience a timeout. (by " - .. name .. ")") + core.chat_send_all(S("Clearing all objects. This may take a long time. " + .. "You may experience a timeout. (by @1)", name)) core.clear_objects(options) core.log("action", "Object clearing done.") - core.chat_send_all("*** Cleared all objects.") + core.chat_send_all("*** "..S("Cleared all objects.")) return true end, }) core.register_chatcommand("msg", { - params = " ", - description = "Send a direct message to a player", + params = S(" "), + description = S("Send a direct message to a player"), privs = {shout=true}, func = function(name, param) local sendto, message = param:match("^(%S+)%s(.+)$") if not sendto then - return false, "Invalid usage, see /help msg." + return false, S("Invalid usage, see /help msg.") end if not core.get_player_by_name(sendto) then - return false, "The player " .. sendto - .. " is not online." + return false, S("The player @1 is not online.", sendto) end core.log("action", "DM from " .. name .. " to " .. sendto .. ": " .. message) - core.chat_send_player(sendto, "DM from " .. name .. ": " - .. message) - return true, "Message sent." + core.chat_send_player(sendto, S("DM from @1: @2", name, message)) + return true, S("Message sent.") end, }) core.register_chatcommand("last-login", { - params = "[]", - description = "Get the last login time of a player or yourself", + params = S("[]"), + description = S("Get the last login time of a player or yourself"), func = function(name, param) if param == "" then param = name @@ -1074,25 +1115,27 @@ core.register_chatcommand("last-login", { local pauth = core.get_auth_handler().get_auth(param) if pauth and pauth.last_login and pauth.last_login ~= -1 then -- Time in UTC, ISO 8601 format - return true, param.."'s last login time was " .. - os.date("!%Y-%m-%dT%H:%M:%SZ", pauth.last_login) + return true, S("@1's last login time was @2.", + param, + os.date("!%Y-%m-%dT%H:%M:%SZ", pauth.last_login)) end - return false, param.."'s last login time is unknown" + return false, S("@1's last login time is unknown.", param) end, }) core.register_chatcommand("clearinv", { - params = "[]", - description = "Clear the inventory of yourself or another player", + params = S("[]"), + description = S("Clear the inventory of yourself or another player"), func = function(name, param) local player if param and param ~= "" and param ~= name then if not core.check_player_privs(name, {server=true}) then - return false, "You don't have permission" - .. " to clear another player's inventory (missing privilege: server)" + return false, S("You don't have permission to " + .. "clear another player's inventory " + .. "(missing privilege: @1).", "server") end player = core.get_player_by_name(param) - core.chat_send_player(param, name.." cleared your inventory.") + core.chat_send_player(param, S("@1 cleared your inventory.", name)) else player = core.get_player_by_name(name) end @@ -1102,25 +1145,25 @@ core.register_chatcommand("clearinv", { player:get_inventory():set_list("craft", {}) player:get_inventory():set_list("craftpreview", {}) core.log("action", name.." clears "..player:get_player_name().."'s inventory") - return true, "Cleared "..player:get_player_name().."'s inventory." + return true, S("Cleared @1's inventory.", player:get_player_name()) else - return false, "Player must be online to clear inventory!" + return false, S("Player must be online to clear inventory!") end end, }) local function handle_kill_command(killer, victim) if core.settings:get_bool("enable_damage") == false then - return false, "Players can't be killed, damage has been disabled." + return false, S("Players can't be killed, damage has been disabled.") end local victimref = core.get_player_by_name(victim) if victimref == nil then - return false, string.format("Player %s is not online.", victim) + return false, S("Player @1 is not online.", victim) elseif victimref:get_hp() <= 0 then if killer == victim then - return false, "You are already dead." + return false, S("You are already dead.") else - return false, string.format("%s is already dead.", victim) + return false, S("@1 is already dead.", victim) end end if not killer == victim then @@ -1128,12 +1171,12 @@ local function handle_kill_command(killer, victim) end -- Kill victim victimref:set_hp(0) - return true, string.format("%s has been killed.", victim) + return true, S("@1 has been killed.", victim) end core.register_chatcommand("kill", { - params = "[]", - description = "Kill player or yourself", + params = S("[]"), + description = S("Kill player or yourself"), privs = {server=true}, func = function(name, param) return handle_kill_command(name, param == "" and name or param) diff --git a/builtin/game/privileges.lua b/builtin/game/privileges.lua index c7417d2f4..aee32a34e 100644 --- a/builtin/game/privileges.lua +++ b/builtin/game/privileges.lua @@ -1,5 +1,7 @@ -- Minetest: builtin/privileges.lua +local S = core.get_translator("__builtin") + -- -- Privileges -- @@ -15,7 +17,7 @@ function core.register_privilege(name, param) def.give_to_admin = def.give_to_singleplayer end if def.description == nil then - def.description = "(no description)" + def.description = S("(no description)") end end local def @@ -28,69 +30,69 @@ function core.register_privilege(name, param) core.registered_privileges[name] = def end -core.register_privilege("interact", "Can interact with things and modify the world") -core.register_privilege("shout", "Can speak in chat") -core.register_privilege("basic_privs", "Can modify 'shout' and 'interact' privileges") -core.register_privilege("privs", "Can modify privileges") +core.register_privilege("interact", S("Can interact with things and modify the world")) +core.register_privilege("shout", S("Can speak in chat")) +core.register_privilege("basic_privs", S("Can modify 'shout' and 'interact' privileges")) +core.register_privilege("privs", S("Can modify privileges")) core.register_privilege("teleport", { - description = "Can teleport self", + description = S("Can teleport self"), give_to_singleplayer = false, }) core.register_privilege("bring", { - description = "Can teleport other players", + description = S("Can teleport other players"), give_to_singleplayer = false, }) core.register_privilege("settime", { - description = "Can set the time of day using /time", + description = S("Can set the time of day using /time"), give_to_singleplayer = false, }) core.register_privilege("server", { - description = "Can do server maintenance stuff", + description = S("Can do server maintenance stuff"), give_to_singleplayer = false, give_to_admin = true, }) core.register_privilege("protection_bypass", { - description = "Can bypass node protection in the world", + description = S("Can bypass node protection in the world"), give_to_singleplayer = false, }) core.register_privilege("ban", { - description = "Can ban and unban players", + description = S("Can ban and unban players"), give_to_singleplayer = false, give_to_admin = true, }) core.register_privilege("kick", { - description = "Can kick players", + description = S("Can kick players"), give_to_singleplayer = false, give_to_admin = true, }) core.register_privilege("give", { - description = "Can use /give and /giveme", + description = S("Can use /give and /giveme"), give_to_singleplayer = false, }) core.register_privilege("password", { - description = "Can use /setpassword and /clearpassword", + description = S("Can use /setpassword and /clearpassword"), give_to_singleplayer = false, give_to_admin = true, }) core.register_privilege("fly", { - description = "Can use fly mode", + description = S("Can use fly mode"), give_to_singleplayer = false, }) core.register_privilege("fast", { - description = "Can use fast mode", + description = S("Can use fast mode"), give_to_singleplayer = false, }) core.register_privilege("noclip", { - description = "Can fly through solid nodes using noclip mode", + description = S("Can fly through solid nodes using noclip mode"), give_to_singleplayer = false, }) core.register_privilege("rollback", { - description = "Can use the rollback functionality", + description = S("Can use the rollback functionality"), give_to_singleplayer = false, }) core.register_privilege("debug", { - description = "Allows enabling various debug options that may affect gameplay", + description = S("Allows enabling various debug options that may affect gameplay"), give_to_singleplayer = false, give_to_admin = true, }) diff --git a/builtin/game/register.lua b/builtin/game/register.lua index 1cff85813..e01c50335 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -1,5 +1,7 @@ -- Minetest: builtin/misc_register.lua +local S = core.get_translator("__builtin") + -- -- Make raw registration functions inaccessible to anyone except this file -- @@ -326,7 +328,7 @@ end core.register_item(":unknown", { type = "none", - description = "Unknown Item", + description = S("Unknown Item"), inventory_image = "unknown_item.png", on_place = core.item_place, on_secondary_use = core.item_secondary_use, @@ -336,7 +338,7 @@ core.register_item(":unknown", { }) core.register_node(":air", { - description = "Air", + description = S("Air"), inventory_image = "air.png", wield_image = "air.png", drawtype = "airlike", @@ -353,7 +355,7 @@ core.register_node(":air", { }) core.register_node(":ignore", { - description = "Ignore", + description = S("Ignore"), inventory_image = "ignore.png", wield_image = "ignore.png", drawtype = "airlike", @@ -370,7 +372,7 @@ core.register_node(":ignore", { core.chat_send_player( placer:get_player_name(), core.colorize("#FF0000", - "You can't place 'ignore' nodes!")) + S("You can't place 'ignore' nodes!"))) return "" end, }) diff --git a/builtin/locale/template.txt b/builtin/locale/template.txt new file mode 100644 index 000000000..c5ace1a2f --- /dev/null +++ b/builtin/locale/template.txt @@ -0,0 +1,224 @@ +# textdomain: __builtin +Empty command.= +Invalid command: @1= +Invalid command usage.= +You don't have permission to run this command (missing privileges: @1).= +Unable to get position of player @1.= +Incorrect area format. Expected: (x1,y1,z1) (x2,y2,z2)= += +Show chat action (e.g., '/me orders a pizza' displays ' orders a pizza')= +Show the name of the server owner= +The administrator of this server is @1.= +There's no administrator named in the config file.= +[]= +Show privileges of yourself or another player= +Player @1 does not exist.= +Privileges of @1: @2= += +Return list of all online players with privilege= +Invalid parameters (see /help haspriv).= +Unknown privilege!= +Players online with the "@1" privilege: @2= +Your privileges are insufficient.= +Unknown privilege: @1= +@1 granted you privileges: @2= + ( | all)= +Give privileges to player= +Invalid parameters (see /help grant).= + | all= +Grant privileges to yourself= +Invalid parameters (see /help grantme).= +@1 revoked privileges from you: @2= +Remove privileges from player= +Invalid parameters (see /help revoke).= +Revoke privileges from yourself= +Invalid parameters (see /help revokeme).= + = +Set player's password= +Name field required.= +Your password was cleared by @1.= +Password of player "@1" cleared.= +Your password was set by @1.= +Password of player "@1" set.= += +Set empty password for a player= +Reload authentication data= +Done.= +Failed.= +Remove a player's data= +Player "@1" removed.= +No such player "@1" to remove.= +Player "@1" is connected, cannot remove.= +Unhandled remove_player return code @1.= +Cannot teleport out of map bounds!= +Cannot get player with name @1.= +Cannot teleport, @1 is attached to an object!= +Teleporting @1 to @2.= +One does not teleport to oneself.= +Cannot get teleportee with name @1.= +Cannot get target player with name @1.= +Teleporting @1 to @2 at @3.= +,, | | ,, | = +Teleport to position or player= +You don't have permission to teleport other players (missing privilege: @1).= +([-n] ) | = +Set or read server configuration setting= +Failed. Use '/set -n ' to create a new setting.= +@1 @= @2= += +Invalid parameters (see /help set).= +Finished emerging @1 blocks in @2ms.= +emergeblocks update: @1/@2 blocks emerged (@3%)= +(here []) | ( )= +Load (or, if nonexistent, generate) map blocks contained in area pos1 to pos2 ( and must be in parentheses)= +Started emerge of area ranging from @1 to @2.= +Delete map blocks contained in area pos1 to pos2 ( and must be in parentheses)= +Successfully cleared area ranging from @1 to @2.= +Failed to clear one or more blocks in area.= +Resets lighting in the area between pos1 and pos2 ( and must be in parentheses)= +Successfully reset light in the area ranging from @1 to @2.= +Failed to load one or more blocks in area.= +List mods installed on the server= +Cannot give an empty item.= +Cannot give an unknown item.= +Giving 'ignore' is not allowed.= +@1 is not a known player.= +@1 partially added to inventory.= +@1 could not be added to inventory.= +@1 added to inventory.= +@1 partially added to inventory of @2.= +@1 could not be added to inventory of @2.= +@1 added to inventory of @2.= + [ []]= +Give item to player= +Name and ItemString required.= + [ []]= +Give item to yourself= +ItemString required.= + [,,]= +Spawn entity at given (or your) position= +EntityName required.= +Unable to spawn entity, player is nil.= +Cannot spawn an unknown entity.= +Invalid parameters (@1).= +@1 spawned.= +@1 failed to spawn.= +Destroy item in hand= +Unable to pulverize, no player.= +Unable to pulverize, no item in hand.= +An item was pulverized.= +[] [] []= +Check who last touched a node or a node near it within the time specified by . Default: range @= 0, seconds @= 86400 @= 24h, limit @= 5. Set to inf for no time limit= +Rollback functions are disabled.= +That limit is too high!= +Checking @1 ...= +Nobody has touched the specified location in @1 seconds.= +@1 @2 @3 -> @4 @5 seconds ago.= +Punch a node (range@=@1, seconds@=@2, limit@=@3).= +( []) | (: [])= +Revert actions of a player. Default for is 60. Set to inf for no time limit= +Invalid parameters. See /help rollback and /help rollback_check.= +Reverting actions of player '@1' since @2 seconds.= +Reverting actions of @1 since @2 seconds.= +(log is too long to show)= +Reverting actions succeeded.= +Reverting actions FAILED.= +Show server status= +This command was disabled by a mod or game.= +[<0..23>:<0..59> | <0..24000>]= +Show or set time of day= +Current time is @1:@2.= +You don't have permission to run this command (missing privilege: @1).= +Invalid time.= +Time of day changed.= +Invalid hour (must be between 0 and 23 inclusive).= +Invalid minute (must be between 0 and 59 inclusive).= +Show day count since world creation= +Current day is @1.= +[ | -1] [reconnect] []= +Shutdown server (-1 cancels a delayed shutdown)= +Server shutting down (operator request).= +Ban the IP of a player or show the ban list= +The ban list is empty.= +Ban list: @1= +Player is not online.= +Failed to ban player.= +Banned @1.= + | = +Remove IP ban belonging to a player/IP= +Failed to unban player/IP.= +Unbanned @1.= + []= +Kick a player= +Failed to kick player @1.= +Kicked @1.= +[full | quick]= +Clear all objects in world= +Invalid usage, see /help clearobjects.= +Clearing all objects. This may take a long time. You may experience a timeout. (by @1)= +Cleared all objects.= + = +Send a direct message to a player= +Invalid usage, see /help msg.= +The player @1 is not online.= +DM from @1: @2= +Message sent.= +Get the last login time of a player or yourself= +@1's last login time was @2.= +@1's last login time is unknown.= +Clear the inventory of yourself or another player= +You don't have permission to clear another player's inventory (missing privilege: @1).= +@1 cleared your inventory.= +Cleared @1's inventory.= +Player must be online to clear inventory!= +Players can't be killed, damage has been disabled.= +Player @1 is not online.= +You are already dead.= +@1 is already dead.= +@1 has been killed.= +Kill player or yourself= +Available commands: @1= +Use '/help ' to get more information, or '/help all' to list everything.= +Available commands:= +Command not available: @1= +[all | privs | ]= +Get help for commands or list privileges= +Available privileges:= +Command= +Parameters= +For more information, click on any entry in the list.= +Double-click to copy the entry to the chat history.= +Command: @1 @2= +Available commands: (see also: /help )= +Close= +Privilege= +Description= +print [] | dump [] | save [ []] | reset= +Handle the profiler and profiling data= +Statistics written to action log.= +Statistics were reset.= +Usage: @1= +Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).= +(no description)= +Can interact with things and modify the world= +Can speak in chat= +Can modify 'shout' and 'interact' privileges= +Can modify privileges= +Can teleport self= +Can teleport other players= +Can set the time of day using /time= +Can do server maintenance stuff= +Can bypass node protection in the world= +Can ban and unban players= +Can kick players= +Can use /give and /giveme= +Can use /setpassword and /clearpassword= +Can use fly mode= +Can use fast mode= +Can fly through solid nodes using noclip mode= +Can use the rollback functionality= +Allows enabling various debug options that may affect gameplay= +Unknown Item= +Air= +Ignore= +You can't place 'ignore' nodes!= diff --git a/builtin/profiler/init.lua b/builtin/profiler/init.lua index a0033d752..7f63dfaea 100644 --- a/builtin/profiler/init.lua +++ b/builtin/profiler/init.lua @@ -15,6 +15,8 @@ --with this program; if not, write to the Free Software Foundation, Inc., --51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +local S = core.get_translator("__builtin") + local function get_bool_default(name, default) local val = core.settings:get_bool(name) if val == nil then @@ -40,9 +42,9 @@ function profiler.init_chatcommand() instrumentation.init_chatcommand() end - local param_usage = "print [filter] | dump [filter] | save [format [filter]] | reset" + local param_usage = S("print [] | dump [] | save [ []] | reset") core.register_chatcommand("profiler", { - description = "handle the profiler and profiling data", + description = S("Handle the profiler and profiling data"), params = param_usage, privs = { server=true }, func = function(name, param) @@ -51,21 +53,19 @@ function profiler.init_chatcommand() if command == "dump" then core.log("action", reporter.print(sampler.profile, arg0)) - return true, "Statistics written to action log" + return true, S("Statistics written to action log.") elseif command == "print" then return true, reporter.print(sampler.profile, arg0) elseif command == "save" then return reporter.save(sampler.profile, args[1] or "txt", args[2]) elseif command == "reset" then sampler.reset() - return true, "Statistics were reset" + return true, S("Statistics were reset.") end - return false, string.format( - "Usage: %s\n" .. - "Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).", - param_usage - ) + return false, + S("Usage: @1", param_usage) .. "\n" .. + S("Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).") end }) diff --git a/src/server.cpp b/src/server.cpp index 81cdd1f8d..a8d452783 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2496,7 +2496,9 @@ void Server::fillMediaCache() // Collect all media file paths std::vector paths; - // The paths are ordered in descending priority + + // ordered in descending priority + paths.push_back(getBuiltinLuaPath() + DIR_DELIM + "locale"); fs::GetRecursiveDirs(paths, porting::path_user + DIR_DELIM + "textures" + DIR_DELIM + "server"); fs::GetRecursiveDirs(paths, m_gamespec.path + DIR_DELIM + "textures"); m_modmgr->getModsMediaPaths(paths); diff --git a/util/updatepo.sh b/util/updatepo.sh index 168483bd4..95acb01ea 100755 --- a/util/updatepo.sh +++ b/util/updatepo.sh @@ -58,6 +58,7 @@ xgettext --package-name=minetest \ --keyword=fgettext_ne \ --keyword=strgettext \ --keyword=wstrgettext \ + --keyword=core.gettext \ --keyword=showTranslatedStatusText \ --output $potfile \ --from-code=utf-8 \