mirror of
https://github.com/luanti-org/luanti.git
synced 2025-09-30 19:22:14 +00:00
Merge 440f3a5ac1
into 499f2284bd
This commit is contained in:
commit
c304564bb0
12 changed files with 225 additions and 9 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -138,3 +138,6 @@ lib/irrlichtmt
|
||||||
|
|
||||||
# Generated mod storage database
|
# Generated mod storage database
|
||||||
client/mod_storage.sqlite
|
client/mod_storage.sqlite
|
||||||
|
|
||||||
|
# Client keyring
|
||||||
|
client/keyring.json
|
||||||
|
|
|
@ -81,6 +81,7 @@ local function register_buttonhandler(this, fields)
|
||||||
core.settings:set("name", fields.name)
|
core.settings:set("name", fields.name)
|
||||||
core.settings:set("address", gamedata.address)
|
core.settings:set("address", gamedata.address)
|
||||||
core.settings:set("remote_port", gamedata.port)
|
core.settings:set("remote_port", gamedata.port)
|
||||||
|
keyringmgr.set_login(gamedata.address, gamedata.port, fields.name, fields.password)
|
||||||
|
|
||||||
core.start()
|
core.start()
|
||||||
end
|
end
|
||||||
|
|
|
@ -23,6 +23,7 @@ dofile(basepath .. "fstk" .. DIR_DELIM .. "ui.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "async_event.lua")
|
dofile(menupath .. DIR_DELIM .. "async_event.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "common.lua")
|
dofile(menupath .. DIR_DELIM .. "common.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "serverlistmgr.lua")
|
dofile(menupath .. DIR_DELIM .. "serverlistmgr.lua")
|
||||||
|
dofile(menupath .. DIR_DELIM .. "keyringmgr.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "game_theme.lua")
|
dofile(menupath .. DIR_DELIM .. "game_theme.lua")
|
||||||
dofile(menupath .. DIR_DELIM .. "content" .. DIR_DELIM .. "init.lua")
|
dofile(menupath .. DIR_DELIM .. "content" .. DIR_DELIM .. "init.lua")
|
||||||
|
|
||||||
|
|
169
builtin/mainmenu/keyringmgr.lua
Normal file
169
builtin/mainmenu/keyringmgr.lua
Normal file
|
@ -0,0 +1,169 @@
|
||||||
|
keyringmgr = {
|
||||||
|
keyring = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
local function get_keyring_path()
|
||||||
|
return core.get_user_path() .. DIR_DELIM .. "client" .. DIR_DELIM .. core.settings:get("keyring_file")
|
||||||
|
end
|
||||||
|
|
||||||
|
local function save_keyring(keyring)
|
||||||
|
core.safe_file_write(get_keyring_path(), core.write_json(keyring))
|
||||||
|
end
|
||||||
|
|
||||||
|
local function read_keyring()
|
||||||
|
local path = get_keyring_path()
|
||||||
|
|
||||||
|
local file = io.open(path, "r")
|
||||||
|
if file then
|
||||||
|
local json = file:read("*all")
|
||||||
|
file:close()
|
||||||
|
return core.parse_json(json)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function is_for_server(keys, address, port)
|
||||||
|
return keys.address == address and keys.port == port
|
||||||
|
end
|
||||||
|
|
||||||
|
local function delete_keys(keyring, address, port)
|
||||||
|
for i = 1, #keyring do
|
||||||
|
local keys = keyring[i]
|
||||||
|
|
||||||
|
if is_for_server(keys, address, port) then
|
||||||
|
table.remove(keyring, i)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
local function get_keys(keyring, address, port)
|
||||||
|
for i = 1, #keyring do
|
||||||
|
local keys = keyring[i]
|
||||||
|
|
||||||
|
if is_for_server(keys, address, port) then
|
||||||
|
return keys
|
||||||
|
end
|
||||||
|
end
|
||||||
|
-- If we don't find any existing keys, we make a blank set for the server
|
||||||
|
local keys = {
|
||||||
|
address = address,
|
||||||
|
port = port,
|
||||||
|
logins = {},
|
||||||
|
last_login = false
|
||||||
|
}
|
||||||
|
table.insert(keyring, 1, keys)
|
||||||
|
save_keyring(keyring)
|
||||||
|
return keys
|
||||||
|
end
|
||||||
|
|
||||||
|
local function rewrite_keys(keyring, address, port, new_keys)
|
||||||
|
delete_keys(keyring, address, port)
|
||||||
|
table.insert(keyring, 1, new_keys)
|
||||||
|
save_keyring(keyring)
|
||||||
|
end
|
||||||
|
|
||||||
|
function keyringmgr.get_keyring()
|
||||||
|
if keyringmgr.keyring then
|
||||||
|
return keyringmgr.keyring
|
||||||
|
end
|
||||||
|
|
||||||
|
keyringmgr.keyring = {}
|
||||||
|
|
||||||
|
-- Add keyring, removing duplicates
|
||||||
|
local seen = {}
|
||||||
|
for _, keys in ipairs(read_keyring() or {}) do
|
||||||
|
local key = ("%s:%d"):format(keys.address:lower(), keys.port)
|
||||||
|
if not seen[key] then
|
||||||
|
seen[key] = true
|
||||||
|
keyringmgr.keyring[#keyringmgr.keyring + 1] = keys
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return keyringmgr.keyring
|
||||||
|
end
|
||||||
|
|
||||||
|
function keyringmgr.set_last_login(address, port, playername)
|
||||||
|
local keyring = keyringmgr.get_keyring()
|
||||||
|
local new_keys = get_keys(keyring, address, port)
|
||||||
|
new_keys.last_login = playername
|
||||||
|
|
||||||
|
rewrite_keys(keyring, address, port, new_keys)
|
||||||
|
end
|
||||||
|
|
||||||
|
function keyringmgr.set_login(address, port, playername, password)
|
||||||
|
-- If the user doesn't want to remember logins, we completely skip the process
|
||||||
|
if not core.settings:get_bool("remember_login") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
assert(type(port) == "number")
|
||||||
|
|
||||||
|
local keyring = keyringmgr.get_keyring()
|
||||||
|
local new_keys = get_keys(keyring, address, port);
|
||||||
|
|
||||||
|
-- Check for existing entires for playername
|
||||||
|
for _, login in ipairs(new_keys.logins or {}) do
|
||||||
|
if login.playername and login.playername == playername then
|
||||||
|
login.password = password
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- If not, we add a new key
|
||||||
|
if not new_keys.logins then
|
||||||
|
new_keys.logins = {}
|
||||||
|
end
|
||||||
|
table.insert(new_keys.logins, 1, {
|
||||||
|
playername = playername,
|
||||||
|
password = password
|
||||||
|
})
|
||||||
|
|
||||||
|
keyringmgr.set_last_login(address, port, playername)
|
||||||
|
|
||||||
|
rewrite_keys(keyring, address, port, new_keys)
|
||||||
|
end
|
||||||
|
|
||||||
|
function keyringmgr.get_login(address, port, playername)
|
||||||
|
assert(type(port) == "number")
|
||||||
|
|
||||||
|
local keyring = keyringmgr.get_keyring()
|
||||||
|
local new_keys = get_keys(keyring, address, port);
|
||||||
|
|
||||||
|
-- Check for existing entires for playername
|
||||||
|
for _, login in ipairs(new_keys.logins) do
|
||||||
|
if login.playername and login.playername == playername then
|
||||||
|
return login
|
||||||
|
end
|
||||||
|
end
|
||||||
|
error("No login found on " .. address .. ":" .. port .. " for player " .. playername)
|
||||||
|
end
|
||||||
|
|
||||||
|
function keyringmgr.remove_login(address, port, playername)
|
||||||
|
assert(type(port) == "number")
|
||||||
|
|
||||||
|
local keyring = keyringmgr.get_keyring()
|
||||||
|
local new_keys = get_keys(keyring, address, port);
|
||||||
|
for i = 1, #new_keys.logins do
|
||||||
|
local login = new_keys.logins[i]
|
||||||
|
|
||||||
|
if (login.playername == playername) then
|
||||||
|
table.remove(new_keys.logins, i)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
rewrite_keys(keyring, address, port, new_keys)
|
||||||
|
end
|
||||||
|
|
||||||
|
function keyringmgr.delete_keys(address, port)
|
||||||
|
local keyring = keyringmgr.get_keyring()
|
||||||
|
delete_keys(keyring, address, port)
|
||||||
|
save_keyring(keyring)
|
||||||
|
end
|
||||||
|
|
||||||
|
function keyringmgr.get_last_login(address, port)
|
||||||
|
local playername = get_keys(keyringmgr.get_keyring(), address, port).last_login
|
||||||
|
if playername then
|
||||||
|
return keyringmgr.get_login(address, port, playername)
|
||||||
|
end
|
||||||
|
return false
|
||||||
|
end
|
|
@ -55,7 +55,15 @@ local function is_selected_fav(server)
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Persists the selected server in the "address" and "remote_port" settings
|
-- Persists the selected server in the "address" and "remote_port" settings
|
||||||
|
local function get_default_playername()
|
||||||
|
return core.settings:get("name")
|
||||||
|
end
|
||||||
|
local function get_default_password()
|
||||||
|
return ""
|
||||||
|
end
|
||||||
|
|
||||||
|
local input_playername = get_default_playername()
|
||||||
|
local input_password = get_default_password()
|
||||||
local function set_selected_server(server)
|
local function set_selected_server(server)
|
||||||
if server == nil then -- reset selection
|
if server == nil then -- reset selection
|
||||||
core.settings:remove("address")
|
core.settings:remove("address")
|
||||||
|
@ -69,6 +77,18 @@ local function set_selected_server(server)
|
||||||
if address and port then
|
if address and port then
|
||||||
core.settings:set("address", address)
|
core.settings:set("address", address)
|
||||||
core.settings:set("remote_port", port)
|
core.settings:set("remote_port", port)
|
||||||
|
|
||||||
|
-- Pull info from last login (if exists)
|
||||||
|
-- This will always fail if remember_login is false
|
||||||
|
-- because nothing has been stored, nor will it ever be
|
||||||
|
local login = keyringmgr.get_last_login(address, port)
|
||||||
|
if login then
|
||||||
|
input_playername = login.playername
|
||||||
|
input_password = login.password
|
||||||
|
else
|
||||||
|
input_playername = get_default_playername()
|
||||||
|
input_password = get_default_password()
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -129,8 +149,8 @@ local function get_formspec(tabview, name, tabdata)
|
||||||
"container[0,4.8]" ..
|
"container[0,4.8]" ..
|
||||||
"label[0.25,0;" .. fgettext("Name") .. "]" ..
|
"label[0.25,0;" .. fgettext("Name") .. "]" ..
|
||||||
"label[2.875,0;" .. fgettext("Password") .. "]" ..
|
"label[2.875,0;" .. fgettext("Password") .. "]" ..
|
||||||
"field[0.25,0.2;2.625,0.75;te_name;;" .. core.formspec_escape(core.settings:get("name")) .. "]" ..
|
"field[0.25,0.2;2.625,0.75;te_name;;" .. core.formspec_escape(input_playername) .. "]" ..
|
||||||
"pwdfield[2.875,0.2;2.625,0.75;te_pwd;]" ..
|
"pwdfield[2.875,0.2;2.625,0.75;te_pwd;;" .. core.formspec_escape(input_password) .. "]" ..
|
||||||
"container_end[]" ..
|
"container_end[]" ..
|
||||||
|
|
||||||
-- Connect
|
-- Connect
|
||||||
|
@ -440,7 +460,6 @@ end
|
||||||
local function main_button_handler(tabview, fields, name, tabdata)
|
local function main_button_handler(tabview, fields, name, tabdata)
|
||||||
if fields.te_name then
|
if fields.te_name then
|
||||||
gamedata.playername = fields.te_name
|
gamedata.playername = fields.te_name
|
||||||
core.settings:set("name", fields.te_name)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if fields.servers then
|
if fields.servers then
|
||||||
|
@ -555,6 +574,7 @@ local function main_button_handler(tabview, fields, name, tabdata)
|
||||||
if server and server.address == gamedata.address and
|
if server and server.address == gamedata.address and
|
||||||
server.port == gamedata.port then
|
server.port == gamedata.port then
|
||||||
|
|
||||||
|
keyringmgr.set_login(server.address, server.port, gamedata.playername, gamedata.password)
|
||||||
serverlistmgr.add_favorite(server)
|
serverlistmgr.add_favorite(server)
|
||||||
|
|
||||||
gamedata.servername = server.name
|
gamedata.servername = server.name
|
||||||
|
|
|
@ -874,6 +874,10 @@ tooltip_append_itemname (Append item name) bool false
|
||||||
# Use a cloud animation for the main menu background.
|
# Use a cloud animation for the main menu background.
|
||||||
menu_clouds (Clouds in menu) bool true
|
menu_clouds (Clouds in menu) bool true
|
||||||
|
|
||||||
|
# Remember each server's entered login info in the 'Join Game' tab
|
||||||
|
# ***WARNING, THIS IS NOT SECURE WHATSOEVER. DO NOT ENABLE THIS***:
|
||||||
|
remember_login (Remember logins) bool false
|
||||||
|
|
||||||
[**HUD]
|
[**HUD]
|
||||||
|
|
||||||
# Modifies the size of the HUD elements.
|
# Modifies the size of the HUD elements.
|
||||||
|
@ -2444,6 +2448,9 @@ enable_remote_media_server (Connect to external media server) [client] bool true
|
||||||
# Multiplayer Tab.
|
# Multiplayer Tab.
|
||||||
serverlist_file (Serverlist file) [client] string favoriteservers.json
|
serverlist_file (Serverlist file) [client] string favoriteservers.json
|
||||||
|
|
||||||
|
# File in client/ that contains your saved usernames and passwords for Multiplayer
|
||||||
|
keyring_file (Keyring file) [client] string keyring.json
|
||||||
|
|
||||||
|
|
||||||
[*Gamepads] [client]
|
[*Gamepads] [client]
|
||||||
|
|
||||||
|
|
|
@ -3108,7 +3108,7 @@ Elements
|
||||||
(`x` and `y` are used as offset values, `w` and `h` are ignored)
|
(`x` and `y` are used as offset values, `w` and `h` are ignored)
|
||||||
* Available since formspec version 2
|
* Available since formspec version 2
|
||||||
|
|
||||||
### `pwdfield[<X>,<Y>;<W>,<H>;<name>;<label>]`
|
### `pwdfield[<X>,<Y>;<W>,<H>;<name>;<label>;<default>]`
|
||||||
|
|
||||||
* Textual password style field; will be sent to server when a button is clicked
|
* Textual password style field; will be sent to server when a button is clicked
|
||||||
* When enter is pressed in field, `fields.key_enter_field` will be sent with the
|
* When enter is pressed in field, `fields.key_enter_field` will be sent with the
|
||||||
|
@ -3118,6 +3118,10 @@ Elements
|
||||||
* `name` is the name of the field as returned in fields to `on_receive_fields`
|
* `name` is the name of the field as returned in fields to `on_receive_fields`
|
||||||
* `label`, if not blank, will be text printed on the top left above the field
|
* `label`, if not blank, will be text printed on the top left above the field
|
||||||
* See `field_close_on_enter` to stop enter closing the formspec
|
* See `field_close_on_enter` to stop enter closing the formspec
|
||||||
|
* `default` (optional) is the default value of the password
|
||||||
|
* `default` may contain variable references such as `${text}` which
|
||||||
|
will fill the value from the metadata value `text`
|
||||||
|
* **Note**: no extra text or more than a single variable is supported ATM.
|
||||||
|
|
||||||
### `field[<X>,<Y>;<W>,<H>;<name>;<label>;<default>]`
|
### `field[<X>,<Y>;<W>,<H>;<name>;<label>;<default>]`
|
||||||
|
|
||||||
|
|
|
@ -271,7 +271,7 @@ local scroll_fs =
|
||||||
"button[0,1;1,1;lorem;Lorem]"..
|
"button[0,1;1,1;lorem;Lorem]"..
|
||||||
"animated_image[0,1;4.5,1;clip_animated_image;testformspec_animation.png;4;100]" ..
|
"animated_image[0,1;4.5,1;clip_animated_image;testformspec_animation.png;4;100]" ..
|
||||||
"button[0,10;1,1;ipsum;Ipsum]"..
|
"button[0,10;1,1;ipsum;Ipsum]"..
|
||||||
"pwdfield[2,2;1,1;lorem2;Lorem]"..
|
"pwdfield[2,2;1,1;lorem2;Lorem;password]"..
|
||||||
"list[current_player;main;4,4;1,5;]"..
|
"list[current_player;main;4,4;1,5;]"..
|
||||||
"box[2,5;3,2;#ffff00]"..
|
"box[2,5;3,2;#ffff00]"..
|
||||||
"image[1,10;3,2;testformspec_item.png]"..
|
"image[1,10;3,2;testformspec_item.png]"..
|
||||||
|
@ -349,7 +349,7 @@ local pages = {
|
||||||
item_image_button[0,6;1,1;testformspec:node;rc_item_image_button_1x1;1x1]
|
item_image_button[0,6;1,1;testformspec:node;rc_item_image_button_1x1;1x1]
|
||||||
item_image_button[1,6;2,2;testformspec:node;rc_item_image_button_2x2;2x2]
|
item_image_button[1,6;2,2;testformspec:node;rc_item_image_button_2x2;2x2]
|
||||||
field[3,.5;3,.5;rc_field;Field;text]
|
field[3,.5;3,.5;rc_field;Field;text]
|
||||||
pwdfield[6,.5;3,1;rc_pwdfield;Password Field]
|
pwdfield[6,.5;3,1;rc_pwdfield;Password Field;password]
|
||||||
field[3,1;3,1;;Read-Only Field;text]
|
field[3,1;3,1;;Read-Only Field;text]
|
||||||
textarea[3,2;3,.5;rc_textarea_small;Textarea;text]
|
textarea[3,2;3,.5;rc_textarea_small;Textarea;text]
|
||||||
textarea[6,2;3,2;rc_textarea_big;Textarea;text\nmore text]
|
textarea[6,2;3,2;rc_textarea_big;Textarea;text\nmore text]
|
||||||
|
|
|
@ -3648,6 +3648,10 @@
|
||||||
# type: string
|
# type: string
|
||||||
# serverlist_file = favoriteservers.json
|
# serverlist_file = favoriteservers.json
|
||||||
|
|
||||||
|
# File in client/ that contains your saved usernames and passwords for Multiplayer
|
||||||
|
# type: string
|
||||||
|
# keyring_file = keyring.json
|
||||||
|
|
||||||
## Gamepads
|
## Gamepads
|
||||||
|
|
||||||
# Enable joysticks. Requires a restart to take effect
|
# Enable joysticks. Requires a restart to take effect
|
||||||
|
|
|
@ -381,6 +381,7 @@ void set_default_settings()
|
||||||
// Main menu
|
// Main menu
|
||||||
settings->setDefault("main_menu_path", "");
|
settings->setDefault("main_menu_path", "");
|
||||||
settings->setDefault("serverlist_file", "favoriteservers.json");
|
settings->setDefault("serverlist_file", "favoriteservers.json");
|
||||||
|
settings->setDefault("keyring_file", "keyring.json");
|
||||||
|
|
||||||
// General font settings
|
// General font settings
|
||||||
settings->setDefault("font_path", porting::getDataPath("fonts" DIR_DELIM "Arimo-Regular.ttf"));
|
settings->setDefault("font_path", porting::getDataPath("fonts" DIR_DELIM "Arimo-Regular.ttf"));
|
||||||
|
|
|
@ -1440,13 +1440,16 @@ void GUIFormSpecMenu::parseFieldCloseOnEnter(parserData *data, const std::string
|
||||||
void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element)
|
void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element)
|
||||||
{
|
{
|
||||||
std::vector<std::string> parts;
|
std::vector<std::string> parts;
|
||||||
if (!precheckElement("pwdfield", element, 4, 4, parts))
|
if (!precheckElement("pwdfield", element, 4, 5, parts))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
std::vector<std::string> v_pos = split(parts[0],',');
|
std::vector<std::string> v_pos = split(parts[0],',');
|
||||||
std::vector<std::string> v_geom = split(parts[1],',');
|
std::vector<std::string> v_geom = split(parts[1],',');
|
||||||
std::string name = parts[2];
|
std::string name = parts[2];
|
||||||
std::string label = parts[3];
|
std::string label = parts[3];
|
||||||
|
std::string default_val = "";
|
||||||
|
if(parts.size() == 5) // If the field has a default value (for backwards compatibility)
|
||||||
|
default_val = parts[4];
|
||||||
|
|
||||||
MY_CHECKPOS("pwdfield",0);
|
MY_CHECKPOS("pwdfield",0);
|
||||||
MY_CHECKGEOM("pwdfield",1);
|
MY_CHECKGEOM("pwdfield",1);
|
||||||
|
@ -1468,6 +1471,9 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element
|
||||||
geom.Y = m_btn_height*2;
|
geom.Y = m_btn_height*2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(m_form_src)
|
||||||
|
default_val = m_form_src->resolveText(default_val);
|
||||||
|
|
||||||
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
|
core::rect<s32> rect = core::rect<s32>(pos.X, pos.Y, pos.X+geom.X, pos.Y+geom.Y);
|
||||||
|
|
||||||
std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
|
std::wstring wlabel = translate_string(utf8_to_wide(unescape_string(label)));
|
||||||
|
@ -1482,7 +1488,7 @@ void GUIFormSpecMenu::parsePwdField(parserData* data, const std::string &element
|
||||||
);
|
);
|
||||||
|
|
||||||
spec.send = true;
|
spec.send = true;
|
||||||
gui::IGUIEditBox *e = Environment->addEditBox(0, rect, true,
|
gui::IGUIEditBox *e = Environment->addEditBox(utf8_to_wide(unescape_string(default_val)).c_str(), rect, true,
|
||||||
data->current_parent, spec.fid);
|
data->current_parent, spec.fid);
|
||||||
|
|
||||||
if (spec.fname == m_focused_element) {
|
if (spec.fname == m_focused_element) {
|
||||||
|
|
|
@ -48,7 +48,7 @@ static inline int checkSettingSecurity(lua_State* L, const std::string &name)
|
||||||
|
|
||||||
const char *disallowed[] = {
|
const char *disallowed[] = {
|
||||||
"main_menu_script", "shader_path", "texture_path", "screenshot_path",
|
"main_menu_script", "shader_path", "texture_path", "screenshot_path",
|
||||||
"serverlist_file", "serverlist_url", "map-dir", "contentdb_url",
|
"serverlist_file", "keyring_file", "serverlist_url", "map-dir", "contentdb_url",
|
||||||
};
|
};
|
||||||
for (const char *name2 : disallowed) {
|
for (const char *name2 : disallowed) {
|
||||||
if (name == name2)
|
if (name == name2)
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue