mirror of
https://github.com/luanti-org/luanti.git
synced 2025-11-06 18:25:21 +01:00
Merge branch 'master' into doc-refactor-2
This commit is contained in:
@@ -85,6 +85,17 @@ public class GameActivity extends NativeActivity {
|
||||
makeFullScreen();
|
||||
}
|
||||
|
||||
private native void saveSettings();
|
||||
|
||||
@Override
|
||||
protected void onStop() {
|
||||
super.onStop();
|
||||
// Avoid losing setting changes in case the app is onDestroy()ed later.
|
||||
// Saving stuff in onStop() is recommended in the Android activity
|
||||
// lifecycle documentation.
|
||||
saveSettings();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
// Ignore the back press so Minetest can handle it
|
||||
|
||||
@@ -92,16 +92,24 @@ core.builtin_auth_handler = {
|
||||
|
||||
core_auth.save(auth_entry)
|
||||
|
||||
-- Run grant callbacks
|
||||
for priv, _ in pairs(privileges) do
|
||||
if not prev_privs[priv] then
|
||||
for priv, value in pairs(privileges) do
|
||||
-- Warnings for improper API usage
|
||||
if value == false then
|
||||
core.log('deprecated', "`false` value given to `minetest.set_player_privs`, "..
|
||||
"this is almost certainly a bug, "..
|
||||
"granting a privilege rather than revoking it")
|
||||
elseif value ~= true then
|
||||
core.log('deprecated', "non-`true` value given to `minetest.set_player_privs`")
|
||||
end
|
||||
-- Run grant callbacks
|
||||
if prev_privs[priv] == nil then
|
||||
core.run_priv_callbacks(name, priv, nil, "grant")
|
||||
end
|
||||
end
|
||||
|
||||
-- Run revoke callbacks
|
||||
for priv, _ in pairs(prev_privs) do
|
||||
if not privileges[priv] then
|
||||
if privileges[priv] == nil then
|
||||
core.run_priv_callbacks(name, priv, nil, "revoke")
|
||||
end
|
||||
end
|
||||
@@ -180,6 +188,20 @@ core.set_player_privs = auth_pass("set_privileges")
|
||||
core.remove_player_auth = auth_pass("delete_auth")
|
||||
core.auth_reload = auth_pass("reload")
|
||||
|
||||
function core.change_player_privs(name, changes)
|
||||
local privs = core.get_player_privs(name)
|
||||
for priv, change in pairs(changes) do
|
||||
if change == true then
|
||||
privs[priv] = true
|
||||
elseif change == false then
|
||||
privs[priv] = nil
|
||||
else
|
||||
error("non-bool value given to `minetest.change_player_privs`")
|
||||
end
|
||||
end
|
||||
core.set_player_privs(name, privs)
|
||||
end
|
||||
|
||||
local record_login = auth_pass("record_login")
|
||||
core.register_on_joinplayer(function(player)
|
||||
record_login(player:get_player_name())
|
||||
|
||||
@@ -33,6 +33,8 @@ core.features = {
|
||||
random_state_restore = true,
|
||||
after_order_expiry_registration = true,
|
||||
wallmounted_rotate = true,
|
||||
item_specific_pointabilities = true,
|
||||
blocking_pointability_type = true,
|
||||
}
|
||||
|
||||
function core.has_feature(arg)
|
||||
|
||||
246
builtin/locale/__builtin.eo.tr
Normal file
246
builtin/locale/__builtin.eo.tr
Normal file
@@ -0,0 +1,246 @@
|
||||
# textdomain: __builtin
|
||||
Invalid parameters (see /help @1).=Nevalidaj parametroj (kontrolu /help @1)
|
||||
Too many arguments, try using just /help <command>=Tro da parametroj, eble provu /help <ordono>
|
||||
Available commands: @1=Nunaj ordonoj: @1
|
||||
Use '/help <cmd>' to get more information, or '/help all' to list everything.=Uzu «/help <ordono>» por specifa informo, aŭ «/help all» por listigi ĉion.
|
||||
Available commands:=Nunaj ordonoj:
|
||||
Command not available: @1=Ordono neuzebla: @1
|
||||
[all | privs | <cmd>] [-t]=[all | privs | <ordono>]
|
||||
Get help for commands or list privileges (-t: output in chat)=Listigi helpon pri ordonoj («all» aŭ <ordono>) aŭ rajtoj («privs») (kun -t: presu en babilejo)
|
||||
Available privileges:=Nunaj rajtoj:
|
||||
Command=Ordono
|
||||
Parameters=Parametroj
|
||||
For more information, click on any entry in the list.=Por pliaj informoj, klaku ajnan listeron.
|
||||
Double-click to copy the entry to the chat history.=Dufoje-klaku por kopii listeron al la babilejo.
|
||||
Command: @1 @2=Ordono: @1 @2
|
||||
Available commands: (see also: /help <cmd>)=Nunaj ordonoj: (vidu ankaŭ: /help <ordono>)
|
||||
Close=Fermi
|
||||
Privilege=Rajto
|
||||
Description=Priskribo
|
||||
Empty command.=Malplena ordono.
|
||||
Invalid command: @1=Nevalida ordono: @1
|
||||
Invalid command usage.=Nevalida ordonouzo.
|
||||
(@1 s)= (@1 s)
|
||||
Command execution took @1 s=Ruliĝo de ordono postulis @1 s
|
||||
You don't have permission to run this command (missing privileges: @1).=Vi ne rajtas uzi tiun ĉi ordonon (mankataj rajtoj: @1)
|
||||
Unable to get position of player @1.=Ne povis akiri pozicion de ludanto @1.
|
||||
Incorrect area format. Expected: (x1,y1,z1) (x2,y2,z2)=Nevalida loko-formo. Atendis: (x1,y1,z1) (x2,y2,z2)
|
||||
<action>=<ago>
|
||||
Show chat action (e.g., '/me orders a pizza' displays '<player name> orders a pizza')=Roli agon en la babilejo (ekz., «/me mendas picon» montras «<ludanto> mendas picon»)
|
||||
Show the name of the server owner=Montri nomon de la serviladministranto
|
||||
The administrator of this server is @1.=La adminstranto de tiu ĉi servilo estas @1.
|
||||
There's no administrator named in the config file.=Estas neniu agordita administranto en la agordodosiero.
|
||||
@1 does not have any privileges.=@1 havas neniun rajton.
|
||||
Privileges of @1: @2=Rajtoj de @1: @2
|
||||
[<name>]=[<nomo>]
|
||||
Show privileges of yourself or another player=Montri rajtojn de vi aŭ alia ludanto
|
||||
Player @1 does not exist.=Ludanto @1 ne ekzistas.
|
||||
<privilege>=<rajto>
|
||||
Return list of all online players with privilege=Listi ĉeretan ludanton kun specifa rajto
|
||||
Invalid parameters (see /help haspriv).=Nevalidaj parametroj (vidu /help haspriv).
|
||||
Unknown privilege!=Nekonata rajto!
|
||||
No online player has the "@1" privilege.=Neniu ĉeretulo havas la rajton «@1».
|
||||
Players online with the "@1" privilege: @2=Ĉeretuloj kun la rajto «@1»: @2
|
||||
Your privileges are insufficient.=Viaj rajtoj ne sufiĉas.
|
||||
Your privileges are insufficient. '@1' only allows you to grant: @2=Viaj rajtoj ne sufiĉas. «@1» sole lasas vin doni: @2
|
||||
Unknown privilege: @1=Nekonata rajto: @1
|
||||
@1 granted you privileges: @2=@1 donis al vi rajtojn: @2
|
||||
<name> (<privilege> [, <privilege2> [<...>]] | all)=<nomo> (<rajto> [, <rajto2> [<...>]] | all)
|
||||
Give privileges to player=Doni rajtojn al ludanto, aŭ aparte, aŭ ĉiun kune («all»)
|
||||
Invalid parameters (see /help grant).=Nevalidaj parametroj (vidu /help grant).
|
||||
<privilege> [, <privilege2> [<...>]] | all=<rajto> [, <rajto2> [<...>]] | all
|
||||
Grant privileges to yourself=Doni rajtojn al vi mem, aŭ aparte, aŭ ĉiun kune («all»)
|
||||
Invalid parameters (see /help grantme).=Nevalidaj parametroj (vidu /help grantme)
|
||||
Your privileges are insufficient. '@1' only allows you to revoke: @2=Viaj rajtoj ne sufiĉas. «@1» sole lasas vin repreni: @2
|
||||
Note: Cannot revoke in singleplayer: @1=Rimarko: Neeblas repreni rajton sole: @1
|
||||
Note: Cannot revoke from admin: @1=Rimarko: Neeblas repreni rajnton de administranto: @1
|
||||
No privileges were revoked.=Neniu rajto reprenita.
|
||||
@1 revoked privileges from you: @2=@1 reprenis rajtojn de vi: @2
|
||||
Remove privileges from player=Repreni rajtojn de ludanto, aŭ aparte, aŭ ĉiun kune («all»)
|
||||
Invalid parameters (see /help revoke).=Nevalidaj parametroj (vidu /help revoke)
|
||||
Revoke privileges from yourself=Repreni rajtojn de vi mem, aŭ aparte, aŭ ĉiun kune («all»)
|
||||
Invalid parameters (see /help revokeme).=Nevalidaj parametroj (vidu /help revokeme).
|
||||
<name> <password>=<nomo> <pasvorto>
|
||||
Set player's password (sent unencrypted, thus insecure)=Agordi pasvorton de ludanto (sendute senĉifre, kaj tiel malsekure)
|
||||
Name field required.=Nomo bezonata.
|
||||
Your password was cleared by @1.=Via pasvorto forviŝiĝis de @1.
|
||||
Password of player "@1" cleared.=Pasvorto de ludanto «@1» forviŝita.
|
||||
Your password was set by @1.=Via pasvorto agordiĝis de @1.
|
||||
Password of player "@1" set.=Pasvorto de ludanto «@1» agordita.
|
||||
<name>=<nomo>
|
||||
Set empty password for a player=Forviŝi pasvorton de ludanto
|
||||
Reload authentication data=Reenlegi aŭtentigajn datumojn
|
||||
Done.=Finite.
|
||||
Failed.=Malsukcese..
|
||||
Remove a player's data=Forviŝi datumojn de ludanto
|
||||
Player "@1" removed.=Ludanto «@1» forigita.
|
||||
No such player "@1" to remove.=Neniu ekzistanta ludanto «@1» forigebla.
|
||||
Player "@1" is connected, cannot remove.=Ludanto «@1» ĉeretas, do ne forigeblas.
|
||||
Unhandled remove_player return code @1.=Netraktata remove_player redonkodo @1.
|
||||
Cannot teleport out of map bounds!=Neeblas teleporti ekstermonden!
|
||||
Cannot get player with name @1.=Neeblas trovi ludanton nomitan «@1».
|
||||
Cannot teleport, @1 is attached to an object!=Ne povas teleporti @1, ĝi estas ligita al objekto!
|
||||
Teleporting @1 to @2.=Teleportante @1 al @2.
|
||||
One does not teleport to oneself.=Oni kutimas ne teleportu al si mem.
|
||||
Cannot get teleportee with name @1.=Ne trovis alteleportaton nomitan @1.
|
||||
Cannot get target player with name @1.=Ne trovis celatan ludanton nomitan @1.
|
||||
Teleporting @1 to @2 at @3.=Teleportante @1 al @2 ĉe @3.
|
||||
<X>,<Y>,<Z> | <to_name> | <name> <X>,<Y>,<Z> | <name> <to_name>=<X>,<Y>,<Z> | <al_nomo> | <nomo> <X>,<Y>,<Z> | <nomo> <al_nomo>
|
||||
Teleport to position or player=Teleportiĝi al pozicio aŭ ludanto
|
||||
You don't have permission to teleport other players (missing privilege: @1).=Vi ne rajtas teleporti aliajn ludantojn (mankas rajto: @1).
|
||||
([-n] <name> <value>) | <name>=([-n] <nomo> <valoro>) | <nomo>
|
||||
Set or read server configuration setting=Agordi aŭ vidi servilan agordon
|
||||
Failed. Cannot modify secure settings. Edit the settings file manually.=Malsukcese. Neeblas redakti sekurajn agordojn. Redaktu la agordodosieron permane.
|
||||
Failed. Use '/set -n <name> <value>' to create a new setting.=Malsukcese. Uzu «/set -n <nomo> <valoro>» por krei novan agordon.
|
||||
@1 @= @2=@1 @= @2
|
||||
<not set>=<ne agordita>
|
||||
Invalid parameters (see /help set).=Nevalidaj parametroj (vidu /help set).
|
||||
Finished emerging @1 blocks in @2ms.=Finenlegis @1 monderojn dum @2ms.
|
||||
emergeblocks update: @1/@2 blocks emerged (@3%)=emergeblocks ĝisdatigo: @1/@2 monderoj enlegitaj (@3%)
|
||||
(here [<radius>]) | (<pos1> <pos2>)=(here [<radius>]) | (<pos1> <pos2>)
|
||||
Load (or, if nonexistent, generate) map blocks contained in area pos1 to pos2 (<pos1> and <pos2> must be in parentheses)=Enlegi (aŭ, laŭnecese, krei) mondopecojn inter la pozicioj poz1 kaj pos2 (<poz1> kaj <poz2> devas esti inter krampoj)
|
||||
Started emerge of area ranging from @1 to @2.=Ekenlegis mondpecojn inter @1 kaj @2.
|
||||
Delete map blocks contained in area pos1 to pos2 (<pos1> and <pos2> must be in parentheses)=Forigi mondpecojn inter la pozicioj poz1 kaj poz2 (<poz1> kaj <poz2> devas esti inter krampoj)
|
||||
Successfully cleared area ranging from @1 to @2.=Sukcese forigis mondpecojn inter @1 kaj @2.
|
||||
Failed to clear one or more blocks in area.=Malsukcesis forigi unu aŭ pli da mondpecoj.
|
||||
Resets lighting in the area between pos1 and pos2 (<pos1> and <pos2> must be in parentheses)=Reŝarĝas lumojn inter la pozicioj poz1 kaj poz2 (<poz1> kaj <poz2> devas esti inter krampoj)
|
||||
Successfully reset light in the area ranging from @1 to @2.=Sukcesis reŝarĝi lumojn inter @1 kaj @2.
|
||||
Failed to load one or more blocks in area.=Malsukcesis enlegante unu aŭ pli da monderoj.
|
||||
List mods installed on the server=Listigi modifaĵojn instalitajn de la servilo.
|
||||
No mods installed.=Neniu modifaĵ instalita.
|
||||
Cannot give an empty item.=Neeblas doni neniun portaĵon.
|
||||
Cannot give an unknown item.=Neeblas doni nekonatan portaĵon.
|
||||
Giving 'ignore' is not allowed.=Doni «ignore» estas ne permesita.
|
||||
@1 is not a known player.=@1 ne estas konata ludanto.
|
||||
@1 partially added to inventory.=@1 parte enmetiĝis al portaĵujon.
|
||||
@1 could not be added to inventory.=@1 ne enmetiĝis al portaĵujon.
|
||||
@1 added to inventory.=@1 enmetiĝis al portaĵujon.
|
||||
@1 partially added to inventory of @2.=@1 parte enmetiĝis al portaĵujon de @2.
|
||||
@1 could not be added to inventory of @2.=@1 ne enmetiĝis al portaĵujon de @2.
|
||||
@1 added to inventory of @2.=@1 enmetiĝis al portaĵujon de @2.
|
||||
<name> <ItemString> [<count> [<wear>]]=<nomo> <PortaĵNomo> [<kvanto> <portu>]]
|
||||
Give item to player=Doni portaĵon al ludanto
|
||||
Name and ItemString required.=Nomo kaj PortaĵNomo postualtaj.
|
||||
<ItemString> [<count> [<wear>]]=<PortaĵNomo> [<kvanto> [<portu>]]
|
||||
Give item to yourself=Doni portaĵon al vi mem.
|
||||
ItemString required.=PortaĵNomo postulata.
|
||||
<EntityName> [<X>,<Y>,<Z>]=<EstaĵoNomo> [<X>, <Y>, <Z>]
|
||||
Spawn entity at given (or your) position=Estigi estaĵon ĉe la donita (aŭ la via) pozicio
|
||||
EntityName required.=EstaĵNomo postulata.
|
||||
Unable to spawn entity, player is nil.=Ne povis estigi estaĵon, ĉar ludanto estas nil.
|
||||
Cannot spawn an unknown entity.=Neeblas estigi nekonatan estaĵon.
|
||||
Invalid parameters (@1).=Nevalidaj parametroj (@1).
|
||||
@1 spawned.=@1 naskiĝis.
|
||||
@1 failed to spawn.=@1 ne naskiĝis.
|
||||
Destroy item in hand=Detrui portaĵon enmanan
|
||||
Unable to pulverize, no player.=Ne povis detrui, neniu ludanto.
|
||||
Unable to pulverize, no item in hand.=Ne povis detrui, ĉar nenio estas tenata.
|
||||
An item was pulverized.=Portaĵo detruiĝis.
|
||||
[<range>] [<seconds>] [<limit>]=[<intertempo>] [<sekundoj>] [<limo>]
|
||||
Check who last touched a node or a node near it within the time specified by <seconds>. Default: range @= 0, seconds @= 86400 @= 24h, limit @= 5. Set <seconds> to inf for no time limit=Kontroli kiu lastafoje tuŝis monderon aŭ monderon proksiman al ĝi dum
|
||||
Rollback functions are disabled.=Malfaraj funkcioj estas malŝaltitaj.
|
||||
That limit is too high!=Tiu limo troaltas!
|
||||
Checking @1 ...=Kontrolas @1…
|
||||
Nobody has touched the specified location in @1 seconds.=Neniu tuŝis tiun lokon dum @1 sekundoj.
|
||||
@1 @2 @3 -> @4 @5 seconds ago.=@1 @2 @3 -> @4 @5 sekundoj antaŭe.
|
||||
Punch a node (range@=@1, seconds@=@2, limit@=@3).=Frapi monderon (intertempo@=@1, sekundoj@=@2, limo@=@3).
|
||||
(<name> [<seconds>]) | (:<actor> [<seconds>])=(<nomo> [<sekundoj>]) | (:<aganto> [<sekundoj>])
|
||||
Revert actions of a player. Default for <seconds> is 60. Set <seconds> to inf for no time limit=Malfari agojn de ludanto. Implicita valoro de <sekundoj> estas 60. Metu <sekundoj> kiel «inf» por neniu tempolimo
|
||||
Invalid parameters. See /help rollback and /help rollback_check.=Nevalidaj parametroj. Vidu /help rollback kaj /help rollback_check.
|
||||
Reverting actions of player '@1' since @2 seconds.=Malfaras agojn de ludanto «@1» ekde @2 sekundoj antaŭ nun.
|
||||
Reverting actions of @1 since @2 seconds.=Malfaras agojn de @1 ekde @2 sekundoj antaŭ nun.
|
||||
(log is too long to show)=(protokolo trolongas por montri)
|
||||
Reverting actions succeeded.=Sukcesis malfari agojn.
|
||||
Reverting actions FAILED.=MALsukcesis malfari agojn.
|
||||
Show server status=Montri staton de servilon.
|
||||
This command was disabled by a mod or game.=Tiun ordonon malŝaltis modifaĵo aŭ ludo.
|
||||
[<0..23>:<0..59> | <0..24000>]=[<0..23>:<0..59> | <0..24000>]
|
||||
Show or set time of day=Montri aŭ ŝanĝi la horon
|
||||
Current time is @1:@2.=Nun estas @1:@2.
|
||||
You don't have permission to run this command (missing privilege: @1).=Vi ne rajtas rulu tiun orodnon (mankata rajto: @1).
|
||||
Invalid time (must be between 0 and 24000).=Nevalida tempo (estu inter 0 kaj 24000)
|
||||
Time of day changed.=Horo ŝanĝita.
|
||||
Invalid hour (must be between 0 and 23 inclusive).=Nevalida horo (estu inter 0 kaj 23, inkluzive).
|
||||
Invalid minute (must be between 0 and 59 inclusive).=Nevalida minuto (estu inter 0 kaj 59, inkluzive).
|
||||
Show day count since world creation=Montri pasitajn tagojn ekde mondokreo
|
||||
Current day is @1.=Nuna tago estas @1.
|
||||
[<delay_in_seconds> | -1] [-r] [<message>]=[<prokrasto_sekunde> | -1] [-r] [<mesaĝo>]
|
||||
Shutdown server (-1 cancels a delayed shutdown, -r allows players to reconnect)=Malŝalti servilon (-1 nuligas planitan malŝalton, -r lasas ludantojn rekonektiĝi)
|
||||
Server shutting down (operator request).=Servilo malŝaltiĝas (laŭ prizorganta peto)
|
||||
Ban the IP of a player or show the ban list=Forbari la IP-adreson de ludanto, aŭ listigi forbaritojn
|
||||
The ban list is empty.=La forbaritolisto malplenas.
|
||||
Ban list: @1=Forbaritoj: @1
|
||||
You cannot ban players in singleplayer!=Vi ne povas forbari ludantojn en unuludanta reĝimo!
|
||||
Player is not online.=Ludanto ne ĉeretas.
|
||||
Failed to ban player.=Malsukcesis forbari ludanton.
|
||||
Banned @1.=Forbaris @1.
|
||||
<name> | <IP_address>=<nomo> | <IP_adreso>
|
||||
Remove IP ban belonging to a player/IP=Nuligi IP-forbaron de ludanto/IP
|
||||
Failed to unban player/IP.=Malsukcesis nuligi forbaron de ludanto/IP-adreso
|
||||
Unbanned @1.=Malforbaris @1.
|
||||
<name> [<reason>]=<nomo> [<kialo>]
|
||||
Kick a player=Elĵeti ludanton
|
||||
Failed to kick player @1.=Malsukcesis elĵeti ludanton @1.
|
||||
Kicked @1.=Elĵetis @1.
|
||||
[full | quick]=[full | quick]
|
||||
Clear all objects in world=Forigu ĉiujn lasitajn portaĵojn en la mondo
|
||||
Invalid usage, see /help clearobjects.=Nevalida uzo, vidu /help clearobjects.
|
||||
Clearing all objects. This may take a long time. You may experience a timeout. (by @1)=Forigante ĉiun lasitan portaĵon. Tio ĉi eble postulos longan tempon. Vi eble malkonektiĝos pro tempo-elĉerpo. (de @1)
|
||||
Cleared all objects.=Forigis ĉiun lasitan portaĵon.
|
||||
<name> <message>=<nomo> <mesaĝo>
|
||||
Send a direct message to a player=Sendu rekte privatan mesaĝon al ludanto
|
||||
Invalid usage, see /help msg.=Nevalida uzo, vidu /help msg.
|
||||
The player @1 is not online.=La ludanto @1 ne ĉeretas.
|
||||
DM from @1: @2=Privata mesaĝo de @1: @2
|
||||
Message sent.=Mesaĝo sendita.
|
||||
Get the last login time of a player or yourself=Vidi la lastan salutotempon de ludanto, aŭ vi mem
|
||||
@1's last login time was @2.=Lasta salutotempo de @1 estas @2.
|
||||
@1's last login time is unknown.=Lasta salutotempo de @1 estas nesciata.
|
||||
Clear the inventory of yourself or another player=Malplenigi la portaĵujon de vi aŭ alia ludanto.
|
||||
You don't have permission to clear another player's inventory (missing privilege: @1).=Vi ne rajtas malplenigi portaĵujon de alia ludanto (mankata rajto: @1).
|
||||
@1 cleared your inventory.=@1 malplenigis vian portaĵujon.
|
||||
Cleared @1's inventory.=Malplenigis portaĵujon de @1.
|
||||
Player must be online to clear inventory!=Por malplenigi onian portaĵujon, tiu devas ĉereti!
|
||||
Players can't be killed, damage has been disabled.=Nemortigeblas ludantoj, ĉar vundado estas malŝaltita.
|
||||
Player @1 is not online.=Ludanto @1 ne ĉeretas.
|
||||
You are already dead.=Vi jam estas mortinta.
|
||||
@1 is already dead.=@1 estas mortinta.
|
||||
@1 has been killed.=@1 estas murdita.
|
||||
Kill player or yourself=Mortigi ludanton aŭ vin mem
|
||||
@1 joined the game.=@1 aliĝis la ludon.
|
||||
@1 left the game.=@1 foriris de la ludo.
|
||||
@1 left the game (timed out).=@1 foriris de la ludo (tempo-elĉerpo)
|
||||
(no description)=(neniu priskribo)
|
||||
Can interact with things and modify the world=Povas interfaci kaj redakti la mondon
|
||||
Can speak in chat=Povas paroli babileje
|
||||
Can modify basic privileges (@1)=Povas redakti bazajn rajtojn (@1)
|
||||
Can modify privileges=Povas redakti rajtojn
|
||||
Can teleport self=Povas teleporti sin
|
||||
Can teleport other players=Povas teleporti aliajn ludantojn
|
||||
Can set the time of day using /time=Povas ŝanĝi la tempon per /time
|
||||
Can do server maintenance stuff=Povas fari servilestrajn aferojn
|
||||
Can bypass node protection in the world=Povas malatenti monderajn protektojn de la mondo
|
||||
Can ban and unban players=Povas forbari kaj malforbari ludantojn
|
||||
Can kick players=Povas elĵeti ludantojn
|
||||
Can use /give and /giveme=Povas uzi /give kaj /giveme
|
||||
Can use /setpassword and /clearpassword=Povas uzi /setpassword kaj /clearpassword
|
||||
Can use fly mode=Povas ŝalti flugan reĝimon
|
||||
Can use fast mode=Povas ŝalti rapidegan reĝimon
|
||||
Can fly through solid nodes using noclip mode=Povas traflugi monderojn per trapasa reĝimo
|
||||
Can use the rollback functionality=Povas uzi malfarajn funkciojn
|
||||
Can enable wireframe=Povas ŝalti ĉirkaŭkadron
|
||||
Unknown Item=Nekonata portaĵo
|
||||
Air=Aero
|
||||
Ignore=Malatenti
|
||||
You can't place 'ignore' nodes!=Vi ne povas meti «malatentajn» monderojn!
|
||||
print [<filter>] | dump [<filter>] | save [<format> [<filter>]] | reset=print [<filtrilo>] | dump [<filtrilo>] | save [<formo> [<filtrilo>]] | reset
|
||||
Handle the profiler and profiling data=Trakti la analizilon kaj analizajn datumojn
|
||||
Statistics written to action log.=Analizoj skribitaj al agoprotokolo.
|
||||
Statistics were reset.=Analizoj forviŝitaj.
|
||||
Usage: @1=Uzado: @1
|
||||
Format can be one of txt, csv, lua, json, json_pretty (structures may be subject to change).=Formo povas estas txt, csv, lua, json, aŭ json_pretty (struktuoj eble iam ŝanĝiĝos).
|
||||
Values below show absolute/relative times spend per server step by the instrumented function.=Valoroj subaj montras la malrelativan/relativan tempon pasigitan de la servilo je ĉiu paŝo de la funkcio.
|
||||
A total of @1 sample(s) were taken.=Sume @1 ekzemplero(j) konserviĝis.
|
||||
The output is limited to '@1'.=La eligo estas limigita al «@1».
|
||||
Saving of profile failed: @1=Konservado de profilo malsukcesis: @1
|
||||
Profile saved to @1=Profilo konservita al @1
|
||||
@@ -608,6 +608,16 @@ local function get_formspec(dialogdata)
|
||||
end
|
||||
|
||||
|
||||
-- On Android, closing the app via the "Recents screen" won't result in a clean
|
||||
-- exit, discarding any setting changes made by the user.
|
||||
-- To avoid that, we write the settings file in more cases on Android.
|
||||
function write_settings_early()
|
||||
if PLATFORM == "Android" then
|
||||
core.settings:write()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function buttonhandler(this, fields)
|
||||
local dialogdata = this.data
|
||||
dialogdata.leftscroll = core.explode_scrollbar_event(fields.leftscroll).value or dialogdata.leftscroll
|
||||
@@ -622,12 +632,15 @@ local function buttonhandler(this, fields)
|
||||
if fields.show_technical_names ~= nil then
|
||||
local value = core.is_yes(fields.show_technical_names)
|
||||
core.settings:set_bool("show_technical_names", value)
|
||||
write_settings_early()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.show_advanced ~= nil then
|
||||
local value = core.is_yes(fields.show_advanced)
|
||||
core.settings:set_bool("show_advanced", value)
|
||||
write_settings_early()
|
||||
|
||||
local suggested_page_id = update_filtered_pages(dialogdata.query)
|
||||
|
||||
@@ -672,12 +685,15 @@ local function buttonhandler(this, fields)
|
||||
|
||||
for i, comp in ipairs(dialogdata.components) do
|
||||
if comp.on_submit and comp:on_submit(fields, this) then
|
||||
write_settings_early()
|
||||
|
||||
-- Clear components so they regenerate
|
||||
dialogdata.components = nil
|
||||
return true
|
||||
end
|
||||
if comp.setting and fields["reset_" .. i] then
|
||||
core.settings:remove(comp.setting.name)
|
||||
write_settings_early()
|
||||
|
||||
-- Clear components so they regenerate
|
||||
dialogdata.components = nil
|
||||
|
||||
@@ -12,13 +12,13 @@ void main (void)
|
||||
//texture sampling rate
|
||||
const float step = 1.0 / 256.0;
|
||||
float tl = texture2D(normalTexture, vec2(uv.x - step, uv.y + step)).r;
|
||||
float t = texture2D(normalTexture, vec2(uv.x - step, uv.y - step)).r;
|
||||
float t = texture2D(normalTexture, vec2(uv.x, uv.y + step)).r;
|
||||
float tr = texture2D(normalTexture, vec2(uv.x + step, uv.y + step)).r;
|
||||
float r = texture2D(normalTexture, vec2(uv.x + step, uv.y)).r;
|
||||
float r = texture2D(normalTexture, vec2(uv.x + step, uv.y )).r;
|
||||
float br = texture2D(normalTexture, vec2(uv.x + step, uv.y - step)).r;
|
||||
float b = texture2D(normalTexture, vec2(uv.x, uv.y - step)).r;
|
||||
float b = texture2D(normalTexture, vec2(uv.x, uv.y - step)).r;
|
||||
float bl = texture2D(normalTexture, vec2(uv.x - step, uv.y - step)).r;
|
||||
float l = texture2D(normalTexture, vec2(uv.x - step, uv.y)).r;
|
||||
float l = texture2D(normalTexture, vec2(uv.x - step, uv.y )).r;
|
||||
float dX = (tr + 2.0 * r + br) - (tl + 2.0 * l + bl);
|
||||
float dY = (bl + 2.0 * b + br) - (tl + 2.0 * t + tr);
|
||||
vec4 bump = vec4 (normalize(vec3 (dX, dY, 0.1)),1.0);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
uniform sampler2D baseTexture;
|
||||
|
||||
uniform vec3 dayLight;
|
||||
uniform vec4 skyBgColor;
|
||||
uniform vec4 fogColor;
|
||||
uniform float fogDistance;
|
||||
uniform float fogShadingParameter;
|
||||
uniform vec3 eyePosition;
|
||||
@@ -448,7 +448,7 @@ void main(void)
|
||||
// Note: clarity = (1 - fogginess)
|
||||
float clarity = clamp(fogShadingParameter
|
||||
- fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0);
|
||||
col = mix(skyBgColor, col, clarity);
|
||||
col = mix(fogColor, col, clarity);
|
||||
col = vec4(col.rgb, base.a);
|
||||
|
||||
gl_FragData[0] = col;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
uniform sampler2D baseTexture;
|
||||
|
||||
uniform vec3 dayLight;
|
||||
uniform vec4 skyBgColor;
|
||||
uniform vec4 fogColor;
|
||||
uniform float fogDistance;
|
||||
uniform float fogShadingParameter;
|
||||
uniform vec3 eyePosition;
|
||||
@@ -449,7 +449,7 @@ void main(void)
|
||||
// Note: clarity = (1 - fogginess)
|
||||
float clarity = clamp(fogShadingParameter
|
||||
- fogShadingParameter * length(eyeVec) / fogDistance, 0.0, 1.0);
|
||||
col = mix(skyBgColor, col, clarity);
|
||||
col = mix(fogColor, col, clarity);
|
||||
col = vec4(col.rgb, base.a);
|
||||
|
||||
gl_FragData[0] = col;
|
||||
|
||||
@@ -12,9 +12,8 @@ While you're playing the game normally (that is, no menu or inventory is
|
||||
shown), the following controls are available:
|
||||
|
||||
* Look around: Touch screen and slide finger
|
||||
* Tap: Place a node
|
||||
* Long tap: Dig node or use the held item
|
||||
|
||||
* Tap: Place a node, punch an object, or use the wielded item (default)
|
||||
* Long tap: Dig a node or use the wielded item
|
||||
* Press back: Pause menu
|
||||
* Touch buttons: Press button
|
||||
* Buttons:
|
||||
|
||||
@@ -5204,6 +5204,10 @@ Minetest includes the following settings to control behavior of privileges:
|
||||
-- wallmounted nodes mounted at floor or ceiling may additionally
|
||||
-- be rotated by 90° with special param2 values (5.9.0)
|
||||
wallmounted_rotate = true,
|
||||
-- Availability of the `pointabilities` property in the item definition (5.9.0)
|
||||
item_specific_pointabilities = true,
|
||||
-- Nodes `pointable` property can be `"blocking"` (5.9.0)
|
||||
blocking_pointability_type = true,
|
||||
}
|
||||
```
|
||||
|
||||
@@ -5739,8 +5743,20 @@ Call these functions only at load time!
|
||||
* `name`: string; if omitted, all auth data should be considered modified
|
||||
* `minetest.set_player_password(name, password_hash)`: Set password hash of
|
||||
player `name`.
|
||||
* `minetest.set_player_privs(name, {priv1=true,...})`: Set privileges of player
|
||||
`name`.
|
||||
* `minetest.set_player_privs(name, privs)`: Set privileges of player `name`.
|
||||
* `privs` is a **set** of privileges:
|
||||
A table where the keys are names of privileges and the values are `true`.
|
||||
* Example: `minetest.set_player_privs("singleplayer", {interact = true, fly = true})`.
|
||||
This **sets** the player privileges to `interact` and `fly`;
|
||||
`singleplayer` will only have these two privileges afterwards.
|
||||
* `minetest.change_player_privs(name, changes)`: Helper to grant or revoke privileges.
|
||||
* `changes`: Table of changes to make.
|
||||
A field `[privname] = true` grants a privilege,
|
||||
whereas `[privname] = false` revokes a privilege.
|
||||
* Example: `minetest.change_player_privs("singleplayer", {interact = true, fly = false})`
|
||||
will grant singleplayer the `interact` privilege
|
||||
and revoke singleplayer's `fly` privilege.
|
||||
All other privileges will remain unchanged.
|
||||
* `minetest.auth_reload()`
|
||||
* See `reload()` in authentication handler definition
|
||||
|
||||
@@ -7744,8 +7760,7 @@ child will follow movement and rotation of that bone.
|
||||
whether `set_sky` accepts this format. Check the legacy format otherwise.
|
||||
* Passing no arguments resets the sky to its default values.
|
||||
* `sky_parameters` is a table with the following optional fields:
|
||||
* `base_color`: ColorSpec, changes fog in "skybox" and "plain".
|
||||
(default: `#ffffff`)
|
||||
* `base_color`: ColorSpec, meaning depends on `type` (default: `#ffffff`)
|
||||
* `body_orbit_tilt`: Float, rotation angle of sun/moon orbit in degrees.
|
||||
By default, orbit is controlled by a client-side setting, and this field is not set.
|
||||
After a value is assigned, it can only be changed to another float value.
|
||||
@@ -7798,6 +7813,9 @@ child will follow movement and rotation of that bone.
|
||||
Any value between [0.0, 0.99] set the fog_start as a fraction of the viewing_range.
|
||||
Any value < 0, resets the behavior to being client-controlled.
|
||||
(default: -1)
|
||||
* `fog_color`: ColorSpec, override the color of the fog.
|
||||
Unlike `base_color` above this will apply regardless of the skybox type.
|
||||
(default: `"#00000000"`, which means no override)
|
||||
> [!WARNING]
|
||||
> The darkening of the ColorSpec is subject to change.
|
||||
* `set_sky(base_color, type, {texture names}, clouds)`
|
||||
@@ -8039,10 +8057,10 @@ Can be obtained using `player:get_meta()`.
|
||||
A 16-bit pseudorandom number generator.
|
||||
Uses a well-known LCG algorithm introduced by K&R.
|
||||
|
||||
> [!NOTE]
|
||||
> `PseudoRandom` is slower and has worse random distribution than `PcgRandom`.
|
||||
> Use `PseudoRandom` only if you need output to match the well-known LCG algorithm introduced by K&R.
|
||||
> Otherwise, use `PcgRandom`.
|
||||
**Note**:
|
||||
`PseudoRandom` is slower and has worse random distribution than `PcgRandom`.
|
||||
Use `PseudoRandom` only if you need output to match the well-known LCG algorithm introduced by K&R.
|
||||
Otherwise, use `PcgRandom`.
|
||||
|
||||
### Constructor
|
||||
|
||||
@@ -8236,7 +8254,9 @@ Player properties need to be saved manually.
|
||||
|
||||
|
||||
pointable = true,
|
||||
-- Whether the object can be pointed at
|
||||
-- Can be `true` if it is pointable, `false` if it can be pointed through,
|
||||
-- or `"blocking"` if it is pointable but not selectable.
|
||||
-- Can be overridden by the `pointabilities` of the held item.
|
||||
|
||||
visual = "cube" / "sprite" / "upright_sprite" / "mesh" / "wielditem" / "item",
|
||||
-- "cube" is a node-sized cube.
|
||||
@@ -8593,6 +8613,27 @@ Used by `minetest.register_node`, `minetest.register_craftitem`, and
|
||||
-- If true, item can point to all liquid nodes (`liquidtype ~= "none"`),
|
||||
-- even those for which `pointable = false`
|
||||
|
||||
pointabilities = {
|
||||
nodes = {
|
||||
["default:stone"] = "blocking",
|
||||
["group:leaves"] = false,
|
||||
},
|
||||
objects = {
|
||||
["modname:entityname"] = true,
|
||||
["group:ghosty"] = true, -- (an armor group)
|
||||
}
|
||||
},
|
||||
-- Contains lists to override the `pointable` property of pointed nodes and objects.
|
||||
-- The index can be a node/entity name or a group with the prefix `"group:"`.
|
||||
-- (For objects `armor_groups` are used and for players the entity name is irrelevant.)
|
||||
-- If multiple fields fit, the following priority order is applied:
|
||||
-- 1. value of matching node/entity name
|
||||
-- 2. `true` for any group
|
||||
-- 3. `false` for any group
|
||||
-- 4. `"blocking"` for any group
|
||||
-- 5. `liquids_pointable` if it is a liquid node
|
||||
-- 6. `pointable` property of the node or object
|
||||
|
||||
light_source = 0,
|
||||
-- When used for nodes: Defines amount of light emitted by node.
|
||||
-- Otherwise: Defines texture glow when viewed as a dropped item
|
||||
@@ -8634,6 +8675,20 @@ Used by `minetest.register_node`, `minetest.register_craftitem`, and
|
||||
-- Otherwise should be name of node which the client immediately places
|
||||
-- upon digging. Server will always update with actual result shortly.
|
||||
|
||||
touch_interaction = {
|
||||
-- Only affects touchscreen clients.
|
||||
-- Defines the meaning of short and long taps with the item in hand.
|
||||
-- The fields in this table have two valid values:
|
||||
-- * "long_dig_short_place" (long tap = dig, short tap = place)
|
||||
-- * "short_dig_long_place" (short tap = dig, long tap = place)
|
||||
-- The field to be used is selected according to the current
|
||||
-- `pointed_thing`.
|
||||
|
||||
pointed_nothing = "long_dig_short_place",
|
||||
pointed_node = "long_dig_short_place",
|
||||
pointed_object = "short_dig_long_place",
|
||||
},
|
||||
|
||||
sound = {
|
||||
-- Definition of item sounds to be played at various events.
|
||||
-- All fields in this table are optional.
|
||||
@@ -8803,7 +8858,11 @@ Used by `minetest.register_node`.
|
||||
|
||||
walkable = true, -- If true, objects collide with node
|
||||
|
||||
pointable = true, -- If true, can be pointed at
|
||||
pointable = true,
|
||||
-- Can be `true` if it is pointable, `false` if it can be pointed through,
|
||||
-- or `"blocking"` if it is pointable but not selectable.
|
||||
-- Can be overridden by the `pointabilities` of the held item.
|
||||
-- A client may be able to point non-pointable nodes, since it isn't checked server-side.
|
||||
|
||||
diggable = true, -- If false, can never be dug
|
||||
|
||||
@@ -10535,8 +10594,8 @@ Used by `minetest.register_authentication_handler`.
|
||||
|
||||
set_privileges = function(name, privileges),
|
||||
-- Set privileges of player `name`.
|
||||
-- `privileges` is in table form, auth data should be created if not
|
||||
-- present.
|
||||
-- `privileges` is in table form: keys are privilege names, values are `true`;
|
||||
-- auth data should be created if not present.
|
||||
|
||||
reload = function(),
|
||||
-- Reload authentication data from the storage location.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
dofile(minetest.get_modpath("testentities").."/visuals.lua")
|
||||
dofile(minetest.get_modpath("testentities").."/selectionbox.lua")
|
||||
dofile(minetest.get_modpath("testentities").."/armor.lua")
|
||||
dofile(minetest.get_modpath("testentities").."/pointable.lua")
|
||||
|
||||
23
games/devtest/mods/testentities/pointable.lua
Normal file
23
games/devtest/mods/testentities/pointable.lua
Normal file
@@ -0,0 +1,23 @@
|
||||
-- Pointability test Entities
|
||||
|
||||
-- Register wrapper for compactness
|
||||
local function register_pointable_testentity(name, pointable)
|
||||
local texture = "testnodes_"..name..".png"
|
||||
minetest.register_entity("testentities:"..name, {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
visual_size = {x = 0.6, y = 0.6, z = 0.6},
|
||||
textures = {
|
||||
texture, texture, texture, texture, texture, texture
|
||||
},
|
||||
pointable = pointable,
|
||||
},
|
||||
on_activate = function(self)
|
||||
self.object:set_armor_groups({[name.."_test"] = 1})
|
||||
end
|
||||
})
|
||||
end
|
||||
|
||||
register_pointable_testentity("pointable", true)
|
||||
register_pointable_testentity("not_pointable", false)
|
||||
register_pointable_testentity("blocking_pointable", "blocking")
|
||||
@@ -663,3 +663,23 @@ minetest.register_node("testnodes:post_effect_color_shaded_true", {
|
||||
is_ground_content = false,
|
||||
groups = {dig_immediate=3},
|
||||
})
|
||||
|
||||
-- Pointability
|
||||
|
||||
-- Register wrapper for compactness
|
||||
local function register_pointable_test_node(name, description, pointable)
|
||||
local texture = "testnodes_"..name..".png"
|
||||
minetest.register_node("testnodes:"..name, {
|
||||
description = S(description),
|
||||
tiles = {texture},
|
||||
drawtype = "glasslike_framed",
|
||||
paramtype = "light",
|
||||
walkable = false,
|
||||
pointable = pointable,
|
||||
groups = {dig_immediate=3, [name.."_test"]=1},
|
||||
})
|
||||
end
|
||||
|
||||
register_pointable_test_node("pointable", "Pointable Node", true)
|
||||
register_pointable_test_node("not_pointable", "Not Pointable Node", false)
|
||||
register_pointable_test_node("blocking_pointable", "Blocking Pointable Node", "blocking")
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 150 B |
Binary file not shown.
|
After Width: | Height: | Size: 152 B |
BIN
games/devtest/mods/testnodes/textures/testnodes_pointable.png
Normal file
BIN
games/devtest/mods/testnodes/textures/testnodes_pointable.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 144 B |
@@ -7,6 +7,20 @@ dofile(minetest.get_modpath("testtools") .. "/light.lua")
|
||||
dofile(minetest.get_modpath("testtools") .. "/privatizer.lua")
|
||||
dofile(minetest.get_modpath("testtools") .. "/particles.lua")
|
||||
|
||||
local pointabilities_nodes = {
|
||||
nodes = {
|
||||
["group:blocking_pointable_test"] = true,
|
||||
["group:not_pointable_test"] = true,
|
||||
},
|
||||
}
|
||||
|
||||
local pointabilities_objects = {
|
||||
objects = {
|
||||
["group:blocking_pointable_test"] = true,
|
||||
["group:not_pointable_test"] = true,
|
||||
},
|
||||
}
|
||||
|
||||
minetest.register_tool("testtools:param2tool", {
|
||||
description = S("Param2 Tool") .."\n"..
|
||||
S("Modify param2 value of nodes") .."\n"..
|
||||
@@ -16,6 +30,7 @@ minetest.register_tool("testtools:param2tool", {
|
||||
S("Sneak+Place: -8"),
|
||||
inventory_image = "testtools_param2tool.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
pointabilities = pointabilities_nodes,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
local pos = minetest.get_pointed_thing_position(pointed_thing)
|
||||
if pointed_thing.type ~= "node" or (not pos) then
|
||||
@@ -58,6 +73,7 @@ minetest.register_tool("testtools:node_setter", {
|
||||
S("Place in air: Manually select a node"),
|
||||
inventory_image = "testtools_node_setter.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
pointabilities = pointabilities_nodes,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
local pos = minetest.get_pointed_thing_position(pointed_thing)
|
||||
if pointed_thing.type == "nothing" then
|
||||
@@ -118,6 +134,10 @@ minetest.register_tool("testtools:remover", {
|
||||
S("Punch: Remove pointed node or object"),
|
||||
inventory_image = "testtools_remover.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
pointabilities = {
|
||||
nodes = pointabilities_nodes.nodes,
|
||||
objects = pointabilities_objects.objects,
|
||||
},
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
local pos = minetest.get_pointed_thing_position(pointed_thing)
|
||||
if pointed_thing.type == "node" and pos ~= nil then
|
||||
@@ -139,6 +159,7 @@ minetest.register_tool("testtools:falling_node_tool", {
|
||||
S("Place: Move pointed node 2 units upwards, then make it fall"),
|
||||
inventory_image = "testtools_falling_node_tool.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
pointabilities = pointabilities_nodes,
|
||||
on_place = function(itemstack, user, pointed_thing)
|
||||
-- Teleport node 1-2 units upwards (if possible) and make it fall
|
||||
local pos = minetest.get_pointed_thing_position(pointed_thing)
|
||||
@@ -192,6 +213,7 @@ minetest.register_tool("testtools:rotator", {
|
||||
S("Aux1+Punch: Roll"),
|
||||
inventory_image = "testtools_entity_rotator.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
pointabilities = pointabilities_objects,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if pointed_thing.type ~= "object" then
|
||||
return
|
||||
@@ -250,6 +272,7 @@ minetest.register_tool("testtools:object_mover", {
|
||||
S("Sneak+Place: Decrease distance"),
|
||||
inventory_image = "testtools_object_mover.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
pointabilities = pointabilities_objects,
|
||||
on_place = mover_config,
|
||||
on_secondary_use = mover_config,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
@@ -296,6 +319,7 @@ minetest.register_tool("testtools:entity_scaler", {
|
||||
S("Sneak+Punch: Decrease scale"),
|
||||
inventory_image = "testtools_entity_scaler.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
pointabilities = pointabilities_objects,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if pointed_thing.type ~= "object" then
|
||||
return
|
||||
@@ -355,6 +379,7 @@ minetest.register_tool("testtools:branding_iron", {
|
||||
S("Devices that accept the returned name also accept \"player:<playername>\" for players."),
|
||||
inventory_image = "testtools_branding_iron.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
pointabilities = pointabilities_objects,
|
||||
on_use = function(_itemstack, user, pointed_thing)
|
||||
local obj
|
||||
local msg
|
||||
@@ -499,6 +524,7 @@ minetest.register_tool("testtools:object_editor", {
|
||||
S("Punch air: Edit yourself"),
|
||||
inventory_image = "testtools_object_editor.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
pointabilities = pointabilities_objects,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if user and user:is_player() then
|
||||
local name = user:get_player_name()
|
||||
@@ -586,6 +612,7 @@ minetest.register_tool("testtools:object_attacher", {
|
||||
S("Aux1+Sneak+Place: Decrease attachment rotation"),
|
||||
inventory_image = "testtools_object_attacher.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
pointabilities = pointabilities_objects,
|
||||
on_place = attacher_config,
|
||||
on_secondary_use = attacher_config,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
@@ -679,6 +706,7 @@ minetest.register_tool("testtools:children_getter", {
|
||||
S("Punch air to show your own 'children'"),
|
||||
inventory_image = "testtools_children_getter.png",
|
||||
groups = { testtool = 1, disable_repair = 1 },
|
||||
pointabilities = pointabilities_objects,
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
if user and user:is_player() then
|
||||
local name = user:get_player_name()
|
||||
@@ -998,3 +1026,41 @@ minetest.register_on_leaveplayer(function(player)
|
||||
meta_latest_keylist[name] = nil
|
||||
node_meta_posses[name] = nil
|
||||
end)
|
||||
|
||||
-- Pointing Staffs
|
||||
|
||||
minetest.register_tool("testtools:blocked_pointing_staff", {
|
||||
description = S("Blocked Pointing Staff").."\n"..
|
||||
S("Can point the Blocking Pointable Node/Object and "..
|
||||
"the Pointable Node/Object is point blocking."),
|
||||
inventory_image = "testtools_blocked_pointing_staff.png",
|
||||
pointabilities = {
|
||||
nodes = {
|
||||
["testnodes:blocking_pointable"] = true,
|
||||
["group:pointable_test"] = "blocking"
|
||||
},
|
||||
objects = {
|
||||
["testentities:blocking_pointable"] = true,
|
||||
["group:pointable_test"] = "blocking"
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_tool("testtools:ultimate_pointing_staff", {
|
||||
description = S("Ultimate Pointing Staff").."\n"..
|
||||
S("Can point all pointable test nodes, objects and liquids."),
|
||||
inventory_image = "testtools_ultimate_pointing_staff.png",
|
||||
liquids_pointable = true,
|
||||
pointabilities = {
|
||||
nodes = {
|
||||
["group:blocking_pointable_test"] = true,
|
||||
["group:pointable_test"] = true,
|
||||
["testnodes:not_pointable"] = true
|
||||
},
|
||||
objects = {
|
||||
["group:blocking_pointable_test"] = true,
|
||||
["group:pointable_test"] = true,
|
||||
["testentities:not_pointable"] = true
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 136 B |
Binary file not shown.
|
After Width: | Height: | Size: 136 B |
@@ -489,7 +489,8 @@ ClientEnvEvent ClientEnvironment::getClientEnvEvent()
|
||||
|
||||
void ClientEnvironment::getSelectedActiveObjects(
|
||||
const core::line3d<f32> &shootline_on_map,
|
||||
std::vector<PointedThing> &objects)
|
||||
std::vector<PointedThing> &objects,
|
||||
const std::optional<Pointabilities> &pointabilities)
|
||||
{
|
||||
auto allObjects = m_ao_manager.getActiveSelectableObjects(shootline_on_map);
|
||||
const v3f line_vector = shootline_on_map.getVector();
|
||||
@@ -516,9 +517,23 @@ void ClientEnvironment::getSelectedActiveObjects(
|
||||
current_raw_normal = current_normal;
|
||||
}
|
||||
if (collision) {
|
||||
current_intersection += obj->getPosition();
|
||||
objects.emplace_back(obj->getId(), current_intersection, current_normal, current_raw_normal,
|
||||
(current_intersection - shootline_on_map.start).getLengthSQ());
|
||||
PointabilityType pointable;
|
||||
if (pointabilities) {
|
||||
if (gcao->isPlayer()) {
|
||||
pointable = pointabilities->matchPlayer(gcao->getGroups()).value_or(
|
||||
gcao->getProperties().pointable);
|
||||
} else {
|
||||
pointable = pointabilities->matchObject(gcao->getName(),
|
||||
gcao->getGroups()).value_or(gcao->getProperties().pointable);
|
||||
}
|
||||
} else {
|
||||
pointable = gcao->getProperties().pointable;
|
||||
}
|
||||
if (pointable != PointabilityType::POINTABLE_NOT) {
|
||||
current_intersection += obj->getPosition();
|
||||
objects.emplace_back(obj->getId(), current_intersection, current_normal, current_raw_normal,
|
||||
(current_intersection - shootline_on_map.start).getLengthSQ(), pointable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,7 +131,8 @@ public:
|
||||
|
||||
virtual void getSelectedActiveObjects(
|
||||
const core::line3d<f32> &shootline_on_map,
|
||||
std::vector<PointedThing> &objects
|
||||
std::vector<PointedThing> &objects,
|
||||
const std::optional<Pointabilities> &pointabilities
|
||||
);
|
||||
|
||||
const std::set<std::string> &getPlayerNames() { return m_player_names; }
|
||||
|
||||
@@ -108,13 +108,6 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
|
||||
return false;
|
||||
}
|
||||
|
||||
// Speed tests (done after irrlicht is loaded to get timer)
|
||||
if (cmd_args.getFlag("speedtests")) {
|
||||
dstream << "Running speed tests" << std::endl;
|
||||
speed_tests();
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_rendering_engine->get_video_driver() == NULL) {
|
||||
errorstream << "Could not initialize video driver." << std::endl;
|
||||
return false;
|
||||
@@ -250,11 +243,8 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
|
||||
}
|
||||
|
||||
// Break out of menu-game loop to shut down cleanly
|
||||
if (!m_rendering_engine->run() || *kill) {
|
||||
if (!g_settings_path.empty())
|
||||
g_settings->updateConfigFile(g_settings_path.c_str());
|
||||
if (!m_rendering_engine->run() || *kill)
|
||||
break;
|
||||
}
|
||||
|
||||
m_rendering_engine->get_video_driver()->setTextureCreationFlag(
|
||||
video::ETCF_CREATE_MIP_MAPS, g_settings->getBool("mip_map"));
|
||||
@@ -299,6 +289,16 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args)
|
||||
receiver->m_touchscreengui = NULL;
|
||||
#endif
|
||||
|
||||
/* Save the settings when leaving the game.
|
||||
* This makes sure that setting changes made in-game are persisted even
|
||||
* in case of a later unclean exit from the mainmenu.
|
||||
* This is especially useful on Android because closing the app from the
|
||||
* "Recents screen" results in an unclean exit.
|
||||
* Caveat: This means that the settings are saved twice when exiting Minetest.
|
||||
*/
|
||||
if (!g_settings_path.empty())
|
||||
g_settings->updateConfigFile(g_settings_path.c_str());
|
||||
|
||||
// If no main menu, show error and exit
|
||||
if (skip_main_menu) {
|
||||
if (!error_message.empty()) {
|
||||
@@ -387,7 +387,7 @@ bool ClientLauncher::launch_game(std::string &error_message,
|
||||
if (cmd_args.exists("password-file")) {
|
||||
std::ifstream passfile(cmd_args.get("password-file"));
|
||||
if (passfile.good()) {
|
||||
getline(passfile, start_data.password);
|
||||
std::getline(passfile, start_data.password);
|
||||
} else {
|
||||
error_message = gettext("Provided password file "
|
||||
"failed to open: ")
|
||||
@@ -444,8 +444,6 @@ bool ClientLauncher::launch_game(std::string &error_message,
|
||||
|
||||
int world_index = menudata.selected_world;
|
||||
if (world_index >= 0 && world_index < (int)worldspecs.size()) {
|
||||
g_settings->set("selected_world_path",
|
||||
worldspecs[world_index].path);
|
||||
start_data.world_spec = worldspecs[world_index];
|
||||
}
|
||||
|
||||
@@ -517,15 +515,7 @@ bool ClientLauncher::launch_game(std::string &error_message,
|
||||
return false;
|
||||
}
|
||||
|
||||
if (porting::signal_handler_killstatus())
|
||||
return true;
|
||||
|
||||
if (!start_data.game_spec.isValid()) {
|
||||
error_message = gettext("Invalid gamespec.");
|
||||
error_message += " (world.gameid=" + worldspec.gameid + ")";
|
||||
errorstream << error_message << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
start_data.world_path = start_data.world_spec.path;
|
||||
@@ -562,105 +552,14 @@ void ClientLauncher::main_menu(MainMenuData *menudata)
|
||||
|
||||
/* leave scene manager in a clean state */
|
||||
m_rendering_engine->get_scene_manager()->clear();
|
||||
}
|
||||
|
||||
void ClientLauncher::speed_tests()
|
||||
{
|
||||
// volatile to avoid some potential compiler optimisations
|
||||
volatile static s16 temp16;
|
||||
volatile static f32 tempf;
|
||||
// Silence compiler warning
|
||||
(void)temp16;
|
||||
static v3f tempv3f1;
|
||||
static v3f tempv3f2;
|
||||
static std::string tempstring;
|
||||
static std::string tempstring2;
|
||||
|
||||
tempv3f1 = v3f();
|
||||
tempv3f2 = v3f();
|
||||
tempstring.clear();
|
||||
tempstring2.clear();
|
||||
|
||||
{
|
||||
infostream << "The following test should take around 20ms." << std::endl;
|
||||
TimeTaker timer("Testing std::string speed");
|
||||
const u32 jj = 10000;
|
||||
for (u32 j = 0; j < jj; j++) {
|
||||
tempstring.clear();
|
||||
tempstring2.clear();
|
||||
const u32 ii = 10;
|
||||
for (u32 i = 0; i < ii; i++) {
|
||||
tempstring2 += "asd";
|
||||
}
|
||||
for (u32 i = 0; i < ii+1; i++) {
|
||||
tempstring += "asd";
|
||||
if (tempstring == tempstring2)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
infostream << "All of the following tests should take around 100ms each."
|
||||
<< std::endl;
|
||||
|
||||
{
|
||||
TimeTaker timer("Testing floating-point conversion speed");
|
||||
tempf = 0.001;
|
||||
for (u32 i = 0; i < 4000000; i++) {
|
||||
temp16 += tempf;
|
||||
tempf += 0.001;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
TimeTaker timer("Testing floating-point vector speed");
|
||||
|
||||
tempv3f1 = v3f(1, 2, 3);
|
||||
tempv3f2 = v3f(4, 5, 6);
|
||||
for (u32 i = 0; i < 10000000; i++) {
|
||||
tempf += tempv3f1.dotProduct(tempv3f2);
|
||||
tempv3f2 += v3f(7, 8, 9);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
TimeTaker timer("Testing std::map speed");
|
||||
|
||||
std::map<v2s16, f32> map1;
|
||||
tempf = -324;
|
||||
const s16 ii = 300;
|
||||
for (s16 y = 0; y < ii; y++) {
|
||||
for (s16 x = 0; x < ii; x++) {
|
||||
map1[v2s16(x, y)] = tempf;
|
||||
tempf += 1;
|
||||
}
|
||||
}
|
||||
for (s16 y = ii - 1; y >= 0; y--) {
|
||||
for (s16 x = 0; x < ii; x++) {
|
||||
tempf = map1[v2s16(x, y)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
infostream << "Around 5000/ms should do well here." << std::endl;
|
||||
TimeTaker timer("Testing mutex speed");
|
||||
|
||||
std::mutex m;
|
||||
u32 n = 0;
|
||||
u32 i = 0;
|
||||
do {
|
||||
n += 10000;
|
||||
for (; i < n; i++) {
|
||||
m.lock();
|
||||
m.unlock();
|
||||
}
|
||||
}
|
||||
// Do at least 10ms
|
||||
while(timer.getTimerTime() < 10);
|
||||
|
||||
u32 dtime = timer.stop();
|
||||
u32 per_ms = n / dtime;
|
||||
infostream << "Done. " << dtime << "ms, " << per_ms << "/ms" << std::endl;
|
||||
}
|
||||
|
||||
/* Save the settings when leaving the mainmenu.
|
||||
* This makes sure that setting changes made in the mainmenu are persisted
|
||||
* even in case of a later unclean exit from the game.
|
||||
* This is especially useful on Android because closing the app from the
|
||||
* "Recents screen" results in an unclean exit.
|
||||
* Caveat: This means that the settings are saved twice when exiting Minetest.
|
||||
*/
|
||||
if (!g_settings_path.empty())
|
||||
g_settings->updateConfigFile(g_settings_path.c_str());
|
||||
}
|
||||
|
||||
@@ -44,8 +44,6 @@ private:
|
||||
|
||||
void main_menu(MainMenuData *menudata);
|
||||
|
||||
void speed_tests();
|
||||
|
||||
bool skip_main_menu = false;
|
||||
bool random_input = false;
|
||||
RenderingEngine *m_rendering_engine = nullptr;
|
||||
|
||||
@@ -165,9 +165,11 @@ void Clouds::render()
|
||||
driver->getFog(fog_color, fog_type, fog_start, fog_end, fog_density,
|
||||
fog_pixelfog, fog_rangefog);
|
||||
|
||||
// Set our own fog
|
||||
driver->setFog(fog_color, fog_type, cloud_full_radius * 0.5,
|
||||
// Set our own fog, unless it was already disabled
|
||||
if (fog_start < FOG_RANGE_ALL) {
|
||||
driver->setFog(fog_color, fog_type, cloud_full_radius * 0.5,
|
||||
cloud_full_radius*1.2, fog_density, fog_pixelfog, fog_rangefog);
|
||||
}
|
||||
|
||||
// Read noise
|
||||
|
||||
|
||||
@@ -411,8 +411,7 @@ GenericCAO::~GenericCAO()
|
||||
|
||||
bool GenericCAO::getSelectionBox(aabb3f *toset) const
|
||||
{
|
||||
if (!m_prop.is_visible || !m_is_visible || m_is_local_player
|
||||
|| !m_prop.pointable) {
|
||||
if (!m_prop.is_visible || !m_is_visible || m_is_local_player) {
|
||||
return false;
|
||||
}
|
||||
*toset = m_selection_box;
|
||||
|
||||
@@ -174,6 +174,8 @@ public:
|
||||
|
||||
inline const ObjectProperties &getProperties() const { return m_prop; }
|
||||
|
||||
inline const std::string &getName() const { return m_name; }
|
||||
|
||||
scene::ISceneNode *getSceneNode() const override;
|
||||
|
||||
scene::IAnimatedMeshSceneNode *getAnimatedMeshSceneNode() const override;
|
||||
@@ -208,6 +210,11 @@ public:
|
||||
return m_is_local_player;
|
||||
}
|
||||
|
||||
inline bool isPlayer() const
|
||||
{
|
||||
return m_is_player;
|
||||
}
|
||||
|
||||
inline bool isVisible() const
|
||||
{
|
||||
return m_is_visible;
|
||||
|
||||
@@ -374,7 +374,7 @@ class GameGlobalShaderConstantSetter : public IShaderConstantSetter
|
||||
bool *m_force_fog_off;
|
||||
f32 *m_fog_range;
|
||||
bool m_fog_enabled;
|
||||
CachedPixelShaderSetting<float, 4> m_sky_bg_color{"skyBgColor"};
|
||||
CachedPixelShaderSetting<float, 4> m_fog_color{"fogColor"};
|
||||
CachedPixelShaderSetting<float> m_fog_distance{"fogDistance"};
|
||||
CachedPixelShaderSetting<float>
|
||||
m_fog_shading_parameter{"fogShadingParameter"};
|
||||
@@ -475,20 +475,13 @@ public:
|
||||
|
||||
void onSetConstants(video::IMaterialRendererServices *services) override
|
||||
{
|
||||
// Background color
|
||||
video::SColor bgcolor = m_sky->getBgColor();
|
||||
video::SColorf bgcolorf(bgcolor);
|
||||
float bgcolorfa[4] = {
|
||||
bgcolorf.r,
|
||||
bgcolorf.g,
|
||||
bgcolorf.b,
|
||||
bgcolorf.a,
|
||||
video::SColorf fogcolorf(m_sky->getFogColor());
|
||||
float fogcolorfa[4] = {
|
||||
fogcolorf.r, fogcolorf.g, fogcolorf.b, fogcolorf.a,
|
||||
};
|
||||
m_sky_bg_color.set(bgcolorfa, services);
|
||||
m_fog_color.set(fogcolorfa, services);
|
||||
|
||||
// Fog distance
|
||||
float fog_distance = 10000 * BS;
|
||||
|
||||
if (m_fog_enabled && !*m_force_fog_off)
|
||||
fog_distance = *m_fog_range;
|
||||
|
||||
@@ -841,6 +834,7 @@ protected:
|
||||
* the camera position. This also gives the maximal distance
|
||||
* of the search.
|
||||
* @param[in] liquids_pointable if false, liquids are ignored
|
||||
* @param[in] pointabilities item specific pointable overriding
|
||||
* @param[in] look_for_object if false, objects are ignored
|
||||
* @param[in] camera_offset offset of the camera
|
||||
* @param[out] selected_object the selected object or
|
||||
@@ -848,6 +842,7 @@ protected:
|
||||
*/
|
||||
PointedThing updatePointedThing(
|
||||
const core::line3d<f32> &shootline, bool liquids_pointable,
|
||||
const std::optional<Pointabilities> &pointabilities,
|
||||
bool look_for_object, const v3s16 &camera_offset);
|
||||
void handlePointingAtNothing(const ItemStack &playerItem);
|
||||
void handlePointingAtNode(const PointedThing &pointed,
|
||||
@@ -981,7 +976,6 @@ private:
|
||||
bool *kill;
|
||||
std::string *error_message;
|
||||
bool *reconnect_requested;
|
||||
scene::ISceneNode *skybox;
|
||||
PausedNodesList paused_animated_nodes;
|
||||
|
||||
bool simple_singleplayer_mode;
|
||||
@@ -1520,7 +1514,6 @@ bool Game::createClient(const GameStartData &start_data)
|
||||
*/
|
||||
sky = new Sky(-1, m_rendering_engine, texture_src, shader_src);
|
||||
scsf->setSky(sky);
|
||||
skybox = NULL; // This is used/set later on in the main run loop
|
||||
|
||||
/* Pre-calculated values
|
||||
*/
|
||||
@@ -1750,7 +1743,7 @@ bool Game::getServerContent(bool *aborted)
|
||||
// End condition
|
||||
if (client->mediaReceived() && client->itemdefReceived() &&
|
||||
client->nodedefReceived()) {
|
||||
break;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Error conditions
|
||||
@@ -1809,7 +1802,9 @@ bool Game::getServerContent(bool *aborted)
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
*aborted = true;
|
||||
infostream << "Connect aborted [device]" << std::endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -3043,10 +3038,6 @@ void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam)
|
||||
// Whether clouds are visible in front of a custom skybox.
|
||||
sky->setCloudsEnabled(event->set_sky->clouds);
|
||||
|
||||
if (skybox) {
|
||||
skybox->remove();
|
||||
skybox = NULL;
|
||||
}
|
||||
// Clear the old textures out in case we switch rendering type.
|
||||
sky->clearSkyboxTextures();
|
||||
// Handle according to type
|
||||
@@ -3106,6 +3097,8 @@ void Game::handleClientEvent_SetSky(ClientEvent *event, CameraOrientation *cam)
|
||||
else
|
||||
sky->setFogStart(rangelim(g_settings->getFloat("fog_start"), 0.0f, 0.99f));
|
||||
|
||||
sky->setFogColor(event->set_sky->fog_color);
|
||||
|
||||
delete event->set_sky;
|
||||
}
|
||||
|
||||
@@ -3343,12 +3336,18 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud)
|
||||
|
||||
PointedThing pointed = updatePointedThing(shootline,
|
||||
selected_def.liquids_pointable,
|
||||
selected_def.pointabilities,
|
||||
!runData.btn_down_for_dig,
|
||||
camera_offset);
|
||||
|
||||
if (pointed != runData.pointed_old)
|
||||
infostream << "Pointing at " << pointed.dump() << std::endl;
|
||||
|
||||
#ifdef HAVE_TOUCHSCREENGUI
|
||||
if (g_touchscreengui)
|
||||
g_touchscreengui->applyContextControls(selected_def.touch_interaction.getMode(pointed));
|
||||
#endif
|
||||
|
||||
// Note that updating the selection mesh every frame is not particularly efficient,
|
||||
// but the halo rendering code is already inefficient so there's no point in optimizing it here
|
||||
hud->updateSelectionMesh(camera_offset);
|
||||
@@ -3449,6 +3448,7 @@ void Game::processPlayerInteraction(f32 dtime, bool show_hud)
|
||||
PointedThing Game::updatePointedThing(
|
||||
const core::line3d<f32> &shootline,
|
||||
bool liquids_pointable,
|
||||
const std::optional<Pointabilities> &pointabilities,
|
||||
bool look_for_object,
|
||||
const v3s16 &camera_offset)
|
||||
{
|
||||
@@ -3465,7 +3465,7 @@ PointedThing Game::updatePointedThing(
|
||||
runData.selected_object = NULL;
|
||||
hud->pointing_at_object = false;
|
||||
|
||||
RaycastState s(shootline, look_for_object, liquids_pointable);
|
||||
RaycastState s(shootline, look_for_object, liquids_pointable, pointabilities);
|
||||
PointedThing result;
|
||||
env.continueRaycast(&s, &result);
|
||||
if (result.type == POINTEDTHING_OBJECT) {
|
||||
@@ -4046,7 +4046,7 @@ void Game::updateFrame(ProfilerGraph *graph, RunStats *stats, f32 dtime,
|
||||
draw_control->wanted_range = MYMIN(draw_control->wanted_range, sky->getFogDistance());
|
||||
}
|
||||
if (draw_control->range_all && sky->getFogDistance() < 0) {
|
||||
runData.fog_range = 100000 * BS;
|
||||
runData.fog_range = FOG_RANGE_ALL;
|
||||
} else {
|
||||
runData.fog_range = draw_control->wanted_range * BS;
|
||||
}
|
||||
@@ -4288,7 +4288,7 @@ void Game::updateShadows()
|
||||
|
||||
void Game::drawScene(ProfilerGraph *graph, RunStats *stats)
|
||||
{
|
||||
const video::SColor bg_color = this->sky->getBgColor();
|
||||
const video::SColor fog_color = this->sky->getFogColor();
|
||||
const video::SColor sky_color = this->sky->getSkyColor();
|
||||
|
||||
/*
|
||||
@@ -4296,21 +4296,21 @@ void Game::drawScene(ProfilerGraph *graph, RunStats *stats)
|
||||
*/
|
||||
if (this->m_cache_enable_fog) {
|
||||
this->driver->setFog(
|
||||
bg_color,
|
||||
fog_color,
|
||||
video::EFT_FOG_LINEAR,
|
||||
this->runData.fog_range * this->sky->getFogStart(),
|
||||
this->runData.fog_range * 1.0f,
|
||||
0.01f,
|
||||
0.f, // unused
|
||||
false, // pixel fog
|
||||
true // range fog
|
||||
);
|
||||
} else {
|
||||
this->driver->setFog(
|
||||
bg_color,
|
||||
fog_color,
|
||||
video::EFT_FOG_LINEAR,
|
||||
100000 * BS,
|
||||
110000 * BS,
|
||||
0.01f,
|
||||
FOG_RANGE_ALL,
|
||||
FOG_RANGE_ALL + 100 * BS,
|
||||
0.f, // unused
|
||||
false, // pixel fog
|
||||
false // range fog
|
||||
);
|
||||
@@ -4484,8 +4484,8 @@ void Game::showPauseMenu()
|
||||
static const std::string control_text = strgettext("Controls:\n"
|
||||
"No menu open:\n"
|
||||
"- slide finger: look around\n"
|
||||
"- tap: place/use\n"
|
||||
"- long tap: dig/punch/use\n"
|
||||
"- tap: place/punch/use (default)\n"
|
||||
"- long tap: dig/use (default)\n"
|
||||
"Menu/inventory open:\n"
|
||||
"- double tap (outside):\n"
|
||||
" --> close\n"
|
||||
|
||||
@@ -416,7 +416,9 @@ void Hud::drawLuaElements(const v3s16 &camera_offset)
|
||||
(e->number >> 0) & 0xFF);
|
||||
std::wstring text = unescape_translate(utf8_to_wide(e->name));
|
||||
const std::string &unit = e->text;
|
||||
// waypoints reuse the item field to store precision, item = precision + 1
|
||||
// Waypoints reuse the item field to store precision,
|
||||
// item = precision + 1 and item = 0 <=> precision = 10 for backwards compatibility.
|
||||
// Also see `push_hud_element`.
|
||||
u32 item = e->item;
|
||||
float precision = (item == 0) ? 10.0f : (item - 1.f);
|
||||
bool draw_precision = precision > 0;
|
||||
|
||||
@@ -43,6 +43,9 @@ class Minimap;
|
||||
|
||||
class RenderingCore;
|
||||
|
||||
// Instead of a mechanism to disable fog we just set it to be really far away
|
||||
#define FOG_RANGE_ALL (100000 * BS)
|
||||
|
||||
class RenderingEngine
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -54,12 +54,12 @@ public:
|
||||
|
||||
float getBrightness() { return m_brightness; }
|
||||
|
||||
const video::SColor &getBgColor() const
|
||||
video::SColor getBgColor() const
|
||||
{
|
||||
return m_visible ? m_bgcolor : m_fallback_bg_color;
|
||||
}
|
||||
|
||||
const video::SColor &getSkyColor() const
|
||||
video::SColor getSkyColor() const
|
||||
{
|
||||
return m_visible ? m_skycolor : m_fallback_bg_color;
|
||||
}
|
||||
@@ -90,6 +90,7 @@ public:
|
||||
const video::SColorf &getCloudColor() const { return m_cloudcolor_f; }
|
||||
|
||||
void setVisible(bool visible) { m_visible = visible; }
|
||||
|
||||
// Set only from set_sky API
|
||||
void setCloudsEnabled(bool clouds_enabled) { m_clouds_enabled = clouds_enabled; }
|
||||
void setFallbackBgColor(video::SColor fallback_bg_color)
|
||||
@@ -111,17 +112,23 @@ public:
|
||||
const std::string &use_sun_tint);
|
||||
void setInClouds(bool clouds) { m_in_clouds = clouds; }
|
||||
void clearSkyboxTextures() { m_sky_params.textures.clear(); }
|
||||
void addTextureToSkybox(const std::string &texture, int material_id,
|
||||
void addTextureToSkybox(const std::string &texture, int material_id,
|
||||
ITextureSource *tsrc);
|
||||
const video::SColorf &getCurrentStarColor() const { return m_star_color; }
|
||||
|
||||
// Note: the Sky class doesn't use these values. It just stores them.
|
||||
void setFogDistance(s16 fog_distance) { m_sky_params.fog_distance = fog_distance; }
|
||||
s16 getFogDistance() const { return m_sky_params.fog_distance; }
|
||||
|
||||
void setFogStart(float fog_start) { m_sky_params.fog_start = fog_start; }
|
||||
float getFogStart() const { return m_sky_params.fog_start; }
|
||||
|
||||
void setVolumetricLightStrength(float volumetric_light_strength) { m_sky_params.volumetric_light_strength = volumetric_light_strength; }
|
||||
float getVolumetricLightStrength() const { return m_sky_params.volumetric_light_strength; }
|
||||
void setFogColor(video::SColor v) { m_sky_params.fog_color = v; }
|
||||
video::SColor getFogColor() const {
|
||||
if (m_sky_params.fog_color.getAlpha() > 0)
|
||||
return m_sky_params.fog_color;
|
||||
return getBgColor();
|
||||
}
|
||||
|
||||
private:
|
||||
aabb3f m_box;
|
||||
|
||||
@@ -480,12 +480,11 @@ void RemoteClient::SetBlockNotSent(v3s16 p)
|
||||
m_blocks_modified.insert(p);
|
||||
}
|
||||
|
||||
void RemoteClient::SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks)
|
||||
void RemoteClient::SetBlocksNotSent(const std::vector<v3s16> &blocks)
|
||||
{
|
||||
m_nothing_to_send_pause_timer = 0;
|
||||
|
||||
for (auto &block : blocks) {
|
||||
v3s16 p = block.first;
|
||||
for (v3s16 p : blocks) {
|
||||
// remove the block from sending and sent sets,
|
||||
// and mark as modified if found
|
||||
if (m_blocks_sending.erase(p) + m_blocks_sent.erase(p) > 0)
|
||||
@@ -707,12 +706,12 @@ std::vector<session_t> ClientInterface::getClientIDs(ClientState min_state)
|
||||
return reply;
|
||||
}
|
||||
|
||||
void ClientInterface::markBlockposAsNotSent(const v3s16 &pos)
|
||||
void ClientInterface::markBlocksNotSent(const std::vector<v3s16> &positions)
|
||||
{
|
||||
RecursiveMutexAutoLock clientslock(m_clients_mutex);
|
||||
for (const auto &client : m_clients) {
|
||||
if (client.second->getState() >= CS_Active)
|
||||
client.second->SetBlockNotSent(pos);
|
||||
client.second->SetBlocksNotSent(positions);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -267,7 +267,7 @@ public:
|
||||
void SentBlock(v3s16 p);
|
||||
|
||||
void SetBlockNotSent(v3s16 p);
|
||||
void SetBlocksNotSent(std::map<v3s16, MapBlock*> &blocks);
|
||||
void SetBlocksNotSent(const std::vector<v3s16> &blocks);
|
||||
|
||||
/**
|
||||
* tell client about this block being modified right now.
|
||||
@@ -284,10 +284,10 @@ public:
|
||||
return m_blocks_sent.find(p) != m_blocks_sent.end();
|
||||
}
|
||||
|
||||
// Increments timeouts and removes timed-out blocks from list
|
||||
// NOTE: This doesn't fix the server-not-sending-block bug
|
||||
// because it is related to emerging, not sending.
|
||||
//void RunSendingTimeouts(float dtime, float timeout);
|
||||
bool markMediaSent(const std::string &name) {
|
||||
auto insert_result = m_media_sent.emplace(name);
|
||||
return insert_result.second; // true = was inserted
|
||||
}
|
||||
|
||||
void PrintInfo(std::ostream &o)
|
||||
{
|
||||
@@ -310,7 +310,7 @@ public:
|
||||
|
||||
ClientState getState() const { return m_state; }
|
||||
|
||||
std::string getName() const { return m_name; }
|
||||
const std::string &getName() const { return m_name; }
|
||||
|
||||
void setName(const std::string &name) { m_name = name; }
|
||||
|
||||
@@ -394,6 +394,12 @@ private:
|
||||
const s16 m_max_gen_distance;
|
||||
const bool m_occ_cull;
|
||||
|
||||
/*
|
||||
Set of media files the client has already requested
|
||||
We won't send the same file twice to avoid bandwidth consumption attacks.
|
||||
*/
|
||||
std::unordered_set<std::string> m_media_sent;
|
||||
|
||||
/*
|
||||
Blocks that are currently on the line.
|
||||
This is used for throttling the sending of blocks.
|
||||
@@ -467,8 +473,8 @@ public:
|
||||
/* get list of active client id's */
|
||||
std::vector<session_t> getClientIDs(ClientState min_state=CS_Active);
|
||||
|
||||
/* mark block as not sent to active client sessions */
|
||||
void markBlockposAsNotSent(const v3s16 &pos);
|
||||
/* mark blocks as not sent on all active clients */
|
||||
void markBlocksNotSent(const std::vector<v3s16> &positions);
|
||||
|
||||
/* verify is server user limit was reached */
|
||||
bool isUserLimitReached();
|
||||
|
||||
@@ -102,24 +102,33 @@ bool Environment::line_of_sight(v3f pos1, v3f pos2, v3s16 *p)
|
||||
}
|
||||
|
||||
/*
|
||||
Check if a node is pointable
|
||||
Check how a node can be pointed at
|
||||
*/
|
||||
inline static bool isPointableNode(const MapNode &n,
|
||||
const NodeDefManager *nodedef , bool liquids_pointable)
|
||||
inline static PointabilityType isPointableNode(const MapNode &n,
|
||||
const NodeDefManager *nodedef, bool liquids_pointable,
|
||||
const std::optional<Pointabilities> &pointabilities)
|
||||
{
|
||||
const ContentFeatures &features = nodedef->get(n);
|
||||
return features.pointable ||
|
||||
(liquids_pointable && features.isLiquid());
|
||||
if (pointabilities) {
|
||||
std::optional<PointabilityType> match =
|
||||
pointabilities->matchNode(features.name, features.groups);
|
||||
if (match)
|
||||
return match.value();
|
||||
}
|
||||
|
||||
if (features.isLiquid() && liquids_pointable)
|
||||
return PointabilityType::POINTABLE;
|
||||
return features.pointable;
|
||||
}
|
||||
|
||||
void Environment::continueRaycast(RaycastState *state, PointedThing *result)
|
||||
void Environment::continueRaycast(RaycastState *state, PointedThing *result_p)
|
||||
{
|
||||
const NodeDefManager *nodedef = getMap().getNodeDefManager();
|
||||
if (state->m_initialization_needed) {
|
||||
// Add objects
|
||||
if (state->m_objects_pointable) {
|
||||
std::vector<PointedThing> found;
|
||||
getSelectedActiveObjects(state->m_shootline, found);
|
||||
getSelectedActiveObjects(state->m_shootline, found, state->m_pointabilities);
|
||||
for (const PointedThing &pointed : found) {
|
||||
state->m_found.push(pointed);
|
||||
}
|
||||
@@ -184,10 +193,15 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result)
|
||||
bool is_valid_position;
|
||||
|
||||
n = map.getNode(np, &is_valid_position);
|
||||
if (!(is_valid_position && isPointableNode(n, nodedef,
|
||||
state->m_liquids_pointable))) {
|
||||
if (!is_valid_position)
|
||||
continue;
|
||||
|
||||
PointabilityType pointable = isPointableNode(n, nodedef,
|
||||
state->m_liquids_pointable,
|
||||
state->m_pointabilities);
|
||||
// If it can be pointed through skip
|
||||
if (pointable == PointabilityType::POINTABLE_NOT)
|
||||
continue;
|
||||
}
|
||||
|
||||
PointedThing result;
|
||||
|
||||
@@ -234,6 +248,7 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result)
|
||||
if (!is_colliding) {
|
||||
continue;
|
||||
}
|
||||
result.pointability = pointable;
|
||||
result.type = POINTEDTHING_NODE;
|
||||
result.node_undersurface = np;
|
||||
result.distanceSq = min_distance_sq;
|
||||
@@ -275,12 +290,16 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result)
|
||||
state->m_previous_node = state->m_iterator.m_current_node_pos;
|
||||
state->m_iterator.next();
|
||||
}
|
||||
// Return empty PointedThing if nothing left on the ray
|
||||
|
||||
// Return empty PointedThing if nothing left on the ray or it is blocking pointable
|
||||
if (state->m_found.empty()) {
|
||||
result->type = POINTEDTHING_NOTHING;
|
||||
result_p->type = POINTEDTHING_NOTHING;
|
||||
} else {
|
||||
*result = state->m_found.top();
|
||||
*result_p = state->m_found.top();
|
||||
state->m_found.pop();
|
||||
if (result_p->pointability == PointabilityType::POINTABLE_BLOCKING) {
|
||||
result_p->type = POINTEDTHING_NOTHING;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <map>
|
||||
#include <atomic>
|
||||
#include <mutex>
|
||||
#include <optional>
|
||||
#include "irr_v3d.h"
|
||||
#include "util/basic_macros.h"
|
||||
#include "line3d.h"
|
||||
@@ -42,6 +43,7 @@ class IGameDef;
|
||||
class Map;
|
||||
struct PointedThing;
|
||||
class RaycastState;
|
||||
struct Pointabilities;
|
||||
|
||||
class Environment
|
||||
{
|
||||
@@ -97,7 +99,8 @@ public:
|
||||
* @param[out] objects found objects
|
||||
*/
|
||||
virtual void getSelectedActiveObjects(const core::line3d<f32> &shootline_on_map,
|
||||
std::vector<PointedThing> &objects) = 0;
|
||||
std::vector<PointedThing> &objects,
|
||||
const std::optional<Pointabilities> &pointabilities) = 0;
|
||||
|
||||
/*!
|
||||
* Returns the next node or object the shootline meets.
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
Copyright (C) 2014 sapier
|
||||
Copyright (C) 2018 srifqi, Muhammad Rifqi Priyo Susanto
|
||||
<muhammadrifqipriyosusanto@gmail.com>
|
||||
Copyright (C) 2024 grorp, Gregor Parzefall
|
||||
<gregor.parzefall@posteo.de>
|
||||
|
||||
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
|
||||
@@ -657,23 +659,13 @@ void TouchScreenGUI::handleReleaseEvent(size_t evt_id)
|
||||
// handle the point used for moving view
|
||||
m_has_move_id = false;
|
||||
|
||||
// if this pointer issued a mouse event issue symmetric release here
|
||||
if (m_move_sent_as_mouse_event) {
|
||||
SEvent translated {};
|
||||
translated.EventType = EET_MOUSE_INPUT_EVENT;
|
||||
translated.MouseInput.X = m_move_downlocation.X;
|
||||
translated.MouseInput.Y = m_move_downlocation.Y;
|
||||
translated.MouseInput.Shift = false;
|
||||
translated.MouseInput.Control = false;
|
||||
translated.MouseInput.ButtonStates = 0;
|
||||
translated.MouseInput.Event = EMIE_LMOUSE_LEFT_UP;
|
||||
if (m_draw_crosshair) {
|
||||
translated.MouseInput.X = m_screensize.X / 2;
|
||||
translated.MouseInput.Y = m_screensize.Y / 2;
|
||||
}
|
||||
m_receiver->OnEvent(translated);
|
||||
} else if (!m_move_has_really_moved) {
|
||||
doRightClick();
|
||||
// If m_tap_state is already set to TapState::ShortTap, we must keep
|
||||
// that value. Otherwise, many short taps will be ignored if you tap
|
||||
// very fast.
|
||||
if (!m_move_has_really_moved && m_tap_state != TapState::LongTap) {
|
||||
m_tap_state = TapState::ShortTap;
|
||||
} else {
|
||||
m_tap_state = TapState::None;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -794,10 +786,8 @@ void TouchScreenGUI::translateEvent(const SEvent &event)
|
||||
m_move_id = event.TouchInput.ID;
|
||||
m_move_has_really_moved = false;
|
||||
m_move_downtime = porting::getTimeMs();
|
||||
m_move_downlocation = touch_pos;
|
||||
m_move_sent_as_mouse_event = false;
|
||||
if (m_draw_crosshair)
|
||||
m_move_downlocation = v2s32(m_screensize.X / 2, m_screensize.Y / 2);
|
||||
// DON'T reset m_tap_state here, otherwise many short taps
|
||||
// will be ignored if you tap very fast.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -820,33 +810,20 @@ void TouchScreenGUI::translateEvent(const SEvent &event)
|
||||
|
||||
const double touch_threshold_sq = m_touchscreen_threshold * m_touchscreen_threshold;
|
||||
|
||||
if (m_has_move_id) {
|
||||
if (event.TouchInput.ID == m_move_id &&
|
||||
(!m_move_sent_as_mouse_event || m_draw_crosshair)) {
|
||||
if (dir_free.getLengthSQ() > touch_threshold_sq || m_move_has_really_moved) {
|
||||
m_move_has_really_moved = true;
|
||||
if (m_has_move_id && event.TouchInput.ID == m_move_id) {
|
||||
if (dir_free.getLengthSQ() > touch_threshold_sq || m_move_has_really_moved) {
|
||||
m_move_has_really_moved = true;
|
||||
|
||||
// update camera_yaw and camera_pitch
|
||||
m_pointer_pos[event.TouchInput.ID] = touch_pos;
|
||||
m_pointer_pos[event.TouchInput.ID] = touch_pos;
|
||||
|
||||
if (m_tap_state == TapState::None || m_draw_crosshair) {
|
||||
// adapt to similar behavior as pc screen
|
||||
const double d = g_settings->getFloat("touchscreen_sensitivity", 0.001f, 10.0f) * 3.0f;
|
||||
|
||||
// update camera_yaw and camera_pitch
|
||||
m_camera_yaw_change -= dir_free.X * d;
|
||||
m_camera_pitch_change += dir_free.Y * d;
|
||||
|
||||
// update shootline
|
||||
// no need to update (X, Y) when using crosshair since the shootline is not used
|
||||
m_shootline = m_device
|
||||
->getSceneManager()
|
||||
->getSceneCollisionManager()
|
||||
->getRayFromScreenCoordinates(touch_pos);
|
||||
}
|
||||
} else if (event.TouchInput.ID == m_move_id && m_move_sent_as_mouse_event) {
|
||||
m_shootline = m_device
|
||||
->getSceneManager()
|
||||
->getSceneCollisionManager()
|
||||
->getRayFromScreenCoordinates(touch_pos);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -931,40 +908,6 @@ void TouchScreenGUI::handleChangedButton(const SEvent &event)
|
||||
handleButtonEvent((touch_gui_button_id) current_button_id, event.TouchInput.ID, true);
|
||||
}
|
||||
|
||||
bool TouchScreenGUI::doRightClick()
|
||||
{
|
||||
v2s32 mPos = v2s32(m_move_downlocation.X, m_move_downlocation.Y);
|
||||
if (m_draw_crosshair) {
|
||||
mPos.X = m_screensize.X / 2;
|
||||
mPos.Y = m_screensize.Y / 2;
|
||||
}
|
||||
|
||||
SEvent translated {};
|
||||
translated.EventType = EET_MOUSE_INPUT_EVENT;
|
||||
translated.MouseInput.X = mPos.X;
|
||||
translated.MouseInput.Y = mPos.Y;
|
||||
translated.MouseInput.Shift = false;
|
||||
translated.MouseInput.Control = false;
|
||||
translated.MouseInput.ButtonStates = EMBSM_RIGHT;
|
||||
|
||||
// update shootline
|
||||
m_shootline = m_device
|
||||
->getSceneManager()
|
||||
->getSceneCollisionManager()
|
||||
->getRayFromScreenCoordinates(mPos);
|
||||
|
||||
translated.MouseInput.Event = EMIE_RMOUSE_PRESSED_DOWN;
|
||||
verbosestream << "TouchScreenGUI::translateEvent right click press" << std::endl;
|
||||
m_receiver->OnEvent(translated);
|
||||
|
||||
translated.MouseInput.ButtonStates = 0;
|
||||
translated.MouseInput.Event = EMIE_RMOUSE_LEFT_UP;
|
||||
verbosestream << "TouchScreenGUI::translateEvent right click release" << std::endl;
|
||||
m_receiver->OnEvent(translated);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TouchScreenGUI::applyJoystickStatus()
|
||||
{
|
||||
if (m_joystick_triggers_aux1) {
|
||||
@@ -1038,37 +981,27 @@ void TouchScreenGUI::step(float dtime)
|
||||
applyJoystickStatus();
|
||||
|
||||
// if a new placed pointer isn't moved for some time start digging
|
||||
if (m_has_move_id &&
|
||||
(!m_move_has_really_moved) &&
|
||||
(!m_move_sent_as_mouse_event)) {
|
||||
if (m_has_move_id && !m_move_has_really_moved && m_tap_state == TapState::None) {
|
||||
u64 delta = porting::getDeltaMs(m_move_downtime, porting::getTimeMs());
|
||||
|
||||
if (delta > MIN_DIG_TIME_MS) {
|
||||
s32 mX = m_move_downlocation.X;
|
||||
s32 mY = m_move_downlocation.Y;
|
||||
if (m_draw_crosshair) {
|
||||
mX = m_screensize.X / 2;
|
||||
mY = m_screensize.Y / 2;
|
||||
}
|
||||
m_shootline = m_device
|
||||
->getSceneManager()
|
||||
->getSceneCollisionManager()
|
||||
->getRayFromScreenCoordinates(v2s32(mX, mY));
|
||||
|
||||
SEvent translated {};
|
||||
translated.EventType = EET_MOUSE_INPUT_EVENT;
|
||||
translated.MouseInput.X = mX;
|
||||
translated.MouseInput.Y = mY;
|
||||
translated.MouseInput.Shift = false;
|
||||
translated.MouseInput.Control = false;
|
||||
translated.MouseInput.ButtonStates = EMBSM_LEFT;
|
||||
translated.MouseInput.Event = EMIE_LMOUSE_PRESSED_DOWN;
|
||||
verbosestream << "TouchScreenGUI::step left click press" << std::endl;
|
||||
m_receiver->OnEvent(translated);
|
||||
m_move_sent_as_mouse_event = true;
|
||||
m_tap_state = TapState::LongTap;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the shootline.
|
||||
// Since not only the pointer position, but also the player position and
|
||||
// thus the camera position can change, it doesn't suffice to update the
|
||||
// shootline when a touch event occurs.
|
||||
// Note that the shootline isn't used if touch_use_crosshair is enabled.
|
||||
if (!m_draw_crosshair) {
|
||||
v2s32 pointer_pos = getPointerPos();
|
||||
m_shootline = m_device
|
||||
->getSceneManager()
|
||||
->getSceneCollisionManager()
|
||||
->getRayFromScreenCoordinates(pointer_pos);
|
||||
}
|
||||
|
||||
m_settings_bar.step(dtime);
|
||||
m_rare_controls_bar.step(dtime);
|
||||
}
|
||||
@@ -1125,3 +1058,100 @@ void TouchScreenGUI::show()
|
||||
|
||||
setVisible(true);
|
||||
}
|
||||
|
||||
v2s32 TouchScreenGUI::getPointerPos()
|
||||
{
|
||||
if (m_draw_crosshair)
|
||||
return v2s32(m_screensize.X / 2, m_screensize.Y / 2);
|
||||
return m_pointer_pos[m_move_id];
|
||||
}
|
||||
|
||||
void TouchScreenGUI::emitMouseEvent(EMOUSE_INPUT_EVENT type)
|
||||
{
|
||||
v2s32 pointer_pos = getPointerPos();
|
||||
|
||||
SEvent event{};
|
||||
event.EventType = EET_MOUSE_INPUT_EVENT;
|
||||
event.MouseInput.X = pointer_pos.X;
|
||||
event.MouseInput.Y = pointer_pos.Y;
|
||||
event.MouseInput.Shift = false;
|
||||
event.MouseInput.Control = false;
|
||||
event.MouseInput.ButtonStates = 0;
|
||||
event.MouseInput.Event = type;
|
||||
m_receiver->OnEvent(event);
|
||||
}
|
||||
|
||||
void TouchScreenGUI::applyContextControls(const TouchInteractionMode &mode)
|
||||
{
|
||||
// Since the pointed thing has already been determined when this function
|
||||
// is called, we cannot use this function to update the shootline.
|
||||
|
||||
bool target_dig_pressed = false;
|
||||
bool target_place_pressed = false;
|
||||
|
||||
u64 now = porting::getTimeMs();
|
||||
|
||||
switch (m_tap_state) {
|
||||
case TapState::ShortTap:
|
||||
if (mode == SHORT_DIG_LONG_PLACE) {
|
||||
if (!m_dig_pressed) {
|
||||
// The button isn't currently pressed, we can press it.
|
||||
m_dig_pressed_until = now + SIMULATED_CLICK_DURATION_MS;
|
||||
// We're done with this short tap.
|
||||
m_tap_state = TapState::None;
|
||||
} else {
|
||||
// The button is already pressed, perhaps due to another short tap.
|
||||
// Release it now, press it again during the next client step.
|
||||
// We can't release and press during the same client step because
|
||||
// the digging code simply ignores that.
|
||||
m_dig_pressed_until = 0;
|
||||
}
|
||||
} else {
|
||||
if (!m_place_pressed) {
|
||||
// The button isn't currently pressed, we can press it.
|
||||
m_place_pressed_until = now + SIMULATED_CLICK_DURATION_MS;
|
||||
// We're done with this short tap.
|
||||
m_tap_state = TapState::None;
|
||||
} else {
|
||||
// The button is already pressed, perhaps due to another short tap.
|
||||
// Release it now, press it again during the next client step.
|
||||
// We can't release and press during the same client step because
|
||||
// the digging code simply ignores that.
|
||||
m_place_pressed_until = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case TapState::LongTap:
|
||||
if (mode == SHORT_DIG_LONG_PLACE)
|
||||
target_place_pressed = true;
|
||||
else
|
||||
target_dig_pressed = true;
|
||||
break;
|
||||
|
||||
case TapState::None:
|
||||
break;
|
||||
}
|
||||
|
||||
// Apply short taps.
|
||||
target_dig_pressed |= now < m_dig_pressed_until;
|
||||
target_place_pressed |= now < m_place_pressed_until;
|
||||
|
||||
if (target_dig_pressed && !m_dig_pressed) {
|
||||
emitMouseEvent(EMIE_LMOUSE_PRESSED_DOWN);
|
||||
m_dig_pressed = true;
|
||||
|
||||
} else if (!target_dig_pressed && m_dig_pressed) {
|
||||
emitMouseEvent(EMIE_LMOUSE_LEFT_UP);
|
||||
m_dig_pressed = false;
|
||||
}
|
||||
|
||||
if (target_place_pressed && !m_place_pressed) {
|
||||
emitMouseEvent(EMIE_RMOUSE_PRESSED_DOWN);
|
||||
m_place_pressed = true;
|
||||
|
||||
} else if (!target_place_pressed && m_place_pressed) {
|
||||
emitMouseEvent(irr::EMIE_RMOUSE_LEFT_UP);
|
||||
m_place_pressed = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
/*
|
||||
Copyright (C) 2014 sapier
|
||||
Copyright (C) 2024 grorp, Gregor Parzefall
|
||||
<gregor.parzefall@posteo.de>
|
||||
|
||||
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
|
||||
@@ -29,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "itemdef.h"
|
||||
#include "client/tile.h"
|
||||
#include "client/game.h"
|
||||
|
||||
@@ -36,6 +39,13 @@ using namespace irr;
|
||||
using namespace irr::core;
|
||||
using namespace irr::gui;
|
||||
|
||||
enum class TapState
|
||||
{
|
||||
None,
|
||||
ShortTap,
|
||||
LongTap,
|
||||
};
|
||||
|
||||
typedef enum
|
||||
{
|
||||
jump_id = 0,
|
||||
@@ -75,6 +85,11 @@ typedef enum
|
||||
#define SETTINGS_BAR_Y_OFFSET 5
|
||||
#define RARE_CONTROLS_BAR_Y_OFFSET 5
|
||||
|
||||
// Our simulated clicks last some milliseconds so that server-side mods have a
|
||||
// chance to detect them via l_get_player_control.
|
||||
// If you tap faster than this value, the simulated clicks are of course shorter.
|
||||
#define SIMULATED_CLICK_DURATION_MS 50
|
||||
|
||||
extern const std::string button_image_names[];
|
||||
extern const std::string joystick_image_names[];
|
||||
|
||||
@@ -161,6 +176,7 @@ public:
|
||||
~TouchScreenGUI();
|
||||
|
||||
void translateEvent(const SEvent &event);
|
||||
void applyContextControls(const TouchInteractionMode &mode);
|
||||
|
||||
void init(ISimpleTextureSource *tsrc);
|
||||
|
||||
@@ -230,8 +246,6 @@ private:
|
||||
size_t m_move_id;
|
||||
bool m_move_has_really_moved = false;
|
||||
u64 m_move_downtime = 0;
|
||||
bool m_move_sent_as_mouse_event = false;
|
||||
v2s32 m_move_downlocation = v2s32(-10000, -10000); // off-screen
|
||||
|
||||
bool m_has_joystick_id = false;
|
||||
size_t m_joystick_id;
|
||||
@@ -283,9 +297,6 @@ private:
|
||||
// handle pressing hotbar items
|
||||
bool isHotbarButton(const SEvent &event);
|
||||
|
||||
// do a right-click
|
||||
bool doRightClick();
|
||||
|
||||
// handle release event
|
||||
void handleReleaseEvent(size_t evt_id);
|
||||
|
||||
@@ -300,6 +311,16 @@ private:
|
||||
|
||||
// rare controls bar
|
||||
AutoHideButtonBar m_rare_controls_bar;
|
||||
|
||||
v2s32 getPointerPos();
|
||||
void emitMouseEvent(EMOUSE_INPUT_EVENT type);
|
||||
TapState m_tap_state = TapState::None;
|
||||
|
||||
bool m_dig_pressed = false;
|
||||
u64 m_dig_pressed_until = 0;
|
||||
|
||||
bool m_place_pressed = false;
|
||||
u64 m_place_pressed_until = 0;
|
||||
};
|
||||
|
||||
extern TouchScreenGUI *g_touchscreengui;
|
||||
|
||||
@@ -35,9 +35,60 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "util/serialize.h"
|
||||
#include "util/container.h"
|
||||
#include "util/thread.h"
|
||||
#include "util/pointedthing.h"
|
||||
#include <map>
|
||||
#include <set>
|
||||
|
||||
TouchInteraction::TouchInteraction()
|
||||
{
|
||||
pointed_nothing = LONG_DIG_SHORT_PLACE;
|
||||
pointed_node = LONG_DIG_SHORT_PLACE;
|
||||
// Map punching to single tap by default.
|
||||
pointed_object = SHORT_DIG_LONG_PLACE;
|
||||
}
|
||||
|
||||
TouchInteractionMode TouchInteraction::getMode(const PointedThing &pointed) const
|
||||
{
|
||||
switch (pointed.type) {
|
||||
case POINTEDTHING_NOTHING:
|
||||
return pointed_nothing;
|
||||
case POINTEDTHING_NODE:
|
||||
return pointed_node;
|
||||
case POINTEDTHING_OBJECT:
|
||||
return pointed_object;
|
||||
default:
|
||||
FATAL_ERROR("Invalid PointedThingType given to TouchInteraction::getMode");
|
||||
}
|
||||
}
|
||||
|
||||
void TouchInteraction::serialize(std::ostream &os) const
|
||||
{
|
||||
writeU8(os, pointed_nothing);
|
||||
writeU8(os, pointed_node);
|
||||
writeU8(os, pointed_object);
|
||||
}
|
||||
|
||||
void TouchInteraction::deSerialize(std::istream &is)
|
||||
{
|
||||
u8 tmp = readU8(is);
|
||||
if (is.eof())
|
||||
throw SerializationError("");
|
||||
if (tmp < TouchInteractionMode_END)
|
||||
pointed_nothing = (TouchInteractionMode)tmp;
|
||||
|
||||
tmp = readU8(is);
|
||||
if (is.eof())
|
||||
throw SerializationError("");
|
||||
if (tmp < TouchInteractionMode_END)
|
||||
pointed_node = (TouchInteractionMode)tmp;
|
||||
|
||||
tmp = readU8(is);
|
||||
if (is.eof())
|
||||
throw SerializationError("");
|
||||
if (tmp < TouchInteractionMode_END)
|
||||
pointed_object = (TouchInteractionMode)tmp;
|
||||
}
|
||||
|
||||
/*
|
||||
ItemDefinition
|
||||
*/
|
||||
@@ -71,6 +122,7 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
|
||||
stack_max = def.stack_max;
|
||||
usable = def.usable;
|
||||
liquids_pointable = def.liquids_pointable;
|
||||
pointabilities = def.pointabilities;
|
||||
if (def.tool_capabilities)
|
||||
tool_capabilities = new ToolCapabilities(*def.tool_capabilities);
|
||||
groups = def.groups;
|
||||
@@ -84,6 +136,7 @@ ItemDefinition& ItemDefinition::operator=(const ItemDefinition &def)
|
||||
range = def.range;
|
||||
palette_image = def.palette_image;
|
||||
color = def.color;
|
||||
touch_interaction = def.touch_interaction;
|
||||
return *this;
|
||||
}
|
||||
|
||||
@@ -115,6 +168,7 @@ void ItemDefinition::reset()
|
||||
stack_max = 99;
|
||||
usable = false;
|
||||
liquids_pointable = false;
|
||||
pointabilities = std::nullopt;
|
||||
delete tool_capabilities;
|
||||
tool_capabilities = NULL;
|
||||
groups.clear();
|
||||
@@ -126,6 +180,7 @@ void ItemDefinition::reset()
|
||||
node_placement_prediction.clear();
|
||||
place_param2.reset();
|
||||
wallmounted_rotate_vertical = false;
|
||||
touch_interaction = TouchInteraction();
|
||||
}
|
||||
|
||||
void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
|
||||
@@ -185,7 +240,17 @@ void ItemDefinition::serialize(std::ostream &os, u16 protocol_version) const
|
||||
os << (u8)place_param2.has_value(); // protocol_version >= 43
|
||||
if (place_param2)
|
||||
os << *place_param2;
|
||||
|
||||
writeU8(os, wallmounted_rotate_vertical);
|
||||
touch_interaction.serialize(os);
|
||||
|
||||
std::string pointabilities_s;
|
||||
if (pointabilities) {
|
||||
std::ostringstream tmp_os(std::ios::binary);
|
||||
pointabilities->serialize(tmp_os);
|
||||
pointabilities_s = tmp_os.str();
|
||||
}
|
||||
os << serializeString16(pointabilities_s);
|
||||
}
|
||||
|
||||
void ItemDefinition::deSerialize(std::istream &is, u16 protocol_version)
|
||||
@@ -260,6 +325,14 @@ void ItemDefinition::deSerialize(std::istream &is, u16 protocol_version)
|
||||
place_param2 = readU8(is);
|
||||
|
||||
wallmounted_rotate_vertical = readU8(is); // 0 if missing
|
||||
touch_interaction.deSerialize(is);
|
||||
|
||||
std::string pointabilities_s = deSerializeString16(is);
|
||||
if (!pointabilities_s.empty()) {
|
||||
std::istringstream tmp_is(pointabilities_s, std::ios::binary);
|
||||
pointabilities = std::make_optional<Pointabilities>();
|
||||
pointabilities->deSerialize(tmp_is);
|
||||
}
|
||||
} catch(SerializationError &e) {};
|
||||
}
|
||||
|
||||
|
||||
@@ -28,9 +28,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "itemgroup.h"
|
||||
#include "sound.h"
|
||||
#include "texture_override.h" // TextureOverride
|
||||
#include "util/pointabilities.h"
|
||||
class IGameDef;
|
||||
class Client;
|
||||
struct ToolCapabilities;
|
||||
struct PointedThing;
|
||||
#ifndef SERVER
|
||||
#include "client/tile.h"
|
||||
struct ItemMesh;
|
||||
@@ -50,6 +52,25 @@ enum ItemType : u8
|
||||
ItemType_END // Dummy for validity check
|
||||
};
|
||||
|
||||
enum TouchInteractionMode : u8
|
||||
{
|
||||
LONG_DIG_SHORT_PLACE,
|
||||
SHORT_DIG_LONG_PLACE,
|
||||
TouchInteractionMode_END, // Dummy for validity check
|
||||
};
|
||||
|
||||
struct TouchInteraction
|
||||
{
|
||||
TouchInteractionMode pointed_nothing;
|
||||
TouchInteractionMode pointed_node;
|
||||
TouchInteractionMode pointed_object;
|
||||
|
||||
TouchInteraction();
|
||||
TouchInteractionMode getMode(const PointedThing &pointed) const;
|
||||
void serialize(std::ostream &os) const;
|
||||
void deSerialize(std::istream &is);
|
||||
};
|
||||
|
||||
struct ItemDefinition
|
||||
{
|
||||
/*
|
||||
@@ -77,8 +98,11 @@ struct ItemDefinition
|
||||
u16 stack_max;
|
||||
bool usable;
|
||||
bool liquids_pointable;
|
||||
// May be NULL. If non-NULL, deleted by destructor
|
||||
std::optional<Pointabilities> pointabilities;
|
||||
|
||||
// They may be NULL. If non-NULL, deleted by destructor
|
||||
ToolCapabilities *tool_capabilities;
|
||||
|
||||
ItemGroupList groups;
|
||||
SoundSpec sound_place;
|
||||
SoundSpec sound_place_failed;
|
||||
@@ -92,6 +116,8 @@ struct ItemDefinition
|
||||
std::optional<u8> place_param2;
|
||||
bool wallmounted_rotate_vertical;
|
||||
|
||||
TouchInteraction touch_interaction;
|
||||
|
||||
/*
|
||||
Some helpful methods
|
||||
*/
|
||||
|
||||
@@ -379,8 +379,6 @@ static void set_allowed_options(OptionList *allowed_options)
|
||||
allowed_options->insert(std::make_pair("recompress", ValueSpec(VALUETYPE_FLAG,
|
||||
_("Recompress the blocks of the given map database."))));
|
||||
#ifndef SERVER
|
||||
allowed_options->insert(std::make_pair("speedtests", ValueSpec(VALUETYPE_FLAG,
|
||||
_("Run speed tests"))));
|
||||
allowed_options->insert(std::make_pair("address", ValueSpec(VALUETYPE_STRING,
|
||||
_("Address to connect to. ('' = local game)"))));
|
||||
allowed_options->insert(std::make_pair("random-input", ValueSpec(VALUETYPE_FLAG,
|
||||
|
||||
@@ -40,13 +40,11 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "util/basic_macros.h"
|
||||
|
||||
static const char *modified_reason_strings[] = {
|
||||
"initial",
|
||||
"reallocate",
|
||||
"reallocate or initial",
|
||||
"setIsUnderground",
|
||||
"setLightingExpired",
|
||||
"setGenerated",
|
||||
"setNode",
|
||||
"setNodeNoCheck",
|
||||
"setTimestamp",
|
||||
"NodeMetaRef::reportMetadataChange",
|
||||
"clearAllObjects",
|
||||
@@ -73,6 +71,7 @@ MapBlock::MapBlock(v3s16 pos, IGameDef *gamedef):
|
||||
m_gamedef(gamedef)
|
||||
{
|
||||
reallocate();
|
||||
assert(m_modified > MOD_STATE_CLEAN);
|
||||
}
|
||||
|
||||
MapBlock::~MapBlock()
|
||||
|
||||
@@ -43,27 +43,27 @@ class VoxelManipulator;
|
||||
//// MapBlock modified reason flags
|
||||
////
|
||||
|
||||
#define MOD_REASON_INITIAL (1 << 0)
|
||||
#define MOD_REASON_REALLOCATE (1 << 1)
|
||||
#define MOD_REASON_SET_IS_UNDERGROUND (1 << 2)
|
||||
#define MOD_REASON_SET_LIGHTING_COMPLETE (1 << 3)
|
||||
#define MOD_REASON_SET_GENERATED (1 << 4)
|
||||
#define MOD_REASON_SET_NODE (1 << 5)
|
||||
#define MOD_REASON_SET_NODE_NO_CHECK (1 << 6)
|
||||
#define MOD_REASON_SET_TIMESTAMP (1 << 7)
|
||||
#define MOD_REASON_REPORT_META_CHANGE (1 << 8)
|
||||
#define MOD_REASON_CLEAR_ALL_OBJECTS (1 << 9)
|
||||
#define MOD_REASON_BLOCK_EXPIRED (1 << 10)
|
||||
#define MOD_REASON_ADD_ACTIVE_OBJECT_RAW (1 << 11)
|
||||
#define MOD_REASON_REMOVE_OBJECTS_REMOVE (1 << 12)
|
||||
#define MOD_REASON_REMOVE_OBJECTS_DEACTIVATE (1 << 13)
|
||||
#define MOD_REASON_TOO_MANY_OBJECTS (1 << 14)
|
||||
#define MOD_REASON_STATIC_DATA_ADDED (1 << 15)
|
||||
#define MOD_REASON_STATIC_DATA_REMOVED (1 << 16)
|
||||
#define MOD_REASON_STATIC_DATA_CHANGED (1 << 17)
|
||||
#define MOD_REASON_EXPIRE_DAYNIGHTDIFF (1 << 18)
|
||||
#define MOD_REASON_VMANIP (1 << 19)
|
||||
#define MOD_REASON_UNKNOWN (1 << 20)
|
||||
enum ModReason : u32 {
|
||||
MOD_REASON_REALLOCATE = 1 << 0,
|
||||
MOD_REASON_SET_IS_UNDERGROUND = 1 << 1,
|
||||
MOD_REASON_SET_LIGHTING_COMPLETE = 1 << 2,
|
||||
MOD_REASON_SET_GENERATED = 1 << 3,
|
||||
MOD_REASON_SET_NODE = 1 << 4,
|
||||
MOD_REASON_SET_TIMESTAMP = 1 << 5,
|
||||
MOD_REASON_REPORT_META_CHANGE = 1 << 6,
|
||||
MOD_REASON_CLEAR_ALL_OBJECTS = 1 << 7,
|
||||
MOD_REASON_BLOCK_EXPIRED = 1 << 8,
|
||||
MOD_REASON_ADD_ACTIVE_OBJECT_RAW = 1 << 9,
|
||||
MOD_REASON_REMOVE_OBJECTS_REMOVE = 1 << 10,
|
||||
MOD_REASON_REMOVE_OBJECTS_DEACTIVATE = 1 << 11,
|
||||
MOD_REASON_TOO_MANY_OBJECTS = 1 << 12,
|
||||
MOD_REASON_STATIC_DATA_ADDED = 1 << 13,
|
||||
MOD_REASON_STATIC_DATA_REMOVED = 1 << 14,
|
||||
MOD_REASON_STATIC_DATA_CHANGED = 1 << 15,
|
||||
MOD_REASON_EXPIRE_DAYNIGHTDIFF = 1 << 16,
|
||||
MOD_REASON_VMANIP = 1 << 17,
|
||||
MOD_REASON_UNKNOWN = 1 << 18,
|
||||
};
|
||||
|
||||
////
|
||||
//// MapBlock itself
|
||||
@@ -155,7 +155,7 @@ public:
|
||||
{
|
||||
if (newflags != m_lighting_complete) {
|
||||
m_lighting_complete = newflags;
|
||||
raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_LIGHTING_COMPLETE);
|
||||
raiseModified(MOD_STATE_WRITE_AT_UNLOAD, MOD_REASON_SET_LIGHTING_COMPLETE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ public:
|
||||
inline void setNodeNoCheck(s16 x, s16 y, s16 z, MapNode n)
|
||||
{
|
||||
data[z * zstride + y * ystride + x] = n;
|
||||
raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE_NO_CHECK);
|
||||
raiseModified(MOD_STATE_WRITE_NEEDED, MOD_REASON_SET_NODE);
|
||||
}
|
||||
|
||||
inline void setNodeNoCheck(v3s16 p, MapNode n)
|
||||
@@ -525,8 +525,8 @@ private:
|
||||
block has been modified from the one on disk.
|
||||
- On the client, this is used for nothing.
|
||||
*/
|
||||
u16 m_modified = MOD_STATE_WRITE_NEEDED;
|
||||
u32 m_modified_reason = MOD_REASON_INITIAL;
|
||||
u16 m_modified = MOD_STATE_CLEAN;
|
||||
u32 m_modified_reason = 0;
|
||||
|
||||
/*
|
||||
When block is removed from active blocks, this is set to gametime.
|
||||
|
||||
@@ -1391,41 +1391,44 @@ void Client::handleCommand_HudSetSky(NetworkPacket* pkt)
|
||||
star_event->type = CE_SET_STARS;
|
||||
star_event->star_params = new StarParams(stars);
|
||||
m_client_event_queue.push(star_event);
|
||||
} else {
|
||||
SkyboxParams skybox;
|
||||
return;
|
||||
}
|
||||
|
||||
SkyboxParams skybox;
|
||||
|
||||
*pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
|
||||
skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
|
||||
|
||||
if (skybox.type == "skybox") {
|
||||
u16 texture_count;
|
||||
std::string texture;
|
||||
|
||||
*pkt >> skybox.bgcolor >> skybox.type >> skybox.clouds >>
|
||||
skybox.fog_sun_tint >> skybox.fog_moon_tint >> skybox.fog_tint_type;
|
||||
|
||||
if (skybox.type == "skybox") {
|
||||
*pkt >> texture_count;
|
||||
for (int i = 0; i < texture_count; i++) {
|
||||
*pkt >> texture;
|
||||
skybox.textures.emplace_back(texture);
|
||||
}
|
||||
*pkt >> texture_count;
|
||||
for (u16 i = 0; i < texture_count; i++) {
|
||||
*pkt >> texture;
|
||||
skybox.textures.emplace_back(texture);
|
||||
}
|
||||
else if (skybox.type == "regular") {
|
||||
*pkt >> skybox.sky_color.day_sky >> skybox.sky_color.day_horizon
|
||||
>> skybox.sky_color.dawn_sky >> skybox.sky_color.dawn_horizon
|
||||
>> skybox.sky_color.night_sky >> skybox.sky_color.night_horizon
|
||||
>> skybox.sky_color.indoors;
|
||||
}
|
||||
|
||||
if (pkt->getRemainingBytes() >= 4) {
|
||||
*pkt >> skybox.body_orbit_tilt;
|
||||
}
|
||||
|
||||
if (pkt->getRemainingBytes() >= 6) {
|
||||
*pkt >> skybox.fog_distance >> skybox.fog_start;
|
||||
}
|
||||
|
||||
ClientEvent *event = new ClientEvent();
|
||||
event->type = CE_SET_SKY;
|
||||
event->set_sky = new SkyboxParams(skybox);
|
||||
m_client_event_queue.push(event);
|
||||
} else if (skybox.type == "regular") {
|
||||
auto &c = skybox.sky_color;
|
||||
*pkt >> c.day_sky >> c.day_horizon >> c.dawn_sky >> c.dawn_horizon
|
||||
>> c.night_sky >> c.night_horizon >> c.indoors;
|
||||
}
|
||||
|
||||
if (pkt->getRemainingBytes() >= 4) {
|
||||
*pkt >> skybox.body_orbit_tilt;
|
||||
}
|
||||
|
||||
if (pkt->getRemainingBytes() >= 6) {
|
||||
*pkt >> skybox.fog_distance >> skybox.fog_start;
|
||||
}
|
||||
|
||||
if (pkt->getRemainingBytes() >= 4) {
|
||||
*pkt >> skybox.fog_color;
|
||||
}
|
||||
|
||||
ClientEvent *event = new ClientEvent();
|
||||
event->type = CE_SET_SKY;
|
||||
event->set_sky = new SkyboxParams(skybox);
|
||||
m_client_event_queue.push(event);
|
||||
}
|
||||
|
||||
void Client::handleCommand_HudSetSun(NetworkPacket *pkt)
|
||||
|
||||
@@ -356,24 +356,24 @@ void Server::handleCommand_Init2(NetworkPacket* pkt)
|
||||
|
||||
void Server::handleCommand_RequestMedia(NetworkPacket* pkt)
|
||||
{
|
||||
std::vector<std::string> tosend;
|
||||
std::unordered_set<std::string> tosend;
|
||||
u16 numfiles;
|
||||
|
||||
*pkt >> numfiles;
|
||||
|
||||
session_t peer_id = pkt->getPeerId();
|
||||
infostream << "Sending " << numfiles << " files to " <<
|
||||
getPlayerName(peer_id) << std::endl;
|
||||
verbosestream << "TOSERVER_REQUEST_MEDIA: requested file(s)" << std::endl;
|
||||
verbosestream << "Client " << getPlayerName(peer_id)
|
||||
<< " requested media file(s):\n";
|
||||
|
||||
for (u16 i = 0; i < numfiles; i++) {
|
||||
std::string name;
|
||||
|
||||
*pkt >> name;
|
||||
|
||||
tosend.emplace_back(name);
|
||||
verbosestream << " " << name << std::endl;
|
||||
tosend.emplace(name);
|
||||
verbosestream << " " << name << "\n";
|
||||
}
|
||||
verbosestream << std::flush;
|
||||
|
||||
sendRequestedMedia(peer_id, tosend);
|
||||
}
|
||||
|
||||
@@ -386,7 +386,7 @@ void ContentFeatures::reset()
|
||||
light_propagates = false;
|
||||
sunlight_propagates = false;
|
||||
walkable = true;
|
||||
pointable = true;
|
||||
pointable = PointabilityType::POINTABLE;
|
||||
diggable = true;
|
||||
climbable = false;
|
||||
buildable_to = false;
|
||||
@@ -504,7 +504,7 @@ void ContentFeatures::serialize(std::ostream &os, u16 protocol_version) const
|
||||
|
||||
// interaction
|
||||
writeU8(os, walkable);
|
||||
writeU8(os, pointable);
|
||||
Pointabilities::serializePointabilityType(os, pointable);
|
||||
writeU8(os, diggable);
|
||||
writeU8(os, climbable);
|
||||
writeU8(os, buildable_to);
|
||||
@@ -617,7 +617,7 @@ void ContentFeatures::deSerialize(std::istream &is, u16 protocol_version)
|
||||
|
||||
// interaction
|
||||
walkable = readU8(is);
|
||||
pointable = readU8(is);
|
||||
pointable = Pointabilities::deSerializePointabilityType(is);
|
||||
diggable = readU8(is);
|
||||
climbable = readU8(is);
|
||||
buildable_to = readU8(is);
|
||||
@@ -1083,7 +1083,7 @@ void NodeDefManager::clear()
|
||||
f.light_propagates = true;
|
||||
f.sunlight_propagates = true;
|
||||
f.walkable = false;
|
||||
f.pointable = false;
|
||||
f.pointable = PointabilityType::POINTABLE_NOT;
|
||||
f.diggable = false;
|
||||
f.buildable_to = true;
|
||||
f.floodable = true;
|
||||
@@ -1104,7 +1104,7 @@ void NodeDefManager::clear()
|
||||
f.light_propagates = false;
|
||||
f.sunlight_propagates = false;
|
||||
f.walkable = false;
|
||||
f.pointable = false;
|
||||
f.pointable = PointabilityType::POINTABLE_NOT;
|
||||
f.diggable = false;
|
||||
f.buildable_to = true; // A way to remove accidental CONTENT_IGNOREs
|
||||
f.is_ground_content = true;
|
||||
|
||||
@@ -35,6 +35,7 @@ class Client;
|
||||
#include "constants.h" // BS
|
||||
#include "texture_override.h" // TextureOverride
|
||||
#include "tileanimation.h"
|
||||
#include "util/pointabilities.h"
|
||||
|
||||
class IItemDefManager;
|
||||
class ITextureSource;
|
||||
@@ -395,8 +396,8 @@ struct ContentFeatures
|
||||
// This is used for collision detection.
|
||||
// Also for general solidness queries.
|
||||
bool walkable;
|
||||
// Player can point to these
|
||||
bool pointable;
|
||||
// Player can point to these, point through or it is blocking
|
||||
PointabilityType pointable;
|
||||
// Player can dig these
|
||||
bool diggable;
|
||||
// Player can climb these
|
||||
|
||||
@@ -73,7 +73,7 @@ std::string ObjectProperties::dump()
|
||||
|
||||
os << ", selectionbox=" << selectionbox.MinEdge << "," << selectionbox.MaxEdge;
|
||||
os << ", rotate_selectionbox=" << rotate_selectionbox;
|
||||
os << ", pointable=" << pointable;
|
||||
os << ", pointable=" << Pointabilities::toStringPointabilityType(pointable);
|
||||
os << ", static_save=" << static_save;
|
||||
os << ", eye_height=" << eye_height;
|
||||
os << ", zoom_fov=" << zoom_fov;
|
||||
@@ -127,7 +127,7 @@ void ObjectProperties::serialize(std::ostream &os) const
|
||||
writeV3F32(os, collisionbox.MaxEdge);
|
||||
writeV3F32(os, selectionbox.MinEdge);
|
||||
writeV3F32(os, selectionbox.MaxEdge);
|
||||
writeU8(os, pointable);
|
||||
Pointabilities::serializePointabilityType(os, pointable);
|
||||
os << serializeString16(visual);
|
||||
writeV3F32(os, visual_size);
|
||||
writeU16(os, textures.size());
|
||||
@@ -188,7 +188,7 @@ void ObjectProperties::deSerialize(std::istream &is)
|
||||
collisionbox.MaxEdge = readV3F32(is);
|
||||
selectionbox.MinEdge = readV3F32(is);
|
||||
selectionbox.MaxEdge = readV3F32(is);
|
||||
pointable = readU8(is);
|
||||
pointable = Pointabilities::deSerializePointabilityType(is);
|
||||
visual = deSerializeString16(is);
|
||||
visual_size = readV3F32(is);
|
||||
textures.clear();
|
||||
|
||||
@@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include <iostream>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "util/pointabilities.h"
|
||||
|
||||
struct ObjectProperties
|
||||
{
|
||||
@@ -36,7 +37,7 @@ struct ObjectProperties
|
||||
aabb3f collisionbox = aabb3f(-0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f);
|
||||
aabb3f selectionbox = aabb3f(-0.5f, -0.5f, -0.5f, 0.5f, 0.5f, 0.5f);
|
||||
bool rotate_selectionbox = false;
|
||||
bool pointable = true;
|
||||
PointabilityType pointable = PointabilityType::POINTABLE;
|
||||
std::string visual = "sprite";
|
||||
std::string mesh = "";
|
||||
v3f visual_size = v3f(1, 1, 1);
|
||||
|
||||
@@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "config.h"
|
||||
#include "filesys.h"
|
||||
#include "log.h"
|
||||
#include "settings.h"
|
||||
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
@@ -39,6 +40,12 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
extern int main(int argc, char *argv[]);
|
||||
|
||||
extern "C" JNIEXPORT void JNICALL
|
||||
Java_net_minetest_minetest_GameActivity_saveSettings(JNIEnv* env, jobject /* this */) {
|
||||
if (!g_settings_path.empty())
|
||||
g_settings->updateConfigFile(g_settings_path.c_str());
|
||||
}
|
||||
|
||||
namespace porting {
|
||||
// used here:
|
||||
void cleanupAndroid();
|
||||
|
||||
@@ -58,12 +58,14 @@ bool RaycastSort::operator() (const PointedThing &pt1,
|
||||
|
||||
|
||||
RaycastState::RaycastState(const core::line3d<f32> &shootline,
|
||||
bool objects_pointable, bool liquids_pointable) :
|
||||
bool objects_pointable, bool liquids_pointable,
|
||||
const std::optional<Pointabilities> &pointabilities) :
|
||||
m_shootline(shootline),
|
||||
m_iterator(shootline.start / BS, shootline.getVector() / BS),
|
||||
m_previous_node(m_iterator.m_current_node_pos),
|
||||
m_objects_pointable(objects_pointable),
|
||||
m_liquids_pointable(liquids_pointable)
|
||||
m_liquids_pointable(liquids_pointable),
|
||||
m_pointabilities(pointabilities)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ public:
|
||||
* @param liquids pointable if false, liquid nodes won't be found
|
||||
*/
|
||||
RaycastState(const core::line3d<f32> &shootline, bool objects_pointable,
|
||||
bool liquids_pointable);
|
||||
bool liquids_pointable, const std::optional<Pointabilities> &pointabilities);
|
||||
|
||||
//! Shootline of the raycast.
|
||||
core::line3d<f32> m_shootline;
|
||||
@@ -55,6 +55,7 @@ public:
|
||||
|
||||
bool m_objects_pointable;
|
||||
bool m_liquids_pointable;
|
||||
const std::optional<Pointabilities> &m_pointabilities;
|
||||
|
||||
//! The code needs to search these nodes around the center node.
|
||||
core::aabbox3d<s16> m_search_range { 0, 0, 0, 0, 0, 0 };
|
||||
|
||||
@@ -83,6 +83,12 @@ void read_item_definition(lua_State* L, int index,
|
||||
|
||||
getboolfield(L, index, "liquids_pointable", def.liquids_pointable);
|
||||
|
||||
lua_getfield(L, index, "pointabilities");
|
||||
if(lua_istable(L, -1)){
|
||||
def.pointabilities = std::make_optional<Pointabilities>(
|
||||
read_pointabilities(L, -1));
|
||||
}
|
||||
|
||||
lua_getfield(L, index, "tool_capabilities");
|
||||
if(lua_istable(L, -1)){
|
||||
def.tool_capabilities = new ToolCapabilities(
|
||||
@@ -138,6 +144,20 @@ void read_item_definition(lua_State* L, int index,
|
||||
def.place_param2 = rangelim(place_param2, 0, U8_MAX);
|
||||
|
||||
getboolfield(L, index, "wallmounted_rotate_vertical", def.wallmounted_rotate_vertical);
|
||||
|
||||
lua_getfield(L, index, "touch_interaction");
|
||||
if (!lua_isnil(L, -1)) {
|
||||
luaL_checktype(L, -1, LUA_TTABLE);
|
||||
|
||||
TouchInteraction &inter = def.touch_interaction;
|
||||
inter.pointed_nothing = (TouchInteractionMode)getenumfield(L, -1, "pointed_nothing",
|
||||
es_TouchInteractionMode, inter.pointed_nothing);
|
||||
inter.pointed_node = (TouchInteractionMode)getenumfield(L, -1, "pointed_node",
|
||||
es_TouchInteractionMode, inter.pointed_node);
|
||||
inter.pointed_object = (TouchInteractionMode)getenumfield(L, -1, "pointed_object",
|
||||
es_TouchInteractionMode, inter.pointed_object);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
@@ -185,6 +205,10 @@ void push_item_definition_full(lua_State *L, const ItemDefinition &i)
|
||||
lua_setfield(L, -2, "usable");
|
||||
lua_pushboolean(L, i.liquids_pointable);
|
||||
lua_setfield(L, -2, "liquids_pointable");
|
||||
if (i.pointabilities) {
|
||||
push_pointabilities(L, *i.pointabilities);
|
||||
lua_setfield(L, -2, "pointabilities");
|
||||
}
|
||||
if (i.tool_capabilities) {
|
||||
push_tool_capabilities(L, *i.tool_capabilities);
|
||||
lua_setfield(L, -2, "tool_capabilities");
|
||||
@@ -199,6 +223,16 @@ void push_item_definition_full(lua_State *L, const ItemDefinition &i)
|
||||
lua_setfield(L, -2, "node_placement_prediction");
|
||||
lua_pushboolean(L, i.wallmounted_rotate_vertical);
|
||||
lua_setfield(L, -2, "wallmounted_rotate_vertical");
|
||||
|
||||
lua_createtable(L, 0, 3);
|
||||
const TouchInteraction &inter = i.touch_interaction;
|
||||
lua_pushstring(L, es_TouchInteractionMode[inter.pointed_nothing].str);
|
||||
lua_setfield(L, -2,"pointed_nothing");
|
||||
lua_pushstring(L, es_TouchInteractionMode[inter.pointed_node].str);
|
||||
lua_setfield(L, -2,"pointed_node");
|
||||
lua_pushstring(L, es_TouchInteractionMode[inter.pointed_object].str);
|
||||
lua_setfield(L, -2,"pointed_object");
|
||||
lua_setfield(L, -2, "touch_interaction");
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
@@ -287,7 +321,12 @@ void read_object_properties(lua_State *L, int index,
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
getboolfield(L, -1, "pointable", prop->pointable);
|
||||
lua_getfield(L, -1, "pointable");
|
||||
if(!lua_isnil(L, -1)){
|
||||
prop->pointable = read_pointability_type(L, -1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
getstringfield(L, -1, "visual", prop->visual);
|
||||
|
||||
getstringfield(L, -1, "mesh", prop->mesh);
|
||||
@@ -428,7 +467,7 @@ void push_object_properties(lua_State *L, ObjectProperties *prop)
|
||||
lua_pushboolean(L, prop->rotate_selectionbox);
|
||||
lua_setfield(L, -2, "rotate");
|
||||
lua_setfield(L, -2, "selectionbox");
|
||||
lua_pushboolean(L, prop->pointable);
|
||||
push_pointability_type(L, prop->pointable);
|
||||
lua_setfield(L, -2, "pointable");
|
||||
lua_pushlstring(L, prop->visual.c_str(), prop->visual.size());
|
||||
lua_setfield(L, -2, "visual");
|
||||
@@ -757,8 +796,14 @@ void read_content_features(lua_State *L, ContentFeatures &f, int index)
|
||||
// This is used for collision detection.
|
||||
// Also for general solidness queries.
|
||||
getboolfield(L, index, "walkable", f.walkable);
|
||||
// Player can point to these
|
||||
getboolfield(L, index, "pointable", f.pointable);
|
||||
|
||||
// Player can point to these, point through or it is blocking
|
||||
lua_getfield(L, index, "pointable");
|
||||
if(!lua_isnil(L, -1)){
|
||||
f.pointable = read_pointability_type(L, -1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
// Player can dig these
|
||||
getboolfield(L, index, "diggable", f.diggable);
|
||||
// Player can climb these
|
||||
@@ -981,7 +1026,7 @@ void push_content_features(lua_State *L, const ContentFeatures &c)
|
||||
lua_setfield(L, -2, "is_ground_content");
|
||||
lua_pushboolean(L, c.walkable);
|
||||
lua_setfield(L, -2, "walkable");
|
||||
lua_pushboolean(L, c.pointable);
|
||||
push_pointability_type(L, c.pointable);
|
||||
lua_setfield(L, -2, "pointable");
|
||||
lua_pushboolean(L, c.diggable);
|
||||
lua_setfield(L, -2, "diggable");
|
||||
@@ -1568,6 +1613,125 @@ ToolCapabilities read_tool_capabilities(
|
||||
return toolcap;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
PointabilityType read_pointability_type(lua_State *L, int index)
|
||||
{
|
||||
if (lua_isboolean(L, index)) {
|
||||
if (lua_toboolean(L, index))
|
||||
return PointabilityType::POINTABLE;
|
||||
else
|
||||
return PointabilityType::POINTABLE_NOT;
|
||||
} else {
|
||||
const char* s = luaL_checkstring(L, index);
|
||||
if (s && !strcmp(s, "blocking")) {
|
||||
return PointabilityType::POINTABLE_BLOCKING;
|
||||
}
|
||||
}
|
||||
throw LuaError("Invalid pointable type.");
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
Pointabilities read_pointabilities(lua_State *L, int index)
|
||||
{
|
||||
Pointabilities pointabilities;
|
||||
|
||||
lua_getfield(L, index, "nodes");
|
||||
if(lua_istable(L, -1)){
|
||||
int ti = lua_gettop(L);
|
||||
lua_pushnil(L);
|
||||
while(lua_next(L, ti) != 0) {
|
||||
// key at index -2 and value at index -1
|
||||
std::string name = luaL_checkstring(L, -2);
|
||||
|
||||
// handle groups
|
||||
if(std::string_view(name).substr(0,6)=="group:") {
|
||||
pointabilities.node_groups[name.substr(6)] = read_pointability_type(L, -1);
|
||||
} else {
|
||||
pointabilities.nodes[name] = read_pointability_type(L, -1);
|
||||
}
|
||||
|
||||
// removes value, keeps key for next iteration
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, index, "objects");
|
||||
if(lua_istable(L, -1)){
|
||||
int ti = lua_gettop(L);
|
||||
lua_pushnil(L);
|
||||
while(lua_next(L, ti) != 0) {
|
||||
// key at index -2 and value at index -1
|
||||
std::string name = luaL_checkstring(L, -2);
|
||||
|
||||
// handle groups
|
||||
if(std::string_view(name).substr(0,6)=="group:") {
|
||||
pointabilities.object_groups[name.substr(6)] = read_pointability_type(L, -1);
|
||||
} else {
|
||||
pointabilities.objects[name] = read_pointability_type(L, -1);
|
||||
}
|
||||
|
||||
// removes value, keeps key for next iteration
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
return pointabilities;
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void push_pointability_type(lua_State *L, PointabilityType pointable)
|
||||
{
|
||||
switch(pointable)
|
||||
{
|
||||
case PointabilityType::POINTABLE:
|
||||
lua_pushboolean(L, true);
|
||||
break;
|
||||
case PointabilityType::POINTABLE_NOT:
|
||||
lua_pushboolean(L, false);
|
||||
break;
|
||||
case PointabilityType::POINTABLE_BLOCKING:
|
||||
lua_pushliteral(L, "blocking");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void push_pointabilities(lua_State *L, const Pointabilities &pointabilities)
|
||||
{
|
||||
// pointabilities table
|
||||
lua_newtable(L);
|
||||
|
||||
if (!pointabilities.nodes.empty() || !pointabilities.node_groups.empty()) {
|
||||
// Create and fill table
|
||||
lua_newtable(L);
|
||||
for (const auto &entry : pointabilities.nodes) {
|
||||
push_pointability_type(L, entry.second);
|
||||
lua_setfield(L, -2, entry.first.c_str());
|
||||
}
|
||||
for (const auto &entry : pointabilities.node_groups) {
|
||||
push_pointability_type(L, entry.second);
|
||||
lua_setfield(L, -2, ("group:" + entry.first).c_str());
|
||||
}
|
||||
lua_setfield(L, -2, "nodes");
|
||||
}
|
||||
|
||||
if (!pointabilities.objects.empty() || !pointabilities.object_groups.empty()) {
|
||||
// Create and fill table
|
||||
lua_newtable(L);
|
||||
for (const auto &entry : pointabilities.objects) {
|
||||
push_pointability_type(L, entry.second);
|
||||
lua_setfield(L, -2, entry.first.c_str());
|
||||
}
|
||||
for (const auto &entry : pointabilities.object_groups) {
|
||||
push_pointability_type(L, entry.second);
|
||||
lua_setfield(L, -2, ("group:" + entry.first).c_str());
|
||||
}
|
||||
lua_setfield(L, -2, "objects");
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
void push_dig_params(lua_State *L,const DigParams ¶ms)
|
||||
{
|
||||
@@ -2001,11 +2165,16 @@ void read_hud_element(lua_State *L, HudElement *elem)
|
||||
elem->name = getstringfield_default(L, 2, "name", "");
|
||||
elem->text = getstringfield_default(L, 2, "text", "");
|
||||
elem->number = getintfield_default(L, 2, "number", 0);
|
||||
if (elem->type == HUD_ELEM_WAYPOINT)
|
||||
// waypoints reuse the item field to store precision, item = precision + 1
|
||||
elem->item = getintfield_default(L, 2, "precision", -1) + 1;
|
||||
else
|
||||
if (elem->type == HUD_ELEM_WAYPOINT) {
|
||||
// Waypoints reuse the item field to store precision,
|
||||
// item = precision + 1 and item = 0 <=> precision = 10 for backwards compatibility.
|
||||
int precision = getintfield_default(L, 2, "precision", 10);
|
||||
if (precision < 0)
|
||||
throw LuaError("Waypoint precision must be non-negative");
|
||||
elem->item = precision + 1;
|
||||
} else {
|
||||
elem->item = getintfield_default(L, 2, "item", 0);
|
||||
}
|
||||
elem->dir = getintfield_default(L, 2, "direction", 0);
|
||||
elem->z_index = MYMAX(S16_MIN, MYMIN(S16_MAX,
|
||||
getintfield_default(L, 2, "z_index", 0)));
|
||||
@@ -2057,8 +2226,10 @@ void push_hud_element(lua_State *L, HudElement *elem)
|
||||
lua_setfield(L, -2, "number");
|
||||
|
||||
if (elem->type == HUD_ELEM_WAYPOINT) {
|
||||
// waypoints reuse the item field to store precision, precision = item - 1
|
||||
lua_pushnumber(L, elem->item - 1);
|
||||
// Waypoints reuse the item field to store precision,
|
||||
// item = precision + 1 and item = 0 <=> precision = 10 for backwards compatibility.
|
||||
// See `Hud::drawLuaElements`, case `HUD_ELEM_WAYPOINT`.
|
||||
lua_pushnumber(L, (elem->item == 0) ? 10 : (elem->item - 1));
|
||||
lua_setfield(L, -2, "precision");
|
||||
}
|
||||
// push the item field for waypoints as well for backwards compatibility
|
||||
|
||||
@@ -39,6 +39,7 @@ extern "C" {
|
||||
#include "util/string.h"
|
||||
#include "itemgroup.h"
|
||||
#include "itemdef.h"
|
||||
#include "util/pointabilities.h"
|
||||
#include "c_types.h"
|
||||
// We do an explicit path include because by default c_content.h include src/client/hud.h
|
||||
// prior to the src/hud.h, which is not good on server only build
|
||||
@@ -107,6 +108,11 @@ ItemStack read_item (lua_State *L, int index, IItemDefM
|
||||
|
||||
struct TileAnimationParams read_animation_definition(lua_State *L, int index);
|
||||
|
||||
PointabilityType read_pointability_type (lua_State *L, int index);
|
||||
Pointabilities read_pointabilities (lua_State *L, int index);
|
||||
void push_pointability_type (lua_State *L, PointabilityType pointable);
|
||||
void push_pointabilities (lua_State *L, const Pointabilities &pointabilities);
|
||||
|
||||
ToolCapabilities read_tool_capabilities (lua_State *L, int table);
|
||||
void push_tool_capabilities (lua_State *L,
|
||||
const ToolCapabilities &prop);
|
||||
|
||||
@@ -32,3 +32,10 @@ struct EnumString es_ItemType[] =
|
||||
{ITEM_TOOL, "tool"},
|
||||
{0, NULL},
|
||||
};
|
||||
|
||||
struct EnumString es_TouchInteractionMode[] =
|
||||
{
|
||||
{LONG_DIG_SHORT_PLACE, "long_dig_short_place"},
|
||||
{SHORT_DIG_LONG_PLACE, "short_dig_long_place"},
|
||||
{0, NULL},
|
||||
};
|
||||
|
||||
@@ -59,3 +59,4 @@ public:
|
||||
|
||||
|
||||
extern EnumString es_ItemType[];
|
||||
extern EnumString es_TouchInteractionMode[];
|
||||
|
||||
@@ -188,7 +188,7 @@ int LuaRaycast::create_object(lua_State *L)
|
||||
}
|
||||
|
||||
LuaRaycast *o = new LuaRaycast(core::line3d<f32>(pos1, pos2),
|
||||
objects, liquids);
|
||||
objects, liquids, std::nullopt);
|
||||
|
||||
*(void **) (lua_newuserdata(L, sizeof(void *))) = o;
|
||||
luaL_getmetatable(L, className);
|
||||
|
||||
@@ -336,8 +336,9 @@ public:
|
||||
LuaRaycast(
|
||||
const core::line3d<f32> &shootline,
|
||||
bool objects_pointable,
|
||||
bool liquids_pointable) :
|
||||
state(shootline, objects_pointable, liquids_pointable)
|
||||
bool liquids_pointable,
|
||||
const std::optional<Pointabilities> &pointabilities) :
|
||||
state(shootline, objects_pointable, liquids_pointable, pointabilities)
|
||||
{}
|
||||
|
||||
//! Creates a LuaRaycast and leaves it on top of the stack.
|
||||
|
||||
@@ -1995,15 +1995,21 @@ int ObjectRef::l_set_sky(lua_State *L)
|
||||
if (!lua_isnil(L, -1))
|
||||
sky_params.fog_tint_type = luaL_checkstring(L, -1);
|
||||
lua_pop(L, 1);
|
||||
|
||||
// pop "sky_color" table
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
|
||||
lua_getfield(L, 2, "fog");
|
||||
if (lua_istable(L, -1)) {
|
||||
sky_params.fog_distance = getintfield_default(L, -1, "fog_distance", sky_params.fog_distance);
|
||||
sky_params.fog_start = getfloatfield_default(L, -1, "fog_start", sky_params.fog_start);
|
||||
sky_params.fog_distance = getintfield_default(L, -1,
|
||||
"fog_distance", sky_params.fog_distance);
|
||||
sky_params.fog_start = getfloatfield_default(L, -1,
|
||||
"fog_start", sky_params.fog_start);
|
||||
|
||||
lua_getfield(L, -1, "fog_color");
|
||||
read_color(L, -1, &sky_params.fog_color);
|
||||
lua_pop(L, 1);
|
||||
}
|
||||
lua_pop(L, 1);
|
||||
} else {
|
||||
// Handle old set_sky calls, and log deprecated:
|
||||
log_deprecated(L, "Deprecated call to set_sky, please check lua_api.md");
|
||||
|
||||
130
src/server.cpp
130
src/server.cpp
@@ -137,6 +137,7 @@ void *ServerThread::run()
|
||||
} catch (con::PeerNotFoundException &e) {
|
||||
infostream<<"Server: PeerNotFoundException"<<std::endl;
|
||||
} catch (ClientNotFoundException &e) {
|
||||
infostream<<"Server: ClientNotFoundException"<<std::endl;
|
||||
} catch (con::ConnectionBindFailed &e) {
|
||||
m_server->setAsyncFatalError(e.what());
|
||||
} catch (LuaError &e) {
|
||||
@@ -949,9 +950,7 @@ void Server::AsyncRunStep(float dtime, bool initial_step)
|
||||
}
|
||||
case MEET_OTHER:
|
||||
prof.add("MEET_OTHER", 1);
|
||||
for (const v3s16 &modified_block : event->modified_blocks) {
|
||||
m_clients.markBlockposAsNotSent(modified_block);
|
||||
}
|
||||
m_clients.markBlocksNotSent(event->modified_blocks);
|
||||
break;
|
||||
default:
|
||||
prof.add("unknown", 1);
|
||||
@@ -963,19 +962,9 @@ void Server::AsyncRunStep(float dtime, bool initial_step)
|
||||
/*
|
||||
Set blocks not sent to far players
|
||||
*/
|
||||
if (!far_players.empty()) {
|
||||
// Convert list format to that wanted by SetBlocksNotSent
|
||||
std::map<v3s16, MapBlock*> modified_blocks2;
|
||||
for (const v3s16 &modified_block : event->modified_blocks) {
|
||||
modified_blocks2[modified_block] =
|
||||
m_env->getMap().getBlockNoCreateNoEx(modified_block);
|
||||
}
|
||||
|
||||
// Set blocks not sent
|
||||
for (const u16 far_player : far_players) {
|
||||
if (RemoteClient *client = getClient(far_player))
|
||||
client->SetBlocksNotSent(modified_blocks2);
|
||||
}
|
||||
for (const u16 far_player : far_players) {
|
||||
if (RemoteClient *client = getClient(far_player))
|
||||
client->SetBlocksNotSent(event->modified_blocks);
|
||||
}
|
||||
|
||||
delete event;
|
||||
@@ -1826,22 +1815,21 @@ void Server::SendSetSky(session_t peer_id, const SkyboxParams ¶ms)
|
||||
pkt << params.clouds;
|
||||
} else { // Handle current clients and future clients
|
||||
pkt << params.bgcolor << params.type
|
||||
<< params.clouds << params.fog_sun_tint
|
||||
<< params.fog_moon_tint << params.fog_tint_type;
|
||||
<< params.clouds << params.fog_sun_tint
|
||||
<< params.fog_moon_tint << params.fog_tint_type;
|
||||
|
||||
if (params.type == "skybox") {
|
||||
pkt << (u16) params.textures.size();
|
||||
for (const std::string &texture : params.textures)
|
||||
pkt << texture;
|
||||
} else if (params.type == "regular") {
|
||||
pkt << params.sky_color.day_sky << params.sky_color.day_horizon
|
||||
<< params.sky_color.dawn_sky << params.sky_color.dawn_horizon
|
||||
<< params.sky_color.night_sky << params.sky_color.night_horizon
|
||||
<< params.sky_color.indoors;
|
||||
auto &c = params.sky_color;
|
||||
pkt << c.day_sky << c.day_horizon << c.dawn_sky << c.dawn_horizon
|
||||
<< c.night_sky << c.night_horizon << c.indoors;
|
||||
}
|
||||
|
||||
pkt << params.body_orbit_tilt;
|
||||
pkt << params.fog_distance << params.fog_start;
|
||||
pkt << params.body_orbit_tilt << params.fog_distance << params.fog_start
|
||||
<< params.fog_color;
|
||||
}
|
||||
|
||||
Send(&pkt);
|
||||
@@ -2628,28 +2616,30 @@ void Server::fillMediaCache()
|
||||
|
||||
void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_code)
|
||||
{
|
||||
std::string lang_suffix = ".";
|
||||
lang_suffix.append(lang_code).append(".tr");
|
||||
|
||||
auto include = [&] (const std::string &name, const MediaInfo &info) -> bool {
|
||||
if (info.no_announce)
|
||||
return false;
|
||||
if (str_ends_with(name, ".tr") && !str_ends_with(name, lang_suffix))
|
||||
return false;
|
||||
return true;
|
||||
};
|
||||
|
||||
// Make packet
|
||||
NetworkPacket pkt(TOCLIENT_ANNOUNCE_MEDIA, 0, peer_id);
|
||||
|
||||
u16 media_sent = 0;
|
||||
std::string lang_suffix;
|
||||
lang_suffix.append(".").append(lang_code).append(".tr");
|
||||
for (const auto &i : m_media) {
|
||||
if (i.second.no_announce)
|
||||
continue;
|
||||
if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
|
||||
continue;
|
||||
media_sent++;
|
||||
if (include(i.first, i.second))
|
||||
media_sent++;
|
||||
}
|
||||
|
||||
pkt << media_sent;
|
||||
|
||||
for (const auto &i : m_media) {
|
||||
if (i.second.no_announce)
|
||||
continue;
|
||||
if (str_ends_with(i.first, ".tr") && !str_ends_with(i.first, lang_suffix))
|
||||
continue;
|
||||
pkt << i.first << i.second.sha1_digest;
|
||||
if (include(i.first, i.second))
|
||||
pkt << i.first << i.second.sha1_digest;
|
||||
}
|
||||
|
||||
pkt << g_settings->get("remote_media");
|
||||
@@ -2659,10 +2649,12 @@ void Server::sendMediaAnnouncement(session_t peer_id, const std::string &lang_co
|
||||
<< "): count=" << media_sent << " size=" << pkt.getSize() << std::endl;
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct SendableMedia
|
||||
{
|
||||
std::string name;
|
||||
std::string path;
|
||||
const std::string &name;
|
||||
const std::string &path;
|
||||
std::string data;
|
||||
|
||||
SendableMedia(const std::string &name, const std::string &path,
|
||||
@@ -2671,30 +2663,53 @@ struct SendableMedia
|
||||
{}
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void Server::sendRequestedMedia(session_t peer_id,
|
||||
const std::vector<std::string> &tosend)
|
||||
const std::unordered_set<std::string> &tosend)
|
||||
{
|
||||
verbosestream<<"Server::sendRequestedMedia(): "
|
||||
<<"Sending files to client"<<std::endl;
|
||||
auto *client = getClient(peer_id, CS_DefinitionsSent);
|
||||
assert(client);
|
||||
|
||||
/* Read files */
|
||||
infostream << "Server::sendRequestedMedia(): Sending "
|
||||
<< tosend.size() << " files to " << client->getName() << std::endl;
|
||||
|
||||
// Put 5kB in one bunch (this is not accurate)
|
||||
u32 bytes_per_bunch = 5000;
|
||||
/* Read files and prepare bunches */
|
||||
|
||||
std::vector< std::vector<SendableMedia> > file_bunches;
|
||||
// Put 5KB in one bunch (this is not accurate)
|
||||
// This is a tradeoff between burdening the network with too many packets
|
||||
// and burdening it with too large split packets.
|
||||
const u32 bytes_per_bunch = 5000;
|
||||
|
||||
std::vector<std::vector<SendableMedia>> file_bunches;
|
||||
file_bunches.emplace_back();
|
||||
|
||||
u32 file_size_bunch_total = 0;
|
||||
// Note that applying a "real" bin packing algorithm here is not necessarily
|
||||
// an improvement (might even perform worse) since games usually have lots
|
||||
// of files larger than 5KB and the current algorithm already minimized
|
||||
// the amount of bunches quite well (at the expense of overshooting).
|
||||
|
||||
u32 file_size_bunch_total = 0;
|
||||
for (const std::string &name : tosend) {
|
||||
if (m_media.find(name) == m_media.end()) {
|
||||
auto it = m_media.find(name);
|
||||
|
||||
if (it == m_media.end()) {
|
||||
errorstream<<"Server::sendRequestedMedia(): Client asked for "
|
||||
<<"unknown file \""<<(name)<<"\""<<std::endl;
|
||||
continue;
|
||||
}
|
||||
const auto &m = it->second;
|
||||
|
||||
const auto &m = m_media[name];
|
||||
// no_announce <=> usually ephemeral dynamic media, which may
|
||||
// have duplicate filenames. So we can't check it.
|
||||
if (!m.no_announce) {
|
||||
if (!client->markMediaSent(name)) {
|
||||
infostream << "Server::sendRequestedMedia(): Client asked has "
|
||||
"requested \"" << name << "\" before, not sending it again."
|
||||
<< std::endl;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// Read data
|
||||
std::string data;
|
||||
@@ -2713,16 +2728,15 @@ void Server::sendRequestedMedia(session_t peer_id,
|
||||
file_bunches.emplace_back();
|
||||
file_size_bunch_total = 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* Create and send packets */
|
||||
|
||||
u16 num_bunches = file_bunches.size();
|
||||
const u16 num_bunches = file_bunches.size();
|
||||
for (u16 i = 0; i < num_bunches; i++) {
|
||||
auto &bunch = file_bunches[i];
|
||||
/*
|
||||
u16 command
|
||||
u16 total number of texture bunches
|
||||
u16 total number of media bunches
|
||||
u16 index of this bunch
|
||||
u32 number of files in this bunch
|
||||
for each file {
|
||||
@@ -2732,18 +2746,20 @@ void Server::sendRequestedMedia(session_t peer_id,
|
||||
data
|
||||
}
|
||||
*/
|
||||
|
||||
NetworkPacket pkt(TOCLIENT_MEDIA, 4 + 0, peer_id);
|
||||
pkt << num_bunches << i << (u32) file_bunches[i].size();
|
||||
|
||||
for (const SendableMedia &j : file_bunches[i]) {
|
||||
const u32 bunch_size = bunch.size();
|
||||
pkt << num_bunches << i << bunch_size;
|
||||
|
||||
for (auto &j : bunch) {
|
||||
pkt << j.name;
|
||||
pkt.putLongString(j.data);
|
||||
}
|
||||
bunch.clear(); // free memory early
|
||||
|
||||
verbosestream << "Server::sendRequestedMedia(): bunch "
|
||||
<< i << "/" << num_bunches
|
||||
<< " files=" << file_bunches[i].size()
|
||||
<< " files=" << bunch_size
|
||||
<< " size=" << pkt.getSize() << std::endl;
|
||||
Send(&pkt);
|
||||
}
|
||||
|
||||
@@ -515,7 +515,7 @@ private:
|
||||
void fillMediaCache();
|
||||
void sendMediaAnnouncement(session_t peer_id, const std::string &lang_code);
|
||||
void sendRequestedMedia(session_t peer_id,
|
||||
const std::vector<std::string> &tosend);
|
||||
const std::unordered_set<std::string> &tosend);
|
||||
void stepPendingDynMediaCallbacks(float dtime);
|
||||
|
||||
// Adds a ParticleSpawner on peer with peer_id (PEER_ID_INEXISTENT == all)
|
||||
|
||||
@@ -245,7 +245,7 @@ std::string LuaEntitySAO::getClientInitializationData(u16 protocol_version)
|
||||
|
||||
// PROTOCOL_VERSION >= 37
|
||||
writeU8(os, 1); // version
|
||||
os << serializeString16(""); // name
|
||||
os << serializeString16(m_init_name); // name
|
||||
writeU8(os, 0); // is_player
|
||||
writeU16(os, getId()); //id
|
||||
writeV3F32(os, m_base_position);
|
||||
@@ -553,7 +553,7 @@ bool LuaEntitySAO::getCollisionBox(aabb3f *toset) const
|
||||
|
||||
bool LuaEntitySAO::getSelectionBox(aabb3f *toset) const
|
||||
{
|
||||
if (!m_prop.is_visible || !m_prop.pointable) {
|
||||
if (!m_prop.is_visible) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ PlayerSAO::PlayerSAO(ServerEnvironment *env_, RemotePlayer *player_, session_t p
|
||||
m_prop.physical = false;
|
||||
m_prop.collisionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
|
||||
m_prop.selectionbox = aabb3f(-0.3f, 0.0f, -0.3f, 0.3f, 1.77f, 0.3f);
|
||||
m_prop.pointable = true;
|
||||
m_prop.pointable = PointabilityType::POINTABLE;
|
||||
// Start of default appearance, this should be overwritten by Lua
|
||||
m_prop.visual = "upright_sprite";
|
||||
m_prop.visual_size = v3f(1, 2, 1);
|
||||
@@ -724,7 +724,7 @@ bool PlayerSAO::getCollisionBox(aabb3f *toset) const
|
||||
|
||||
bool PlayerSAO::getSelectionBox(aabb3f *toset) const
|
||||
{
|
||||
if (!m_prop.is_visible || !m_prop.pointable) {
|
||||
if (!m_prop.is_visible) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -1830,7 +1830,8 @@ bool ServerEnvironment::getActiveObjectMessage(ActiveObjectMessage *dest)
|
||||
|
||||
void ServerEnvironment::getSelectedActiveObjects(
|
||||
const core::line3d<f32> &shootline_on_map,
|
||||
std::vector<PointedThing> &objects)
|
||||
std::vector<PointedThing> &objects,
|
||||
const std::optional<Pointabilities> &pointabilities)
|
||||
{
|
||||
std::vector<ServerActiveObject *> objs;
|
||||
getObjectsInsideRadius(objs, shootline_on_map.start,
|
||||
@@ -1863,10 +1864,26 @@ void ServerEnvironment::getSelectedActiveObjects(
|
||||
current_raw_normal = current_normal;
|
||||
}
|
||||
if (collision) {
|
||||
current_intersection += pos;
|
||||
objects.emplace_back(
|
||||
(s16) obj->getId(), current_intersection, current_normal, current_raw_normal,
|
||||
(current_intersection - shootline_on_map.start).getLengthSQ());
|
||||
PointabilityType pointable;
|
||||
if (pointabilities) {
|
||||
if (LuaEntitySAO* lsao = dynamic_cast<LuaEntitySAO*>(obj)) {
|
||||
pointable = pointabilities->matchObject(lsao->getName(),
|
||||
usao->getArmorGroups()).value_or(props->pointable);
|
||||
} else if (PlayerSAO* psao = dynamic_cast<PlayerSAO*>(obj)) {
|
||||
pointable = pointabilities->matchPlayer(psao->getArmorGroups()).value_or(
|
||||
props->pointable);
|
||||
} else {
|
||||
pointable = props->pointable;
|
||||
}
|
||||
} else {
|
||||
pointable = props->pointable;
|
||||
}
|
||||
if (pointable != PointabilityType::POINTABLE_NOT) {
|
||||
current_intersection += pos;
|
||||
objects.emplace_back(
|
||||
(s16) obj->getId(), current_intersection, current_normal, current_raw_normal,
|
||||
(current_intersection - shootline_on_map.start).getLengthSQ(), pointable);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,7 +315,8 @@ public:
|
||||
|
||||
virtual void getSelectedActiveObjects(
|
||||
const core::line3d<f32> &shootline_on_map,
|
||||
std::vector<PointedThing> &objects
|
||||
std::vector<PointedThing> &objects,
|
||||
const std::optional<Pointabilities> &pointabilities
|
||||
);
|
||||
|
||||
/*
|
||||
|
||||
@@ -46,7 +46,7 @@ struct SkyboxParams
|
||||
float body_orbit_tilt { INVALID_SKYBOX_TILT };
|
||||
s16 fog_distance { -1 };
|
||||
float fog_start { -1.0f };
|
||||
float volumetric_light_strength { 0.0f };
|
||||
video::SColor fog_color; // override, only used if alpha > 0
|
||||
};
|
||||
|
||||
struct SunParams
|
||||
@@ -102,6 +102,7 @@ public:
|
||||
sky.fog_sun_tint = video::SColor(255, 244, 125, 29);
|
||||
sky.fog_moon_tint = video::SColorf(0.5, 0.6, 0.8, 1).toSColor();
|
||||
sky.fog_tint_type = "default";
|
||||
sky.fog_color = video::SColor(0);
|
||||
return sky;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ set (UNITTEST_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_compression.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_connection.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_craft.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_datastructures.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_filesys.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_inventory.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/test_irrptr.cpp
|
||||
|
||||
181
src/unittest/test_datastructures.cpp
Normal file
181
src/unittest/test_datastructures.cpp
Normal file
@@ -0,0 +1,181 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2024 sfan5
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU Lesser General Public License as published by
|
||||
the Free Software Foundation; either version 2.1 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public License along
|
||||
with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
*/
|
||||
|
||||
#include "test.h"
|
||||
|
||||
#include "util/container.h"
|
||||
|
||||
class TestDataStructures : public TestBase
|
||||
{
|
||||
public:
|
||||
TestDataStructures() { TestManager::registerTestModule(this); }
|
||||
const char *getName() { return "TestDataStructures"; }
|
||||
|
||||
void runTests(IGameDef *gamedef);
|
||||
|
||||
void testMap1();
|
||||
void testMap2();
|
||||
void testMap3();
|
||||
void testMap4();
|
||||
void testMap5();
|
||||
};
|
||||
|
||||
static TestDataStructures g_test_instance;
|
||||
|
||||
void TestDataStructures::runTests(IGameDef *gamedef)
|
||||
{
|
||||
rawstream << "-------- ModifySafeMap" << std::endl;
|
||||
TEST(testMap1);
|
||||
TEST(testMap2);
|
||||
TEST(testMap3);
|
||||
TEST(testMap4);
|
||||
TEST(testMap5);
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
struct TrackerState {
|
||||
bool copied = false;
|
||||
bool deleted = false;
|
||||
};
|
||||
|
||||
class Tracker {
|
||||
TrackerState *res = nullptr;
|
||||
|
||||
inline void trackDeletion() { res && (res->deleted = true); }
|
||||
|
||||
public:
|
||||
Tracker() {}
|
||||
Tracker(TrackerState &res) : res(&res) {}
|
||||
|
||||
operator bool() const { return !!res; }
|
||||
|
||||
Tracker(const Tracker &other) { *this = other; }
|
||||
Tracker &operator=(const Tracker &other) {
|
||||
trackDeletion();
|
||||
res = other.res;
|
||||
res && (res->copied = true);
|
||||
return *this;
|
||||
}
|
||||
Tracker(Tracker &&other) { *this = std::move(other); }
|
||||
Tracker &operator=(Tracker &&other) {
|
||||
trackDeletion();
|
||||
res = other.res;
|
||||
other.res = nullptr;
|
||||
return *this;
|
||||
}
|
||||
|
||||
~Tracker() { trackDeletion(); }
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
void TestDataStructures::testMap1()
|
||||
{
|
||||
ModifySafeMap<int, Tracker> map;
|
||||
TrackerState t0, t1;
|
||||
|
||||
// no-copy put
|
||||
map.put(1, Tracker(t0));
|
||||
UASSERT(!t0.copied);
|
||||
UASSERT(!t0.deleted);
|
||||
|
||||
// overwrite during iter
|
||||
bool once = false;
|
||||
for (auto &it : map.iter()) {
|
||||
(void)it;
|
||||
map.put(1, Tracker(t1));
|
||||
UASSERT(t0.deleted);
|
||||
UASSERT(!t1.copied);
|
||||
UASSERT(!t1.deleted);
|
||||
if (once |= 1)
|
||||
break;
|
||||
}
|
||||
UASSERT(once);
|
||||
}
|
||||
|
||||
void TestDataStructures::testMap2()
|
||||
{
|
||||
ModifySafeMap<int, Tracker> map;
|
||||
TrackerState t0, t1;
|
||||
|
||||
// overwrite
|
||||
map.put(1, Tracker(t0));
|
||||
map.put(1, Tracker(t1));
|
||||
UASSERT(t0.deleted);
|
||||
UASSERT(!t1.copied);
|
||||
UASSERT(!t1.deleted);
|
||||
}
|
||||
|
||||
void TestDataStructures::testMap3()
|
||||
{
|
||||
ModifySafeMap<int, Tracker> map;
|
||||
TrackerState t0, t1;
|
||||
|
||||
// take
|
||||
map.put(1, Tracker(t0));
|
||||
{
|
||||
auto v = map.take(1);
|
||||
UASSERT(!t0.copied);
|
||||
UASSERT(!t0.deleted);
|
||||
}
|
||||
UASSERT(t0.deleted);
|
||||
|
||||
// remove during iter
|
||||
map.put(1, Tracker(t1));
|
||||
for (auto &it : map.iter()) {
|
||||
(void)it;
|
||||
map.remove(1);
|
||||
UASSERT(t1.deleted);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void TestDataStructures::testMap4()
|
||||
{
|
||||
ModifySafeMap<int, u32> map;
|
||||
|
||||
// overwrite + take during iter
|
||||
map.put(1, 100);
|
||||
for (auto &it : map.iter()) {
|
||||
(void)it;
|
||||
map.put(1, 200);
|
||||
u32 taken = map.take(1);
|
||||
UASSERTEQ(u32, taken, 200);
|
||||
break;
|
||||
}
|
||||
|
||||
UASSERT(map.get(1) == u32());
|
||||
UASSERTEQ(size_t, map.size(), 0);
|
||||
}
|
||||
|
||||
void TestDataStructures::testMap5()
|
||||
{
|
||||
ModifySafeMap<int, u32> map;
|
||||
|
||||
// overwrite 2x during iter
|
||||
map.put(9001, 9001);
|
||||
for (auto &it : map.iter()) {
|
||||
(void)it;
|
||||
map.put(1, 100);
|
||||
map.put(1, 200);
|
||||
UASSERTEQ(u32, map.get(1), 200);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ set(UTIL_SRCS
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/metricsbackend.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/numeric.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/pointedthing.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/pointabilities.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/quicktune.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/serialize.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/sha1.cpp
|
||||
|
||||
@@ -376,10 +376,16 @@ public:
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
if (m_iterating)
|
||||
m_new.emplace(key, value);
|
||||
else
|
||||
m_values.emplace(key, value);
|
||||
if (m_iterating) {
|
||||
auto it = m_values.find(key);
|
||||
if (it != m_values.end()) {
|
||||
it->second = V();
|
||||
m_garbage++;
|
||||
}
|
||||
m_new[key] = value;
|
||||
} else {
|
||||
m_values[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
void put(const K &key, V &&value) {
|
||||
@@ -387,10 +393,16 @@ public:
|
||||
assert(false);
|
||||
return;
|
||||
}
|
||||
if (m_iterating)
|
||||
m_new.emplace(key, std::move(value));
|
||||
else
|
||||
m_values.emplace(key, std::move(value));
|
||||
if (m_iterating) {
|
||||
auto it = m_values.find(key);
|
||||
if (it != m_values.end()) {
|
||||
it->second = V();
|
||||
m_garbage++;
|
||||
}
|
||||
m_new[key] = std::move(value);
|
||||
} else {
|
||||
m_values[key] = std::move(value);
|
||||
}
|
||||
}
|
||||
|
||||
V take(const K &key) {
|
||||
@@ -405,7 +417,8 @@ public:
|
||||
auto it = m_values.find(key);
|
||||
if (it == m_values.end())
|
||||
return ret;
|
||||
ret = std::move(it->second);
|
||||
if (!ret)
|
||||
ret = std::move(it->second);
|
||||
if (m_iterating) {
|
||||
it->second = V();
|
||||
m_garbage++;
|
||||
@@ -516,7 +529,8 @@ private:
|
||||
std::map<K, V> m_values;
|
||||
std::map<K, V> m_new;
|
||||
unsigned int m_iterating = 0;
|
||||
size_t m_garbage = 0; // upper bound for null-placeholders in m_values
|
||||
// approximate amount of null-placeholders in m_values, reliable for != 0 tests
|
||||
size_t m_garbage = 0;
|
||||
|
||||
static constexpr size_t GC_MIN_SIZE = 30;
|
||||
};
|
||||
|
||||
147
src/util/pointabilities.cpp
Normal file
147
src/util/pointabilities.cpp
Normal file
@@ -0,0 +1,147 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2023 cx384
|
||||
|
||||
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 "pointabilities.h"
|
||||
|
||||
#include "serialize.h"
|
||||
#include "exceptions.h"
|
||||
#include <sstream>
|
||||
|
||||
PointabilityType Pointabilities::deSerializePointabilityType(std::istream &is)
|
||||
{
|
||||
PointabilityType pointable_type = static_cast<PointabilityType>(readU8(is));
|
||||
switch(pointable_type) {
|
||||
case PointabilityType::POINTABLE:
|
||||
case PointabilityType::POINTABLE_NOT:
|
||||
case PointabilityType::POINTABLE_BLOCKING:
|
||||
break;
|
||||
default:
|
||||
// Default to POINTABLE in case of unknown PointabilityType type.
|
||||
pointable_type = PointabilityType::POINTABLE;
|
||||
break;
|
||||
}
|
||||
return pointable_type;
|
||||
}
|
||||
|
||||
void Pointabilities::serializePointabilityType(std::ostream &os, PointabilityType pointable_type)
|
||||
{
|
||||
writeU8(os, static_cast<u8>(pointable_type));
|
||||
}
|
||||
|
||||
std::string Pointabilities::toStringPointabilityType(PointabilityType pointable_type)
|
||||
{
|
||||
switch(pointable_type) {
|
||||
case PointabilityType::POINTABLE:
|
||||
return "true";
|
||||
case PointabilityType::POINTABLE_NOT:
|
||||
return "false";
|
||||
case PointabilityType::POINTABLE_BLOCKING:
|
||||
return "\"blocking\"";
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
std::optional<PointabilityType> Pointabilities::matchNode(const std::string &name,
|
||||
const ItemGroupList &groups) const
|
||||
{
|
||||
auto i = nodes.find(name);
|
||||
return i == nodes.end() ? matchGroups(groups, node_groups) : i->second;
|
||||
}
|
||||
|
||||
std::optional<PointabilityType> Pointabilities::matchObject(const std::string &name,
|
||||
const ItemGroupList &groups) const
|
||||
{
|
||||
auto i = objects.find(name);
|
||||
return i == objects.end() ? matchGroups(groups, object_groups) : i->second;
|
||||
}
|
||||
|
||||
std::optional<PointabilityType> Pointabilities::matchPlayer(const ItemGroupList &groups) const
|
||||
{
|
||||
return matchGroups(groups, object_groups);
|
||||
}
|
||||
|
||||
std::optional<PointabilityType> Pointabilities::matchGroups(const ItemGroupList &groups,
|
||||
const std::unordered_map<std::string, PointabilityType> &pointable_groups)
|
||||
{
|
||||
// prefers POINTABLE over POINTABLE_NOT over POINTABLE_BLOCKING
|
||||
bool blocking = false;
|
||||
bool not_pointable = false;
|
||||
for (auto const &ability : pointable_groups) {
|
||||
if (itemgroup_get(groups, ability.first) > 0) {
|
||||
switch(ability.second) {
|
||||
case PointabilityType::POINTABLE:
|
||||
return PointabilityType::POINTABLE;
|
||||
case PointabilityType::POINTABLE_NOT:
|
||||
not_pointable = true;
|
||||
break;
|
||||
default:
|
||||
blocking = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (not_pointable)
|
||||
return PointabilityType::POINTABLE_NOT;
|
||||
if (blocking)
|
||||
return PointabilityType::POINTABLE_BLOCKING;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void Pointabilities::serializeTypeMap(std::ostream &os,
|
||||
const std::unordered_map<std::string, PointabilityType> &map)
|
||||
{
|
||||
writeU32(os, map.size());
|
||||
for (const auto &entry : map) {
|
||||
os << serializeString16(entry.first);
|
||||
writeU8(os, (u8)entry.second);
|
||||
}
|
||||
}
|
||||
|
||||
void Pointabilities::deSerializeTypeMap(std::istream &is,
|
||||
std::unordered_map<std::string, PointabilityType> &map)
|
||||
{
|
||||
map.clear();
|
||||
u32 size = readU32(is);
|
||||
for (u32 i = 0; i < size; i++) {
|
||||
std::string name = deSerializeString16(is);
|
||||
PointabilityType type = Pointabilities::deSerializePointabilityType(is);
|
||||
map[name] = type;
|
||||
}
|
||||
}
|
||||
|
||||
void Pointabilities::serialize(std::ostream &os) const
|
||||
{
|
||||
writeU8(os, 0); // version
|
||||
serializeTypeMap(os, nodes);
|
||||
serializeTypeMap(os, node_groups);
|
||||
serializeTypeMap(os, objects);
|
||||
serializeTypeMap(os, object_groups);
|
||||
}
|
||||
|
||||
void Pointabilities::deSerialize(std::istream &is)
|
||||
{
|
||||
int version = readU8(is);
|
||||
if (version != 0)
|
||||
throw SerializationError("unsupported Pointabilities version");
|
||||
|
||||
deSerializeTypeMap(is, nodes);
|
||||
deSerializeTypeMap(is, node_groups);
|
||||
deSerializeTypeMap(is, objects);
|
||||
deSerializeTypeMap(is, object_groups);
|
||||
}
|
||||
79
src/util/pointabilities.h
Normal file
79
src/util/pointabilities.h
Normal file
@@ -0,0 +1,79 @@
|
||||
/*
|
||||
Minetest
|
||||
Copyright (C) 2023 cx384
|
||||
|
||||
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 <string>
|
||||
#include <unordered_map>
|
||||
#include "itemgroup.h"
|
||||
#include <optional>
|
||||
#include "irrlichttypes.h"
|
||||
|
||||
enum class PointabilityType : u8
|
||||
{
|
||||
// Can be pointed through.
|
||||
// Note: This MUST be the 0-th item in the enum for backwards compat.
|
||||
// Older Minetest versions send "pointable=false" as "0".
|
||||
POINTABLE_NOT,
|
||||
// Is pointable.
|
||||
// Note: This MUST be the 1-th item in the enum for backwards compat:
|
||||
// Older Minetest versions send "pointable=true" as "1".
|
||||
POINTABLE,
|
||||
// Note: Since (u8) 2 is truthy,
|
||||
// older clients will understand this as "pointable=true",
|
||||
// which is a reasonable fallback.
|
||||
POINTABLE_BLOCKING,
|
||||
};
|
||||
|
||||
// An object to store overridden pointable properties
|
||||
struct Pointabilities
|
||||
{
|
||||
// Nodes
|
||||
std::unordered_map<std::string, PointabilityType> nodes;
|
||||
std::unordered_map<std::string, PointabilityType> node_groups;
|
||||
|
||||
// Objects
|
||||
std::unordered_map<std::string, PointabilityType> objects;
|
||||
std::unordered_map<std::string, PointabilityType> object_groups; // armor_groups
|
||||
|
||||
// Match functions return fitting pointability,
|
||||
// otherwise the default pointability should be used.
|
||||
|
||||
std::optional<PointabilityType> matchNode(const std::string &name,
|
||||
const ItemGroupList &groups) const;
|
||||
std::optional<PointabilityType> matchObject(const std::string &name,
|
||||
const ItemGroupList &groups) const;
|
||||
// For players only armor groups will work
|
||||
std::optional<PointabilityType> matchPlayer(const ItemGroupList &groups) const;
|
||||
|
||||
void serialize(std::ostream &os) const;
|
||||
void deSerialize(std::istream &is);
|
||||
|
||||
// For a save enum conversion.
|
||||
static PointabilityType deSerializePointabilityType(std::istream &is);
|
||||
static void serializePointabilityType(std::ostream &os, PointabilityType pointable_type);
|
||||
static std::string toStringPointabilityType(PointabilityType pointable_type);
|
||||
|
||||
private:
|
||||
static std::optional<PointabilityType> matchGroups(const ItemGroupList &groups,
|
||||
const std::unordered_map<std::string, PointabilityType> &pointable_groups);
|
||||
static void serializeTypeMap(std::ostream &os,
|
||||
const std::unordered_map<std::string, PointabilityType> &map);
|
||||
static void deSerializeTypeMap(std::istream &is,
|
||||
std::unordered_map<std::string, PointabilityType> &map);
|
||||
};
|
||||
@@ -25,7 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
|
||||
PointedThing::PointedThing(const v3s16 &under, const v3s16 &above,
|
||||
const v3s16 &real_under, const v3f &point, const v3f &normal,
|
||||
u16 box_id, f32 distSq):
|
||||
u16 box_id, f32 distSq, PointabilityType pointab):
|
||||
type(POINTEDTHING_NODE),
|
||||
node_undersurface(under),
|
||||
node_abovesurface(above),
|
||||
@@ -33,17 +33,19 @@ PointedThing::PointedThing(const v3s16 &under, const v3s16 &above,
|
||||
intersection_point(point),
|
||||
intersection_normal(normal),
|
||||
box_id(box_id),
|
||||
distanceSq(distSq)
|
||||
distanceSq(distSq),
|
||||
pointability(pointab)
|
||||
{}
|
||||
|
||||
PointedThing::PointedThing(u16 id, const v3f &point,
|
||||
const v3f &normal, const v3f &raw_normal, f32 distSq) :
|
||||
PointedThing::PointedThing(u16 id, const v3f &point, const v3f &normal,
|
||||
const v3f &raw_normal, f32 distSq, PointabilityType pointab) :
|
||||
type(POINTEDTHING_OBJECT),
|
||||
object_id(id),
|
||||
intersection_point(point),
|
||||
intersection_normal(normal),
|
||||
raw_intersection_normal(raw_normal),
|
||||
distanceSq(distSq)
|
||||
distanceSq(distSq),
|
||||
pointability(pointab)
|
||||
{}
|
||||
|
||||
std::string PointedThing::dump() const
|
||||
@@ -118,12 +120,13 @@ bool PointedThing::operator==(const PointedThing &pt2) const
|
||||
{
|
||||
if ((node_undersurface != pt2.node_undersurface)
|
||||
|| (node_abovesurface != pt2.node_abovesurface)
|
||||
|| (node_real_undersurface != pt2.node_real_undersurface))
|
||||
|| (node_real_undersurface != pt2.node_real_undersurface)
|
||||
|| (pointability != pt2.pointability))
|
||||
return false;
|
||||
}
|
||||
else if (type == POINTEDTHING_OBJECT)
|
||||
{
|
||||
if (object_id != pt2.object_id)
|
||||
if (object_id != pt2.object_id || pointability != pt2.pointability)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -23,8 +23,9 @@ with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
#include "irr_v3d.h"
|
||||
#include <iostream>
|
||||
#include <string>
|
||||
#include "pointabilities.h"
|
||||
|
||||
enum PointedThingType : u8
|
||||
enum PointedThingType :u8
|
||||
{
|
||||
POINTEDTHING_NOTHING,
|
||||
POINTEDTHING_NODE,
|
||||
@@ -90,15 +91,20 @@ struct PointedThing
|
||||
* ray's start point and the intersection point in irrlicht coordinates.
|
||||
*/
|
||||
f32 distanceSq = 0;
|
||||
/*!
|
||||
* How the object or node has been pointed at.
|
||||
*/
|
||||
PointabilityType pointability = PointabilityType::POINTABLE_NOT;
|
||||
|
||||
//! Constructor for POINTEDTHING_NOTHING
|
||||
PointedThing() = default;
|
||||
//! Constructor for POINTEDTHING_NODE
|
||||
PointedThing(const v3s16 &under, const v3s16 &above,
|
||||
const v3s16 &real_under, const v3f &point, const v3f &normal,
|
||||
u16 box_id, f32 distSq);
|
||||
u16 box_id, f32 distSq, PointabilityType pointability);
|
||||
//! Constructor for POINTEDTHING_OBJECT
|
||||
PointedThing(u16 id, const v3f &point, const v3f &normal, const v3f &raw_normal, f32 distSq);
|
||||
PointedThing(u16 id, const v3f &point, const v3f &normal, const v3f &raw_normal, f32 distSq,
|
||||
PointabilityType pointability);
|
||||
std::string dump() const;
|
||||
void serialize(std::ostream &os) const;
|
||||
void deSerialize(std::istream &is);
|
||||
|
||||
Reference in New Issue
Block a user