##### Minetest version
<!--
-Paste Minetest version between quotes below
-If you are on a devel version, please add git commit hash
+Paste Minetest version between quotes below.
+If you are on a devel version, please add git commit hash.
You can use `minetest --version` to find it.
-->
```
<!-- Describe your problem here -->
##### Steps to reproduce
-<!-- For bug reports or build issues, explain how the problem happened -->
+<!-- Explain how the problem has happened, providing a minimal test (i.e. a code snippet reduced to the bone) where possible -->
erlehmann, Warr1024, rollerozxa:
textures/base/pack/no_screenshot.png
+kilbith:
+ textures/base/pack/server_favorite.png
+
+SmallJoker
+ textures/base/pack/server_favorite_delete.png (based on server_favorite.png)
+
License of Minetest source code
-------------------------------
android:name=".UnzipService"
android:enabled="true"
android:exported="false" />
- </application>
+
+ <provider
+ android:name="androidx.core.content.FileProvider"
+ android:authorities="net.minetest.minetest.fileprovider"
+ android:grantUriPermissions="true"
+ android:exported="false">
+ <meta-data
+ android:name="android.support.FILE_PROVIDER_PATHS"
+ android:resource="@xml/filepaths" />
+ </provider>
+
+</application>
</manifest>
import android.os.Build;
import android.os.Bundle;
import android.text.InputType;
+import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.WindowManager;
import androidx.annotation.Keep;
import androidx.appcompat.app.AlertDialog;
+import androidx.core.content.FileProvider;
+import java.io.File;
import java.util.Objects;
// Native code finds these methods by name (see porting_android.cpp).
public String getCachePath() {
return Utils.getCacheDirectory(this).getAbsolutePath();
}
+
+ public void shareFile(String path) {
+ File file = new File(path);
+ if (!file.exists()) {
+ Log.e("GameActivity", "File " + file.getAbsolutePath() + " doesn't exist");
+ return;
+ }
+
+ Uri fileUri = FileProvider.getUriForFile(this, "net.minetest.minetest.fileprovider", file);
+
+ Intent intent = new Intent(Intent.ACTION_SEND, fileUri);
+ intent.setDataAndType(fileUri, getContentResolver().getType(fileUri));
+ intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ intent.putExtra(Intent.EXTRA_STREAM, fileUri);
+
+ Intent shareIntent = Intent.createChooser(intent, null);
+ startActivity(shareIntent);
+ }
}
--- /dev/null
+<paths>
+ <external-files-path path="Minetest/" name="minetest" />
+</paths>
}
function core.formspec_escape(text)
-- Use explicit character set instead of dot here because it doubles the performance
- return text and text:gsub("[\\%[%];,]", formspec_escapes)
+ return text and string.gsub(text, "[\\%[%];,]", formspec_escapes)
end
end
end
end
+
+function confirmation_formspec(message, confirm_id, confirm_label, cancel_id, cancel_label)
+ return "size[10,2.5,true]" ..
+ "label[0.5,0.5;" .. message .. "]" ..
+ "style[" .. confirm_id .. ";bgcolor=red]" ..
+ "button[0.5,1.5;2.5,0.5;" .. confirm_id .. ";" .. confirm_label .. "]" ..
+ "button[7.0,1.5;2.5,0.5;" .. cancel_id .. ";" .. cancel_label .. "]"
+end
function confirm_overwrite.get_formspec()
local package = confirm_overwrite.package
- return "size[11.5,4.5,true]" ..
- "label[2,2;" ..
- fgettext("\"$1\" already exists. Would you like to overwrite it?", package.name) .. "]"..
- "style[install;bgcolor=red]" ..
- "button[3.25,3.5;2.5,0.5;install;" .. fgettext("Overwrite") .. "]" ..
- "button[5.75,3.5;2.5,0.5;cancel;" .. fgettext("Cancel") .. "]"
+ return confirmation_formspec(
+ fgettext("\"$1\" already exists. Would you like to overwrite it?", package.name),
+ 'install', fgettext("Overwrite"),
+ 'cancel', fgettext("Cancel"))
end
function confirm_overwrite.handle_submit(this, fields)
--------------------------------------------------------------------------------
local function delete_content_formspec(dialogdata)
- local retval =
- "size[11.5,4.5,true]" ..
- "label[2,2;" ..
- fgettext("Are you sure you want to delete \"$1\"?", dialogdata.content.name) .. "]"..
- "style[dlg_delete_content_confirm;bgcolor=red]" ..
- "button[3.25,3.5;2.5,0.5;dlg_delete_content_confirm;" .. fgettext("Delete") .. "]" ..
- "button[5.75,3.5;2.5,0.5;dlg_delete_content_cancel;" .. fgettext("Cancel") .. "]"
-
- return retval
+ return confirmation_formspec(
+ fgettext("Are you sure you want to delete \"$1\"?", dialogdata.content.name),
+ 'dlg_delete_content_confirm', fgettext("Delete"),
+ 'dlg_delete_content_cancel', fgettext("Cancel"))
end
--------------------------------------------------------------------------------
local function delete_world_formspec(dialogdata)
- local retval =
- "size[10,2.5,true]" ..
- "label[0.5,0.5;" ..
- fgettext("Delete World \"$1\"?", dialogdata.delete_name) .. "]" ..
- "style[world_delete_confirm;bgcolor=red]" ..
- "button[0.5,1.5;2.5,0.5;world_delete_confirm;" .. fgettext("Delete") .. "]" ..
- "button[7.0,1.5;2.5,0.5;world_delete_cancel;" .. fgettext("Cancel") .. "]"
- return retval
+ return confirmation_formspec(
+ fgettext("Delete World \"$1\"?", dialogdata.delete_name),
+ 'world_delete_confirm', fgettext("Delete"),
+ 'world_delete_cancel', fgettext("Cancel"))
end
local function delete_world_buttonhandler(this, fields)
--- /dev/null
+--Minetest
+--Copyright (C) 2022 rubenwardy
+--
+--This program is free software; you can redistribute it and/or modify
+--it under the terms of the GNU Lesser General Public License as published by
+--the Free Software Foundation; either version 2.1 of the License, or
+--(at your option) any later version.
+--
+--This program is distributed in the hope that it will be useful,
+--but WITHOUT ANY WARRANTY; without even the implied warranty of
+--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+--GNU Lesser General Public License for more details.
+--
+--You should have received a copy of the GNU Lesser General Public License along
+--with this program; if not, write to the Free Software Foundation, Inc.,
+--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+--------------------------------------------------------------------------------
+
+local function register_formspec(dialogdata)
+ local title = fgettext("Joining $1", dialogdata.server and dialogdata.server.name or dialogdata.address)
+ local buttons_y = 4 + 1.3
+ if dialogdata.error then
+ buttons_y = buttons_y + 0.8
+ end
+
+ local retval = {
+ "formspec_version[4]",
+ "size[8,", tostring(buttons_y + 1.175), "]",
+ "set_focus[", (dialogdata.name ~= "" and "password" or "name"), "]",
+ "label[0.375,0.8;", title, "]",
+ "field[0.375,1.575;7.25,0.8;name;", core.formspec_escape(fgettext("Name")), ";",
+ core.formspec_escape(dialogdata.name), "]",
+ "pwdfield[0.375,2.875;7.25,0.8;password;", core.formspec_escape(fgettext("Password")), "]",
+ "pwdfield[0.375,4.175;7.25,0.8;password_2;", core.formspec_escape(fgettext("Confirm Password")), "]"
+ }
+
+ if dialogdata.error then
+ table.insert_all(retval, {
+ "box[0.375,", tostring(buttons_y - 0.9), ";7.25,0.6;darkred]",
+ "label[0.625,", tostring(buttons_y - 0.6), ";", core.formspec_escape(dialogdata.error), "]",
+ })
+ end
+
+ table.insert_all(retval, {
+ "container[0.375,", tostring(buttons_y), "]",
+ "button[0,0;2.5,0.8;dlg_register_confirm;", fgettext("Register"), "]",
+ "button[4.75,0;2.5,0.8;dlg_register_cancel;", fgettext("Cancel"), "]",
+ "container_end[]",
+ })
+
+ return table.concat(retval, "")
+end
+
+--------------------------------------------------------------------------------
+local function register_buttonhandler(this, fields)
+ this.data.name = fields.name
+ this.data.error = nil
+
+ if fields.dlg_register_confirm or fields.key_enter then
+ if fields.name == "" then
+ this.data.error = fgettext("Missing name")
+ return true
+ end
+ if fields.password ~= fields.password_2 then
+ this.data.error = fgettext("Passwords do not match")
+ return true
+ end
+
+ gamedata.playername = fields.name
+ gamedata.password = fields.password
+ gamedata.address = this.data.address
+ gamedata.port = this.data.port
+ gamedata.allow_login_or_register = "register"
+ gamedata.selected_world = 0
+
+ assert(gamedata.address and gamedata.port)
+
+ local server = this.data.server
+ if server then
+ serverlistmgr.add_favorite(server)
+ gamedata.servername = server.name
+ gamedata.serverdescription = server.description
+ else
+ gamedata.servername = ""
+ gamedata.serverdescription = ""
+
+ serverlistmgr.add_favorite({
+ address = gamedata.address,
+ port = gamedata.port,
+ })
+ end
+
+ core.settings:set("name", fields.name)
+ core.settings:set("address", gamedata.address)
+ core.settings:set("remote_port", gamedata.port)
+
+ core.start()
+ end
+
+ if fields["dlg_register_cancel"] then
+ this:delete()
+ return true
+ end
+
+ return false
+end
+
+--------------------------------------------------------------------------------
+function create_register_dialog(address, port, server)
+ assert(address)
+ assert(type(port) == "number")
+
+ local retval = dialog_create("dlg_register",
+ register_formspec,
+ register_buttonhandler,
+ nil)
+ retval.data.address = address
+ retval.data.port = port
+ retval.data.server = server
+ retval.data.name = core.settings:get("name") or ""
+ return retval
+end
mt_color_grey = "#AAAAAA"
mt_color_blue = "#6389FF"
+mt_color_lightblue = "#99CCFF"
mt_color_green = "#72FF63"
mt_color_dark_green = "#25C191"
mt_color_orange = "#FF8800"
dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua")
dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua")
dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua")
+dofile(menupath .. DIR_DELIM .. "dlg_register.lua")
dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua")
local tabs = {}
fgettext("Active renderer:") .. "\n" ..
core.formspec_escape(core.get_screen_info().render_info) .. "]"
- if PLATFORM ~= "Android" then
+ if PLATFORM == "Android" then
+ fs = fs .. "button[0,4;3.5,1;share_debug;" .. fgettext("Share debug log") .. "]"
+ else
fs = fs .. "tooltip[userdata;" ..
fgettext("Opens the directory that contains user-provided worlds, games, mods,\n" ..
"and texture packs in a file manager / explorer.") .. "]"
core.open_url("https://www.minetest.net")
end
+ if fields.share_debug then
+ local path = core.get_user_path() .. DIR_DELIM .. "debug.txt"
+ core.share_file(path)
+ end
+
if fields.userdata then
core.open_dir(core.get_user_path())
end
"field[4.25,0.5;1.25,0.75;te_port;;" ..
core.formspec_escape(core.settings:get("remote_port")) .. "]" ..
- -- Name / Password
- "label[0.25,1.55;" .. fgettext("Name") .. "]" ..
- "label[3,1.55;" .. fgettext("Password") .. "]" ..
- "field[0.25,1.75;2.75,0.75;te_name;;" ..
- core.formspec_escape(core.settings:get("name")) .. "]" ..
- "pwdfield[3,1.75;2.5,0.75;te_pwd;]" ..
-
-- Description Background
- "label[0.25,2.75;" .. fgettext("Server Description") .. "]" ..
- "box[0.25,3;5.25,2.75;#999999]"..
+ "label[0.25,1.6;" .. fgettext("Server Description") .. "]" ..
+ "box[0.25,1.85;5.25,2.7;#999999]"..
+
+ -- Name / Password
+ "container[0,4.8]" ..
+ "label[0.25,0;" .. fgettext("Name") .. "]" ..
+ "label[3,0;" .. fgettext("Password") .. "]" ..
+ "field[0.25,0.2;2.625,0.75;te_name;;" .. core.formspec_escape(core.settings:get("name")) .. "]" ..
+ "pwdfield[2.875,0.2;2.625,0.75;te_pwd;]" ..
+ "container_end[]" ..
-- Connect
- "button[3,6;2.5,0.75;btn_mp_connect;" .. fgettext("Connect") .. "]"
+ "button[3,6;2.5,0.75;btn_mp_login;" .. fgettext("Login") .. "]"
+
+ if core.settings:get_bool("enable_split_login_register") then
+ retval = retval .. "button[0.25,6;2.5,0.75;btn_mp_register;" .. fgettext("Register") .. "]"
+ end
if tabdata.selected then
if gamedata.fav then
- retval = retval .. "button[0.25,6;2.5,0.75;btn_delete_favorite;" ..
- fgettext("Del. Favorite") .. "]"
+ retval = retval .. "tooltip[btn_delete_favorite;" .. fgettext("Remove favorite") .. "]"
+ retval = retval .. "style[btn_delete_favorite;padding=6]"
+ retval = retval .. "image_button[5,1.3;0.5,0.5;" .. defaulttexturedir ..
+ "server_favorite_delete.png;btn_delete_favorite;]"
end
if gamedata.serverdescription then
- retval = retval .. "textarea[0.25,3;5.25,2.75;;;" ..
+ retval = retval .. "textarea[0.25,1.85;5.2,2.75;;;" ..
core.formspec_escape(gamedata.serverdescription) .. "]"
end
end
return true
end
- if (fields.btn_mp_connect or fields.key_enter)
+ if (fields.btn_mp_login or fields.key_enter)
and fields.te_address ~= "" and fields.te_port then
gamedata.playername = fields.te_name
gamedata.password = fields.te_pwd
gamedata.address = fields.te_address
gamedata.port = tonumber(fields.te_port)
+
+ local enable_split_login_register = core.settings:get_bool("enable_split_login_register")
+ gamedata.allow_login_or_register = enable_split_login_register and "login" or "any"
gamedata.selected_world = 0
local idx = core.get_table_index("servers")
return true
end
+ if fields.btn_mp_register and fields.te_address ~= "" and fields.te_port then
+ local idx = core.get_table_index("servers")
+ local server = idx and tabdata.lookup[idx]
+ if server and (server.address ~= fields.te_address or server.port ~= tonumber(fields.te_port)) then
+ server = nil
+ end
+
+ if server and not is_server_protocol_compat_or_error(
+ server.proto_min, server.proto_max) then
+ return true
+ end
+
+ local dlg = create_register_dialog(fields.te_address, tonumber(fields.te_port), server)
+ dlg:set_parent(tabview)
+ tabview:hide()
+ dlg:show()
+ return true
+ end
+
return false
end
.. getSettingIndex.NodeHighlighting() .. "]" ..
"dropdown[0.25,3.6;3.5;dd_leaves_style;" .. dd_options.leaves[1] .. ";"
.. getSettingIndex.Leaves() .. "]" ..
- "box[4,0;3.75,4.5;#999999]" ..
+ "box[4,0;3.75,4.9;#999999]" ..
"label[4.25,0.1;" .. fgettext("Texturing:") .. "]" ..
"dropdown[4.25,0.55;3.5;dd_filters;" .. dd_options.filters[1] .. ";"
.. getSettingIndex.Filter() .. "]" ..
"label[4.25,2.15;" .. fgettext("Antialiasing:") .. "]" ..
"dropdown[4.25,2.6;3.5;dd_antialiasing;" .. dd_options.antialiasing[1] .. ";"
.. getSettingIndex.Antialiasing() .. "]" ..
- "label[4.25,3.45;" .. fgettext("Screen:") .. "]" ..
- "checkbox[4.25,3.6;cb_autosave_screensize;" .. fgettext("Autosave Screen Size") .. ";"
- .. dump(core.settings:get_bool("autosave_screensize")) .. "]" ..
"box[8,0;3.75,4.5;#999999]"
local video_driver = core.settings:get("video_driver")
if core.settings:get("touchscreen_threshold") ~= nil then
tab_string = tab_string ..
- "label[4.3,4.2;" .. fgettext("Touchthreshold: (px)") .. "]" ..
- "dropdown[4.25,4.65;3.5;dd_touchthreshold;0,10,20,30,40,50;" ..
+ "label[4.25,3.5;" .. fgettext("Touch threshold (px):") .. "]" ..
+ "dropdown[4.25,3.95;3.5;dd_touchthreshold;0,10,20,30,40,50;" ..
((tonumber(core.settings:get("touchscreen_threshold")) / 10) + 1) ..
- "]box[4.0,4.5;3.75,1.0;#999999]"
+ "]"
+ else
+ tab_string = tab_string ..
+ "label[4.25,3.65;" .. fgettext("Screen:") .. "]" ..
+ "checkbox[4.25,3.9;cb_autosave_screensize;" .. fgettext("Autosave Screen Size") .. ";"
+ .. dump(core.settings:get_bool("autosave_screensize")) .. "]"
end
if shaders_enabled then
# 0 to disable queueing and -1 to make the queue size unlimited.
max_out_chat_queue_size (Maximum size of the out chat queue) int 20
-# Enable register confirmation when connecting to server.
-# If disabled, new account will be registered automatically.
-enable_register_confirmation (Enable register confirmation) bool true
+# If enabled, account registration is separate from login in the UI.
+# If disabled, new accounts will be registered automatically when logging in.
+enable_split_login_register (Enable split login/register) bool true
[*Advanced]
### Serialized
This is called "stackstring" or "itemstring". It is a simple string with
-1-3 components: the full item identifier, an optional amount and an optional
-wear value. Syntax:
+1-4 components:
- <identifier> [<amount>[ <wear>]]
+1. Full item identifier ("item name")
+2. Optional amount
+3. Optional wear value
+4. Optional item metadata
+
+Syntax:
+
+ <identifier> [<amount>[ <wear>[ <metadata>]]]
Examples:
* `"default:dirt 5"`: 5 dirt
* `"default:pick_stone"`: a new stone pickaxe
* `"default:pick_wood 1 21323"`: a wooden pickaxe, ca. 1/3 worn out
+* `[[default:pick_wood 1 21323 "\u0001description\u0002My worn out pick\u0003"]]`:
+ * a wooden pickaxe from the `default` mod,
+ * amount must be 1 (pickaxe is a tool), ca. 1/3 worn out (it's a tool),
+ * with the `description` field set to `"My worn out pick"` in its metadata
+* `[[default:dirt 5 0 "\u0001description\u0002Special dirt\u0003"]]`:
+ * analogeous to the above example
+ * note how the wear is set to `0` as dirt is not a tool
+
+You should ideally use the `ItemStack` format to build complex item strings
+(especially if they use item metadata)
+without relying on the serialization format. Example:
+
+ local stack = ItemStack("default:pick_wood")
+ stack:set_wear(21323)
+ stack:get_meta():set_string("description", "My worn out pick")
+ local itemstring = stack:to_string()
+
+Additionally the methods `minetest.itemstring_with_palette(item, palette_index)`
+and `minetest.itemstring_with_color(item, colorstring)` may be used to create
+item strings encoding color information in their metadata.
### Table format
{"group:water"},
{"group:bowl"},
},
- -- preserve = {"group:bowl"}, -- Not implemented yet (TODO)
}
Another example: Make red wool from white wool and red dye:
* `minetest.pointed_thing_to_face_pos(placer, pointed_thing)`: returns a
position.
* returns the exact position on the surface of a pointed node
+* `minetest.get_tool_wear_after_use(uses [, initial_wear])`
+ * Simulates a tool being used once and returns the added wear,
+ such that, if only this function is used to calculate wear,
+ the tool will break exactly after `uses` times of uses
+ * `uses`: Number of times the tool can be used
+ * `initial_wear`: The initial wear the tool starts with (default: 0)
* `minetest.get_dig_params(groups, tool_capabilities [, wear])`:
Simulates an item that digs a node.
Returns a table with the following fields:
or those of the hand if none are defined for this item type
* `add_wear(amount)`
* Increases wear by `amount` if the item is a tool, otherwise does nothing
+ * Valid `amount` range is [0,65536]
* `amount`: number, integer
+* `add_wear_by_uses(max_uses)`
+ * Increases wear in such a way that, if only this function is called,
+ the item breaks after `max_uses` times
+ * Valid `max_uses` range is [0,65536]
+ * Does nothing if item is not a tool or if `max_uses` is 0
* `add_item(item)`: returns leftover `ItemStack`
* Put some item or stack onto this stack
* `item_fits(item)`: returns `true` if item or stack can be fully added to
core.open_dir(path)
^ opens the path in the system file browser/explorer, returns false on failure.
^ Must be an existing directory.
+core.share_file(path)
+^ Android only. Shares file using the share popup
core.get_version() (possible in async calls)
^ returns current core version
MtEventManager *event,
RenderingEngine *rendering_engine,
bool ipv6,
- GameUI *game_ui
+ GameUI *game_ui,
+ ELoginRegister allow_login_or_register
):
m_mesh_update_thread(this),
m_tsrc(tsrc),
m_media_downloader(new ClientMediaDownloader()),
m_state(LC_Created),
m_game_ui(game_ui),
- m_modchannel_mgr(new ModChannelMgr())
+ m_modchannel_mgr(new ModChannelMgr()),
+ m_allow_login_or_register(allow_login_or_register)
{
// Add local player
m_env.setLocalPlayer(new LocalPlayer(this, playername));
initial_step = false;
}
else if(m_state == LC_Created) {
- if (m_is_registration_confirmation_state) {
- // Waiting confirmation
- return;
- }
float &counter = m_connection_reinit_timer;
counter -= dtime;
if(counter <= 0.0) {
Send(&pkt);
}
-void Client::promptConfirmRegistration(AuthMechanism chosen_auth_mechanism)
-{
- m_chosen_auth_mech = chosen_auth_mechanism;
- m_is_registration_confirmation_state = true;
-}
-
-void Client::confirmRegistration()
-{
- m_is_registration_confirmation_state = false;
- startAuth(m_chosen_auth_mech);
-}
-
void Client::startAuth(AuthMechanism chosen_auth_mechanism)
{
m_chosen_auth_mech = chosen_auth_mechanism;
#include "mesh_generator_thread.h"
#include "network/address.h"
#include "network/peerhandler.h"
+#include "gameparams.h"
#include <fstream>
#define CLIENT_CHAT_MESSAGE_LIMIT_PER_10S 10.0f
MtEventManager *event,
RenderingEngine *rendering_engine,
bool ipv6,
- GameUI *game_ui
+ GameUI *game_ui,
+ ELoginRegister allow_login_or_register
);
~Client();
u16 getProtoVersion()
{ return m_proto_ver; }
- void confirmRegistration();
- bool m_is_registration_confirmation_state = false;
+ ELoginRegister m_allow_login_or_register = ELoginRegister::Any;
bool m_simple_singleplayer_mode;
float mediaReceiveProgress();
static AuthMechanism choseAuthMech(const u32 mechs);
void sendInit(const std::string &playerName);
- void promptConfirmRegistration(AuthMechanism chosen_auth_mechanism);
void startAuth(AuthMechanism chosen_auth_mechanism);
void sendDeletedBlocks(std::vector<v3s16> &blocks);
void sendGotBlocks(const std::vector<v3s16> &blocks);
start_data.name = menudata.name;
start_data.password = menudata.password;
start_data.address = std::move(menudata.address);
+ start_data.allow_login_or_register = menudata.allow_login_or_register;
server_name = menudata.servername;
server_description = menudata.serverdescription;
// Number of blocks occlusion culled
u32 blocks_occlusion_culled = 0;
- // No occlusion culling when free_move is on and camera is
- // inside ground
+ // No occlusion culling when free_move is on and camera is inside ground
bool occlusion_culling_enabled = true;
- if ((g_settings->getBool("free_move") && g_settings->getBool("noclip")) || g_settings->getBool("freecam")) {
+ if (m_control.allow_noclip || g_settings->getBool("freecam")) {
MapNode n = getNode(cam_pos_nodes);
- if (n.getContent() == CONTENT_IGNORE ||
- m_nodedef->get(n).solidness == 2)
+ if (n.getContent() == CONTENT_IGNORE || m_nodedef->get(n).solidness == 2)
occlusion_culling_enabled = false;
}
MapNode n = getNode(floatToInt(m_camera_position, BS));
- // - If the player is in a solid node, make everything black.
- // - If the player is in liquid, draw a semi-transparent overlay.
- // - Do not if player is in third person mode
const ContentFeatures& features = m_nodedef->get(n);
video::SColor post_effect_color = features.post_effect_color;
- if(features.solidness == 2 && !((g_settings->getBool("noclip") || g_settings->getBool("freecam")) &&
- (m_client->checkLocalPrivilege("noclip") || g_settings->getBool("freecam"))) &&
- cam_mode == CAMERA_MODE_FIRST)
- {
+
+ // If the camera is in a solid node, make everything black.
+ // (first person mode only)
+ if (features.solidness == 2 && cam_mode == CAMERA_MODE_FIRST &&
+ !(m_control.allow_noclip || g_settings->getBool("freecam"))) {
post_effect_color = video::SColor(255, 0, 0, 0);
}
- if (post_effect_color.getAlpha() != 0)
- {
+
+ if (post_effect_color.getAlpha() != 0) {
// Draw a full-screen rectangle
video::IVideoDriver* driver = SceneManager->getVideoDriver();
v2u32 ss = driver->getScreenSize();
struct MapDrawControl
{
- // Overrides limits by drawing everything
- bool range_all = false;
// Wanted drawing range
float wanted_range = 0.0f;
+ // Overrides limits by drawing everything
+ bool range_all = false;
+ // Allow rendering out of bounds
+ bool allow_noclip = false;
// show a wire frame for debugging
bool show_wireframe = false;
};
#include "gameparams.h"
#include "gettext.h"
#include "gui/guiChatConsole.h"
-#include "gui/guiConfirmRegistration.h"
#include "gui/guiFormSpecMenu.h"
#include "gui/guiKeyChangeMenu.h"
#include "gui/guiPasswordChange.h"
start_data.password, start_data.address,
*draw_control, texture_src, shader_src,
itemdef_manager, nodedef_manager, sound, eventmgr,
- m_rendering_engine, connect_address.isIPv6(), m_game_ui.get());
+ m_rendering_engine, connect_address.isIPv6(), m_game_ui.get(),
+ start_data.allow_login_or_register);
client->migrateModStorage();
} catch (const BaseException &e) {
*error_message = fmtgettext("Error creating client: %s", e.what());
break;
}
- if (client->m_is_registration_confirmation_state) {
- if (registration_confirmation_shown) {
- // Keep drawing the GUI
- m_rendering_engine->draw_menu_scene(guienv, dtime, true);
- } else {
- registration_confirmation_shown = true;
- (new GUIConfirmRegistration(guienv, guienv->getRootGUIElement(), -1,
- &g_menumgr, client, start_data.name, start_data.password,
- connection_aborted, texture_src))->drop();
- }
- } else {
- wait_time += dtime;
- // Only time out if we aren't waiting for the server we started
- if (!start_data.address.empty() && wait_time > 10) {
- *error_message = gettext("Connection timed out.");
- errorstream << *error_message << std::endl;
- break;
- }
-
- // Update status
- showOverlayMessage(N_("Connecting to server..."), dtime, 20);
+ wait_time += dtime;
+ // Only time out if we aren't waiting for the server we started
+ if (!start_data.address.empty() && wait_time > 10) {
+ *error_message = gettext("Connection timed out.");
+ errorstream << *error_message << std::endl;
+ break;
}
+
+ // Update status
+ showOverlayMessage(N_("Connecting to server..."), dtime, 20);
}
} catch (con::PeerNotFoundException &e) {
// TODO: Should something be done here? At least an info/error
void Game::updateDebugState()
{
LocalPlayer *player = client->getEnv().getLocalPlayer();
+
+ // debug UI and wireframe
bool has_debug = client->checkPrivilege("debug");
bool has_basic_debug = has_debug || (player->hud_flags & HUD_FLAG_BASIC_DEBUG);
hud->disableBlockBounds();
if (!has_debug)
draw_control->show_wireframe = false;
+
+ // noclip
+ draw_control->allow_noclip = m_cache_enable_noclip && client->checkPrivilege("noclip");
}
void Game::updateProfilers(const RunStats &stats, const FpsControl &draw_times,
float direct_brightness;
bool sunlight_seen;
- if ((m_cache_enable_noclip && m_cache_enable_free_move) || g_settings->getBool("freecam")) {
+ // When in noclip mode force same sky brightness as above ground so you
+ // can see properly
+ if ((draw_control->allow_noclip && m_cache_enable_free_move &&
+ client->checkPrivilege("fly")) || g_settings->getBool("freecam")) {
direct_brightness = time_brightness;
sunlight_seen = true;
} else {
camera_impact = getSpeed().Y * -1;
}
- {
- camera_barely_in_ceiling = false;
- v3s16 camera_np = floatToInt(getEyePosition(), BS);
- MapNode n = map->getNode(camera_np);
- if (n.getContent() != CONTENT_IGNORE) {
- if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
- camera_barely_in_ceiling = true;
- }
- }
-
/*
Check properties of the node on which the player is standing
*/
v3f LocalPlayer::getEyeOffset() const
{
- float eye_height = camera_barely_in_ceiling ? m_eye_height - 0.125f : m_eye_height;
- return v3f(0.0f, BS * eye_height, 0.0f);
+ return v3f(0.0f, BS * m_eye_height, 0.0f);
}
ClientActiveObject *LocalPlayer::getParent() const
camera_impact = getSpeed().Y * -1.0f;
}
- {
- camera_barely_in_ceiling = false;
- v3s16 camera_np = floatToInt(getEyePosition(), BS);
- MapNode n = map->getNode(camera_np);
- if (n.getContent() != CONTENT_IGNORE) {
- if (nodemgr->get(n).walkable && nodemgr->get(n).solidness == 2)
- camera_barely_in_ceiling = true;
- }
- }
-
/*
Update the node last under the player
*/
u16 m_breath = PLAYER_MAX_BREATH_DEFAULT;
f32 m_yaw = 0.0f;
f32 m_pitch = 0.0f;
- bool camera_barely_in_ceiling = false;
aabb3f m_collisionbox = aabb3f(-BS * 0.30f, 0.0f, -BS * 0.30f, BS * 0.30f,
BS * 1.75f, BS * 0.30f);
float m_eye_height = 1.625f;
settings->setDefault("enable_client_modding", "true");
settings->setDefault("max_out_chat_queue_size", "20");
settings->setDefault("pause_on_lost_focus", "false");
- settings->setDefault("enable_register_confirmation", "true");
+ settings->setDefault("enable_split_login_register", "true");
settings->setDefault("chat_weblink_color", "#8888FF");
// Cheat Menu
// Altered settings for macOS
#if defined(__MACH__) && defined(__APPLE__)
settings->setDefault("keymap_sneak", "KEY_SHIFT");
- settings->setDefault("fps_max", "0");
#endif
#ifdef HAVE_TOUCHSCREENGUI
#pragma once
#include "irrlichttypes.h"
-
-struct SubgameSpec;
+#include "content/subgames.h"
// Information provided from "main"
struct GameParams
bool is_dedicated_server;
};
+enum class ELoginRegister {
+ Any = 0,
+ Login,
+ Register
+};
+
// Information processed by main menu
struct GameStartData : GameParams
{
std::string address;
bool local_server;
+ ELoginRegister allow_login_or_register = ELoginRegister::Any;
+
// "world_path" must be kept in sync!
WorldSpec world_spec;
};
${CMAKE_CURRENT_SOURCE_DIR}/guiButtonImage.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiButtonItemImage.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiChatConsole.cpp
- ${CMAKE_CURRENT_SOURCE_DIR}/guiConfirmRegistration.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiEditBox.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiEditBoxWithScrollbar.cpp
${CMAKE_CURRENT_SOURCE_DIR}/guiEngine.cpp
+++ /dev/null
-/*
-Minetest
-Copyright (C) 2018 srifqi, Muhammad Rifqi Priyo Susanto
- <muhammadrifqipriyosusanto@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#include "guiConfirmRegistration.h"
-#include "client/client.h"
-#include "guiButton.h"
-#include <IGUICheckBox.h>
-#include <IGUIButton.h>
-#include <IGUIStaticText.h>
-#include <IGUIFont.h>
-#include "guiEditBoxWithScrollbar.h"
-#include "porting.h"
-
-#ifdef HAVE_TOUCHSCREENGUI
- #include "client/renderingengine.h"
-#endif
-
-#include "gettext.h"
-
-// Continuing from guiPasswordChange.cpp
-const int ID_confirmPassword = 262;
-const int ID_confirm = 263;
-const int ID_intotext = 264;
-const int ID_cancel = 265;
-const int ID_message = 266;
-
-GUIConfirmRegistration::GUIConfirmRegistration(gui::IGUIEnvironment *env,
- gui::IGUIElement *parent, s32 id, IMenuManager *menumgr, Client *client,
- const std::string &playername, const std::string &password,
- bool *aborted, ISimpleTextureSource *tsrc) :
- GUIModalMenu(env, parent, id, menumgr),
- m_client(client), m_playername(playername), m_password(password),
- m_aborted(aborted), m_tsrc(tsrc)
-{
-#ifdef HAVE_TOUCHSCREENGUI
- m_touchscreen_visible = false;
-#endif
-}
-
-void GUIConfirmRegistration::regenerateGui(v2u32 screensize)
-{
- acceptInput();
- removeAllChildren();
-
- /*
- Calculate new sizes and positions
- */
-#ifdef HAVE_TOUCHSCREENGUI
- const float s = m_gui_scale * RenderingEngine::getDisplayDensity() / 2;
-#else
- const float s = m_gui_scale;
-#endif
- DesiredRect = core::rect<s32>(
- screensize.X / 2 - 600 * s / 2,
- screensize.Y / 2 - 360 * s / 2,
- screensize.X / 2 + 600 * s / 2,
- screensize.Y / 2 + 360 * s / 2
- );
- recalculateAbsolutePosition(false);
-
- v2s32 size = DesiredRect.getSize();
- v2s32 topleft_client(0, 0);
-
- const wchar_t *text;
-
- /*
- Add stuff
- */
- s32 ypos = 30 * s;
- {
- core::rect<s32> rect2(0, 0, 540 * s, 180 * s);
- rect2 += topleft_client + v2s32(30 * s, ypos);
- static const std::string info_text_template = strgettext(
- "You are about to join this server with the name \"%s\" for the "
- "first time.\n"
- "If you proceed, a new account using your credentials will be "
- "created on this server.\n"
- "Please retype your password and click 'Register and Join' to "
- "confirm account creation, or click 'Cancel' to abort.");
- char info_text_buf[1024];
- porting::mt_snprintf(info_text_buf, sizeof(info_text_buf),
- info_text_template.c_str(), m_playername.c_str());
-
- std::wstring info_text_w = utf8_to_wide(info_text_buf);
- gui::IGUIEditBox *e = new GUIEditBoxWithScrollBar(info_text_w.c_str(),
- true, Environment, this, ID_intotext, rect2, false, true);
- e->drop();
- e->setMultiLine(true);
- e->setWordWrap(true);
- e->setTextAlignment(gui::EGUIA_UPPERLEFT, gui::EGUIA_CENTER);
- }
-
- ypos += 200 * s;
- {
- core::rect<s32> rect2(0, 0, 540 * s, 30 * s);
- rect2 += topleft_client + v2s32(30 * s, ypos);
- gui::IGUIEditBox *e = Environment->addEditBox(m_pass_confirm.c_str(),
- rect2, true, this, ID_confirmPassword);
- e->setPasswordBox(true);
- Environment->setFocus(e);
- }
-
- ypos += 50 * s;
- {
- core::rect<s32> rect2(0, 0, 230 * s, 35 * s);
- rect2 = rect2 + v2s32(size.X / 2 - 220 * s, ypos);
- text = wgettext("Register and Join");
- GUIButton::addButton(Environment, rect2, m_tsrc, this, ID_confirm, text);
- delete[] text;
- }
- {
- core::rect<s32> rect2(0, 0, 120 * s, 35 * s);
- rect2 = rect2 + v2s32(size.X / 2 + 70 * s, ypos);
- text = wgettext("Cancel");
- GUIButton::addButton(Environment, rect2, m_tsrc, this, ID_cancel, text);
- delete[] text;
- }
- {
- core::rect<s32> rect2(0, 0, 500 * s, 40 * s);
- rect2 += topleft_client + v2s32(30 * s, ypos + 40 * s);
- text = wgettext("Passwords do not match!");
- IGUIElement *e = Environment->addStaticText(
- text, rect2, false, true, this, ID_message);
- e->setVisible(false);
- delete[] text;
- }
-}
-
-void GUIConfirmRegistration::drawMenu()
-{
- gui::IGUISkin *skin = Environment->getSkin();
- if (!skin)
- return;
- video::IVideoDriver *driver = Environment->getVideoDriver();
-
- video::SColor bgcolor(140, 0, 0, 0);
- driver->draw2DRectangle(bgcolor, AbsoluteRect, &AbsoluteClippingRect);
-
- gui::IGUIElement::draw();
-#ifdef __ANDROID__
- getAndroidUIInput();
-#endif
-}
-
-void GUIConfirmRegistration::closeMenu(bool goNext)
-{
- if (goNext) {
- m_client->confirmRegistration();
- } else {
- *m_aborted = true;
- infostream << "Connect aborted [Escape]" << std::endl;
- }
- quitMenu();
-}
-
-void GUIConfirmRegistration::acceptInput()
-{
- gui::IGUIElement *e;
- e = getElementFromId(ID_confirmPassword);
- if (e)
- m_pass_confirm = e->getText();
-}
-
-bool GUIConfirmRegistration::processInput()
-{
- if (utf8_to_wide(m_password) != m_pass_confirm) {
- gui::IGUIElement *e = getElementFromId(ID_message);
- if (e)
- e->setVisible(true);
- return false;
- }
- return true;
-}
-
-bool GUIConfirmRegistration::OnEvent(const SEvent &event)
-{
- if (event.EventType == EET_KEY_INPUT_EVENT) {
- // clang-format off
- if ((event.KeyInput.Key == KEY_ESCAPE ||
- event.KeyInput.Key == KEY_CANCEL) &&
- event.KeyInput.PressedDown) {
- closeMenu(false);
- return true;
- }
- // clang-format on
- if (event.KeyInput.Key == KEY_RETURN && event.KeyInput.PressedDown) {
- acceptInput();
- if (processInput())
- closeMenu(true);
- return true;
- }
- }
-
- if (event.EventType != EET_GUI_EVENT)
- return Parent ? Parent->OnEvent(event) : false;
-
- if (event.GUIEvent.EventType == gui::EGET_ELEMENT_FOCUS_LOST && isVisible()) {
- if (!canTakeFocus(event.GUIEvent.Element)) {
- infostream << "GUIConfirmRegistration: Not allowing focus change."
- << std::endl;
- // Returning true disables focus change
- return true;
- }
- } else if (event.GUIEvent.EventType == gui::EGET_BUTTON_CLICKED) {
- switch (event.GUIEvent.Caller->getID()) {
- case ID_confirm:
- acceptInput();
- if (processInput())
- closeMenu(true);
- return true;
- case ID_cancel:
- closeMenu(false);
- return true;
- }
- } else if (event.GUIEvent.EventType == gui::EGET_EDITBOX_ENTER) {
- switch (event.GUIEvent.Caller->getID()) {
- case ID_confirmPassword:
- acceptInput();
- if (processInput())
- closeMenu(true);
- return true;
- }
- }
-
- return false;
-}
-
-#ifdef __ANDROID__
-bool GUIConfirmRegistration::getAndroidUIInput()
-{
- if (!hasAndroidUIInput() || m_jni_field_name != "password")
- return false;
-
- // still waiting
- if (porting::getInputDialogState() == -1)
- return true;
-
- m_jni_field_name.clear();
-
- gui::IGUIElement *e = getElementFromId(ID_confirmPassword);
-
- if (!e || e->getType() != irr::gui::EGUIET_EDIT_BOX)
- return false;
-
- std::string text = porting::getInputDialogValue();
- e->setText(utf8_to_wide(text).c_str());
- return false;
-}
-#endif
+++ /dev/null
-/*
-Minetest
-Copyright (C) 2018 srifqi, Muhammad Rifqi Priyo Susanto
- <muhammadrifqipriyosusanto@gmail.com>
-
-This program is free software; you can redistribute it and/or modify
-it under the terms of the GNU Lesser General Public License as published by
-the Free Software Foundation; either version 2.1 of the License, or
-(at your option) any later version.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-GNU Lesser General Public License for more details.
-
-You should have received a copy of the GNU Lesser General Public License along
-with this program; if not, write to the Free Software Foundation, Inc.,
-51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-*/
-
-#pragma once
-
-#include "irrlichttypes_extrabloated.h"
-#include "modalMenu.h"
-#include <string>
-
-class Client;
-class ISimpleTextureSource;
-
-class GUIConfirmRegistration : public GUIModalMenu
-{
-public:
- GUIConfirmRegistration(gui::IGUIEnvironment *env, gui::IGUIElement *parent,
- s32 id, IMenuManager *menumgr, Client *client,
- const std::string &playername, const std::string &password,
- bool *aborted, ISimpleTextureSource *tsrc);
- /*
- Remove and re-add (or reposition) stuff
- */
- void regenerateGui(v2u32 screensize);
-
- void drawMenu();
-
- void closeMenu(bool goNext);
-
- void acceptInput();
-
- bool processInput();
-
- bool OnEvent(const SEvent &event);
-#ifdef __ANDROID__
- bool getAndroidUIInput();
-#endif
-
-private:
- std::wstring getLabelByID(s32 id) { return L""; }
- std::string getNameByID(s32 id) { return "password"; }
-
- Client *m_client = nullptr;
- const std::string &m_playername;
- const std::string &m_password;
- bool *m_aborted = nullptr;
- std::wstring m_pass_confirm = L"";
- ISimpleTextureSource *m_tsrc;
-};
#pragma once
#include "irrlichttypes_extrabloated.h"
+#include "gameparams.h"
#include <string>
#include <list>
// Data to be passed to the script
MainMenuDataForScript script_data;
+ ELoginRegister allow_login_or_register = ELoginRegister::Any;
+
MainMenuData() = default;
};
u32 Mapgen::getBlockSeed2(v3s16 p, s32 seed)
{
- u32 n = 1619 * p.X + 31337 * p.Y + 52591 * p.Z + 1013 * seed;
+ // Multiply by unsigned number to avoid signed overflow (UB)
+ u32 n = 1619U * p.X + 31337U * p.Y + 52591U * p.Z + 1013U * seed;
n = (n >> 13) ^ n;
return (n * (n * n * 60493 + 19990303) + 1376312589);
}
// Authenticate using that method, or abort if there wasn't any method found
if (chosen_auth_mechanism != AUTH_MECHANISM_NONE) {
- if (chosen_auth_mechanism == AUTH_MECHANISM_FIRST_SRP &&
- !m_simple_singleplayer_mode &&
- !getServerAddress().isLocalhost() &&
- g_settings->getBool("enable_register_confirmation")) {
- promptConfirmRegistration(chosen_auth_mechanism);
+ bool is_register = chosen_auth_mechanism == AUTH_MECHANISM_FIRST_SRP;
+ ELoginRegister mode = is_register ? ELoginRegister::Register : ELoginRegister::Login;
+ if (m_allow_login_or_register != ELoginRegister::Any &&
+ m_allow_login_or_register != mode) {
+ m_chosen_auth_mech = AUTH_MECHANISM_NONE;
+ m_access_denied = true;
+ if (m_allow_login_or_register == ELoginRegister::Login) {
+ m_access_denied_reason =
+ gettext("Name is not registered. To create an account on this server, click 'Register'");
+ } else {
+ m_access_denied_reason =
+ gettext("Name is taken. Please choose another name");
+ }
+ m_con->Disconnect();
} else {
startAuth(chosen_auth_mechanism);
}
#define NOISE_MAGIC_X 1619
#define NOISE_MAGIC_Y 31337
#define NOISE_MAGIC_Z 52591
-#define NOISE_MAGIC_SEED 1013
+// Unsigned magic seed prevents undefined behavior.
+#define NOISE_MAGIC_SEED 1013U
typedef float (*Interp2dFxn)(
float v00, float v10, float v01, float v11,
jnienv->CallVoidMethod(app_global->activity->clazz, url_open, jurl);
}
+void shareFileAndroid(const std::string &path)
+{
+ jmethodID url_open = jnienv->GetMethodID(nativeActivity, "shareFile",
+ "(Ljava/lang/String;)V");
+
+ FATAL_ERROR_IF(url_open == nullptr,
+ "porting::shareFileAndroid unable to find java openURI method");
+
+ jstring jurl = jnienv->NewStringUTF(path.c_str());
+ jnienv->CallVoidMethod(app_global->activity->clazz, url_open, jurl);
+}
+
int getInputDialogState()
{
jmethodID dialogstate = jnienv->GetMethodID(nativeActivity,
void openURIAndroid(const std::string &url);
+/**
+ * Opens a share intent to the file at path
+ *
+ * @param path
+ */
+void shareFileAndroid(const std::string &path);
+
/**
* WORKAROUND for not working callbacks from java -> c++
* get current state of input dialog
}
// add_wear(self, amount) -> true/false
-// The range for "amount" is [0,65535]. Wear is only added if the item
+// The range for "amount" is [0,65536]. Wear is only added if the item
// is a tool. Adding wear might destroy the item.
// Returns true if the item is (or was) a tool.
int LuaItemStack::l_add_wear(lua_State *L)
return 1;
}
+// add_wear_by_uses(self, max_uses) -> true/false
+// The range for "max_uses" is [0,65536].
+// Adds wear to the item in such a way that, if
+// only this function is called to add wear, the item
+// will be destroyed exactly after `max_uses` times of calling it.
+// No-op if `max_uses` is 0 or item is not a tool.
+// Returns true if the item is (or was) a tool.
+int LuaItemStack::l_add_wear_by_uses(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ LuaItemStack *o = checkobject(L, 1);
+ ItemStack &item = o->m_stack;
+ u32 max_uses = readParam<int>(L, 2);
+ u32 add_wear = calculateResultWear(max_uses, item.wear);
+ bool result = item.addWear(add_wear, getGameDef(L)->idef());
+ lua_pushboolean(L, result);
+ return 1;
+}
+
// add_item(self, itemstack or itemstring or table or nil) -> itemstack
// Returns leftover item stack
int LuaItemStack::l_add_item(lua_State *L)
luamethod(LuaItemStack, get_definition),
luamethod(LuaItemStack, get_tool_capabilities),
luamethod(LuaItemStack, add_wear),
+ luamethod(LuaItemStack, add_wear_by_uses),
luamethod(LuaItemStack, add_item),
luamethod(LuaItemStack, item_fits),
luamethod(LuaItemStack, take_item),
static int l_get_tool_capabilities(lua_State *L);
// add_wear(self, amount) -> true/false
- // The range for "amount" is [0,65535]. Wear is only added if the item
+ // The range for "amount" is [0,65536]. Wear is only added if the item
// is a tool. Adding wear might destroy the item.
// Returns true if the item is (or was) a tool.
static int l_add_wear(lua_State *L);
+ // add_wear_by_uses(self, max_uses) -> true/false
+ // The range for "max_uses" is [0,65536].
+ // Adds wear to the item in such a way that, if
+ // only this function is called to add wear, the item
+ // will be destroyed exactly after `max_uses` times of calling it.
+ // No-op if `max_uses` is 0 or item is not a tool.
+ // Returns true if the item is (or was) a tool.
+ static int l_add_wear_by_uses(lua_State *L);
+
// add_item(self, itemstack or itemstring or table or nil) -> itemstack
// Returns leftover item stack
static int l_add_item(lua_State *L);
data->password = getTextData(L,"password");
data->address = getTextData(L,"address");
data->port = getTextData(L,"port");
+
+ const auto val = getTextData(L, "allow_login_or_register");
+ if (val == "login")
+ data->allow_login_or_register = ELoginRegister::Login;
+ else if (val == "register")
+ data->allow_login_or_register = ELoginRegister::Register;
+ else
+ data->allow_login_or_register = ELoginRegister::Any;
}
data->serverdescription = getTextData(L,"serverdescription");
data->servername = getTextData(L,"servername");
return 1;
}
+/******************************************************************************/
+int ModApiMainMenu::l_share_file(lua_State *L)
+{
+#ifdef __ANDROID__
+ std::string path = luaL_checkstring(L, 1);
+ porting::shareFileAndroid(path);
+ lua_pushboolean(L, true);
+#else
+ lua_pushboolean(L, false);
+#endif
+ return 1;
+}
+
/******************************************************************************/
int ModApiMainMenu::l_do_async_callback(lua_State *L)
{
API_FCT(get_max_supp_proto);
API_FCT(open_url);
API_FCT(open_dir);
+ API_FCT(share_file);
API_FCT(do_async_callback);
}
static int l_open_dir(lua_State *L);
+ static int l_share_file(lua_State *L);
+
// async
static int l_do_async_callback(lua_State *L);
return 1;
}
+// get_tool_wear_after_use(uses[, initial_wear])
+int ModApiUtil::l_get_tool_wear_after_use(lua_State *L)
+{
+ NO_MAP_LOCK_REQUIRED;
+ u32 uses = readParam<int>(L, 1);
+ u16 initial_wear = readParam<int>(L, 2, 0);
+ u16 wear = calculateResultWear(uses, initial_wear);
+ lua_pushnumber(L, wear);
+ return 1;
+}
+
// get_dig_params(groups, tool_capabilities[, wear])
int ModApiUtil::l_get_dig_params(lua_State *L)
{
API_FCT(parse_json);
API_FCT(write_json);
+ API_FCT(get_tool_wear_after_use);
API_FCT(get_dig_params);
API_FCT(get_hit_params);
// write_json(data[, styled])
static int l_write_json(lua_State *L);
+ // get_tool_wear_after_use(uses[, initial_wear])
+ static int l_get_tool_wear_after_use(lua_State *L);
+
// get_dig_params(groups, tool_capabilities[, wear])
static int l_get_dig_params(lua_State *L);
v3s16 pos;
MapNode n;
content_t c;
- lbm_lookup_map::const_iterator it = getLBMsIntroducedAfter(stamp);
+ bool pos_valid; // dummy, we know it's valid
+ auto it = getLBMsIntroducedAfter(stamp);
for (; it != m_lbm_lookup.end(); ++it) {
// Cache previous version to speedup lookup which has a very high performance
// penalty on each call
- content_t previous_c{};
- std::vector<LoadingBlockModifierDef *> *lbm_list = nullptr;
+ content_t previous_c = CONTENT_IGNORE;
+ const std::vector<LoadingBlockModifierDef *> *lbm_list = nullptr;
for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++)
for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++)
for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) {
- n = block->getNodeNoEx(pos);
+ n = block->getNodeNoCheck(pos, &pos_valid);
c = n.getContent();
// If content_t are not matching perform an LBM lookup
if (previous_c != c) {
- lbm_list = (std::vector<LoadingBlockModifierDef *> *)
- it->second.lookup(c);
+ lbm_list = it->second.lookup(c);
previous_c = c;
}
}
}
-static u32 calculateResultWear(const u32 uses, const u16 initial_wear)
+u32 calculateResultWear(const u32 uses, const u16 initial_wear)
{
if (uses == 0) {
// Trivial case: Infinite uses
u16 initial_wear = 0
);
+u32 calculateResultWear(const u32 uses, const u16 initial_wear);
f32 getToolRange(const ItemDefinition &def_selected, const ItemDefinition &def_hand);
void runTests(IGameDef *gamedef);
+ void testNoise2dAtOriginWithZeroSeed();
+ void testNoise2dWithMaxSeed();
+ void testNoise2dWithFunPrimes();
void testNoise2dPoint();
void testNoise2dBulk();
+ void testNoise3dAtOriginWithZeroSeed();
+ void testNoise3dWithMaxSeed();
+ void testNoise3dWithFunPrimes();
void testNoise3dPoint();
void testNoise3dBulk();
void testNoiseInvalidParams();
void TestNoise::runTests(IGameDef *gamedef)
{
+ TEST(testNoise2dAtOriginWithZeroSeed);
+ TEST(testNoise2dWithMaxSeed);
+ TEST(testNoise2dWithFunPrimes);
TEST(testNoise2dPoint);
TEST(testNoise2dBulk);
+ TEST(testNoise3dAtOriginWithZeroSeed);
+ TEST(testNoise3dWithMaxSeed);
+ TEST(testNoise3dWithFunPrimes);
TEST(testNoise3dPoint);
TEST(testNoise3dBulk);
TEST(testNoiseInvalidParams);
////////////////////////////////////////////////////////////////////////////////
+void TestNoise::testNoise2dAtOriginWithZeroSeed()
+{
+ float actual{ noise2d(0, 0, 0) };
+ constexpr float expected{ -0.281791f };
+ UASSERT(std::fabs(actual - expected) <= 0.00001);
+}
+
+void TestNoise::testNoise2dWithMaxSeed()
+{
+ float actual{ noise2d(4096, 4096, 2147483647) };
+ constexpr float expected{ 0.950606f };
+ UASSERT(std::fabs(actual - expected) <= 0.00001);
+}
+
+void TestNoise::testNoise2dWithFunPrimes()
+{
+ float actual{ noise2d(-3947, -2333, 7027) };
+ constexpr float expected{ -0.294907f };
+ UASSERT(std::fabs(actual - expected) <= 0.00001);
+}
+
void TestNoise::testNoise2dPoint()
{
NoiseParams np_normal(20, 40, v3f(50, 50, 50), 9, 5, 0.6, 2.0);
}
}
+void TestNoise::testNoise3dAtOriginWithZeroSeed()
+{
+ float actual{ noise2d(0, 0, 0) };
+ constexpr float expected{ -0.281791f };
+ UASSERT(std::fabs(actual - expected) <= 0.00001);
+}
+
+void TestNoise::testNoise3dWithMaxSeed()
+{
+ float actual{ noise3d(4096, 4096, 4096, 2147483647) };
+ constexpr float expected{ -0.775243f };
+ UASSERT(std::fabs(actual - expected) <= 0.00001);
+}
+
+void TestNoise::testNoise3dWithFunPrimes()
+{
+ float actual{ noise2d(3903, -1723, 7411) };
+ constexpr float expected{ 0.989124f };
+ UASSERT(std::fabs(actual - expected) <= 0.00001);
+}
+
void TestNoise::testNoise3dPoint()
{
NoiseParams np_normal(20, 40, v3f(50, 50, 50), 9, 5, 0.6, 2.0);
#include <windows.h>
#endif
-#ifdef __NetBSD__
- #include <sys/param.h>
- #if __NetBSD_Version__ <= 999001500
- #define BSD_ICONV_USED
- #endif
-#elif defined(_ICONV_H_) && (defined(__FreeBSD__) || defined(__OpenBSD__) || \
- defined(__DragonFly__))
- #define BSD_ICONV_USED
-#endif
-
#ifndef _WIN32
static bool convert(const char *to, const char *from, char *outbuf,
{
iconv_t cd = iconv_open(to, from);
-#ifdef BSD_ICONV_USED
- const char *inbuf_ptr = inbuf;
-#else
char *inbuf_ptr = inbuf;
-#endif
char *outbuf_ptr = outbuf;
size_t *inbuf_left_ptr = &inbuf_size;